A structured electronic invoice being checked against the EN 16931 rule set

E-invoicing is going mandatory across the EU. Every public buyer in Europe already has to accept invoices in the EN 16931 semantic model, and Germany has begun phasing in mandatory B2B e-invoicing built on the same standard. The format turns out to be the easy part. The hard part is proving a given invoice actually conforms โ€” that every business rule holds, every code comes from the right list, and every conditional field is present exactly when the standard says it must be. That is what this engine does.

๐Ÿงพ What EN 16931 Actually Is

EN 16931 defines a semantic data model for the core invoice โ€” roughly 160 business terms (an invoice number, a seller VAT identifier, a line net amount) โ€” plus a long list of business rules that constrain them. The rules come in families:

  • BR-xx โ€” structural rules, e.g. “an Invoice shall have an Invoice number.”
  • BR-CO-xx โ€” calculation rules, e.g. “the sum of line net amounts shall equal the invoice line net total.”
  • BR-CL-xx โ€” code-list rules, e.g. “the currency shall be coded with ISO 4217.”
  • BR-S / BR-Z / BR-E / BR-AE โ€ฆ โ€” the VAT-category rules that make totals and exemption reasons hang together.

The same model is carried in two XML syntaxes: UBL (OASIS) and UN/CEFACT CII (the Cross Industry Invoice). A conformance engine has to speak both, because a supplier can send you either.

๐Ÿ”ฌ Clean-Room Rules, Differential-Tested

The official validation artefacts ship as Schematron โ€” XSLT-driven assertions that are authoritative but heavy to run and awkward to embed in a pipeline. Rather than shipping the Schematron, the engine implements each business rule as a small, direct check over a parsed invoice.

To be sure those checks match the reference exactly, every rule is differential-tested: a corpus of valid and deliberately-broken invoices is run through both the official Schematron and this engine, and the pass/fail verdicts have to agree rule-for-rule. A rule is not considered done until its verdicts line up with the Schematron on every fixture โ€” that is the whole point, and it is what keeps the clean-room implementation honest.

๐Ÿ‡ฉ๐Ÿ‡ช XRechnung: The German Layer on Top

EN 16931 is the floor; countries tighten it with a CIUS (Core Invoice Usage Specification). Germany’s is XRechnung, which adds its own BR-DE-xx rules on top of the European base โ€” a buyer reference (Leitweg-ID) becomes mandatory, seller contact details are required, and several fields EN 16931 leaves optional are promoted to required. The engine layers those national rules over the EN 16931 core, so you can validate against plain EN 16931 or the stricter XRechnung profile with the same tool.

๐Ÿ“„ Reading the Invoice Out of a PDF

Here is the part people do not expect. Factur-X (France) and ZUGFeRD (Germany) are hybrid invoices: an ordinary, human-readable PDF/A-3 that carries the full structured invoice XML embedded as an attachment inside the PDF. The visible page is for a person; the embedded factur-x.xml / zugferd-invoice.xml is for a machine.

So the engine takes a PDF, pulls the embedded XML out of the PDF/A-3 attachment table, and validates that. In practice this means you can drop in the exact file a supplier emailed you and get a machine verdict โ€” no separate XML export, no copy-paste, no “can you also send the XML?” round-trip.

โœ… SARIF and a CI Gate

Findings come out as SARIF โ€” the Static Analysis Results Interchange Format. SARIF is what GitHub reads to draw inline annotations, so a failing rule shows up right on the offending line instead of buried in a log. Packaged as a GitHub Action, the engine becomes a conformance gate: open a pull request that changes an invoice template, and if the output stops conforming, the check turns red before the invoice ever reaches a customer or a tax authority. The same binary runs locally and self-hosted โ€” there is no service to call and nothing leaves your machine.

โš ๏ธ Honest Limits

  • Conformance, not truth. It can tell you the totals are internally consistent and the codes are valid; it cannot know that your VAT rate is the commercially correct one for the transaction.
  • Scope is EN 16931 + XRechnung. Other national profiles โ€” Peppol BIS, France’s CIUS, Italy’s FatturaPA โ€” are their own rule layers and are out of scope for now.
  • PDF extraction targets Factur-X / ZUGFeRD. A scanned PDF with no embedded XML has nothing structured to validate โ€” there is simply no invoice data in it to check.

๐Ÿงฐ Why Build It This Way

Conformance tooling today is usually one of two things: a large Java Schematron runner, or a paid SaaS you upload your invoices to. Neither fits a CI pipeline that wants to stay fast, offline, and auditable. A small engine with clean-room rules โ€” differential-proven against the reference, able to read the PDFs suppliers actually send, and speaking SARIF so the results land where developers already look โ€” drops into a build with no drama. That is the whole design goal: make “is this invoice conformant?” a green check, not a project.

— DankDev


Tags: E-Invoicing ยท EN 16931 ยท XRechnung ยท Factur-X ยท SARIF ยท GitHub Actions

E-InvoicingEN 16931XRechnungFactur-XSARIFGitHub Actions

โ† Back to all posts