Zusätzliche Adressen importieren

Ich habe bei version 22.04 und 23.0 versucht zusätzliche Adressen zu importieren. Diese werden immer nur unter Sonstige neu angelegt. Versucht habe ich das zu Anfang über die E-Mail in denen ich die gleiche Nummer hatte und dann den Schlüssel zum Aktualiesieren auf E-Mail gelegt habe.

Dann habe ich die rowid’s exportiert und in einer Spalte angehängt. Beim Import diese Spalte als ID angewählt und den Schlüssel darauf gelegt. Versucht habe ich das mit einer .xlsx und einer .csv Datei.
Hat da jemand eine Idee ?

Hallo Walter,

hast du versucht Kontakte zu vorhandenen Geschäftspartnern hinzuzufügen?
Ich würde gerne helfen aber den Ablauf, den du beschreibst, kann ich nicht nachvollziehen.

Kannst du bitte Schritt für Schritt beschreiben was du gemacht hast. Dann finden wir auch sicherlich eine Lösung.

VG,
Kim

Hallo Kim
Ich habe 2 Dateien angelegt , eine mit den Kundenaddessen und eine mit den zusätzlichen Addressen. Die erste habe ich als Geschäftspartner importiert. Die zweite als Kontakte/Adressen. Das Problem besteht darin das ich ja die zusätzlichen Addressen mit den Ursprungsaddressen verknüpfen muß. Dabei habe ich die Wahl zwischen ID E-Mail Kundennamen. Egal was ich versuche die zusätzlichen Adressen landen immer unter Kontakte-Adressen-Sonstige und werden nicht mit den Bestandskontakten verknüpft. Die Identischen Referenznummern habe ich natürlich benutzt. Selbst mit der rowid habe ich das probiert.

Hallo Walter,
das verknüpfende Felde ist “s.fk_soc” welches der Name des Geschäftspartners ist.
Diesen musst du nochmal bei deinen zusätzlichen Kontakten mit aufführen. Ausserdem bitte
darauf achten, dass “Nachname” (s.lastname) ein Pflichtfeld ist und immer einen Inhalt haben muss.

Hoffe das hilft,
Kim

Hallo Walter,

so wie Du es beschreibst, hast Du ein ähnliches Problem wie ich: Du hast Geschäftspartner angelegt und möchtest die Adressen der zweiten Datei als Kontakte für den jeweiligen Geschäftspartner anlegen?

Bei mir ist es so, dass meine Kundendaten je eine Rechnungs- und eine Lieferadresse enthalten. Mit der Rechnungsadresse und E-Mail usw. erzeuge ich den Geschäftspartnereintrag (sozusagen den Haupteintrag).

Kim hatte mir seine Idee mit je einem “Untereintrag pro Verwendungszweck” (= Kontakt) vorgestellt, und das gefällt mir sehr gut. Ich erzeuge daher pro Geschäftspartner zwei Kontakte: (nochmal) einen für die Rechnungsadresse und einen für die Lieferadresse. In dem Feld “Standardkontakt/-Adresse für” (das sieht man erst, wenn man einen Kontakt angelegt hat und dann auf “Ändern” geht - k.A. warum das erst dann sichtbar ist) trage ich dann (natürlich automatisch) jeweils die entsprechende Funktion ein (also Rechnungsadresse oder Lieferadresse). Diese (soll) dann dazu führen, dass bei der späteren Dokumentenerstellung (Rechnung/Lieferschein) für diesen Geschäftspartner je nach Dokument die entsprechende Adresse automatisch verwendet wird. Soweit bin ich aber noch nicht.

Immerhin läuft jetzt mein Konvertierungsskript soweit sauber. Ich gehe also nicht über die offizielle CSV-Import-Funktion sondern mache es direkt.

Wenn es für Dich von Interesse ist und Du Dich etwas in PHP auskennst, dann kann ich mein Skript mal hier einstellen. Da habe ich noch ein paar weitere Dinge eingebaut (Konvertierung von vierstelligen Kundennummern auf fünfstellige mit führenden Nullen usw.) - aber das lässt sich ja rausnehmen.

Viele Grüße,

Christoph

1 „Gefällt mir“

Hallo Kim, Hallo Christoph
Vielen Dank für eure Hilfe.
Der Schlüssel ist beim Adressen- und Kontaktimport ist wie Kim schon schrieb der Name des Geschäftspartners fk_soc. Dieser muß immer mit angegeben werden.
@ Christoph
Gerne kannst du das Script hier einstellen.

Viele Grüße,

Walter

Hallo Walter,

super, dass es geklappt hat.

Hier mal mein Skript. Mittlerweile habe ich das aber doch einigermaßen “aufgebohrt”. Als Goodie wird nun zumindest für unsere deutschen Kunden auch noch das passende Bundesland aus der Plz ermittelt und eingetragen - ist ganz nett :slight_smile:

Es ist auch immer noch nicht ganz fertig (das Feld “Sonstiges” meiner alten Kartei wird bspw. noch nicht übernommen), aber mein altes System läuft ja auch noch und wenn dann alles bei Dolibarr eingerichtet ist, dann wird migriert.

Natürlich ist es auch nicht optimiert - aber meine knapp 9000 Kunden wurden in knapp einer Minute übernommen.

Meine CSV hat folgende Spalten:
„Kd.-Nr.“,„R-Anrede“,„R-Vorname“,„R-Nachname“,„R-Unternehmen“,„R-Anschrift 1“,„R-Anschrift 2“,„R-Anschrift 3“,„R-Plz“,„R-Ort“,„R-Land“,„L-Anrede“,„L-Vorname“,„L-Nachname“,„L-Unternehmen“,„L-Anschrift 1“,„L-Anschrift 2“,„L-Anschrift 3“,„L-Plz“,„L-Ort“,„L-Land“,„E-Mail“,„Telefon“,„Telefax“,„USt.-IDNr.“,„Gutschriften“,„Werbung“,„Rechnungen“,„Sonstiges“

Ich hoffe, soweit reichen die Kommentare. Bei Fragen: fragen!

Und auch Verbesserungen und Korrekturen werden gerne gesehen :wink:

Viele Grüße,
Christoph

<?php

// Nur CLI erlauben
if (php_sapi_name() !== 'cli')
{
    die("Nur CLI erlaubt\n");
}

define('NOLOGIN', 1);
define('NOCSRFCHECK', 1);
define('NOREQUIREMENU', 1);
define('NOREQUIREHTML', 1);
define('NOIPCHECK', 1);

require __DIR__ . '/../dolibarr/htdocs/main.inc.php';

require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';

global $db, $user;

// Admin-User laden (wichtig für create/delete Rechte)
$user = new User($db);
$user->fetch(1);

//$file = fopen('kundenadressen-test.csv', 'r');
$file = fopen('kundenadressen et-faktura.csv', 'r');

if (!$file)
{
    die("CSV nicht gefunden\n");
}

// Header lesen (Komma-getrennt!)
$header = fgetcsv($file, 0, ',');

print "Starte Import...\n";


// Hole die Typenummern für die Standardkontakteinträge für Rechnung und Lieferadresse
$typeBilling = 0;
$typeShipping = 0;
$sql = "SELECT rowid, code
        FROM ".MAIN_DB_PREFIX."c_type_contact
        WHERE element='facture'
        AND source='external'
        AND code IN ('BILLING','SHIPPING')";
$res = $db->query($sql);

while ($obj = $db->fetch_object($res))
{
    if ($obj->code === 'BILLING') {
        $typeBilling = $obj->rowid;
    }
    if ($obj->code === 'SHIPPING') {
        $typeShipping = $obj->rowid;
    }
}

// Debug
echo "\nRechnung: " . $typeBilling . "\n";
echo "Lieferung: " . $typeShipping . "\n\n";

$plzMap = [
'01'=>'Sachsen',
'02'=>'Sachsen',
'03'=>'Brandenburg',
'04'=>'Sachsen',
'05'=>'Nordrhein-Westfalen',
'06'=>'Sachsen-Anhalt',
'07'=>'Thüringen',
'08'=>'Sachsen',
'09'=>'Sachsen',

'10'=>'Berlin',
'11'=>'Berlin',

'12'=>'Brandenburg',
'13'=>'Brandenburg',
'14'=>'Brandenburg',
'15'=>'Brandenburg',
'16'=>'Brandenburg',

'17'=>'Mecklenburg-Vorpommern',
'18'=>'Mecklenburg-Vorpommern',
'19'=>'Mecklenburg-Vorpommern',

'20'=>'Hamburg',
'21'=>'Hamburg',
'22'=>'Hamburg',

'23'=>'Schleswig-Holstein',
'24'=>'Schleswig-Holstein',
'25'=>'Schleswig-Holstein',

'26'=>'Niedersachsen',
'27'=>'Niedersachsen',

'28'=>'Bremen',

'29'=>'Niedersachsen',
'30'=>'Niedersachsen',
'31'=>'Niedersachsen',

'32'=>'Nordrhein-Westfalen',
'33'=>'Nordrhein-Westfalen',

'34'=>'Hessen',
'35'=>'Hessen',
'36'=>'Hessen',

'37'=>'Niedersachsen',
'38'=>'Niedersachsen',

'39'=>'Sachsen-Anhalt',

'40'=>'Nordrhein-Westfalen',
'41'=>'Nordrhein-Westfalen',
'42'=>'Nordrhein-Westfalen',
'43'=>'Nordrhein-Westfalen',
'44'=>'Nordrhein-Westfalen',
'45'=>'Nordrhein-Westfalen',
'46'=>'Nordrhein-Westfalen',
'47'=>'Nordrhein-Westfalen',
'48'=>'Nordrhein-Westfalen',

'49'=>'Niedersachsen',

'50'=>'Nordrhein-Westfalen',
'51'=>'Nordrhein-Westfalen',
'52'=>'Nordrhein-Westfalen',

'53'=>'Nordrhein-Westfalen',

'54'=>'Rheinland-Pfalz',

'55'=>'Rheinland-Pfalz',
'56'=>'Rheinland-Pfalz',
'57'=>'Nordrhein-Westfalen',
'58'=>'Nordrhein-Westfalen',
'59'=>'Nordrhein-Westfalen',

'60'=>'Hessen',
'61'=>'Hessen',
'62'=>'Hessen',
'63'=>'Hessen',
'64'=>'Hessen',
'65'=>'Hessen',

'66'=>'Saarland',

'67'=>'Rheinland-Pfalz',
'68'=>'Baden-Württemberg',
'69'=>'Baden-Württemberg',

'70'=>'Baden-Württemberg',
'71'=>'Baden-Württemberg',
'72'=>'Baden-Württemberg',
'73'=>'Baden-Württemberg',
'74'=>'Baden-Württemberg',
'75'=>'Baden-Württemberg',
'76'=>'Baden-Württemberg',
'77'=>'Baden-Württemberg',
'78'=>'Baden-Württemberg',
'79'=>'Baden-Württemberg',

'80'=>'Bayern',
'81'=>'Bayern',
'82'=>'Bayern',
'83'=>'Bayern',
'84'=>'Bayern',
'85'=>'Bayern',
'86'=>'Bayern',
'87'=>'Bayern',
'88'=>'Baden-Württemberg',

'89'=>'Bayern',

'90'=>'Bayern',
'91'=>'Bayern',
'92'=>'Bayern',
'93'=>'Bayern',
'94'=>'Bayern',
'95'=>'Bayern',
'96'=>'Bayern',
'97'=>'Bayern',
'98'=>'Bayern',

'99'=>'Thüringen'
];

// -----------------------------
// Bundesland aus PLZ ermitteln (nur für DE)
// -----------------------------
function getStateIdFromZip($zip, $country_code, $plzMap, $db)
{
    if (strtoupper($country_code) !== 'DE') return 0; // nur Deutschland

    $state_name = '';
    $zipPrefix = substr($zip, 0, 2); // nur die ersten 2 Ziffern
    if (isset($plzMap[$zipPrefix])) {
        $state_name = $plzMap[$zipPrefix];
    }

    if ($state_name) {
        $sql = "SELECT rowid
                FROM ".MAIN_DB_PREFIX."c_departements 
                WHERE LOWER(nom) LIKE LOWER('%".$db->escape($state_name)."%') 
                LIMIT 1";
        $res = $db->query($sql);

        if ($res) {
            if ($obj = $db->fetch_object($res)) {
                return (int)$obj->rowid;
            }
        } else {
            // Abfrage fehlgeschlagen – Debugausgabe
            dol_syslog("getStateIdFromZip SQL-Fehler: ".$db->lasterror()." SQL=".$sql, LOG_ERR);
        }
    }
    return 0; // fallback
}

// Mapping CSV Anrede → Dolibarr Code
$anredeMapping = [
    'Herr' => 'MR',
    'Mr.'  => 'MR',
    'Frau' => 'MME',
    'Mrs.' => 'MME'
];

// Für Tests eine maximale Anzahl an Datensätzen einlesen
$max_data = 100;
$nr_of_data = 0;

while (($data = fgetcsv($file, 0, ',')) !== false)
{
    // Anzahl der Testdatensätze erreicht?
    $nr_of_data ++;
    if ($nr_of_data >= $max_data) break;
    
    $row = array_combine($header, $data);

    $db->begin();

    try {

        // -----------------------------
        // Kundennummer 5-stellig
        // -----------------------------
        $code_client = trim($row['Kd.-Nr.'] ?? '');

        if (ctype_digit($code_client)) {
            $code_client = sprintf('%05d', $code_client);
        }


        // -----------------------------
        // Name bestimmen
        // -----------------------------
        $firma    = trim($row['R-Unternehmen'] ?? '');
        $vorname  = trim($row['R-Vorname'] ?? '');
        $nachname = trim($row['R-Nachname'] ?? '');

        // Anrede bei Rechnungsadresse
        $r_anrede = trim($row['R-Anrede'] ?? '');
        $civilityCodeBilling = $anredeMapping[$r_anrede] ?? '';

        // Anrede bei Lieferadresse
        $l_anrede = trim($row['L-Anrede'] ?? '');
        $civilityCodeShipping = $anredeMapping[$l_anrede] ?? '';

        // Wenn Kein Namen angegeben war, dann setze id eFirmenadresse in den Kontaktnamen/-bezeichnung
        // Dann gibt es auch keine Anrede
        if ($vorname === '' && $nachname === '' && $firma !== '')
        {
            $nachname = $firma;
            $civilityCodeBilling = '';
            $civilityCodeShipping = '';
        }

        $name = ($firma !== '') ? $firma : trim($vorname . ' ' . $nachname);

        // -----------------------------
        // Rechnungsadresse zusammenbauen
        // -----------------------------
        $addressParts = [];

        foreach (['R-Anschrift 1','R-Anschrift 2','R-Anschrift 3'] as $field)
        {
            $val = trim($row[$field] ?? '');
            if ($val !== '') {
                $addressParts[] = $val;
            }
        }

        $address = implode("\n", $addressParts);

        $zip     = trim($row['R-Plz'] ?? '');
        $town    = trim($row['R-Ort'] ?? '');
        $country = trim($row['R-Land'] ?? 'DE');

        $email = trim($row['E-Mail'] ?? '');
        $phone = trim($row['Telefon'] ?? '');
        $fax   = trim($row['Telefax'] ?? '');
        $vat   = trim($row['USt.-IDNr.'] ?? '');

        // -----------------------------
        // Firma anlegen
        // -----------------------------
        $soc = new Societe($db);

        $soc->name         = $name;
        $soc->code_client  = $code_client;
        $soc->address      = $address;
        $soc->zip          = $zip;
        $soc->town         = $town;
        $soc->country_code = $country;
        $soc->email        = $email;
        $soc->phone        = $phone;
        $soc->fax          = $fax;
        $soc->tva_intra    = $vat;
        $soc->state_id     = getStateIdFromZip($zip, $country, $plzMap, $db); // nur DE
        $soc->client       = 1;   // Kunde
        $soc->fournisseur  = 0;   // Kein Lieferant
        $soc->status       = 1;

        $result = $soc->create($user);

        // Debug
        //if ($code_client == "08696") echo ">>>>>>>>>>"; 
        //echo " Kundennr.: " . $code_client . " " . $soc->state_id . "\n";

        if ($result <= 0) {
            throw new Exception($soc->error);
        }

        $socid = $soc->id;

        // -----------------------------
        // Vertriebsmitarbeiter ENTFERNEN
        // -----------------------------
        $db->query("DELETE FROM ".MAIN_DB_PREFIX."societe_commerciaux WHERE fk_soc = ".((int)$socid));

        // -----------------------------
        // Hauptkontakt = Rechnungsadresse
        // -----------------------------
        
        $billing = new Contact($db);
        $billing->socid = $socid;
        $billing->firstname = $vorname;
        $billing->lastname  = $nachname;
        //$billing->poste     = 'Rechnungsadresse';
        $billing->address  = $address;
        $billing->zip      = $zip;
        $billing->town     = $town;
        $billing->country_id    = getCountry($country, '3');  // ISO2 aus CSV → interne ID
        $billing->email    = $email;
        $billing->phone_pro = $phone;
        $billing->status = 1;
        $billing->civility_code  = $civilityCodeBilling;
        $billing->state_id  = getStateIdFromZip($zip, $country, $plzMap, $db);  // nur DE
        
        // Nach Anlegen setze den Standardkontakttyp auf Rechnung
        if ($billing->create($user) > 0)
        {
            $db->query("
                INSERT INTO ".MAIN_DB_PREFIX."societe_contacts
                (entity, date_creation, fk_soc, fk_c_type_contact, fk_socpeople)
                VALUES
                (1, NOW(), ".$socid.", ".$typeBilling.", ".$billing->id.")
            ");
        }

        $billing->update($billing->id, $user);  // Erst jetzt wird country_code gespeichert (bei create nicht!)
        
        // -----------------------------
        // Lieferadresse
        // -----------------------------

        $shipParts = [];

        foreach (['L-Anschrift 1','L-Anschrift 2','L-Anschrift 3'] as $field)
        {
            $val = trim($row[$field] ?? '');
            if ($val !== '') {
                $shipParts[] = $val;
            }
        }

        $shippingAddress = implode("\n", $shipParts);
        if ($shippingAddress === '') {
            $shippingAddress = $address;
        }

        $shipping = new Contact($db);
        $shipping->socid        = $socid;
        $shipping->firstname    = $vorname;
        $shipping->lastname     = $nachname;
        //$shipping->poste        = 'Lieferadresse';
        $shipping->address      = $shippingAddress;
        $shipping->zip          = trim($row['L-Plz'] ?? $zip);
        $shipping->town         = trim($row['L-Ort'] ?? $town);
        $l_country = trim($row['L-Land'] ?? $country);
        $shipping->country_id   = getCountry($l_country, '3');
        $shipping->email        = $email;
        $shipping->phone_pro    = $phone;
        $shipping->status = 1;
        $shipping->civility_code = $civilityCodeShipping;
        $shipping->state_id = getStateIdFromZip($shipping->zip, $l_country, $plzMap, $db);

        // Nach Anlegen setze den Standardkontakttyp auf Rechnung
        if ($shipping->create($user) > 0)
        {
            $db->query("
                INSERT INTO ".MAIN_DB_PREFIX."societe_contacts
                (entity, date_creation, fk_soc, fk_c_type_contact, fk_socpeople)
                VALUES
                (1, NOW(), ".$socid.", ".$typeShipping.", ".$shipping->id.")
            ");
        }

        $shipping->update($shipping->id, $user);  // Erst jetzt wird country_code gespeichert (bei create nicht!)
        
        $db->commit();

        print "Importiert: $code_client\n";

    }
    catch (Exception $e)
    {
        $db->rollback();
        print "Fehler bei ".$row['Kd.-Nr.'].": ".$e->getMessage()."\n";
    }
}

fclose($file);

print "Fertig.\n";

Hallo Christoph
ich hatte mir die Adressen in LibreCalc teils mit Macros angepaßt. Diese Datei ist einfacher editiertbar und ich laufe keine Gefahr mir die Datenbank zu versauen.

Dein Script werde ich aber mal testen.

Gruß

Walter

Hallo Walter,

Ja, der Import über CSV ist im Zweifel immer der sicherere Weg.

Leider fehlen bei der Importfunktion einige Werte/Atrribute beim Mapping, so dass das für mich nicht ausreichte.

Aber ich habe eh noch einen längeren Weg vor mir. Ich muss noch ein passendes Modul für die Anbindung an meinen Webshop bauen, ELSTER-Ausgabe, ebenso eine Ausgabe für den Packplatz, den Etikettendruck per DHL-API - und natürlich die Ausgabe von ZUGFeRD-Rechnungen.

Viele spannende Sachen :wink: