# BR-23: Add the unitCode attribute to the line quantity

The `cbc:InvoicedQuantity` or `cbc:CreditedQuantity` on each line must carry a `unitCode` attribute naming its unit of measure.

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

## The short answer

`BR-23` fails when the line quantity has no `unitCode` attribute, as in `<cbc:InvoicedQuantity>2</cbc:InvoicedQuantity>`. Add `unitCode` with the UN/ECE Recommendation 20 code for the unit the quantity is counted in, and use the same code on `cac:Price/cbc:BaseQuantity` if the line has one.

The attribute has to sit on the quantity element itself. A `unitCodeListID` without `unitCode`, or a unit mentioned only in the item name or description, does not count.

## What the rule checks

For each `cac:InvoiceLine` or `cac:CreditNoteLine`, the rule passes when the line quantity element carries an attribute named `unitCode`. It checks that the attribute exists, not what it says.

An empty or blank attribute gets past this rule and is caught by others. When tried, `unitCode=""` and `unitCode=" "` each reported `BR-CL-23`, because neither is a recognised code, and `PEPPOL-EN16931-R130`, because neither matches the `C62` on the base quantity. `BR-23` itself was not reported.

A line with no quantity element at all fails here too, together with `BR-22`, because there is no element to carry the attribute.

The unit on `cac:Price/cbc:BaseQuantity` plays no part in this rule. When the unit was removed from both the quantity and the base quantity, only `BR-23` was reported: the Peppol unit comparison runs only on a base quantity that has a unit.

| Term | Meaning | UBL element |
|---|---|---|
| BT-130 | Invoiced quantity unit of measure code | `cac:InvoiceLine/cbc:InvoicedQuantity/@unitCode (cac:CreditNoteLine/cbc:CreditedQuantity/@unitCode in a credit note)` |
| BT-129 | Invoiced quantity | `cac:InvoiceLine/cbc:InvoicedQuantity` |
| BT-150 | Item price base quantity unit of measure code | `cac:InvoiceLine/cac:Price/cbc:BaseQuantity/@unitCode` |

## How an integration ends up here

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

- The source system stores quantities as plain numbers and has no unit field for the export to use.
- The serialiser writes attributes only when a value is present, and the unit is null for service lines or manually entered lines.
- The unit is sent in `unitCodeListID`, or only in the item description, and the `unitCode` attribute is left out.
- A credit note mapper builds `cbc:CreditedQuantity` from scratch and copies the number without the attribute.

## How to fix it

1. Find the line from the finding location, then find the unit of its quantity in the source: the sales unit of the product, or the unit the service is charged in.
2. Map that unit to its UN/ECE Recommendation 20 code, for example `C62` for one, `H87` for piece, `HUR` for hour or `KGM` for kilogram, and write it in upper case to `unitCode` on `cbc:InvoicedQuantity` or `cbc:CreditedQuantity`.
3. Write the same code on `cac:Price/cbc:BaseQuantity` when the line has one. `PEPPOL-EN16931-R130` rejects a base quantity whose unit differs from the quantity unit.
4. Do not answer the Peppol finding by stripping the unit from the base quantity as well. That silences `PEPPOL-EN16931-R130` and leaves `BR-23` exactly where it was.

## 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 quantity is 2 with no unit, while the price is per 1 C62

```xml
<cac:InvoiceLine>
  <cbc:ID>1</cbc:ID>
  <cbc:InvoicedQuantity>2</cbc:InvoicedQuantity>
  <cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
  <!-- item omitted from this fragment -->
  <cac:Price>
    <cbc:PriceAmount currencyID="GBP">12.5</cbc:PriceAmount>
    <cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
  </cac:Price>
</cac:InvoiceLine>
```

Fragment of the corrected invoice: the quantity and the price base quantity are both in C62

```xml
<cac:InvoiceLine>
  <cbc:ID>1</cbc:ID>
  <cbc:InvoicedQuantity unitCode="C62">2</cbc:InvoicedQuantity>
  <cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
  <!-- item omitted from this fragment -->
  <cac:Price>
    <cbc:PriceAmount currencyID="GBP">12.5</cbc:PriceAmount>
    <cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
  </cac:Price>
</cac:InvoiceLine>
```

The only difference is `unitCode="C62"` on `cbc:InvoicedQuantity`, missing in the failing invoice and present in the corrected one. The failing document also reports `PEPPOL-EN16931-R130` at `cbc:BaseQuantity`: the price base quantity says `C62`, and a quantity with no unit cannot match it. Adding the attribute to the quantity clears both.

### What the validator reported

- The failing invoice reports **BR-23** and [PEPPOL-EN16931-R130](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R130.md). The corrected document passes every layer with no findings.
  - [Download the failing XML](https://ironfang.uk/finance/rule-examples/BR-23-invalid.xml)
  - [Download the corrected XML](https://ironfang.uk/finance/rule-examples/invoice-minimal.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 `cbc:InvoicedQuantity` in a UBL `Invoice` and `cbc:CreditedQuantity` in a `CreditNote`. When tried on a credit note, removing the attribute from the credited quantity reported `BR-23` and `PEPPOL-EN16931-R130`, the same pair as the invoice.
- The UBL schema treats `unitCode` as optional, so its absence passes the XSD layer and is left to this rule.
- Which code is acceptable is decided by `BR-CL-23`, not here. Once the attribute exists, a value such as `EACH` or `pcs` moves the finding to that rule.

## Related rules

- [BR-CL-23 checks that the unit code, once present, is a recognised UN/ECE code](https://ironfang.uk/docs/finance/rules/BR-CL-23.md)
- [PEPPOL-EN16931-R130 requires the price base quantity to use the same unit as the line quantity](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R130.md)
- [BR-22 reports a line with no quantity element at all, and fires with this rule](https://ironfang.uk/docs/finance/rules/BR-22.md)
- [BR-21 is the other presence check on the line itself, for the line identifier](https://ironfang.uk/docs/finance/rules/BR-21.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-23](https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-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-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
