# BR-CO-20: Put a date in every line period, or leave the period out

A `cac:InvoicePeriod` on an invoice or credit note line must hold `cbc:StartDate`, `cbc:EndDate` or both. Unlike the document period, a code alone fails.

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

## The short answer

`BR-CO-20` fails when a `cac:InvoicePeriod` inside a line contains neither `cbc:StartDate` nor `cbc:EndDate`. Put the service dates of that line in it, or drop the element from lines that do not bill for a period.

The recorded example is the empty `<cac:InvoicePeriod/>` a template writes when both dates are null. Peppol rejects that element for being empty as well, under `PEPPOL-EN16931-R008`.

## What the rule checks

The rule visits each line `cac:InvoicePeriod`, in `cac:InvoiceLine` or `cac:CreditNoteLine`, and passes when at least one of the two date elements is there. A start date alone or an end date alone is enough; when tried, each passed every layer.

Nothing else in a line period counts. When tried, a line period holding only `cbc:DescriptionCode` `35` reported `BR-CO-20` with the warning `UBL-CR-523`, and one holding only a `cbc:Description` reported it with `UBL-CR-524`.

The line rule is stricter than the one for the whole document. When tried, a period holding only `cbc:DescriptionCode` `35` passed at document level, where `BR-CO-19` accepts the code as a VAT point date code.

Whitespace does not make the element any less empty. When tried, an opening and a closing `cac:InvoicePeriod` tag on separate lines, with only whitespace between them, reported the same two rules as the recorded example.

Blank date elements never reach this rule: an empty `cbc:StartDate` or `cbc:EndDate` is not a valid date in the UBL schema, and when tried the XSD layer failed before any business rule ran.

| Term | Meaning | UBL element |
|---|---|---|
| BG-26 | Invoice line period | `cac:InvoiceLine/cac:InvoicePeriod (cac:CreditNoteLine/cac:InvoicePeriod in a credit note)` |
| BT-134 | Invoice line period start date | `cac:InvoiceLine/cac:InvoicePeriod/cbc:StartDate (cac:CreditNoteLine/cac:InvoicePeriod/cbc:StartDate in a credit note)` |
| BT-135 | Invoice line period end date | `cac:InvoiceLine/cac:InvoicePeriod/cbc:EndDate (cac:CreditNoteLine/cac:InvoicePeriod/cbc:EndDate in a credit note)` |

## How an integration ends up here

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

- The line template always writes `cac:InvoicePeriod` and fills the dates only for subscription or usage lines, so one-off items get an empty period.
- The serialiser drops null date fields but keeps their parent element.
- A billing period label such as a month name is written to `cbc:Description` on the line instead of being turned into dates.
- The mapping for the document period, which may carry a VAT point date code, was reused for lines, where the code has no place.

## How to fix it

1. Find the line in the finding location, for example `cac:InvoiceLine[1]/cac:InvoicePeriod[1]`.
2. If the line bills a service period, write its first day to `cbc:StartDate` and its last day to `cbc:EndDate` as `YYYY-MM-DD`, or at least the one date the source holds.
3. If the line is not tied to a period, remove `cac:InvoicePeriod` from it. The group is optional on a line.
4. Emit the element only when one of its dates has a value, so that a null can never produce an empty group again.
5. Keep period labels out of the line: `cbc:Description` and `cbc:DescriptionCode` are outside the model there and draw warnings.

## Before and after

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

Fragment of the failing invoice: line 1 has an empty period element

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

Fragment of the corrected invoice: the line period runs from 1 to 15 August

```xml
<cac:InvoiceLine>
  <cbc:ID>1</cbc:ID>
  <cbc:InvoicedQuantity unitCode="C62">2</cbc:InvoicedQuantity>
  <cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
  <cac:InvoicePeriod>
    <cbc:StartDate>2026-08-01</cbc:StartDate>
    <cbc:EndDate>2026-08-15</cbc:EndDate>
  </cac:InvoicePeriod>
  <!-- item and price omitted from this fragment -->
</cac:InvoiceLine>
```

The failing invoice has an empty `<cac:InvoicePeriod/>` on line 1 where the corrected one has a start date of 2026-08-01 and an end date of 2026-08-15; the document period for August is the same in both. Beside `BR-CO-20`, the failing document reports `PEPPOL-EN16931-R008` at the same element, because an element with no content is not allowed. Filling in the dates, or removing the element, clears both.

### What the validator reported

- The failing invoice reports **BR-CO-20** and [PEPPOL-EN16931-R008](https://ironfang.com/docs/finance/rules/PEPPOL-EN16931-R008.md). The corrected document passes every layer with no findings.
  - [Download the failing XML](https://ironfang.com/finance/rule-examples/BR-CO-20-invalid.xml)
  - [Download the corrected XML](https://ironfang.com/finance/rule-examples/period-valid.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`. When tried, an empty period on a `cac:CreditNoteLine` reported `BR-CO-20` and `PEPPOL-EN16931-R008` in the same way.
- The period of the whole document is a matter for `BR-CO-19`: when tried, an empty `cac:InvoicePeriod` under the root reported that rule, not this one.
- Once both dates are present, `BR-30` checks their order, and against a document period `PEPPOL-EN16931-R110` and `PEPPOL-EN16931-R111` check that the line stays inside it.

## Related rules

- [BR-CO-19 is the date requirement for the invoicing period of the whole document](https://ironfang.com/docs/finance/rules/BR-CO-19.md)
- [BR-30 checks that a line period with both dates does not end before it starts](https://ironfang.com/docs/finance/rules/BR-30.md)
- [PEPPOL-EN16931-R008 rejects the empty line period of the recorded example](https://ironfang.com/docs/finance/rules/PEPPOL-EN16931-R008.md)
- [PEPPOL-EN16931-R110 compares each line start date with the start of the invoicing period](https://ironfang.com/docs/finance/rules/PEPPOL-EN16931-R110.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-28.1: source checked 2026-09-28, explanation last updated 2026-09-28.

[The official definition of BR-CO-20](https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-CO-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.

## Links

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