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)
## 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
- Fix: `{*_contact_name}` liefert jetzt Nachname statt Vorname

View file

@ -1,6 +1,6 @@
# 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
@ -9,6 +9,7 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
- Serviceadresse für Servicedokumente
- Automatische Erkennung des Dokumenttyps (Angebot/Auftrag/Rechnung)
- CUSTOMER-Kontakt als Fallback für BILLING bei Angeboten/Aufträgen
- Fallback-Platzhalter: Rechnungskontakt oder automatisch Kundenname
## Installation
@ -23,58 +24,66 @@ Dolibarr-Modul zur Verwendung von Rechnungs-, Liefer- und Serviceadressen in ODT
| Auftrag (Commande) | `commande` | BILLING, CUSTOMER, 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)
| Tag | Beschreibung |
|-----|--------------|
| `{billing_contact_firstname}` | Vorname |
| `{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 |
| `{billing_contact_country}` | Land |
| ODT-Tag | E-Mail-Tag | Beschreibung |
|---------|------------|--------------|
| `{billing_contact_firstname}` | `__BILLING_CONTACT_FIRSTNAME__` | Vorname |
| `{billing_contact_name}` | `__BILLING_CONTACT_NAME__` | Nachname |
| `{billing_contact_fullname}` | `__BILLING_CONTACT_FULLNAME__` | Vorname + Nachname |
| `{billing_contact_address}` | `__BILLING_CONTACT_ADDRESS__` | Straße und Hausnummer |
| `{billing_contact_zip}` | `__BILLING_CONTACT_ZIP__` | Postleitzahl |
| `{billing_contact_town}` | `__BILLING_CONTACT_TOWN__` | Ort |
| `{billing_contact_country}` | `__BILLING_CONTACT_COUNTRY__` | Land |
### Lieferadresse (Delivery/Shipping)
| Tag | Beschreibung |
|-----|--------------|
| `{delivery_contact_firstname}` | Vorname |
| `{delivery_contact_name}` | Nachname |
| `{delivery_contact_fullname}` | Vorname + Nachname |
| `{delivery_contact_address}` | Straße und Hausnummer |
| `{delivery_contact_zip}` | Postleitzahl |
| `{delivery_contact_town}` | Ort |
| `{delivery_contact_country}` | Land |
| ODT-Tag | E-Mail-Tag | Beschreibung |
|---------|------------|--------------|
| `{delivery_contact_firstname}` | `__DELIVERY_CONTACT_FIRSTNAME__` | Vorname |
| `{delivery_contact_name}` | `__DELIVERY_CONTACT_NAME__` | Nachname |
| `{delivery_contact_fullname}` | `__DELIVERY_CONTACT_FULLNAME__` | Vorname + Nachname |
| `{delivery_contact_address}` | `__DELIVERY_CONTACT_ADDRESS__` | Straße und Hausnummer |
| `{delivery_contact_zip}` | `__DELIVERY_CONTACT_ZIP__` | Postleitzahl |
| `{delivery_contact_town}` | `__DELIVERY_CONTACT_TOWN__` | Ort |
| `{delivery_contact_country}` | `__DELIVERY_CONTACT_COUNTRY__` | Land |
### Serviceadresse (Service)
| Tag | Beschreibung |
|-----|--------------|
| `{service_contact_firstname}` | Vorname |
| `{service_contact_name}` | Nachname |
| `{service_contact_fullname}` | Vorname + Nachname |
| `{service_contact_address}` | Straße und Hausnummer |
| `{service_contact_zip}` | Postleitzahl |
| `{service_contact_town}` | Ort |
| `{service_contact_country}` | Land |
| ODT-Tag | E-Mail-Tag | Beschreibung |
|---------|------------|--------------|
| `{service_contact_firstname}` | `__SERVICE_CONTACT_FIRSTNAME__` | Vorname |
| `{service_contact_name}` | `__SERVICE_CONTACT_NAME__` | Nachname |
| `{service_contact_fullname}` | `__SERVICE_CONTACT_FULLNAME__` | Vorname + Nachname |
| `{service_contact_address}` | `__SERVICE_CONTACT_ADDRESS__` | Straße und Hausnummer |
| `{service_contact_zip}` | `__SERVICE_CONTACT_ZIP__` | Postleitzahl |
| `{service_contact_town}` | `__SERVICE_CONTACT_TOWN__` | Ort |
| `{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
### Beispiel: Rechnungsadresse mit Fallback auf Kundenadresse
### Beispiel: Automatischer Fallback (empfohlen)
```
[!-- IF {billing_contact_fullname} --]
{billing_contact_fullname}
{billing_or_thirdparty}
{billing_contact_address}
{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
@ -86,6 +95,14 @@ Rechnungsadresse: Lieferadresse:
{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
1. Öffne das Dokument (Angebot/Auftrag/Rechnung)
@ -125,6 +142,6 @@ GPL v3 oder höher
## Version
- **Version:** 1.2
- **Version:** 1.3
- **Dolibarr:** 15.0+
- **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'
// 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
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';

View file

@ -1,13 +1,19 @@
<?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).
* 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_*)
*
* Platzhalter (ODT-Syntax / E-Mail-Syntax):
* {billing_contact_fullname} / __BILLING_CONTACT_FULLNAME__
* {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 Translate $langs Sprachobjekt
@ -30,7 +36,7 @@ function deliveryinvoiceaddress_completesubstitutionarray(&$substitutionarray, $
// Platzhalter-Prefixe je Kontakttyp
$codeToPrefix = array(
'BILLING' => 'billing_contact',
'CUSTOMER' => 'billing_contact', // Fallback für Angebote/Aufträge
'CUSTOMER' => 'billing_contact',
'SERVICE' => 'service_contact',
'SHIPPING' => 'delivery_contact',
);
@ -38,12 +44,19 @@ function deliveryinvoiceaddress_completesubstitutionarray(&$substitutionarray, $
// Felder pro Kontakt
$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 ($fields as $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
$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);
}
$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 ?? '';
$data = array(
'firstname' => $firstname,
'name' => $lastname,
'fullname' => $fullname,
'address' => $obj->address ?? '',
'zip' => $obj->zip ?? '',
'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);
// 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'];
}