v1.2: Fix name-Substitution, neuer fullname-Platzhalter, Code-Refactoring
- Fix: {*_contact_name} liefert jetzt Nachname statt Vorname
- Neu: {*_contact_fullname} (Vorname + Nachname, MAIN_FIRSTNAME_NAME_POSITION)
- Refactoring: eine JOIN-Query statt N+1, Mapping-Array, Elementtyp-Check
- Dokumentation aktualisiert
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
af72845f5c
commit
e8694aa0a4
3 changed files with 85 additions and 129 deletions
|
|
@ -1,5 +1,12 @@
|
|||
# CHANGELOG MODULE DELIVERYINVOICEADDRESS FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
|
||||
|
||||
1.1
|
||||
## 1.2
|
||||
|
||||
- Fix: `{*_contact_name}` liefert jetzt Nachname statt Vorname
|
||||
- Neu: `{*_contact_fullname}` Platzhalter (Vorname + Nachname, berücksichtigt MAIN_FIRSTNAME_NAME_POSITION)
|
||||
- Refactoring: Kontaktdaten per JOIN in einer Query statt N+1 Queries
|
||||
- Code aufgeräumt: Mapping-Array statt if/elseif, Null-Coalescing, weniger Logging
|
||||
|
||||
## 1.1
|
||||
|
||||
Initial version
|
||||
|
|
|
|||
32
README.md
32
README.md
|
|
@ -8,7 +8,7 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
|
|||
- Lieferadresse für Versanddokumente
|
||||
- Serviceadresse für Servicedokumente
|
||||
- Automatische Erkennung des Dokumenttyps (Angebot/Auftrag/Rechnung)
|
||||
- Fallback auf Kundenadresse wenn kein spezieller Kontakt vorhanden
|
||||
- CUSTOMER-Kontakt als Fallback für BILLING bei Angeboten/Aufträgen
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -30,7 +30,8 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
|
|||
| Tag | Beschreibung |
|
||||
|-----|--------------|
|
||||
| `{billing_contact_firstname}` | Vorname |
|
||||
| `{billing_contact_name}` | Vorname (Fallback: Nachname) |
|
||||
| `{billing_contact_name}` | Nachname |
|
||||
| `{billing_contact_fullname}` | Vorname + Nachname (berücksichtigt `MAIN_FIRSTNAME_NAME_POSITION`) |
|
||||
| `{billing_contact_address}` | Straße und Hausnummer |
|
||||
| `{billing_contact_zip}` | Postleitzahl |
|
||||
| `{billing_contact_town}` | Ort |
|
||||
|
|
@ -41,7 +42,8 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
|
|||
| Tag | Beschreibung |
|
||||
|-----|--------------|
|
||||
| `{delivery_contact_firstname}` | Vorname |
|
||||
| `{delivery_contact_name}` | Vorname (Fallback: Nachname) |
|
||||
| `{delivery_contact_name}` | Nachname |
|
||||
| `{delivery_contact_fullname}` | Vorname + Nachname |
|
||||
| `{delivery_contact_address}` | Straße und Hausnummer |
|
||||
| `{delivery_contact_zip}` | Postleitzahl |
|
||||
| `{delivery_contact_town}` | Ort |
|
||||
|
|
@ -52,7 +54,8 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
|
|||
| Tag | Beschreibung |
|
||||
|-----|--------------|
|
||||
| `{service_contact_firstname}` | Vorname |
|
||||
| `{service_contact_name}` | Vorname (Fallback: Nachname) |
|
||||
| `{service_contact_name}` | Nachname |
|
||||
| `{service_contact_fullname}` | Vorname + Nachname |
|
||||
| `{service_contact_address}` | Straße und Hausnummer |
|
||||
| `{service_contact_zip}` | Postleitzahl |
|
||||
| `{service_contact_town}` | Ort |
|
||||
|
|
@ -63,22 +66,22 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
|
|||
### Beispiel: Rechnungsadresse mit Fallback auf Kundenadresse
|
||||
|
||||
```
|
||||
[!-- IF {billing_contact_name} --]
|
||||
{billing_contact_name}
|
||||
[!-- IF {billing_contact_fullname} --]
|
||||
{billing_contact_fullname}
|
||||
{billing_contact_address}
|
||||
{billing_contact_zip} {billing_contact_town}
|
||||
[!-- ELSE {billing_contact_name} --]
|
||||
[!-- ELSE {billing_contact_fullname} --]
|
||||
{company_name}
|
||||
{company_address}
|
||||
{company_zip} {company_town}
|
||||
[!-- ENDIF {billing_contact_name} --]
|
||||
[!-- ENDIF {billing_contact_fullname} --]
|
||||
```
|
||||
|
||||
### Beispiel: Zwei Adressen nebeneinander
|
||||
|
||||
```
|
||||
Rechnungsadresse: Lieferadresse:
|
||||
{billing_contact_name} {delivery_contact_name}
|
||||
{billing_contact_fullname} {delivery_contact_fullname}
|
||||
{billing_contact_address} {delivery_contact_address}
|
||||
{billing_contact_zip} {billing_contact_town} {delivery_contact_zip} {delivery_contact_town}
|
||||
```
|
||||
|
|
@ -92,16 +95,6 @@ Rechnungsadresse: Lieferadresse:
|
|||
- **Kundenkontakt für Lieferung** (SHIPPING) → `delivery_contact_*`
|
||||
- **Kundenkontakt für Service** (SERVICE) → `service_contact_*`
|
||||
|
||||
## Debugging
|
||||
|
||||
Bei Problemen Log-Level auf DEBUG setzen:
|
||||
|
||||
1. Home → Setup → Logs → Syslog aktivieren
|
||||
2. Dokument generieren
|
||||
3. Log prüfen (z.B. `dolibarr.log`)
|
||||
|
||||
Relevante Log-Einträge beginnen mit: `DeliveryInvoiceAddress:`
|
||||
|
||||
## Technische Details
|
||||
|
||||
### Datenbankstruktur
|
||||
|
|
@ -111,6 +104,7 @@ Relevante Log-Einträge beginnen mit: `DeliveryInvoiceAddress:`
|
|||
| `llx_element_contact` | Verknüpfung Dokument ↔ Kontakt |
|
||||
| `llx_c_type_contact` | Kontakttyp-Definitionen |
|
||||
| `llx_socpeople` | Kontaktdaten |
|
||||
| `llx_c_country` | Länderbezeichnung |
|
||||
|
||||
### Hauptfunktion
|
||||
|
||||
|
|
|
|||
|
|
@ -1,140 +1,95 @@
|
|||
<?php
|
||||
|
||||
function deliveryinvoiceaddress_completesubstitutionarray(&$substitutionarray,$langs,$object)
|
||||
/**
|
||||
* Substitution-Funktion für Kontaktadressen in Dokumenten.
|
||||
*
|
||||
* Unterstützt Angebote (propal), Aufträge (commande) und Rechnungen (facture).
|
||||
* Stellt Platzhalter für Rechnungs-, Service- und Lieferkontakte bereit:
|
||||
* {billing_contact_firstname}, {billing_contact_name}, {billing_contact_fullname},
|
||||
* {billing_contact_address}, {billing_contact_zip}, {billing_contact_town}, {billing_contact_country}
|
||||
* (analog für service_contact_* und delivery_contact_*)
|
||||
*
|
||||
* @param array $substitutionarray Referenz auf das Substitution-Array
|
||||
* @param Translate $langs Sprachobjekt
|
||||
* @param Object $object Dolibarr-Objekt (Facture, Propal, Commande)
|
||||
*/
|
||||
function deliveryinvoiceaddress_completesubstitutionarray(&$substitutionarray, $langs, $object)
|
||||
{
|
||||
global $conf,$db;
|
||||
global $conf, $db;
|
||||
|
||||
// Prüfe ob $object existiert und eine gültige ID hat
|
||||
if (!is_object($object) || empty($object->id)) {
|
||||
dol_syslog("DeliveryInvoiceAddress: Object ist null oder hat keine ID - überspringe", LOG_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
dol_syslog("DeliveryInvoiceAddress: START - Object class: ".get_class($object).", ID: ".$object->id, LOG_DEBUG);
|
||||
|
||||
// Bestimme den Element-Typ basierend auf dem Objekt
|
||||
$elementType = isset($object->element) ? $object->element : '';
|
||||
|
||||
// Element-Typ bestimmen (facture, propal, commande)
|
||||
$elementType = $object->element ?? '';
|
||||
if (empty($elementType)) {
|
||||
dol_syslog("DeliveryInvoiceAddress: Kein Element-Typ gefunden - überspringe", LOG_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
dol_syslog("DeliveryInvoiceAddress: Element-Typ: ".$elementType, LOG_DEBUG);
|
||||
// Platzhalter-Prefixe je Kontakttyp
|
||||
$codeToPrefix = array(
|
||||
'BILLING' => 'billing_contact',
|
||||
'CUSTOMER' => 'billing_contact', // Fallback für Angebote/Aufträge
|
||||
'SERVICE' => 'service_contact',
|
||||
'SHIPPING' => 'delivery_contact',
|
||||
);
|
||||
|
||||
// Initialisiere alle Felder
|
||||
$substitutionarray['billing_contact_firstname'] = '';
|
||||
$substitutionarray['billing_contact_name'] = '';
|
||||
$substitutionarray['billing_contact_address'] = '';
|
||||
$substitutionarray['billing_contact_zip'] = '';
|
||||
$substitutionarray['billing_contact_town'] = '';
|
||||
$substitutionarray['billing_contact_country'] = '';
|
||||
// Felder pro Kontakt
|
||||
$fields = array('firstname', 'name', 'fullname', 'address', 'zip', 'town', 'country');
|
||||
|
||||
$substitutionarray['service_contact_firstname'] = '';
|
||||
$substitutionarray['service_contact_name'] = '';
|
||||
$substitutionarray['service_contact_address'] = '';
|
||||
$substitutionarray['service_contact_zip'] = '';
|
||||
$substitutionarray['service_contact_town'] = '';
|
||||
$substitutionarray['service_contact_country'] = '';
|
||||
// Alle Platzhalter leer initialisieren
|
||||
foreach (array('billing_contact', 'service_contact', 'delivery_contact') as $prefix) {
|
||||
foreach ($fields as $field) {
|
||||
$substitutionarray[$prefix.'_'.$field] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$substitutionarray['delivery_contact_firstname'] = '';
|
||||
$substitutionarray['delivery_contact_name'] = '';
|
||||
$substitutionarray['delivery_contact_address'] = '';
|
||||
$substitutionarray['delivery_contact_zip'] = '';
|
||||
$substitutionarray['delivery_contact_town'] = '';
|
||||
$substitutionarray['delivery_contact_country'] = '';
|
||||
|
||||
// Lade Kontakte direkt aus der DB - mit korrektem element-Typ!
|
||||
$sql = "SELECT ec.rowid, ec.fk_socpeople, tc.code";
|
||||
// Kontaktdaten per JOIN direkt laden
|
||||
$sql = "SELECT tc.code, sp.rowid, sp.firstname, sp.lastname, sp.address, sp.zip, sp.town,";
|
||||
$sql .= " co.label as country";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX."element_contact as ec";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_type_contact as tc ON ec.fk_c_type_contact = tc.rowid";
|
||||
$sql .= " JOIN ".MAIN_DB_PREFIX."c_type_contact as tc ON ec.fk_c_type_contact = tc.rowid";
|
||||
$sql .= " JOIN ".MAIN_DB_PREFIX."socpeople as sp ON ec.fk_socpeople = sp.rowid";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as co ON sp.fk_pays = co.rowid";
|
||||
$sql .= " WHERE ec.element_id = ".((int) $object->id);
|
||||
$sql .= " AND tc.element = '".$db->escape($elementType)."'";
|
||||
$sql .= " AND tc.code IN ('BILLING', 'SERVICE', 'SHIPPING', 'CUSTOMER')";
|
||||
|
||||
dol_syslog("DeliveryInvoiceAddress: SQL: ".$sql, LOG_DEBUG);
|
||||
|
||||
$resql = $db->query($sql);
|
||||
if (!$resql) {
|
||||
dol_syslog("DeliveryInvoiceAddress: SQL ERROR: ".$db->lasterror(), LOG_ERR);
|
||||
dol_syslog("DeliveryInvoiceAddress: SQL FEHLER: ".$db->lasterror(), LOG_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
$num = $db->num_rows($resql);
|
||||
dol_syslog("DeliveryInvoiceAddress: ".$num." Kontakte gefunden", LOG_DEBUG);
|
||||
|
||||
if ($num == 0) {
|
||||
dol_syslog("DeliveryInvoiceAddress: Keine Kontakte gefunden - ENDE", LOG_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
|
||||
|
||||
while ($obj = $db->fetch_object($resql)) {
|
||||
dol_syslog("DeliveryInvoiceAddress: Verarbeite Kontakt - ID: ".$obj->fk_socpeople.", Code: ".$obj->code, LOG_DEBUG);
|
||||
|
||||
$contactobj = new Contact($db);
|
||||
$result = $contactobj->fetch($obj->fk_socpeople);
|
||||
|
||||
if ($result > 0) {
|
||||
$firstname = isset($contactobj->firstname) ? $contactobj->firstname : '';
|
||||
$lastname = isset($contactobj->lastname) ? $contactobj->lastname : '';
|
||||
$address = isset($contactobj->address) ? $contactobj->address : '';
|
||||
$zip = isset($contactobj->zip) ? $contactobj->zip : '';
|
||||
$town = isset($contactobj->town) ? $contactobj->town : '';
|
||||
$country = isset($contactobj->country) ? $contactobj->country : '';
|
||||
|
||||
// Name-Logik: Vorname falls vorhanden, sonst Nachname
|
||||
$name = !empty($firstname) ? $firstname : $lastname;
|
||||
|
||||
dol_syslog("DeliveryInvoiceAddress: Kontakt geladen - Vorname: ".$firstname.", Nachname: ".$lastname.", Code: ".$obj->code, LOG_DEBUG);
|
||||
|
||||
// BILLING und CUSTOMER werden beide als Rechnungskontakt behandelt
|
||||
// BILLING hat Priorität, CUSTOMER nur wenn BILLING noch leer ist
|
||||
if ($obj->code == 'BILLING') {
|
||||
$substitutionarray['billing_contact_firstname'] = $firstname;
|
||||
$substitutionarray['billing_contact_name'] = $name;
|
||||
$substitutionarray['billing_contact_address'] = $address;
|
||||
$substitutionarray['billing_contact_zip'] = $zip;
|
||||
$substitutionarray['billing_contact_town'] = $town;
|
||||
$substitutionarray['billing_contact_country'] = $country;
|
||||
dol_syslog("DeliveryInvoiceAddress: BILLING gesetzt: ".$name." - ".$address, LOG_DEBUG);
|
||||
$prefix = $codeToPrefix[$obj->code] ?? null;
|
||||
if (!$prefix) {
|
||||
continue;
|
||||
}
|
||||
elseif ($obj->code == 'CUSTOMER' && empty($substitutionarray['billing_contact_name'])) {
|
||||
// CUSTOMER als Fallback für billing_contact (bei Angeboten/Aufträgen)
|
||||
$substitutionarray['billing_contact_firstname'] = $firstname;
|
||||
$substitutionarray['billing_contact_name'] = $name;
|
||||
$substitutionarray['billing_contact_address'] = $address;
|
||||
$substitutionarray['billing_contact_zip'] = $zip;
|
||||
$substitutionarray['billing_contact_town'] = $town;
|
||||
$substitutionarray['billing_contact_country'] = $country;
|
||||
dol_syslog("DeliveryInvoiceAddress: CUSTOMER als BILLING gesetzt: ".$name." - ".$address, LOG_DEBUG);
|
||||
|
||||
// CUSTOMER nur als Fallback wenn BILLING noch leer
|
||||
if ($obj->code == 'CUSTOMER' && !empty($substitutionarray['billing_contact_name'])) {
|
||||
continue;
|
||||
}
|
||||
elseif ($obj->code == 'SERVICE') {
|
||||
$substitutionarray['service_contact_firstname'] = $firstname;
|
||||
$substitutionarray['service_contact_name'] = $name;
|
||||
$substitutionarray['service_contact_address'] = $address;
|
||||
$substitutionarray['service_contact_zip'] = $zip;
|
||||
$substitutionarray['service_contact_town'] = $town;
|
||||
$substitutionarray['service_contact_country'] = $country;
|
||||
dol_syslog("DeliveryInvoiceAddress: SERVICE gesetzt: ".$name." - ".$address, LOG_DEBUG);
|
||||
}
|
||||
elseif ($obj->code == 'SHIPPING') {
|
||||
$substitutionarray['delivery_contact_firstname'] = $firstname;
|
||||
$substitutionarray['delivery_contact_name'] = $name;
|
||||
$substitutionarray['delivery_contact_address'] = $address;
|
||||
$substitutionarray['delivery_contact_zip'] = $zip;
|
||||
$substitutionarray['delivery_contact_town'] = $town;
|
||||
$substitutionarray['delivery_contact_country'] = $country;
|
||||
dol_syslog("DeliveryInvoiceAddress: SHIPPING gesetzt: ".$name." - ".$address, LOG_DEBUG);
|
||||
}
|
||||
} else {
|
||||
dol_syslog("DeliveryInvoiceAddress: FEHLER beim Laden von Kontakt ID ".$obj->fk_socpeople, LOG_ERR);
|
||||
|
||||
$firstname = $obj->firstname ?? '';
|
||||
$lastname = $obj->lastname ?? '';
|
||||
|
||||
// Fullname: Vorname + Nachname, Reihenfolge je nach Dolibarr-Einstellung
|
||||
$fullname = trim($firstname.' '.$lastname);
|
||||
if (getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {
|
||||
$fullname = trim($lastname.' '.$firstname);
|
||||
}
|
||||
|
||||
$substitutionarray[$prefix.'_firstname'] = $firstname;
|
||||
$substitutionarray[$prefix.'_name'] = $lastname;
|
||||
$substitutionarray[$prefix.'_fullname'] = $fullname;
|
||||
$substitutionarray[$prefix.'_address'] = $obj->address ?? '';
|
||||
$substitutionarray[$prefix.'_zip'] = $obj->zip ?? '';
|
||||
$substitutionarray[$prefix.'_town'] = $obj->town ?? '';
|
||||
$substitutionarray[$prefix.'_country'] = $obj->country ?? '';
|
||||
}
|
||||
|
||||
$db->free($resql);
|
||||
|
||||
dol_syslog("DeliveryInvoiceAddress: ENDE - Alle Substitutionen gesetzt", LOG_DEBUG);
|
||||
}
|
||||
?>
|
||||
|
|
|
|||
Loading…
Reference in a new issue