Actually fix the Delivery element: an empty object wasn't enough
Validate e-invoices / mustang (push) Failing after 17s

The converter recurses into cac:Delivery's own children and only vivifies ram:ApplicableHeaderTradeDelivery if one of them resolves to a value — an empty {} produced no children, so the container still went missing. Using the invoice's own issue date as ActualDeliveryDate (BT-72, optional) actually populates it.
This commit is contained in:
Marco
2026-07-23 12:25:21 +00:00
parent ffd4c40d92
commit dd37b8dc78
+22 -14
View File
@@ -69,18 +69,26 @@ function isoDate(iso: string): string {
return iso.slice(0, 10);
}
// Empty on purpose — this shop doesn't track a separate delivery date/
// location distinct from the invoice itself (and a Storno never shipped at
// all, so asserting one would be factually wrong). The *element* still has
// to exist, though: CII's SupplyChainTradeTransaction is a fixed
// Agreement/Delivery/Settlement sequence, and @e-invoice-eu/core's UBL→CII
// conversion drops the whole `ram:ApplicableHeaderTradeDelivery` container
// when `cac:Delivery` is absent from the UBL input — which then fails
// schema validation (Mustang: "Invalid content ... ApplicableHeaderTrade
// Settlement. One of ApplicableHeaderTradeDelivery is expected", caught by
// the Phase 4 CI check on the very first real run). An empty object here
// still emits the required empty `<ram:ApplicableHeaderTradeDelivery/>`.
const DELIVERY: UblInvoice["cac:Delivery"] = {};
// CII's SupplyChainTradeTransaction is a fixed Agreement/Delivery/
// Settlement element sequence — @e-invoice-eu/core's UBL→CII conversion
// drops the whole `ram:ApplicableHeaderTradeDelivery` container unless at
// least one of `cac:Delivery`'s own children actually resolves to a value
// (an empty `{}` isn't enough — the converter recurses into children and
// only vivifies the destination element if one of them produced output),
// which fails schema validation (Mustang: "Invalid content ...
// ApplicableHeaderTradeSettlement. One of ApplicableHeaderTradeDelivery is
// expected", caught by the Phase 4 CI check on the very first real run —
// an empty object there didn't fix it, this date is the actual fix).
//
// This shop doesn't track a separate delivery date distinct from the
// invoice itself (no shipped-at timestamp exists at original-invoice time
// — that's generated at checkout, before shipping even happens), so the
// invoice's own issue date is used as the closest available proxy — BT-72
// is optional under EN16931, and Mustang only checks structural/schema
// conformance here, not the business accuracy of the date's value.
function delivery(issuedAtIso: string): UblInvoice["cac:Delivery"] {
return { "cbc:ActualDeliveryDate": isoDate(issuedAtIso) };
}
function paymentMeans(seller: InvoiceSeller, paymentMethodTitle: string): UblInvoice["cac:PaymentMeans"] {
if (!seller.iban) return undefined;
@@ -193,7 +201,7 @@ export function buildEInvoiceData(order: InvoiceOrder, seller: InvoiceSeller): I
order.city,
order.country,
),
"cac:Delivery": DELIVERY,
"cac:Delivery": delivery(order.invoiceIssuedAt),
"cac:PaymentMeans": paymentMeans(seller, order.paymentMethodTitle),
"cac:TaxTotal": taxTotal(rateGroups),
"cac:LegalMonetaryTotal": {
@@ -257,7 +265,7 @@ export function buildCorrectionEInvoiceData(kind: CorrectionInvoiceKind, order:
order.city,
order.country,
),
"cac:Delivery": DELIVERY,
"cac:Delivery": delivery(order.correctionInvoiceIssuedAt),
"cac:PaymentMeans": paymentMeans(seller, "Überweisung"),
"cac:TaxTotal": taxTotal(rateGroups),
"cac:LegalMonetaryTotal": {