v1.3: E-Mail-Platzhalter, Fallback-Tags für Vorname/Nachname

- Alle Platzhalter auch als __KEY__-Format für E-Mail-Vorlagen
- billing_or_thirdparty_firstname/name mit Kundenname-Fallback
- Dokumentation aktualisiert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-02-23 18:37:50 +01:00
parent e8694aa0a4
commit 632dc0d177
4 changed files with 127 additions and 53 deletions

View file

@ -1,5 +1,11 @@
# CHANGELOG MODULE DELIVERYINVOICEADDRESS FOR [DOLIBARR ERP CRM](https://www.dolibarr.org) # CHANGELOG MODULE DELIVERYINVOICEADDRESS FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
## 1.3
- Neu: Alle Platzhalter auch als `__KEY__`-Format für E-Mail-Vorlagen
- Neu: `billing_or_thirdparty` Fallback-Platzhalter (Rechnungskontakt oder Kundenname)
- Neu: `billing_or_thirdparty_firstname` und `billing_or_thirdparty_name` mit Fallback
## 1.2 ## 1.2
- Fix: `{*_contact_name}` liefert jetzt Nachname statt Vorname - Fix: `{*_contact_name}` liefert jetzt Nachname statt Vorname

View file

@ -1,6 +1,6 @@
# DeliveryInvoiceAddress # DeliveryInvoiceAddress
Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT-Vorlagen. Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT-Vorlagen und E-Mail-Templates.
## Funktionen ## Funktionen
@ -9,6 +9,7 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
- Serviceadresse für Servicedokumente - Serviceadresse für Servicedokumente
- Automatische Erkennung des Dokumenttyps (Angebot/Auftrag/Rechnung) - Automatische Erkennung des Dokumenttyps (Angebot/Auftrag/Rechnung)
- CUSTOMER-Kontakt als Fallback für BILLING bei Angeboten/Aufträgen - CUSTOMER-Kontakt als Fallback für BILLING bei Angeboten/Aufträgen
- Fallback-Platzhalter: Rechnungskontakt oder automatisch Kundenname
## Installation ## Installation
@ -23,58 +24,66 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
| Auftrag (Commande) | `commande` | BILLING, CUSTOMER, SERVICE, SHIPPING | | Auftrag (Commande) | `commande` | BILLING, CUSTOMER, SERVICE, SHIPPING |
| Rechnung (Facture) | `facture` | BILLING, SERVICE, SHIPPING | | Rechnung (Facture) | `facture` | BILLING, SERVICE, SHIPPING |
## Verfügbare ODT-Tags ## Verfügbare Platzhalter
Alle Platzhalter sind in zwei Formaten verfügbar:
- **ODT-Vorlagen**: `{key}` (z.B. `{billing_contact_fullname}`)
- **E-Mail-Vorlagen**: `__KEY__` (z.B. `__BILLING_CONTACT_FULLNAME__`)
### Rechnungsadresse (Billing) ### Rechnungsadresse (Billing)
| Tag | Beschreibung | | ODT-Tag | E-Mail-Tag | Beschreibung |
|-----|--------------| |---------|------------|--------------|
| `{billing_contact_firstname}` | Vorname | | `{billing_contact_firstname}` | `__BILLING_CONTACT_FIRSTNAME__` | Vorname |
| `{billing_contact_name}` | Nachname | | `{billing_contact_name}` | `__BILLING_CONTACT_NAME__` | Nachname |
| `{billing_contact_fullname}` | Vorname + Nachname (berücksichtigt `MAIN_FIRSTNAME_NAME_POSITION`) | | `{billing_contact_fullname}` | `__BILLING_CONTACT_FULLNAME__` | Vorname + Nachname |
| `{billing_contact_address}` | Straße und Hausnummer | | `{billing_contact_address}` | `__BILLING_CONTACT_ADDRESS__` | Straße und Hausnummer |
| `{billing_contact_zip}` | Postleitzahl | | `{billing_contact_zip}` | `__BILLING_CONTACT_ZIP__` | Postleitzahl |
| `{billing_contact_town}` | Ort | | `{billing_contact_town}` | `__BILLING_CONTACT_TOWN__` | Ort |
| `{billing_contact_country}` | Land | | `{billing_contact_country}` | `__BILLING_CONTACT_COUNTRY__` | Land |
### Lieferadresse (Delivery/Shipping) ### Lieferadresse (Delivery/Shipping)
| Tag | Beschreibung | | ODT-Tag | E-Mail-Tag | Beschreibung |
|-----|--------------| |---------|------------|--------------|
| `{delivery_contact_firstname}` | Vorname | | `{delivery_contact_firstname}` | `__DELIVERY_CONTACT_FIRSTNAME__` | Vorname |
| `{delivery_contact_name}` | Nachname | | `{delivery_contact_name}` | `__DELIVERY_CONTACT_NAME__` | Nachname |
| `{delivery_contact_fullname}` | Vorname + Nachname | | `{delivery_contact_fullname}` | `__DELIVERY_CONTACT_FULLNAME__` | Vorname + Nachname |
| `{delivery_contact_address}` | Straße und Hausnummer | | `{delivery_contact_address}` | `__DELIVERY_CONTACT_ADDRESS__` | Straße und Hausnummer |
| `{delivery_contact_zip}` | Postleitzahl | | `{delivery_contact_zip}` | `__DELIVERY_CONTACT_ZIP__` | Postleitzahl |
| `{delivery_contact_town}` | Ort | | `{delivery_contact_town}` | `__DELIVERY_CONTACT_TOWN__` | Ort |
| `{delivery_contact_country}` | Land | | `{delivery_contact_country}` | `__DELIVERY_CONTACT_COUNTRY__` | Land |
### Serviceadresse (Service) ### Serviceadresse (Service)
| Tag | Beschreibung | | ODT-Tag | E-Mail-Tag | Beschreibung |
|-----|--------------| |---------|------------|--------------|
| `{service_contact_firstname}` | Vorname | | `{service_contact_firstname}` | `__SERVICE_CONTACT_FIRSTNAME__` | Vorname |
| `{service_contact_name}` | Nachname | | `{service_contact_name}` | `__SERVICE_CONTACT_NAME__` | Nachname |
| `{service_contact_fullname}` | Vorname + Nachname | | `{service_contact_fullname}` | `__SERVICE_CONTACT_FULLNAME__` | Vorname + Nachname |
| `{service_contact_address}` | Straße und Hausnummer | | `{service_contact_address}` | `__SERVICE_CONTACT_ADDRESS__` | Straße und Hausnummer |
| `{service_contact_zip}` | Postleitzahl | | `{service_contact_zip}` | `__SERVICE_CONTACT_ZIP__` | Postleitzahl |
| `{service_contact_town}` | Ort | | `{service_contact_town}` | `__SERVICE_CONTACT_TOWN__` | Ort |
| `{service_contact_country}` | Land | | `{service_contact_country}` | `__SERVICE_CONTACT_COUNTRY__` | Land |
### Fallback-Platzhalter (Rechnungskontakt ODER Kundenname)
Zeigt den Rechnungskontakt an, wenn zugeordnet. Sonst automatisch den Kundennamen.
| ODT-Tag | E-Mail-Tag | Beschreibung |
|---------|------------|--------------|
| `{billing_or_thirdparty}` | `__BILLING_OR_THIRDPARTY__` | Fullname oder Kundenname |
| `{billing_or_thirdparty_firstname}` | `__BILLING_OR_THIRDPARTY_FIRSTNAME__` | Vorname (leer bei Fallback) |
| `{billing_or_thirdparty_name}` | `__BILLING_OR_THIRDPARTY_NAME__` | Nachname oder Kundenname |
## Verwendung in ODT-Vorlagen ## Verwendung in ODT-Vorlagen
### Beispiel: Rechnungsadresse mit Fallback auf Kundenadresse ### Beispiel: Automatischer Fallback (empfohlen)
``` ```
[!-- IF {billing_contact_fullname} --] {billing_or_thirdparty}
{billing_contact_fullname}
{billing_contact_address} {billing_contact_address}
{billing_contact_zip} {billing_contact_town} {billing_contact_zip} {billing_contact_town}
[!-- ELSE {billing_contact_fullname} --]
{company_name}
{company_address}
{company_zip} {company_town}
[!-- ENDIF {billing_contact_fullname} --]
``` ```
### Beispiel: Zwei Adressen nebeneinander ### Beispiel: Zwei Adressen nebeneinander
@ -86,6 +95,14 @@ Rechnungsadresse: Lieferadresse:
{billing_contact_zip} {billing_contact_town} {delivery_contact_zip} {delivery_contact_town} {billing_contact_zip} {billing_contact_town} {delivery_contact_zip} {delivery_contact_town}
``` ```
## Verwendung in E-Mail-Vorlagen
```
Sehr geehrte/r __BILLING_OR_THIRDPARTY__,
anbei erhalten Sie Ihre Rechnung __REF__.
```
## Kontakte zuordnen ## Kontakte zuordnen
1. Öffne das Dokument (Angebot/Auftrag/Rechnung) 1. Öffne das Dokument (Angebot/Auftrag/Rechnung)
@ -125,6 +142,6 @@ GPL v3 oder höher
## Version ## Version
- **Version:** 1.2 - **Version:** 1.3
- **Dolibarr:** 15.0+ - **Dolibarr:** 15.0+
- **PHP:** 7.1+ - **PHP:** 7.1+

View file

@ -76,7 +76,7 @@ class modDeliveryInvoiceAddress extends DolibarrModules
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@deliveryinvoiceaddress' $this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@deliveryinvoiceaddress'
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z' // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
$this->version = '1.2'; $this->version = '1.3';
// Url to the file with your last numberversion of this module // Url to the file with your last numberversion of this module
//$this->url_last_version = 'http://www.example.com/versionmodule.txt'; //$this->url_last_version = 'http://www.example.com/versionmodule.txt';

View file

@ -1,13 +1,19 @@
<?php <?php
/** /**
* Substitution-Funktion für Kontaktadressen in Dokumenten. * Substitution-Funktion für Kontaktadressen in Dokumenten und E-Mails.
* *
* Unterstützt Angebote (propal), Aufträge (commande) und Rechnungen (facture). * 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}, * Platzhalter (ODT-Syntax / E-Mail-Syntax):
* {billing_contact_address}, {billing_contact_zip}, {billing_contact_town}, {billing_contact_country} * {billing_contact_fullname} / __BILLING_CONTACT_FULLNAME__
* (analog für service_contact_* und delivery_contact_*) * {billing_contact_firstname} / __BILLING_CONTACT_FIRSTNAME__
* {billing_contact_name} / __BILLING_CONTACT_NAME__
* ... (analog für service_contact_* und delivery_contact_*)
*
* Spezial-Platzhalter mit Fallback auf Kundenname:
* {billing_or_thirdparty} / __BILLING_OR_THIRDPARTY__
* Rechnungskontakt-Fullname, oder Kundenname wenn kein Kontakt zugeordnet
* *
* @param array $substitutionarray Referenz auf das Substitution-Array * @param array $substitutionarray Referenz auf das Substitution-Array
* @param Translate $langs Sprachobjekt * @param Translate $langs Sprachobjekt
@ -30,7 +36,7 @@ function deliveryinvoiceaddress_completesubstitutionarray(&$substitutionarray, $
// Platzhalter-Prefixe je Kontakttyp // Platzhalter-Prefixe je Kontakttyp
$codeToPrefix = array( $codeToPrefix = array(
'BILLING' => 'billing_contact', 'BILLING' => 'billing_contact',
'CUSTOMER' => 'billing_contact', // Fallback für Angebote/Aufträge 'CUSTOMER' => 'billing_contact',
'SERVICE' => 'service_contact', 'SERVICE' => 'service_contact',
'SHIPPING' => 'delivery_contact', 'SHIPPING' => 'delivery_contact',
); );
@ -38,12 +44,19 @@ function deliveryinvoiceaddress_completesubstitutionarray(&$substitutionarray, $
// Felder pro Kontakt // Felder pro Kontakt
$fields = array('firstname', 'name', 'fullname', 'address', 'zip', 'town', 'country'); $fields = array('firstname', 'name', 'fullname', 'address', 'zip', 'town', 'country');
// Alle Platzhalter leer initialisieren // Alle Platzhalter leer initialisieren (ODT + E-Mail-Format)
foreach (array('billing_contact', 'service_contact', 'delivery_contact') as $prefix) { foreach (array('billing_contact', 'service_contact', 'delivery_contact') as $prefix) {
foreach ($fields as $field) { foreach ($fields as $field) {
$substitutionarray[$prefix.'_'.$field] = ''; $substitutionarray[$prefix.'_'.$field] = '';
$substitutionarray['__'.strtoupper($prefix.'_'.$field).'__'] = '';
} }
} }
$substitutionarray['billing_or_thirdparty'] = '';
$substitutionarray['__BILLING_OR_THIRDPARTY__'] = '';
$substitutionarray['billing_or_thirdparty_firstname'] = '';
$substitutionarray['__BILLING_OR_THIRDPARTY_FIRSTNAME__'] = '';
$substitutionarray['billing_or_thirdparty_name'] = '';
$substitutionarray['__BILLING_OR_THIRDPARTY_NAME__'] = '';
// Kontaktdaten per JOIN direkt laden // Kontaktdaten per JOIN direkt laden
$sql = "SELECT tc.code, sp.rowid, sp.firstname, sp.lastname, sp.address, sp.zip, sp.town,"; $sql = "SELECT tc.code, sp.rowid, sp.firstname, sp.lastname, sp.address, sp.zip, sp.town,";
@ -82,14 +95,52 @@ function deliveryinvoiceaddress_completesubstitutionarray(&$substitutionarray, $
$fullname = trim($lastname.' '.$firstname); $fullname = trim($lastname.' '.$firstname);
} }
$substitutionarray[$prefix.'_firstname'] = $firstname; $data = array(
$substitutionarray[$prefix.'_name'] = $lastname; 'firstname' => $firstname,
$substitutionarray[$prefix.'_fullname'] = $fullname; 'name' => $lastname,
$substitutionarray[$prefix.'_address'] = $obj->address ?? ''; 'fullname' => $fullname,
$substitutionarray[$prefix.'_zip'] = $obj->zip ?? ''; 'address' => $obj->address ?? '',
$substitutionarray[$prefix.'_town'] = $obj->town ?? ''; 'zip' => $obj->zip ?? '',
$substitutionarray[$prefix.'_country'] = $obj->country ?? ''; 'town' => $obj->town ?? '',
'country' => $obj->country ?? '',
);
// ODT-Format + E-Mail-Format setzen
foreach ($data as $field => $value) {
$substitutionarray[$prefix.'_'.$field] = $value;
$substitutionarray['__'.strtoupper($prefix.'_'.$field).'__'] = $value;
}
} }
$db->free($resql); $db->free($resql);
// Fallback-Platzhalter: Rechnungskontakt oder Kundenname
$hasContact = !empty($substitutionarray['billing_contact_name']);
if ($hasContact) {
$substitutionarray['billing_or_thirdparty'] = $substitutionarray['billing_contact_fullname'];
$substitutionarray['billing_or_thirdparty_firstname'] = $substitutionarray['billing_contact_firstname'];
$substitutionarray['billing_or_thirdparty_name'] = $substitutionarray['billing_contact_name'];
} else {
// Fallback auf Kundenname (thirdparty)
$thirdpartyName = '';
if (!empty($object->thirdparty) && is_object($object->thirdparty)) {
$thirdpartyName = $object->thirdparty->name ?? '';
} elseif (!empty($object->socid)) {
$sqltp = "SELECT nom as name FROM ".MAIN_DB_PREFIX."societe WHERE rowid = ".((int) $object->socid);
$restp = $db->query($sqltp);
if ($restp && $db->num_rows($restp) > 0) {
$objtp = $db->fetch_object($restp);
$thirdpartyName = $objtp->name ?? '';
}
}
$substitutionarray['billing_or_thirdparty'] = $thirdpartyName;
$substitutionarray['billing_or_thirdparty_firstname'] = '';
$substitutionarray['billing_or_thirdparty_name'] = $thirdpartyName;
}
// E-Mail-Format (__KEY__) spiegeln
$substitutionarray['__BILLING_OR_THIRDPARTY__'] = $substitutionarray['billing_or_thirdparty'];
$substitutionarray['__BILLING_OR_THIRDPARTY_FIRSTNAME__'] = $substitutionarray['billing_or_thirdparty_firstname'];
$substitutionarray['__BILLING_OR_THIRDPARTY_NAME__'] = $substitutionarray['billing_or_thirdparty_name'];
} }