The short answer
BR-CL-20 fails when cbc:AllowanceChargeReasonCode on a charge (cbc:ChargeIndicator of true) is not a code from UNTDID 7161, the list of charges and services. Put the code for the service being charged in its place, for example FC (Freight service) where the recorded example wrote FREIGHT, or drop the code and describe the charge in cbc:AllowanceChargeReason.
PEPPOL-EN16931-CL003 checks the same element against the same list at the Peppol layer, so both are reported and both clear together.
What the rule checks
The rule covers every cbc:AllowanceChargeReasonCode in a cac:AllowanceCharge that reads as a charge, at document level and on lines. When tried, FREIGHT on the line charge of the rich invoice was reported at cac:InvoiceLine[1]/cac:AllowanceCharge[2].
Surrounding whitespace is trimmed, and the rest is compared case-sensitively. Lower-case fc failed when tried, and FC passed.
One code per element. FC DL was rejected when tried, since any value with an inner space fails.
Allowance codes do not count here. The UNTDID 5189 code 95 on the document charge failed just as a word would.
An empty cbc:AllowanceChargeReasonCode fails the list check as well, and brings PEPPOL-EN16931-R008 for the empty element.
The reason text is not read. BR-CO-06, which should compare charge code and text, always passes in this release.
| Term | Meaning | UBL element |
|---|---|---|
| BT-105 | Document level charge reason code | cac:AllowanceCharge/cbc:AllowanceChargeReasonCode |
| BT-145 | Invoice line charge reason code | cac:InvoiceLine/cac:AllowanceCharge/cbc:AllowanceChargeReasonCode (cac:CreditNoteLine in a credit note) |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- The charge type from the source system, such as
FREIGHT,POSTAGEorHANDLING, is written straight into the code element. - The code is sent in lower case because the mapping table was typed that way or a formatter lower-cases identifiers.
- One lookup table serves allowances and charges, so a numeric allowance code lands on a charge.
- Several services are billed as one charge and their codes are joined into one value.
How to fix it
- From the finding location, identify the charge:
cac:AllowanceCharge[2]under the root in the recorded example, or an entry inside a line. - Find the UNTDID 7161 entry for the service in the list Peppol publishes. Some entries from it:
FCFreight service,DLDelivery,PCPacking,CGCleaning. - Write the code in upper case, exactly as listed, in
cbc:AllowanceChargeReasonCode, and keep any local description incbc:AllowanceChargeReason. - Where two services are charged, send two
cac:AllowanceChargeelements with one code each, and keep the charge total and the VAT breakdown in step. - If no code describes the charge, omit the code and send the text only.
Validate your corrected invoice
Before and after
These are fragments, not complete documents. The complete synthetic documents they come from are linked below.
Fragment of the failing invoice: the document charge has the word FREIGHT as its reason code
<cac:AllowanceCharge>
<cbc:ChargeIndicator>true</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>FREIGHT</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Example document charge</cbc:AllowanceChargeReason>
<!-- percentage, amount, base amount and tax category omitted from this fragment -->
</cac:AllowanceCharge>Fragment of the corrected invoice: the reason code is CG, a code on the UNTDID 7161 list
<cac:AllowanceCharge>
<cbc:ChargeIndicator>true</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>CG</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Example document charge</cbc:AllowanceChargeReason>
<!-- percentage, amount, base amount and tax category omitted from this fragment -->
</cac:AllowanceCharge>In the failing invoice the document charge has FREIGHT as its reason code, and in the corrected invoice CG, which is on the list. The failing document also reports PEPPOL-EN16931-CL003, because the Peppol layer holds its own copy of the UNTDID 7161 check and rejects the same element. CG means Cleaning, so it only illustrates a valid value; for a freight charge the matching code is FC, which passed when tried in the same place.
What the validator reported
- The failing invoice reports BR-CL-20 and
PEPPOL-EN16931-CL003. The corrected document passes every layer with no findings.Download the failing XMLDownload the corrected XML
Recorded on phive 12.1.0 / phive-rules-peppol 4.5.6 / Saxon-HE 12.10, the engine behind the free validator, using synthetic data. A recorded result is regression evidence for these documents; it is not a certification.
Where it applies
- Applies to
InvoiceandCreditNotealike;FREIGHTon the document charge of a credit note reported this rule andPEPPOL-EN16931-CL003when tried. - Allowance codes are checked against UNTDID 5189 by
BR-CL-19. Which of the two lists applies followscbc:ChargeIndicator. - Whether a document-level charge has any reason at all is a separate question, answered by
BR-CO-22.
Related rules
- BR-CL-19 is the code list check for allowance reason codes
- BR-CO-22 requires a document-level charge to have a reason code or text
- BR-CO-12 checks the charge total, which a split charge must still match
- Browse every rule in the reference
- Background: Understanding EN 16931 validation errors
Scope and source
Written for Peppol BIS Billing 3.0.21 (May 2026), EN 16931 1.3.16, as applied to UBL 2.1 Invoice and CreditNote documents. Other profiles, syntaxes and releases can define this identifier differently. Guidance version 2026-09-24.1: source checked 2026-09-24, explanation last updated 2026-09-24.
The official definition of BR-CL-20 carries the normative wording and test. This page is our explanation of it, not a copy.
Guidance does not change the engine verdict. Fixing this finding does not mean the document passes every layer, and validation does not certify legal or tax compliance or transmit a document over Peppol.
