Fix: EN16931-Lieferdatum (BT-72) und leere XML-Elemente in ZUGFeRD-Rechnungen
This commit is contained in:
parent
6bff984ca0
commit
0488edba7c
3 changed files with 31 additions and 8 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))) {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1-260530
|
||||
1-260601
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue