Add innergemeinschaftliche-Lieferung (VAT exemption) support
Validate e-invoices / mustang (push) Successful in 18s

InvoiceOrder/CorrectionInvoiceOrder gained an optional vatExempt flag
— items already carry 0% rate and de-grossed prices when true (the
caller's responsibility), this only controls display/XML mapping:

- Visual PDF: replaces the "enthält X% MwSt." annotation with
  "Steuerfreie innergemeinschaftliche Lieferung (§4 Nr. 1b UStG)" on
  both the invoice and its Storno/Gutschrift.
- EN16931 XML (buildEInvoiceData.ts): VAT category 'K' instead of 'S'
  on every line/tax-subtotal, plus BT-120/BT-121 exemption reason
  (VATEX-EU-IC) — but only on cac:TaxTotal's own TaxSubtotal.TaxCategory,
  not on InvoiceLine/AllowanceCharge's TaxCategory, which
  @e-invoice-eu/core's schema rejects those same two fields on (caught
  locally before Mustang ever saw it — a fully-rendered PDF/A-3 with a
  hand-inflated CII XML confirmed CategoryCode/ExemptionReasonCode/
  ExemptionReason land correctly).

Also surfaced (not fixed, unrelated to this change): a seller with no
iban configured throws in @e-invoice-eu/core's UBL→CII conversion —
doesn't affect this shop's own production data (iban is always set),
noting for whoever touches this next.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-07-23 18:59:19 +00:00
parent 01374c47cd
commit 942b8a8632
3 changed files with 88 additions and 33 deletions
+23 -6
View File
@@ -145,6 +145,14 @@ export type InvoiceOrder = {
// the address. Neither implies the other (see Orders.ts's own comment).
companyName?: string | null;
vatId?: string | null;
// Innergemeinschaftliche Lieferung (§4 Nr. 1b UStG) — decided server-side
// at checkout via a live VIES lookup (see the frontend's api/checkout/
// route.ts and lib/vatExemption.ts), never guessed from vatId's mere
// presence/format. When true, every item's own taxRatePercent is already
// 0 and unitPrice already de-grossed (the actual charged/persisted
// figures) — this flag only controls the exemption note shown here,
// it doesn't itself change any arithmetic in this file.
vatExempt?: boolean;
deliveryMethod: "address" | "packstation";
street?: string | null;
packstationNumber?: string | null;
@@ -373,12 +381,21 @@ export function InvoiceDocument({ order, seller }: { order: InvoiceOrder; seller
<Text style={styles.grandTotalValue}>{formatPrice(order.total)}</Text>
</View>
<View style={styles.vatNotes}>
{rateGroups.map((g) => (
<View style={styles.vatNoteRow} key={g.rate}>
<Text style={styles.vatNoteLabel}>enthält {g.rate}% MwSt.</Text>
<Text style={styles.vatNoteValue}>{formatPrice(g.tax)}</Text>
</View>
))}
{/* Exempt orders already have every item at 0% (see this
file's own InvoiceOrder.vatExempt comment) — "enthält
0% MwSt.: 0,00 €" would be a meaningless thing to print,
so this replaces the whole per-rate breakdown with the
actual legal basis instead. */}
{order.vatExempt ? (
<Text style={styles.vatNoteLabel}>Steuerfreie innergemeinschaftliche Lieferung (§4 Nr. 1b UStG)</Text>
) : (
rateGroups.map((g) => (
<View style={styles.vatNoteRow} key={g.rate}>
<Text style={styles.vatNoteLabel}>enthält {g.rate}% MwSt.</Text>
<Text style={styles.vatNoteValue}>{formatPrice(g.tax)}</Text>
</View>
))
)}
</View>
</View>
{paid && (