Skip to content
GitHub

The canonical model

The core is still EN 16931-1, field for field, so every EN-based format maps 1:1. National formats carry concepts EN has no term for. Those go, in order of preference:

  1. EU-wide additions to the core, used by several countries:
    • withholdings + totals.withholdingTotal (IT ritenuta, ES IRPF, PT retenção). payable = taxInclusive − prepaid + rounding − withholdingTotal − splitPaymentVat (the last term is Italian split payment, EsigibilitaIVA = S).
    • otherTaxes for non-VAT taxes (ES recargo de equivalencia, IT stamp duty).
    • payments[] (several means/accounts) and a separate paymentSchedule[] (due dates and amounts, optionally pointing at a payment entry). FatturaPA, Facturae and KSeF allow several of each; EN allows one.
    • additionalParties[] with a role for parties EN has no slot for: KSeF Podmiot3 (factor, issuer on the seller’s behalf, payer, additional buyer…), FatturaPA third-party issuer, Facturae third party, e-arve factor.
    • nationalTaxIds on parties (codice fiscale, NIP, NIF, OIB, AFM, IČO), because several formats identify parties only that way.
    • nationalType next to the UNCL 1001 typeCode (FatturaPA TD04, KSeF KOR, Facturae OR…), and correction for corrective-invoice reasons and methods.
    • nationalCode on VAT categories (FatturaPA Natura N6.2 → AE, KSeF oo → AE), so the mapping can be audited.
    • On lines: correctionState (before/after/delta) for KSeF corrections that list each line before and after (StanPrzed), otherTaxes/withholdings for Facturae lines with several taxes, and subLines (XRechnung extension, Factur-X EXTENDED, France EXTENDED-CTC-FR).
    • issueTime, exchangeRate, unitText for free-text units.
  2. clearance[]: tax-authority IDs (SdI ID, KSeF number, ANAF index, myDATA MARK), status and QR payload. Signatures go in source.signatures (CAdES, XAdES, detached).
  3. extensions.{it,pl,es,hr,gr,pt,fr,de,cz,at,fi,ee} for country-only fields: split payment (IT EsigibilitaIVA=S, PL P_18A), Italian pension funds, CUP/CIG codes, KSeF annotations, DIR3 units in Facturae, Croatian operator OIB, ISDOC symbols…
  4. source.unmapped (XPath → value) for anything left. source.derived records every inferred value and source.warnings records every lossy mapping.

source also carries fileSha256 and payloadSha256 (one received file can hold several invoices, so deduplicate on file hash + indexInFile/fragmentPath, not on invoice number), optional field-level provenance (canonical path → source XPath), and a validation summary kept per layer (parse, schema, semantic, profile, signature, clearance), where unavailable (validator could not run) is distinct from failed.

Other design rules are unchanged from the PEPPOL/Svefaktura version: decimal strings for money, YYYY-MM-DD dates, open code-list unions, credit notes keep their source signs (effectiveSign()), and EN-mandatory fields are optional in the type with a separate completeness check.