# BR-CO-22: State the reason for each document-level charge

A charge on the whole document, such as freight or packing, must carry a reason code from UNTDID 7161, a reason text, or both.

- Layer: EN 16931
- Severity: fatal (the document is invalid)
- Topics: Allowances and charges
- Official definition: https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-CO-22/
- Explanation last updated: 2026-09-24

## The short answer

`BR-CO-22` fails when a root-level `cac:AllowanceCharge` marked as a charge, with `cbc:ChargeIndicator` of `true`, does not say what the charge is for. Add `cbc:AllowanceChargeReasonCode` with the UNTDID 7161 code for the service being charged, `cbc:AllowanceChargeReason` with a description, or both.

Each charge without a reason is reported twice: once as `BR-CO-22` and once as `BR-38`, which applies the identical test. They share one fix.

## What the rule checks

Only `cac:AllowanceCharge` elements that are direct children of the `Invoice` or `CreditNote` root and are flagged as charges are examined. Each must contain at least one of the two reason elements.

A code without text passes, and so does text without a code: both variants of the corrected invoice were accepted when tried.

Presence is all this rule looks for. An empty `cbc:AllowanceChargeReasonCode` got past it when tried, but was then reported by `BR-CL-20`, `PEPPOL-EN16931-CL003` and `PEPPOL-EN16931-R008`.

Charges on a line have their own pair of rules, `BR-CO-24` and `BR-44`. Removing the reason text from the line charge of the rich invoice reported those two, not `BR-CO-22`.

The wording of the text is never judged against the code. `BR-CO-06`, which exists for that purpose, has a test that always passes in this release.

| Term | Meaning | UBL element |
|---|---|---|
| BG-21 | Document level charges | `cac:AllowanceCharge[cbc:ChargeIndicator = true]` |
| BT-104 | Document level charge reason | `cac:AllowanceCharge/cbc:AllowanceChargeReason` |
| BT-105 | Document level charge reason code | `cac:AllowanceCharge/cbc:AllowanceChargeReasonCode` |

## How an integration ends up here

Possible causes, from the shape of the rule rather than from measured usage:

- Freight or handling is added as a fixed fee by a shipping module that stores an amount and a VAT category but no service type.
- The charge description lives in the printed layout template and is not part of the data passed to the XML mapping.
- A surcharge is applied automatically, for example on small orders, by a pricing rule that has no name to pass on.
- Blank reason fields are dropped by the serialiser, and the reason was blank on this charge.

## How to fix it

1. Locate the charge from the finding. In the recorded example it is `cac:AllowanceCharge[2]`, because the index counts allowances and charges under the root together.
2. Establish what service or cost the charge recovers, from the order, the shipping record or the pricing rule that added it.
3. Look the service up in the UNTDID 7161 charge list that Peppol publishes; `FC` is Freight service, `DL` is Delivery and `PC` is Packing, for example. Write the matching code in `cbc:AllowanceChargeReasonCode`, and if nothing fits, send `cbc:AllowanceChargeReason` text alone.
4. Place the code straight after `cbc:ChargeIndicator` and the text after the code, both before `cbc:MultiplierFactorNumeric` and `cbc:Amount`, or the XSD layer rejects the document.
5. Record the reason where the charge is created in the source system, so later documents do not repeat the gap.

## Before and after

These are fragments, not complete documents. The complete synthetic documents they come from are linked below.

Fragment of the failing invoice: a 10 per cent document charge with no reason code and no reason text

```xml
<cac:AllowanceCharge>
  <cbc:ChargeIndicator>true</cbc:ChargeIndicator>
  <cbc:MultiplierFactorNumeric>10</cbc:MultiplierFactorNumeric>
  <cbc:Amount currencyID="GBP">3.50</cbc:Amount>
  <cbc:BaseAmount currencyID="GBP">35.00</cbc:BaseAmount>
  <!-- tax category omitted from this fragment -->
</cac:AllowanceCharge>
```

Fragment of the corrected invoice: the same charge with reason code CG and a reason text

```xml
<cac:AllowanceCharge>
  <cbc:ChargeIndicator>true</cbc:ChargeIndicator>
  <cbc:AllowanceChargeReasonCode>CG</cbc:AllowanceChargeReasonCode>
  <cbc:AllowanceChargeReason>Example document charge</cbc:AllowanceChargeReason>
  <cbc:MultiplierFactorNumeric>10</cbc:MultiplierFactorNumeric>
  <cbc:Amount currencyID="GBP">3.50</cbc:Amount>
  <cbc:BaseAmount currencyID="GBP">35.00</cbc:BaseAmount>
  <!-- tax category omitted from this fragment -->
</cac:AllowanceCharge>
```

Two elements are missing from the document charge in the failing invoice: `cbc:AllowanceChargeReasonCode` with `CG` and `cbc:AllowanceChargeReason` with `Example document charge`. The failing document reports `BR-38` beside `BR-CO-22` because the two rules test the same thing on the same charge, so restoring the reason clears both. `CG` means Cleaning in UNTDID 7161 and the recorded text is generic; a real charge should carry the code and wording for its own service.

### What the validator reported

- The failing invoice reports `BR-38` and **BR-CO-22**. The corrected document passes every layer with no findings.
  - [Download the failing XML](https://ironfang.uk/finance/rule-examples/BR-CO-22-invalid.xml)
  - [Download the corrected XML](https://ironfang.uk/finance/rule-examples/invoice-rich.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 `Invoice` and `CreditNote`. A credit note built the same way reported `BR-38` and `BR-CO-22` on the same charge when tried.
- The EN 16931 layer reads `cbc:ChargeIndicator` as a boolean. A charge written with `1` instead of `true` still counts as a charge here: when tried without a reason, it reported this rule and `BR-38`, alongside `PEPPOL-EN16931-R043` for the spelling.
- Whether a code belongs to the charge list is a separate question, answered by `BR-CL-20`.

## Related rules

- [BR-CO-21 is the matching requirement for document-level allowances](https://ironfang.uk/docs/finance/rules/BR-CO-21.md)
- [BR-CL-20 checks a charge reason code against the UNTDID 7161 list](https://ironfang.uk/docs/finance/rules/BR-CL-20.md)
- [BR-CO-12 checks that the charge amounts add up to the charge total](https://ironfang.uk/docs/finance/rules/BR-CO-12.md)
- [PEPPOL-EN16931-R043 requires the charge indicator to be written as true or false](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R043.md)

## 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-CO-22](https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-CO-22/) 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.

## Links

- [This rule as a web page](https://ironfang.uk/docs/finance/rules/BR-CO-22)
- [Free Peppol invoice validator](https://ironfang.uk/tools/peppol-validator)
- [Rule index](https://ironfang.uk/docs/finance/rules.md)
- [Ironfang Finance API docs](https://ironfang.uk/docs/finance)
- The same rule is available to MCP clients as the tool `finance.rule.get` on https://mcp.ironfang.uk/mcp
