einvoice / EN 16931 / XRechnung rule reference

BR-CL-23

What this rule means and how to fix it

Unit code MUST be coded per UN/ECE Rec 20 with Rec 21 extension.

Der Einheiten-Code MUSS gemäß der UN/ECE-Empfehlung 20 mit der Erweiterung Rec 21 codiert werden.

Deutsche Fassung: Übersetzung der gleichen BT/BG-Semantik (translation — not the official assert).

Requires
Unit code MUST be coded according to the UN/ECE Recommendation 20 with Rec 21 extension
Business terms
— (no single business term)
Syntax
Binds the UBL-2.1 Invoice / CreditNote syntax and the UN/CEFACT-CII syntax
Location (UBL-2.1 path)
cbc:InvoicedQuantity[@unitCode]
Fix
Encode `cbc:InvoicedQuantity[@unitCode]` using a valid value from UN/ECE Recommendation 20 with Rec 21 extension.
Fix (Deutsch)
Codieren Sie `cbc:InvoicedQuantity[@unitCode]` mit einem gültigen Wert aus der geforderten Codeliste: Der Einheiten-Code MUSS gemäß der UN/ECE-Empfehlung 20 mit der Erweiterung Rec 21 codiert werden.
Severity
fatal
Provenance source
en16931-ubl
Provenance assert

Unit code MUST be coded according to the UN/ECE Recommendation 20 with Rec 21 extension

The Location above is a UBL-2.1 path, and no UN/CEFACT-CII path for this rule is published here; Fix and Fix (Deutsch), verbatim from remediation_catalog.json and not reworded here; the UN/CEFACT-CII binding of this rule is differentially proven against the official CEN EN 16931 1.3.16 Schematron.

Where to check the fix: paste the corrected invoice into the browser validator at /einvoice/validate/ — it runs this same engine locally in your browser, with no upload, and stops reporting BR-CL-23 once the edit lands.

Korrektur prüfen: Laden Sie die geänderte Rechnung in den Browser-Validator unter /einvoice/validate/ — er führt dieselbe Engine lokal im Browser aus, ohne Upload, und meldet BR-CL-23 nicht mehr, sobald die Korrektur greift.

The same invoice, failing and fixed

No vendored test suite ships a labelled example for this rule, so this pair was found rather than written. The engine was run over the vendored instance corpus/xrechnung-testsuite/src/test/technical-cases/cius/01.06_minimal_test_ubl.xml under --profile xrechnung, then re-run over the same file with one mechanical edit applied to one element, over and over, until the edit below changed the verdict: the unitCode attribute of InvoicedQuantity was changed from XPP to ZZZ, a token that appears in no code list. The page publishes it because the engine reported BR-CL-23 for the edited document and did not report it for the untouched one.

UBL Invoice — fails

The vendored file around the change, verbatim:

<cac:InvoiceLine>
  <cbc:ID>1</cbc:ID>
  <cbc:InvoicedQuantity unitCode="ZZZ">30</cbc:InvoicedQuantity>
  <cbc:LineExtensionAmount currencyID="EUR">3986.34</cbc:LineExtensionAmount>
  <cac:Item>
    <cbc:Name>Beratung</cbc:Name>
    <cac:ClassifiedTaxCategory>
      <cbc:ID>S</cbc:ID>
      <cbc:Percent>19</cbc:Percent>
      <cac:TaxScheme>
        <cbc:ID>VAT</cbc:ID>
      </cac:TaxScheme>
    </cac:ClassifiedTaxCategory>
  </cac:Item>
  <cac:Price>
    <cbc:PriceAmount currencyID="EUR">132.878</cbc:PriceAmount>
  </cac:Price>
</cac:InvoiceLine>

UBL Invoice — passes

The same lines as 01.06_minimal_test_ubl.xml actually ships them — this block is the fix for the block above:

<cac:InvoiceLine>
  <cbc:ID>1</cbc:ID>
  <cbc:InvoicedQuantity unitCode="XPP">30</cbc:InvoicedQuantity>
  <cbc:LineExtensionAmount currencyID="EUR">3986.34</cbc:LineExtensionAmount>
  <cac:Item>
    <cbc:Name>Beratung</cbc:Name>
    <cac:ClassifiedTaxCategory>
      <cbc:ID>S</cbc:ID>
      <cbc:Percent>19</cbc:Percent>
      <cac:TaxScheme>
        <cbc:ID>VAT</cbc:ID>
      </cac:TaxScheme>
    </cac:ClassifiedTaxCategory>
  </cac:Item>
  <cac:Price>
    <cbc:PriceAmount currencyID="EUR">132.878</cbc:PriceAmount>
  </cac:Price>
</cac:InvoiceLine>

What the engine reports for the edited invoice

{
  "element": "cbc:InvoicedQuantity/@unitCode",
  "field": "cbc:InvoicedQuantity/@unitCode",
  "fix_hint": "Encode `cbc:InvoicedQuantity[@unitCode]` using a valid value from UN/ECE Recommendation 20 with Rec 21 extension.",
  "location": "cbc:InvoicedQuantity[@unitCode]",
  "message": "Unit code MUST be coded according to the UN/ECE Recommendation 20 with Rec 21 extension; 'ZZZ' is not a listed unit code.",
  "rule": "BR-CL-23",
  "severity": "fatal",
  "terms": [],
  "title": "Unit code MUST be coded per UN/ECE Rec 20 with Rec 21 extension."
}

Rule ids the engine reports for 01.06_minimal_test_ubl.xml untouched: BR-DE-TMP-32. After the edit, BR-CL-23 is reported that was not before.

Two honest limits. This is an edit that provokes the rule, not the only one, and it is not a claim about how real exporters break it. And it is the reverse of the fix a reader needs: the passing block is the vendored file's own content, which is what a conformant invoice looks like here.

Reproduce it yourself: take corpus/xrechnung-testsuite/src/test/technical-cases/cius/01.06_minimal_test_ubl.xml from this repository, apply the one edit named above, and run einvoice validate 01.06_minimal_test_ubl.xml --profile xrechnung --format json, or paste the file into the browser validator, which runs this same engine locally with no upload.

Everything here is free and open source — pick up whatever helps, at your own pace: