diff --git a/Changes.md b/Changes.md index e23bf68..42ba768 100644 --- a/Changes.md +++ b/Changes.md @@ -2,6 +2,11 @@ --- +## 2026-06-01 – Fix: EN16931-Validierungsfehler in ZUGFeRD-Rechnungen + +- **[BR-FX-EN-04]** behoben: `ZugferdService` setzt jetzt BT-72 (Liefer-/Leistungsdatum) via `setDocumentSupplyChainEvent()`; das Datum wird aus dem Feld `leistungszeitraum` geparst (Format `DD.MM.YYYY` → direkt; Format `MM.YYYY` → letzter Tag des Monats; sonst Fallback auf Rechnungsdatum) +- **[PEPPOL-EN16931-R008]** behoben: Leere Strings in optionalen Adressfeldern (Straße, PLZ, Ort) werden als `null` übergeben, sodass keine leeren XML-Elemente entstehen + ## 2026-05-28 – Feature: Buchungsvorlagen im Buchungsdialog - **Datenbank:** 3 neue Tabellen `buchungsvorlagen`, `buchungsvorlage_felder`, `buchungsvorlage_regeln` (Migrationen 078–080); initiale Vorlage "Essensbeleg n. abzugsfähig (nur USt.)" per Migration 080 eingespielt diff --git a/src/Services/ZugferdService.php b/src/Services/ZugferdService.php index 11bebc5..02e91f6 100644 --- a/src/Services/ZugferdService.php +++ b/src/Services/ZugferdService.php @@ -30,17 +30,21 @@ class ZugferdService 'EUR' ); + // BT-72: Liefer-/Leistungsdatum aus leistungszeitraum ableiten (EN16931-Pflichtfeld) + $deliveryDate = $this->parseLeistungszeitraumDate($inv['leistungszeitraum'] ?? '', $rechnungsDatum); + $doc->setDocumentSupplyChainEvent($deliveryDate); + // Käuferreferenz (BT-10) $doc->setDocumentBuyerReference($inv['kundennummer']); // Verkäufer (BG-4) $doc->setDocumentSeller($settings['firma'] ?? ''); $doc->setDocumentSellerAddress( - $settings['strasse'] ?? '', + $settings['strasse'] ?: null, null, null, - $settings['plz'] ?? '', - $settings['ort'] ?? '', + $settings['plz'] ?: null, + $settings['ort'] ?: null, 'DE' ); if (!empty($settings['ust_id'])) { @@ -55,12 +59,12 @@ class ZugferdService : trim(($inv['anrede'] ?? '') . ' ' . ($inv['vorname'] ?? '') . ' ' . ($inv['nachname'] ?? '')); $doc->setDocumentBuyer($buyerName, $inv['kundennummer']); $doc->setDocumentBuyerAddress( - $inv['strasse'] ?? '', + $inv['strasse'] ?: null, null, null, - $inv['plz'] ?? '', - $inv['ort'] ?? '', - $inv['land'] ?? 'DE' + $inv['plz'] ?: null, + $inv['ort'] ?: null, + $inv['land'] ?: 'DE' ); if (!empty($inv['ust_id'])) { $doc->addDocumentBuyerVATRegistrationNumber($inv['ust_id']); @@ -148,6 +152,20 @@ class ZugferdService return $pdfBuilder->downloadString(); } + private function parseLeistungszeitraumDate(string $lz, DateTime $fallback): DateTime + { + // DD.MM.YYYY + $dt = DateTime::createFromFormat('d.m.Y', trim($lz)); + if ($dt !== false) { + return $dt; + } + // MM.YYYY → letzter Tag des Monats + if (preg_match('/^(\d{1,2})\.(\d{4})$/', trim($lz), $m)) { + return new DateTime(sprintf('last day of %04d-%02d', (int)$m[2], (int)$m[1])); + } + return $fallback; + } + private function mapEinheit(string $einheit): string { return match (strtolower(trim($einheit))) { diff --git a/src/version.txt b/src/version.txt index d7504ca..4be5dc2 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -1-260530 +1-260601