Make InvoiceSeller.vatId optional for Kleinunternehmer sellers
Validate e-invoices / mustang (push) Successful in 18s
Validate e-invoices / mustang (push) Successful in 18s
Payload's company-settings.vatId is no longer hard-required (a Kleinunternehmer often has no USt-IdNr. at all) — this package's InvoiceSeller.vatId follows suit, with every renderer now omitting the USt-IdNr. line/field entirely instead of printing/serializing an empty value. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -80,6 +80,8 @@ Unlike `vatExempt`, this package does **not** expect `unitPrice` to be de-grosse
|
||||
- Visual PDF (`invoicePdf.tsx`/`correctionInvoicePdf.tsx`): the "enthält X% MwSt." annotation under Gesamt becomes "Gemäß § 19 UStG wird keine Umsatzsteuer berechnet." instead — takes precedence over the `vatExempt` note if (implausibly) both were ever set.
|
||||
- E-invoice XML (`buildEInvoiceData.ts`): VAT category `E` + a free-text exemption reason, no VATEX code — see "E-invoicing" above.
|
||||
|
||||
`InvoiceSeller.vatId` is now optional (`string | null`, was a plain required `string`) for the same reason — a Kleinunternehmer commonly never registers for an USt-IdNr. at all (no intra-EU trade). Every renderer treats a missing value as "omit the USt-IdNr. line/field" rather than printing an empty one: the visual PDF footer drops the whole `· USt-IdNr. …` segment, and `buildEInvoiceData.ts`'s `sellerParty()` omits `cac:PartyTaxScheme` entirely rather than emitting one with a blank `CompanyID`.
|
||||
|
||||
## CI validation (Mustang)
|
||||
|
||||
Every push/PR runs `.gitea/workflows/validate-einvoice.yml` against git.mk360.de's own self-hosted Gitea Actions runner (`vps-runner`, registered on the same VPS as Gitea/Payload — see `/home/marco/dev/docker/docker-compose.yml`'s `act_runner` service):
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@einfach-produktiv/invoicing",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"private": true,
|
||||
"description": "Shared invoice / correction-invoice (Stornorechnung, Gutschrift) PDF generation and VAT-breakdown math, consumed as a git dependency by both the einfach-produktiv frontend and the payload backend — not published to npm.",
|
||||
"type": "module",
|
||||
|
||||
@@ -379,8 +379,10 @@ function CorrectionInvoiceDocument({ kind, order, seller }: { kind: CorrectionIn
|
||||
|
||||
<View style={styles.footer} fixed>
|
||||
<Text>
|
||||
{seller.sellerName} · {seller.sellerStreet}, {seller.sellerZip} {seller.sellerCity} · {seller.sellerEmail} · USt-IdNr.{" "}
|
||||
{seller.vatId}
|
||||
{seller.sellerName} · {seller.sellerStreet}, {seller.sellerZip} {seller.sellerCity} · {seller.sellerEmail}
|
||||
{/* Same "omit entirely when unset" as invoicePdf.tsx's own
|
||||
footer — see seller.ts's own comment on vatId. */}
|
||||
{seller.vatId ? ` · USt-IdNr. ${seller.vatId}` : ""}
|
||||
{seller.registerCourt && seller.registerNumber ? ` · ${seller.registerCourt} · ${seller.registerNumber}` : ""}
|
||||
{seller.managingDirector ? ` · Geschäftsführung: ${seller.managingDirector}` : ""}
|
||||
</Text>
|
||||
|
||||
@@ -185,12 +185,21 @@ function sellerParty(seller: InvoiceSeller): UblInvoice["cac:AccountingSupplierP
|
||||
"cbc:PostalZone": seller.sellerZip,
|
||||
"cac:Country": { "cbc:IdentificationCode": countryCode(seller.sellerCountry) as SellerCountryCode },
|
||||
},
|
||||
"cac:PartyTaxScheme": [
|
||||
{
|
||||
"cbc:CompanyID": seller.vatId,
|
||||
"cac:TaxScheme": { "cbc:ID": "VAT" },
|
||||
},
|
||||
],
|
||||
// Omitted entirely (not a PartyTaxScheme with an empty CompanyID)
|
||||
// when unset — a Kleinunternehmer (§19 UStG) often has no USt-IdNr.
|
||||
// at all, see seller.ts's own comment. Same "always present in this
|
||||
// shop's real production data today, but must degrade safely" spirit
|
||||
// as paymentMeans()'s own `!seller.iban` check above.
|
||||
...(seller.vatId
|
||||
? {
|
||||
"cac:PartyTaxScheme": [
|
||||
{
|
||||
"cbc:CompanyID": seller.vatId,
|
||||
"cac:TaxScheme": { "cbc:ID": "VAT" },
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
"cac:PartyLegalEntity": { "cbc:RegistrationName": seller.sellerName },
|
||||
"cac:Contact": { "cbc:ElectronicMail": seller.sellerEmail },
|
||||
},
|
||||
|
||||
+5
-2
@@ -449,8 +449,11 @@ export function InvoiceDocument({ order, seller }: { order: InvoiceOrder; seller
|
||||
|
||||
<View style={styles.footer} fixed>
|
||||
<Text>
|
||||
{seller.sellerName} · {seller.sellerStreet}, {seller.sellerZip} {seller.sellerCity} · {seller.sellerEmail} · USt-IdNr.{" "}
|
||||
{seller.vatId}
|
||||
{seller.sellerName} · {seller.sellerStreet}, {seller.sellerZip} {seller.sellerCity} · {seller.sellerEmail}
|
||||
{/* Omitted entirely (not "USt-IdNr. " with a blank value) when
|
||||
unset — a Kleinunternehmer (§19 UStG) often has no USt-IdNr.
|
||||
at all, see seller.ts's own comment. */}
|
||||
{seller.vatId ? ` · USt-IdNr. ${seller.vatId}` : ""}
|
||||
{seller.registerCourt && seller.registerNumber ? ` · ${seller.registerCourt} · ${seller.registerNumber}` : ""}
|
||||
{seller.managingDirector ? ` · Geschäftsführung: ${seller.managingDirector}` : ""}
|
||||
</Text>
|
||||
|
||||
+8
-1
@@ -9,7 +9,14 @@ export type InvoiceSeller = {
|
||||
sellerCity: string;
|
||||
sellerCountry: string;
|
||||
sellerEmail: string;
|
||||
vatId: string;
|
||||
// Optional as of the Kleinunternehmerregelung (§19 UStG) addition — a
|
||||
// Kleinunternehmer often only has a Steuernummer, never registers for an
|
||||
// USt-IdNr. at all (no intra-EU trade), so company-settings.vatId is no
|
||||
// longer a hard-required field on the Payload side (see that
|
||||
// collection's own comment). Every renderer here already treats a
|
||||
// missing vatId as "omit the USt-IdNr. line/field" rather than printing
|
||||
// an empty value.
|
||||
vatId?: string | null;
|
||||
taxRatePercent: number;
|
||||
// Structured (Phase 2 of the e-invoicing migration — EN16931 wants
|
||||
// discrete PaymentMeans data, not a free-text paragraph a human
|
||||
|
||||
Reference in New Issue
Block a user