# BR-CO-23: Add a reason to each allowance on an invoice line

An allowance attached to an invoice or credit note line must name its reason with a code, text or both. The price discount inside `cac:Price` is not covered.

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

## The short answer

`BR-CO-23` fails when a `cac:AllowanceCharge` inside `cac:InvoiceLine` (or `cac:CreditNoteLine`) has `cbc:ChargeIndicator` of `false` and carries neither `cbc:AllowanceChargeReasonCode` nor `cbc:AllowanceChargeReason`. Give the line discount its reason: a UNTDID 5189 code, a description, or both.

The same allowance is also reported as `BR-42`, a second EN 16931 rule with an identical test. Adding the reason once satisfies both.

## What the rule checks

The rule looks at each `cac:AllowanceCharge` that is a direct child of a line and reads as an allowance. The allowance is accepted as soon as either reason element exists in it.

Every allowance is judged on its own, so a line with two allowances needs a reason on each, and each line in the document is visited.

The price discount inside `cac:Price` is a different element and is outside this rule. The recorded price-discount example has no reason on its price-level allowance and validates cleanly.

Line charges are outside it too. The rich invoice has a line charge with reason text and no code, which is enough for its own rule, `BR-CO-24`.

An element that exists satisfies the rule even with no content. When tried, an empty `cbc:AllowanceChargeReason` on the line allowance passed `BR-CO-23` and was reported by `PEPPOL-EN16931-R008` instead.

| Term | Meaning | UBL element |
|---|---|---|
| BG-27 | Invoice line allowances | `cac:InvoiceLine/cac:AllowanceCharge[cbc:ChargeIndicator = false] (cac:CreditNoteLine/cac:AllowanceCharge in a credit note)` |
| BT-139 | Invoice line allowance reason | `cac:InvoiceLine/cac:AllowanceCharge/cbc:AllowanceChargeReason` |
| BT-140 | Invoice line allowance reason code | `cac:InvoiceLine/cac:AllowanceCharge/cbc:AllowanceChargeReasonCode` |

## How an integration ends up here

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

- A customer or volume discount is applied per line by a pricing engine that records the percentage and amount but not the discount type.
- Line allowances are generated from the difference between list price and selling price, which leaves no reason to attach.
- The reason is mapped for document-level allowances, but the line-level mapping was written separately and omits it.
- The reason text is trimmed or sanitised to nothing, and the element is then left out.

## How to fix it

1. Find the line and the allowance in the finding location, for example `cac:InvoiceLine[1]/cac:AllowanceCharge[1]`. The second index counts every `cac:AllowanceCharge` on that line, charges included.
2. Carry the discount type through from the pricing record that produced the allowance.
3. Where the type matches a UNTDID 5189 entry, write that code in `cbc:AllowanceChargeReasonCode` (`95` for a plain discount). Add `cbc:AllowanceChargeReason` with the description, or send the text alone when no code fits.
4. Keep the elements in UBL order, straight after `cbc:ChargeIndicator` and before `cbc:MultiplierFactorNumeric` and `cbc:Amount`. No amount on the line needs recalculating.

## 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 5 per cent allowance on line 1 has no reason, the line charge beside it has one

```xml
<cac:InvoiceLine>
  <cbc:ID>1</cbc:ID>
  <!-- note, quantity, line net amount, accounting cost and order line reference omitted from this fragment -->
  <cac:AllowanceCharge>
    <cbc:ChargeIndicator>false</cbc:ChargeIndicator>
    <cbc:MultiplierFactorNumeric>5</cbc:MultiplierFactorNumeric>
    <cbc:Amount currencyID="GBP">2.00</cbc:Amount>
    <cbc:BaseAmount currencyID="GBP">40.00</cbc:BaseAmount>
  </cac:AllowanceCharge>
  <cac:AllowanceCharge>
    <cbc:ChargeIndicator>true</cbc:ChargeIndicator>
    <cbc:AllowanceChargeReason>Example charge</cbc:AllowanceChargeReason>
    <cbc:Amount currencyID="GBP">0.01</cbc:Amount>
  </cac:AllowanceCharge>
  <!-- item and price omitted from this fragment -->
</cac:InvoiceLine>
```

Fragment of the corrected invoice: the line allowance carries reason code 95 and a reason text

```xml
<cac:InvoiceLine>
  <cbc:ID>1</cbc:ID>
  <!-- note, quantity, line net amount, accounting cost and order line reference omitted from this fragment -->
  <cac:AllowanceCharge>
    <cbc:ChargeIndicator>false</cbc:ChargeIndicator>
    <cbc:AllowanceChargeReasonCode>95</cbc:AllowanceChargeReasonCode>
    <cbc:AllowanceChargeReason>Example discount</cbc:AllowanceChargeReason>
    <cbc:MultiplierFactorNumeric>5</cbc:MultiplierFactorNumeric>
    <cbc:Amount currencyID="GBP">2.00</cbc:Amount>
    <cbc:BaseAmount currencyID="GBP">40.00</cbc:BaseAmount>
  </cac:AllowanceCharge>
  <!-- line charge, item and price omitted from this fragment -->
</cac:InvoiceLine>
```

On line 1 the corrected invoice has reason code `95` and the text `Example discount` on the 5 per cent allowance; the failing invoice has neither, and nothing else changes. The failing document also reports `BR-42`, which applies the same test as `BR-CO-23` to the same allowance, so the one edit clears both. The line charge next to it already had reason text and was not reported.

### What the validator reported

- The failing invoice reports `BR-42` and **BR-CO-23**. The corrected document passes every layer with no findings.
  - [Download the failing XML](https://ironfang.uk/finance/rule-examples/BR-CO-23-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 `cac:InvoiceLine` in an `Invoice` and `cac:CreditNoteLine` in a `CreditNote`. The credit note version of the recorded example reported `BR-42` and `BR-CO-23` at `cac:CreditNoteLine[1]/cac:AllowanceCharge[1]` when tried.
- Only the EN 16931 layer reports a missing reason. When a code is present, the Peppol layer checks it against the allowance list as `PEPPOL-EN16931-CL002`, and the EN 16931 layer as `BR-CL-19`.
- A line allowance amount is part of the line net amount calculation, but adding a reason changes no figure.

## Related rules

- [BR-CO-21 is the same requirement for allowances on the whole document](https://ironfang.uk/docs/finance/rules/BR-CO-21.md)
- [BR-CL-19 checks a line allowance reason code against the UNTDID 5189 list](https://ironfang.uk/docs/finance/rules/BR-CL-19.md)
- [PEPPOL-EN16931-R120 subtracts the line allowance when it checks the line net amount](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R120.md)
- [PEPPOL-EN16931-R040 checks the line allowance amount against its base amount and percentage](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R040.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-23](https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-CO-23/) 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-23)
- [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
