Finding first: in our October run, 4 generator configurations pass KoSIT (XRechnung 3.0.2), the CEN EN 16931 rules and Peppol BIS 3.0.20, yet on 472 of 600 invoices each the amount due (BT-115) differs from what the shop order actually charged.
No validator catches this, because no validator knows the original order total. It only checks that the invoice is consistent with itself.
What we ran
- 15,000 synthetic invoices from 12 open-source generators (25 configurations × 600 orders: 300 B2B + 300 B2C).
- Validators are pinned by SHA-256: KoSIT 1.6.3, CEN EN 16931 and Peppol BIS 3.0.20.
- The orders are deterministic, tax-inclusive shop carts with % discounts and shipping, i.e. what a WooCommerce or Odoo shop actually produces.
Full report: https://kobima6813.github.io/einvoice-conformance-matrix/reports/2026-10/
The "passes but wrong" cases
| Configuration | Validators | Amount due ≠ order total |
|---|---|---|
| Mustang 2.26.0 (library-computed totals) | all pass | 472 / 600 |
| speedata/einvoice 0.0.23, CII | all pass | 472 / 600 |
| speedata/einvoice 0.0.23, UBL | all pass | 472 / 600 |
| josemmo/einvoicing 0.3.1, 2-decimal rounding | all pass | 472 / 600 |
The cause is the same in all four. A gross (tax-inclusive) cart is converted into EN 16931 net arithmetic, and rounding lands in a different place than the shop rounded. The amounts are usually cents, but the invoice no longer matches what the customer paid, and that breaks automatic payment matching. Mustang already merged a fix (PR #1318). It isn't released yet.
Validator failures we reported upstream
-
Odoo 18/19 Factur-X: PEPPOL-EN16931-R120 on 242/300 B2B invoices and BR-CO-10 / BR-S-08 with
round_globally. We tested two fix PRs (#297750, #297752) against the corpus, and the errors go to 0. The maintainer confirmed the results. -
WooCommerce PDF Invoices & Packing Slips 5.16.4:
ram:RoundingAmountis out of XSD order, so 238/600 Factur-X invoices are XSD-invalid (#1662). Another user reproduced it independently. - horstoeko/zugferd: the VAT breakdown for exempt categories was wrong. It's fixed in v1.0.133.
- speedata/einvoice: UBL element order and a BT-27/BT-28 swap. Both are fixed in v0.0.23.
- josemmo/einvoicing 0.3.1: the default config gives 590/600 CEN failures (#93). It passes once a rounding preset is set.
- ZUGFeRD-csharp 18.0.1: CII without a delivery date is XSD-invalid. This was already known upstream (PR #981), so it's not our report.
France: 30 of 30 pass
France's receiving obligation started on 1 Sep 2026. We added 5 French scenarios: standard 20 % + 5.5 %, the VAT franchise (art. 293 B), domestic reverse charge, an intra-EU supply and all four French rates. Each one ran in 2 Factur-X profiles (EN16931, EXTENDED) on 3 generator versions (drafthorse + factur-x, horstoeko 1.0.132 and 1.0.133). All 30 pass.
Honest caveats:
- In our first run, horstoeko failed BR-IC-12. That was our driver's mistake: the ship-to address is dropped unless
setDocumentShipTo()is called beforesetDocumentShipToAddress(). It was not a library bug. - We have not tested the French platform-specific (PA/PPF) rule sets, because they aren't available as free, pinned validators.
Free self-check
You can check one of your own invoices here: https://kobima6813.github.io/einvoice-conformance-matrix/check.html
It runs in your browser, and the file is not uploaded anywhere. It covers a subset of EN 16931 and tells you how many problems it found. It is not a replacement for KoSIT or a Peppol validator.
Limits
- The orders are synthetic. Real catalogs have edge cases we don't generate yet.
- Each configuration is one library version with one setting. Your integration code can add or hide bugs.
- Corrections are welcome. Every number links to a file in the repo, and you can reproduce it with
scripts/run_matrix.sh.
Repo and home page: https://kobima6813.github.io/einvoice-conformance-matrix/
If you maintain one of these generators and think a result is wrong, open an issue and I'll re-run it.
Top comments (0)