The core EN 16931 rules — which fields an invoice has to carry
The BR family is the base layer of EN 16931: it does not check whether your numbers are right, but whether the fields that make a document an invoice under European law are present at all.
59 rules · 59 explained · Rule set v2026-08-31
Why nearly every BR failure has the same cause
These rules almost never fail alone. Map an existing PDF invoice template into XML and the fields that were never visible on paper have no source in the system either — and you get five or ten BR findings at once.
So the fastest route is not to work through them one at a time but to lay the mapping against the mandatory-field list once. The fields that surface are nearly always the same ones: BT-24 (specification identifier), BT-3 (invoice type), BT-5 (currency) and the seller’s tax details.
What these rules do not check
An invoice that passes every BR rule is formally complete and can still be substantively wrong: the wrong tax rate, the wrong description of the supply, the wrong recipient. The BMF letter of 15 October 2025 calls that its own class of error, and no technical check detects it.
The converse also holds: a BR breach matters for VAT only insofar as it touches a mandatory particular. A missing specification identifier makes the document technically unusable and says nothing about the input tax deduction.
All core rules
With the official rule text. Rules with a written explanation carry their cause and fix directly beneath.
BR-01
Specification identifier (BT-24) missing
An Invoice shall have a Specification identifier (BT-24).
- What the rule requires
- The invoice must carry a specification identifier (BT-24), which tells the recipient which rule set the invoice was built against.
- Why it fires
- Two causes, and the second is the nastier one. Either the field is missing entirely — it was never visible on a paper invoice, so nothing in the source system fills it. Or it carries an identifier inherited from an older template, in which case the invoice is checked against a rule set you did not mean and the findings match nothing.
- How to fix it
- Set BT-24 to the identifier of the version you actually produce. For XRechnung 3.0 that is urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0 — the namespace is xeinkauf.de. For a plain EN 16931 invoice with no national flavour, urn:cen.eu:en16931:2017 is enough. Identifiers from earlier versions look almost identical and differ only in namespace and version number, so an inherited one is nearly invisible on reading: compare it character by character. The value goes in cbc:CustomizationID in UBL, ram:GuidelineSpecifiedDocumentContextParameter/ram:ID in CII.
- What the validator checks
UBLnormalize-space(cbc:CustomizationID) != ''CIInormalize-space(rsm:ExchangedDocumentContext/ram:GuidelineSpecifiedDocumentContextParameter/ram:ID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cbc:ID>RE-2026-0042</cbc:ID> <cbc:IssueDate>2026-08-26</cbc:IssueDate> <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>Fixed <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</cbc:CustomizationID> <cbc:ID>RE-2026-0042</cbc:ID> <cbc:IssueDate>2026-08-26</cbc:IssueDate> <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>BR-02
Invoice number (BT-1) missing
An Invoice shall have an Invoice number (BT-1).
- What the rule requires
- The invoice must carry an invoice number (BT-1) — the identifier the recipient books it under, pays against and refers back to. A reminder, a credit note and a remittance advice all point at it later.
- Why it fires
- In practice almost always a mapping error: the field is named differently internally and never populated on export, or the number is assigned only after the document is generated.
- How to fix it
- Populate BT-1 with your invoice number before generating the document — cbc:ID at document level in UBL, ram:ID inside ExchangedDocument in CII. Watch the data type while you do: BT-1 is text, not a number. Route it through a numeric field on the way and leading zeros disappear — “RE-0042” becomes 42, and the invoice goes out carrying a number that does not exist in your books. No rule catches that; the reconciliation does.
- What the validator checks
UBLnormalize-space(cbc:ID) != ''CIInormalize-space(rsm:ExchangedDocument/ram:ID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</cbc:CustomizationID> <cbc:IssueDate>2026-08-26</cbc:IssueDate>Fixed <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</cbc:CustomizationID> <cbc:ID>RE-2026-0042</cbc:ID> <cbc:IssueDate>2026-08-26</cbc:IssueDate>BR-03
Issue date (BT-2) missing
An Invoice shall have an Invoice issue date (BT-2).
- What the rule requires
- The invoice must carry an issue date (BT-2) — the date it was issued, not the date of supply and not the date it was sent.
- Why it fires
- Usually a format problem rather than a missing value, and it behaves differently per syntax. In UBL, BR-03 only checks that the field is not empty; a timestamp instead of a date is rejected by the schema, not by this rule. In CII, BR-03 itself fires.
- How to fix it
- Send BT-2 as YYYY-MM-DD in UBL. In CII, write the value as eight digits and set format="102" on the udt:DateTimeString — the expression behind BR-03 selects exactly that element, so with 610 there it finds nothing and reports a missing issue date. It is not only the BR-TMP-7 warning that fires: the invoice is refused.
- What the validator checks
UBLnormalize-space(cbc:IssueDate) != ''CIInormalize-space(rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString[@format='102']) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cbc:ID>RE-2026-0042</cbc:ID> <cbc:IssueDate>26.08.2026</cbc:IssueDate>Fixed <cbc:ID>RE-2026-0042</cbc:ID> <cbc:IssueDate>2026-08-26</cbc:IssueDate>BR-04
An Invoice shall have an Invoice type code (BT-3).
- What the rule requires
- The invoice must carry an invoice type code (BT-3) — the code saying whether this is an invoice, a credit note or a partial invoice.
- Why it fires
- The document type is often a house abbreviation in the source system with no mapping to a standard code. Where the mapping is missing, nothing gets written in preference to something wrong.
- How to fix it
- Set BT-3 to a UNTDID 1001 code — 380 for a commercial invoice, 384 for a correction: cbc:InvoiceTypeCode in UBL, ram:TypeCode in CII. 381 for a credit note is the exception: in CII it goes in that same ram:TypeCode, while in UBL BR-CL-01 permits it only in cbc:CreditNoteTypeCode — a credit note there is a CreditNote document of its own. On which codes are permitted, see BR-DE-17.
- What the validator checks
UBLnormalize-space(cbc:InvoiceTypeCode) != '' or normalize-space(cbc:CreditNoteTypeCode) !=''CIInormalize-space(rsm:ExchangedDocument/ram:TypeCode) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cbc:ID>RE-2026-0042</cbc:ID> <cbc:IssueDate>2026-08-26</cbc:IssueDate>Fixed <cbc:ID>RE-2026-0042</cbc:ID> <cbc:IssueDate>2026-08-26</cbc:IssueDate> <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>BR-05
An Invoice shall have an Invoice currency code (BT-5).
- What the rule requires
- The invoice must name an invoice currency (BT-5). Every amount in the document is read in that currency.
- Why it fires
- A company that invoices only in euro keeps the currency in no field at all — it is printed into the layout. It is then entirely absent from the structured record.
- How to fix it
- Set BT-5 to the ISO 4217 code, EUR on the German market: cbc:DocumentCurrencyCode in UBL, ram:InvoiceCurrencyCode in CII. The amounts additionally carry their own currencyID attribute, which has to agree with it.
- What the validator checks
UBLnormalize-space(cbc:DocumentCurrencyCode) != ''CIInormalize-space(rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceCurrencyCode) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode> <cbc:BuyerReference>04011000-12345-03</cbc:BuyerReference>Fixed <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode> <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode> <cbc:BuyerReference>04011000-12345-03</cbc:BuyerReference>BR-06
An Invoice shall contain the Seller name (BT-27).
- What the rule requires
- The invoice must contain the seller name (BT-27) — the full registered name the company trades under.
- Why it fires
- The sender lives in the letterhead rather than in the invoice data: it is the same on every invoice and so is maintained in the layout instead of the record. It falls out when the XML is produced.
- How to fix it
- Put the registered name in BT-27: cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName in UBL, ram:SellerTradeParty/ram:Name in CII. A differing trading name goes in BT-28 as well.
- What the validator checks
UBLnormalize-space(cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName) != ''CIInormalize-space(rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:Name) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AccountingSupplierParty> <cac:Party> <cac:PostalAddress> <cbc:CityName>Berlin</cbc:CityName> </cac:PostalAddress> </cac:Party> </cac:AccountingSupplierParty>Fixed <cac:AccountingSupplierParty> <cac:Party> <cac:PostalAddress> <cbc:CityName>Berlin</cbc:CityName> </cac:PostalAddress> <cac:PartyLegalEntity> <cbc:RegistrationName>Muster GmbH</cbc:RegistrationName> </cac:PartyLegalEntity> </cac:Party> </cac:AccountingSupplierParty>BR-07
Buyer name (BT-44) missing
An Invoice shall contain the Buyer name (BT-44).
- What the rule requires
- The invoice must contain the buyer name (BT-44) — the name the recipient expects the invoice under.
- Why it fires
- Customer master data usually holds several name fields: display name, search term, invoice recipient. The mapping reads one that is empty for some customers because it is only maintained for searching.
- How to fix it
- Set BT-44 to the name of the invoice recipient: cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName in UBL, ram:BuyerTradeParty/ram:Name in CII. A differing buyer trading name goes in BT-45 as well — cac:PartyName/cbc:Name. Set only there, BT-44 stays empty and BR-07 goes on reporting a missing name while the XML plainly has one.
- What the validator checks
UBLnormalize-space(cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName) != ''CIInormalize-space(rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:Name) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AccountingCustomerParty> <cac:Party> <cac:PostalAddress> <cbc:CityName>Bonn</cbc:CityName> </cac:PostalAddress> </cac:Party> </cac:AccountingCustomerParty>Fixed <cac:AccountingCustomerParty> <cac:Party> <cac:PostalAddress> <cbc:CityName>Bonn</cbc:CityName> </cac:PostalAddress> <cac:PartyLegalEntity> <cbc:RegistrationName>Behörde Bonn</cbc:RegistrationName> </cac:PartyLegalEntity> </cac:Party> </cac:AccountingCustomerParty>BR-08
Seller postal address (BG-5) missing
An Invoice shall contain the Seller postal address.
Wording in CII
An Invoice shall contain the Seller postal address (BG-5).
- What the rule requires
- The invoice must contain the seller postal address (BG-5) — the group holding street, city, post code and country.
- Why it fires
- The same cause as the name: your own address is part of the layout. Sometimes a single address field is mapped without the surrounding group ever being created.
- How to fix it
- Create cac:AccountingSupplierParty/cac:Party/cac:PostalAddress in UBL, ram:SellerTradeParty/ram:PostalTradeAddress in CII. City and post code are additionally required by BR-DE-3 and BR-DE-4, the country by BR-09. No rule in the rule set requires the street itself (BT-35): a seller address of city, post code and country passes, street or no street.
- What the validator checks
UBLexists(cac:AccountingSupplierParty/cac:Party/cac:PostalAddress)CIIrsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:PostalTradeAddress
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:Party> <cac:PartyLegalEntity> <cbc:RegistrationName>Muster GmbH</cbc:RegistrationName> </cac:PartyLegalEntity> </cac:Party>Fixed <cac:Party> <cac:PostalAddress> <cbc:StreetName>Musterstraße 1</cbc:StreetName> <cbc:CityName>Berlin</cbc:CityName> <cbc:PostalZone>10115</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>DE</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress> <cac:PartyLegalEntity> <cbc:RegistrationName>Muster GmbH</cbc:RegistrationName> </cac:PartyLegalEntity> </cac:Party>BR-09
The Seller postal address (BG-5) shall contain a Seller country code (BT-40).
- What the rule requires
- The seller postal address must contain a country code (BT-40) — the two-letter ISO 3166-1 code, not the country's name spelled out.
- Why it fires
- Addresses live in many systems as multi-line free text with the country on the last line. Splitting them into individual fields leaves exactly that line over, because it carries no code.
- How to fix it
- Translate the country into its ISO code once and set it in BT-40: cac:PostalAddress/cac:Country/cbc:IdentificationCode in UBL, ram:PostalTradeAddress/ram:CountryID in CII. DE, not Germany.
- What the validator checks
UBLnormalize-space(cac:Country/cbc:IdentificationCode) != ''CIInormalize-space(rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:PostalTradeAddress/ram:CountryID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:PostalAddress> <cbc:CityName>Berlin</cbc:CityName> <cbc:PostalZone>10115</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>Deutschland</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress>Fixed <cac:PostalAddress> <cbc:CityName>Berlin</cbc:CityName> <cbc:PostalZone>10115</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>DE</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress>BR-10
An Invoice shall contain the Buyer postal address (BG-8).
- What the rule requires
- The invoice must contain the buyer postal address (BG-8) — the counterpart to BG-5 on the recipient's side.
- Why it fires
- On public-sector invoices the address is treated as dispensable because the Leitweg-ID already routes the document. The norm wants it regardless: it identifies who received the service, not where the file goes.
- How to fix it
- Create cac:AccountingCustomerParty/cac:Party/cac:PostalAddress in UBL, ram:BuyerTradeParty/ram:PostalTradeAddress in CII. City and post code are additionally required by BR-DE-8 and BR-DE-9, the country by BR-11.
- What the validator checks
UBLexists(cac:AccountingCustomerParty/cac:Party/cac:PostalAddress)CIIrsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:PostalTradeAddress
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AccountingCustomerParty> <cac:Party> <cac:PartyLegalEntity> <cbc:RegistrationName>Behörde Bonn</cbc:RegistrationName> </cac:PartyLegalEntity> </cac:Party> </cac:AccountingCustomerParty>Fixed <cac:AccountingCustomerParty> <cac:Party> <cac:PostalAddress> <cbc:CityName>Bonn</cbc:CityName> <cbc:PostalZone>53113</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>DE</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress> <cac:PartyLegalEntity> <cbc:RegistrationName>Behörde Bonn</cbc:RegistrationName> </cac:PartyLegalEntity> </cac:Party> </cac:AccountingCustomerParty>BR-11
The Buyer postal address shall contain a Buyer country code (BT-55).
- What the rule requires
- The buyer postal address must contain a country code (BT-55) — the same requirement as BR-09, on the recipient's side.
- Why it fires
- For purely domestic customers the country is frequently not maintained at all, because it goes without saying. What goes without saying sits in no field.
- How to fix it
- Set the ISO 3166-1 code in BT-55: cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cac:Country/cbc:IdentificationCode in UBL, ram:BuyerTradeParty/ram:PostalTradeAddress/ram:CountryID in CII. A mapping default is defensible here.
- What the validator checks
UBLnormalize-space(cac:Country/cbc:IdentificationCode) != ''CIInormalize-space(rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:PostalTradeAddress/ram:CountryID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:PostalAddress> <cbc:CityName>Bonn</cbc:CityName> <cbc:PostalZone>53113</cbc:PostalZone> </cac:PostalAddress>Fixed <cac:PostalAddress> <cbc:CityName>Bonn</cbc:CityName> <cbc:PostalZone>53113</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>DE</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress>BR-12
An Invoice shall have the Sum of Invoice line net amount (BT-106).
- What the rule requires
- The invoice must contain the sum of line net amounts (BT-106). The norm requires each of the totals explicitly — none of them may be left to be inferred from the others.
- Why it fires
- The value is rarely a field in your own invoice object: it follows from the lines and is computed for display. In the document it has to be written out.
- How to fix it
- Write BT-106 into cac:LegalMonetaryTotal/cbc:LineExtensionAmount in UBL, or ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:LineTotalAmount in CII. How the value has to agree with the other totals is covered by BR-CO-10 through BR-CO-16.
- What the validator checks
UBLexists(cbc:LineExtensionAmount)CII(ram:LineTotalAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:LegalMonetaryTotal> <cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount> <cbc:TaxInclusiveAmount currencyID="EUR">1190.00</cbc:TaxInclusiveAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal>Fixed <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount> <cbc:TaxInclusiveAmount currencyID="EUR">1190.00</cbc:TaxInclusiveAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal>BR-13
An Invoice shall have the Invoice total amount without VAT (BT-109).
- What the rule requires
- The invoice must contain the total amount without VAT (BT-109). The norm requires each of the totals explicitly — none of them may be left to be inferred from the others.
- Why it fires
- Where there are no document-level allowances or charges, this looks identical to the line sum, and only one of the two gets written.
- How to fix it
- Write BT-109 into cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount in UBL, or ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:TaxBasisTotalAmount in CII. How the value has to agree with the other totals is covered by BR-CO-10 through BR-CO-16.
- What the validator checks
UBLexists(cbc:TaxExclusiveAmount)CII(ram:TaxBasisTotalAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:TaxInclusiveAmount currencyID="EUR">1190.00</cbc:TaxInclusiveAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal>Fixed <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount> <cbc:TaxInclusiveAmount currencyID="EUR">1190.00</cbc:TaxInclusiveAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal>BR-14
An Invoice shall have the Invoice total amount with VAT (BT-112).
- What the rule requires
- The invoice must contain the total amount with VAT (BT-112). The norm requires each of the totals explicitly — none of them may be left to be inferred from the others.
- Why it fires
- The gross total sits at the bottom of the PDF and is produced there by formatting rather than kept as a data field. It is missing when the mapping runs.
- How to fix it
- Write BT-112 into cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount in UBL, or ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:GrandTotalAmount in CII. How the value has to agree with the other totals is covered by BR-CO-10 through BR-CO-16.
- What the validator checks
UBLexists(cbc:TaxInclusiveAmount)CII(ram:GrandTotalAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal>Fixed <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount> <cbc:TaxInclusiveAmount currencyID="EUR">1190.00</cbc:TaxInclusiveAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal>BR-15
An Invoice shall have the Amount due for payment (BT-115).
- What the rule requires
- The invoice must contain the amount due for payment (BT-115). The norm requires each of the totals explicitly — none of them may be left to be inferred from the others.
- Why it fires
- On invoices without a prepayment the amount due equals the gross total, and an identical value looks redundant. The norm wants it in its own right regardless.
- How to fix it
- Write BT-115 into cac:LegalMonetaryTotal/cbc:PayableAmount in UBL, or ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:DuePayableAmount in CII. How the value has to agree with the other totals is covered by BR-CO-10 through BR-CO-16.
- What the validator checks
UBLexists(cbc:PayableAmount)CII(ram:DuePayableAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount> <cbc:TaxInclusiveAmount currencyID="EUR">1190.00</cbc:TaxInclusiveAmount> </cac:LegalMonetaryTotal>Fixed <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount> <cbc:TaxInclusiveAmount currencyID="EUR">1190.00</cbc:TaxInclusiveAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal>BR-16
Invoice with no line (BG-25)
An Invoice shall have at least one Invoice line (BG-25)
- What the rule requires
- The invoice must contain at least one invoice line (BG-25). A document consisting only of header and total data is not permitted.
- Why it fires
- Appears when the generator builds lines from an item list and that list is empty — typically flat-fee or instalment invoices that are managed internally without line items.
- How to fix it
- Emit exactly one line even for a flat amount: cac:InvoiceLine in UBL, ram:IncludedSupplyChainTradeLineItem in CII. That single line then has to be complete in itself — a line identifier (BR-21), quantity 1 (BR-22), the unit code C62 for “one” (BR-23), a net amount (BR-24), an item name (BR-25) and a net price (BR-26). Create the line and leave those six out and you have traded one error for six.
- What the validator checks
UBLexists(cac:InvoiceLine) or exists(cac:CreditNoteLine)CII//ram:IncludedSupplyChainTradeLineItem
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal>Fixed <cac:LegalMonetaryTotal> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount> </cac:LegalMonetaryTotal> <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">1</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Pauschale</cbc:Name> <cac:ClassifiedTaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:ClassifiedTaxCategory> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">1000.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>BR-17
The Payee name (BT-59) shall be provided in the Invoice, if the Payee (BG-10) is different from the Seller (BG-4)
- What the rule requires
- Where the payee (BG-10) differs from the seller, the payee name (BT-59) must be provided. Who receives the money may not be left open.
- Why it fires
- The group is created because a different bank account has to be sent — factoring, or a group subsidiary collecting centrally. The name is treated as incidental, since the IBAN steers the payment anyway.
- How to fix it
- Set BT-59 in cac:PayeeParty/cac:PartyName/cbc:Name in UBL, ram:PayeeTradeParty/ram:Name in CII — or omit the group entirely where seller and payee are the same party.
- What the validator checks
UBLexists(cac:PartyName/cbc:Name) and (not(cac:PartyName/cbc:Name = ../cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name) and not(cac:PartyIdentification/cbc:ID = ../cac:AccountingSupplierParty/cac:Party/cac:PartyIdentification/cbc:ID) )CII(ram:Name) and (not(ram:Name = ../../ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:Name) and not(ram:ID = ../../ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:ID) and not(ram:SpecifiedLegalOrganization/ram:ID = ../../ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedLegalOrganization/ram:ID))
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:PayeeParty> <cac:PartyIdentification> <cbc:ID>FACTOR-01</cbc:ID> </cac:PartyIdentification> </cac:PayeeParty>Fixed <cac:PayeeParty> <cac:PartyIdentification> <cbc:ID>FACTOR-01</cbc:ID> </cac:PartyIdentification> <cac:PartyName> <cbc:Name>Muster Factoring GmbH</cbc:Name> </cac:PartyName> </cac:PayeeParty>BR-18
The Seller tax representative name (BT-62) shall be provided in the Invoice, if the Seller (BG-4) has a Seller tax representative party (BG-11)
- What the rule requires
- Where the seller has a tax representative (BG-11), the representative's name (BT-62) must be provided.
- Why it fires
- The group is usually created only to hold the representative's VAT identifier, that being the part with tax consequences. The name does not follow it in.
- How to fix it
- Set BT-62 in cac:TaxRepresentativeParty/cac:PartyName/cbc:Name in UBL, ram:SellerTaxRepresentativeTradeParty/ram:Name in CII. The address and VAT identifier are required by BR-19 and BR-56.
- What the validator checks
UBLnormalize-space(cac:PartyName/cbc:Name) != ''CIInormalize-space(ram:Name) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:TaxRepresentativeParty> <cac:PartyTaxScheme> <cbc:CompanyID>DE811234567</cbc:CompanyID> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:PartyTaxScheme> </cac:TaxRepresentativeParty>Fixed <cac:TaxRepresentativeParty> <cac:PartyName> <cbc:Name>Steuerkanzlei Muster</cbc:Name> </cac:PartyName> <cac:PartyTaxScheme> <cbc:CompanyID>DE811234567</cbc:CompanyID> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:PartyTaxScheme> </cac:TaxRepresentativeParty>BR-19
The Seller tax representative postal address (BG-12) shall be provided in the Invoice, if the Seller (BG-4) has a Seller tax representative party (BG-11).
- What the rule requires
- Where the seller has a tax representative, that representative's postal address (BG-12) must be provided — the same requirement as BR-08, for a third party.
- Why it fires
- In master data the representative is often only a name and a tax number; no full address is maintained, because nothing is ever sent to them.
- How to fix it
- Create cac:TaxRepresentativeParty/cac:PostalAddress in UBL, ram:SellerTaxRepresentativeTradeParty/ram:PostalTradeAddress in CII. The country code inside it is required by BR-20.
- What the validator checks
UBLexists(cac:PostalAddress)CII(ram:PostalTradeAddress)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:TaxRepresentativeParty> <cac:PartyName> <cbc:Name>Steuerkanzlei Muster</cbc:Name> </cac:PartyName> </cac:TaxRepresentativeParty>Fixed <cac:TaxRepresentativeParty> <cac:PartyName> <cbc:Name>Steuerkanzlei Muster</cbc:Name> </cac:PartyName> <cac:PostalAddress> <cbc:CityName>Berlin</cbc:CityName> <cbc:PostalZone>10115</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>DE</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress> </cac:TaxRepresentativeParty>BR-20
Tax representative without a country (BT-69)
The Seller tax representative postal address (BG-12) shall contain a Tax representative country code (BT-69), if the Seller (BG-4) has a Seller tax representative party (BG-11).
- What the rule requires
- Where the seller has a tax representative, that representative's address must carry a country code (BT-69) — the same requirement as BR-09 and BR-11, for a third party.
- Why it fires
- As with the other two addresses: the country sits as free text on the last address line, or is not carried at all because it goes without saying.
- How to fix it
- Set the ISO 3166-1 code in cac:TaxRepresentativeParty/cac:PostalAddress/cac:Country/cbc:IdentificationCode in UBL, or the representative's ram:PostalTradeAddress/ram:CountryID in CII. If you have no tax representative at all, the country code is the wrong answer: delete the BG-11 group and BR-18, BR-19 and BR-20 fall away together.
- What the validator checks
UBLnormalize-space(cac:Country/cbc:IdentificationCode) != ''CIInormalize-space(ram:PostalTradeAddress/ram:CountryID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:TaxRepresentativeParty> <cac:PostalAddress> <cbc:CityName>Berlin</cbc:CityName> <cbc:PostalZone>10115</cbc:PostalZone> </cac:PostalAddress> </cac:TaxRepresentativeParty>Fixed <cac:TaxRepresentativeParty> <cac:PostalAddress> <cbc:CityName>Berlin</cbc:CityName> <cbc:PostalZone>10115</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>DE</cbc:IdentificationCode> </cac:Country> </cac:PostalAddress> </cac:TaxRepresentativeParty>BR-21
Each Invoice line (BG-25) shall have an Invoice line identifier (BT-126).
- What the rule requires
- Every invoice line (BG-25) must carry a line identifier (BT-126). It is the anchor the recipient uses to discuss or dispute a single row.
- Why it fires
- Lines are built as a list whose order implicitly is the number. The index is not written out when serialising, because it was never a field on the source object.
- How to fix it
- Assign an identifier unique within the invoice in BT-126: cbc:ID inside cac:InvoiceLine in UBL, ram:AssociatedDocumentLineDocument/ram:LineID in CII. A counter from 1 is enough.
- What the validator checks
UBLnormalize-space(cbc:ID) != ''CIInormalize-space(ram:AssociatedDocumentLineDocument/ram:LineID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>BR-22
Each Invoice line (BG-25) shall have an Invoiced quantity (BT-129).
- What the rule requires
- Every invoice line must carry an invoiced quantity (BT-129) — including where the line is a flat fee.
- Why it fires
- For services and flat fees the source system holds no quantity, only an amount. The field stays empty because there seems to be nothing to say in it.
- How to fix it
- For a flat fee write a quantity of 1 and the amount as the unit price: cbc:InvoicedQuantity in UBL, ram:BilledQuantity in CII. Its unit of measure is required by BR-23.
- What the validator checks
UBLexists(cbc:InvoicedQuantity) or exists(cbc:CreditedQuantity)CII(ram:SpecifiedLineTradeDelivery/ram:BilledQuantity)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>BR-23
An Invoice line (BG-25) shall have an Invoiced quantity unit of measure code (BT-130).
- What the rule requires
- The invoiced quantity must carry a unit of measure (BT-130) — a code from UN/ECE Recommendation 20, not your item master's own abbreviation.
- Why it fires
- Systems keep units as text: pcs, hrs, kg. The code is a translation that has to be built in the mapping first, and without it the attribute stays empty.
- How to fix it
- Set the unitCode attribute on cbc:InvoicedQuantity in UBL, ram:BilledQuantity in CII: C62 for pieces, HUR for hours, KGM for kilograms, MTR for metres.
- What the validator checks
UBLexists(cbc:InvoicedQuantity/@unitCode) or exists(cbc:CreditedQuantity/@unitCode)CII(ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity>10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>BR-24
Each Invoice line (BG-25) shall have an Invoice line net amount (BT-131).
- What the rule requires
- Every invoice line must carry a net amount (BT-131) — the amount of the row before VAT.
- Why it fires
- The amount is computed from quantity and price for display and so is not kept as a field of its own. The document needs it written out, and written so that it adds up.
- How to fix it
- Write the row amount into cbc:LineExtensionAmount in UBL, ram:LineTotalAmount in CII. How it must follow from quantity, price and discounts is checked by PEPPOL-EN16931-R120.
- What the validator checks
UBLexists(cbc:LineExtensionAmount)CII(ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeSettlementLineMonetarySummation/ram:LineTotalAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>BR-25
Item name (BT-153) missing
Each Invoice line (BG-25) shall contain the Item name (BT-153).
- What the rule requires
- Every invoice line must carry an item name (BT-153). Without one the row states an amount for something unnamed.
- Why it fires
- The description comes from a long-text field that is allowed to be empty, while the internal article number is always populated. The mapping reads the long text and finds nothing.
- How to fix it
- Put a short description in cac:Item/cbc:Name in UBL, ram:SpecifiedTradeProduct/ram:Name in CII. The full text belongs beside it in BT-154 and the article number in BT-155.
- What the validator checks
UBLnormalize-space(cac:Item/cbc:Name) != ''CIInormalize-space(ram:SpecifiedTradeProduct/ram:Name) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>BR-26
Each Invoice line (BG-25) shall contain the Item net price (BT-146).
- What the rule requires
- Every invoice line must carry an item net price (BT-146) — the price per unit after any price discount.
- Why it fires
- Flat fees, and lines that know only a total, carry no unit price. The row then has an amount and nothing it could have come from.
- How to fix it
- Write the unit price into cac:Price/cbc:PriceAmount in UBL, ram:NetPriceProductTradePrice/ram:ChargeAmount in CII. For a flat fee the unit price is the total, at a quantity of 1.
- What the validator checks
UBLexists(cac:Price/cbc:PriceAmount)CII(ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity> <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> </cac:Item> <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price> </cac:InvoiceLine>BR-27
Negative net price (BT-146)
The Item net price (BT-146) shall NOT be negative.
- What the rule requires
- A line's net price may not be negative. A minus belongs in the quantity or in a credit note, never in the price.
- Why it fires
- Reversal rows and rebates are readily modelled as a negative price, because that works immediately in your own total. The norm provides other means for it.
- How to fix it
- Model a rebate as an allowance on the line (BG-27), or issue a credit note. In CII that carries type 381 in ram:TypeCode. In UBL it does not: BR-CL-01 permits 381 only in cbc:CreditNoteTypeCode — that is, in a CreditNote document — and set in cbc:InvoiceTypeCode it reports an invalid code. The price in cac:Price/cbc:PriceAmount stays positive either way.
- What the validator checks
UBL(cac:Price/cbc:PriceAmount) >= 0CII(ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount) >= 0
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:Price> <cbc:PriceAmount currencyID="EUR">-100.00</cbc:PriceAmount> </cac:Price>Fixed <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> </cac:Price>BR-28
Negative gross price (BT-148)
The Item gross price (BT-148) shall NOT be negative.
- What the rule requires
- A line's gross price (BT-148) may not be negative — the list price before the discount, not the net price of BR-27. Unlike that one it is optional: with no gross price there is nothing for BR-28 to report.
- Why it fires
- Whoever sets the net price negative usually sets the gross price negative too, both coming from one source. The two rules therefore almost always report together.
- How to fix it
- Keep cac:Price/cac:AllowanceCharge/cbc:BaseAmount in UBL, or ram:GrossPriceProductTradePrice/ram:ChargeAmount in CII, positive, and put the sign where the norm provides for it — see BR-27. Once a gross price is sent, PEPPOL-EN16931-R046 also requires it to reconcile: net price = gross price − discount.
- What the validator checks
UBL(cac:Price/cac:AllowanceCharge/cbc:BaseAmount) >= 0 or not(exists(cac:Price/cac:AllowanceCharge/cbc:BaseAmount))CII(ram:SpecifiedLineTradeAgreement/ram:GrossPriceProductTradePrice/ram:ChargeAmount >= 0) or not(ram:SpecifiedLineTradeAgreement/ram:GrossPriceProductTradePrice/ram:ChargeAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">10.00</cbc:Amount> <cbc:BaseAmount currencyID="EUR">-110.00</cbc:BaseAmount> </cac:AllowanceCharge> </cac:Price>Fixed <cac:Price> <cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount> <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">10.00</cbc:Amount> <cbc:BaseAmount currencyID="EUR">110.00</cbc:BaseAmount> </cac:AllowanceCharge> </cac:Price>BR-29
If both Invoicing period start date (BT-73) and Invoicing period end date (BT-74) are given then the Invoicing period end date (BT-74) shall be later or equal to the Invoicing period start date (BT-73).
- What the rule requires
- Where both the start and the end of the invoicing period are given, the end must fall on or after the start.
- Why it fires
- The period is filled from two fields maintained separately. A typo in the year, or an end date left over from the previous month, reverses the order.
- How to fix it
- Check the order before writing cac:InvoicePeriod in UBL, ram:BillingSpecifiedPeriod in CII. A single-day period with identical start and end is permitted.
- What the validator checks
UBL(exists(cbc:EndDate) and exists(cbc:StartDate) and xs:date(cbc:EndDate) >= xs:date(cbc:StartDate)) or not(exists(cbc:StartDate)) or not(exists(cbc:EndDate))CII(ram:EndDateTime/udt:DateTimeString[@format = '102']) >= (ram:StartDateTime/udt:DateTimeString[@format = '102']) or not (ram:EndDateTime) or not (ram:StartDateTime)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoicePeriod> <cbc:StartDate>2026-08-01</cbc:StartDate> <cbc:EndDate>2026-07-31</cbc:EndDate> </cac:InvoicePeriod>Fixed <cac:InvoicePeriod> <cbc:StartDate>2026-08-01</cbc:StartDate> <cbc:EndDate>2026-08-31</cbc:EndDate> </cac:InvoicePeriod>BR-30
If both Invoice line period start date (BT-134) and Invoice line period end date (BT-135) are given then the Invoice line period end date (BT-135) shall be later or equal to the Invoice line period start date (BT-134).
- What the rule requires
- Where both the start and the end of a line period are given, the end must fall on or after the start — the same check as BR-29, per line.
- Why it fires
- Line periods are produced in a loop out of contract data. Where a contract runs across a year boundary, the year on one of the two dates is readily computed wrong.
- How to fix it
- Check the order per line before writing cac:InvoicePeriod inside cac:InvoiceLine. That the period must also fit inside the invoicing period is required by PEPPOL-EN16931-R110 and R111.
- What the validator checks
UBL(exists(cbc:EndDate) and exists(cbc:StartDate) and xs:date(cbc:EndDate) >= xs:date(cbc:StartDate)) or not(exists(cbc:StartDate)) or not(exists(cbc:EndDate))CII(ram:EndDateTime/udt:DateTimeString[@format = '102']) >= (ram:StartDateTime/udt:DateTimeString[@format = '102']) or not (ram:EndDateTime) or not (ram:StartDateTime)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:InvoicePeriod> <cbc:StartDate>2026-12-01</cbc:StartDate> <cbc:EndDate>2026-01-31</cbc:EndDate> </cac:InvoicePeriod> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:InvoicePeriod> <cbc:StartDate>2026-12-01</cbc:StartDate> <cbc:EndDate>2027-01-31</cbc:EndDate> </cac:InvoicePeriod> </cac:InvoiceLine>BR-31
Each Document level allowance (BG-20) shall have a Document level allowance amount (BT-92).
- What the rule requires
- Every document-level allowance (BG-20) must carry an amount (BT-92). An allowance or charge without an amount changes nothing, and then does not belong in the document.
- Why it fires
- The discount is already folded into the line amounts and the group is created only as an explanation — with a reason, but no amount.
- How to fix it
- Write the amount into cbc:Amount inside cac:AllowanceCharge in UBL, ram:ActualAmount inside ram:SpecifiedTradeAllowanceCharge in CII. On how amount, base amount and percentage relate, see PEPPOL-EN16931-R040.
- What the validator checks
UBLexists(cbc:Amount)CII(../ram:ActualAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Rabatt</cbc:AllowanceChargeReason> </cac:AllowanceCharge>Fixed <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Rabatt</cbc:AllowanceChargeReason> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge>BR-32
Each Document level allowance (BG-20) shall have a Document level allowance VAT category code (BT-95).
- What the rule requires
- Every document-level allowance (BG-20) must carry a VAT category code (BT-95). It decides which block of the VAT breakdown the amount flows into.
- Why it fires
- The allowance or charge is thought of as a bare amount rather than as something taxable. Only the breakdown needs the classification — and then fails to find it.
- How to fix it
- Set BT-95 in cac:AllowanceCharge/cac:TaxCategory/cbc:ID together with cbc:Percent and cac:TaxScheme in UBL, or ram:CategoryTradeTax in CII. It is the same category code as on the lines — S, E, Z, AE.
- What the validator checks
UBLexists(cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID)CII(../ram:CategoryTradeTax[upper-case(ram:TypeCode) = 'VAT']/ram:CategoryCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge>Fixed <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:AllowanceCharge>BR-33
Each Document level allowance (BG-20) shall have a Document level allowance reason (BT-97) or a Document level allowance reason code (BT-98).
- What the rule requires
- Every document-level allowance (BG-20) must carry a reason — in plain text (BT-97), as a code (BT-98), or both. An amount with no justification is something the recipient cannot check.
- Why it fires
- This rule and BR-CO-21 are one check written twice in the norm. They always report together: a single missing reason produces two error codes, not two errors.
- How to fix it
- Set cbc:AllowanceChargeReason or cbc:AllowanceChargeReasonCode inside cac:AllowanceCharge in UBL, ram:Reason or ram:ReasonCode in CII. That clears BR-CO-21 at the same time. On the code and the text agreeing, see BR-CO-05 — a rule the validator never reports.
- What the validator checks
UBLexists(cbc:AllowanceChargeReason) or exists(cbc:AllowanceChargeReasonCode)CII(../ram:Reason) or (../ram:ReasonCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge>Fixed <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Mengenrabatt</cbc:AllowanceChargeReason> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge>BR-36
Each Document level charge (BG-21) shall have a Document level charge amount (BT-99).
- What the rule requires
- Every document-level charge (BG-21) must carry an amount (BT-99). An allowance or charge without an amount changes nothing, and then does not belong in the document.
- Why it fires
- Freight and packaging are carried as text in the invoice footer. The group is built from that text, and the amount stays inside it rather than in a field of its own.
- How to fix it
- Write the amount into cbc:Amount inside cac:AllowanceCharge in UBL, ram:ActualAmount inside ram:SpecifiedTradeAllowanceCharge in CII. On how amount, base amount and percentage relate, see PEPPOL-EN16931-R040.
- What the validator checks
UBLexists(cbc:Amount)CII(../ram:ActualAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Versandkosten</cbc:AllowanceChargeReason> </cac:AllowanceCharge>Fixed <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Versandkosten</cbc:AllowanceChargeReason> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge>BR-37
Each Document level charge (BG-21) shall have a Document level charge VAT category code (BT-102).
- What the rule requires
- Every document-level charge (BG-21) must carry a VAT category code (BT-102). It decides which block of the VAT breakdown the amount flows into.
- Why it fires
- The allowance or charge is thought of as a bare amount rather than as something taxable. Only the breakdown needs the classification — and then fails to find it.
- How to fix it
- Set BT-102 in cac:AllowanceCharge/cac:TaxCategory/cbc:ID together with cbc:Percent and cac:TaxScheme in UBL, or ram:CategoryTradeTax in CII. It is the same category code as on the lines — S, E, Z, AE.
- What the validator checks
UBLexists(cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID)CII(../ram:CategoryTradeTax[upper-case(ram:TypeCode) = 'VAT']/ram:CategoryCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge>Fixed <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:AllowanceCharge>BR-38
Each Document level charge (BG-21) shall have a Document level charge reason (BT-104) or a Document level charge reason code (BT-105).
- What the rule requires
- Every document-level charge (BG-21) must carry a reason — in plain text (BT-104), as a code (BT-105), or both. An amount with no justification is something the recipient cannot check.
- Why it fires
- This rule and BR-CO-22 are one check written twice in the norm. They always report together: a single missing reason produces two error codes, not two errors.
- How to fix it
- Set cbc:AllowanceChargeReason or cbc:AllowanceChargeReasonCode inside cac:AllowanceCharge in UBL, ram:Reason or ram:ReasonCode in CII. That clears BR-CO-22 at the same time. On the code and the text agreeing, see BR-CO-06 — a rule the validator never reports.
- What the validator checks
UBLexists(cbc:AllowanceChargeReason) or exists(cbc:AllowanceChargeReasonCode)CII(../ram:Reason) or (../ram:ReasonCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge>Fixed <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Versandkosten</cbc:AllowanceChargeReason> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge>BR-41
Each Invoice line allowance (BG-27) shall have an Invoice line allowance amount (BT-136).
- What the rule requires
- Every invoice line allowance (BG-27) must carry an amount (BT-136). An allowance or charge without an amount changes nothing, and then does not belong in the document.
- Why it fires
- Line discounts arrive from pricing as a percentage. The percentage is carried through and the amount that follows from it is never worked out.
- How to fix it
- Write the amount into cbc:Amount inside cac:AllowanceCharge in UBL, ram:ActualAmount inside ram:SpecifiedTradeAllowanceCharge in CII. On how amount, base amount and percentage relate, see PEPPOL-EN16931-R040.
- What the validator checks
UBLexists(cbc:Amount)CII(../ram:ActualAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Rabatt</cbc:AllowanceChargeReason> </cac:AllowanceCharge> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Rabatt</cbc:AllowanceChargeReason> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge> </cac:InvoiceLine>BR-42
Line allowance with no reason
Each Invoice line allowance (BG-27) shall have an Invoice line allowance reason (BT-139) or an Invoice line allowance reason code (BT-140).
- What the rule requires
- Every invoice line allowance (BG-27) must carry a reason — in plain text (BT-139), as a code (BT-140), or both. An amount with no justification is something the recipient cannot check.
- Why it fires
- This rule and BR-CO-23 are one check written twice in the norm. They always report together: a single missing reason produces two error codes, not two errors.
- How to fix it
- Set cbc:AllowanceChargeReason or cbc:AllowanceChargeReasonCode inside cac:AllowanceCharge in UBL, ram:Reason or ram:ReasonCode in CII. That clears BR-CO-23 at the same time. Choose the code and BR-CL-19 applies as well: only 19 values from UNCL 5189 are permitted for it. The plain text is bound to no list — write that if in doubt. On the code and the text agreeing, see BR-CO-07 — a rule the validator never reports.
- What the validator checks
UBLexists(cbc:AllowanceChargeReason) or exists(cbc:AllowanceChargeReasonCode)CII(../ram:Reason) or (../ram:ReasonCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:AllowanceCharge> <cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Mengenrabatt</cbc:AllowanceChargeReason> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge> </cac:InvoiceLine>BR-43
Line charge with no amount (BT-141)
Each Invoice line charge (BG-28) shall have an Invoice line charge amount (BT-141).
- What the rule requires
- Every invoice line charge (BG-28) must carry an amount (BT-141) — a charge with no amount changes nothing and does not belong in the document.
- Why it fires
- Line charges are rare and often only half-built in the mapping: the group exists, and the amount is bound to a field the source system does not have.
- How to fix it
- Write the amount into cbc:Amount inside cac:AllowanceCharge in UBL, ram:ActualAmount inside ram:SpecifiedTradeAllowanceCharge in CII. On how amount, base amount and percentage relate, see PEPPOL-EN16931-R040.
- What the validator checks
UBLexists(cbc:Amount)CII(../ram:ActualAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Expresszuschlag</cbc:AllowanceChargeReason> </cac:AllowanceCharge> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Expresszuschlag</cbc:AllowanceChargeReason> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge> </cac:InvoiceLine>BR-44
Each Invoice line charge shall have an Invoice line charge reason or an invoice line allowance reason code.
Wording in CII
Each Invoice line charge (BG-28) shall have an Invoice line charge reason (BT-144) or an Invoice line charge reason code (BT-145).
- What the rule requires
- Every invoice line charge (BG-28) must carry a reason — in plain text (BT-144), as a code (BT-145), or both. An amount with no justification is something the recipient cannot check.
- Why it fires
- This rule and BR-CO-24 are one check written twice in the norm. They always report together: a single missing reason produces two error codes, not two errors.
- How to fix it
- Set cbc:AllowanceChargeReason or cbc:AllowanceChargeReasonCode inside cac:AllowanceCharge in UBL, ram:Reason or ram:ReasonCode in CII. That clears BR-CO-24 at the same time. On the code and the text agreeing, see BR-CO-08 — a rule the validator never reports.
- What the validator checks
UBLexists(cbc:AllowanceChargeReason) or exists(cbc:AllowanceChargeReasonCode)CII(../ram:Reason) or (../ram:ReasonCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge> </cac:InvoiceLine>Fixed <cac:InvoiceLine> <cbc:ID>1</cbc:ID> <cac:AllowanceCharge> <cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:AllowanceChargeReason>Expresszuschlag</cbc:AllowanceChargeReason> <cbc:Amount currencyID="EUR">50.00</cbc:Amount> </cac:AllowanceCharge> </cac:InvoiceLine>BR-45
Each VAT breakdown (BG-23) shall have a VAT category taxable amount (BT-116).
- What the rule requires
- Every VAT breakdown block (BG-23) must contain the category taxable amount (BT-116). The breakdown is where the tax becomes traceable.
- Why it fires
- The breakdown is built from the tax amounts, those being what is visible on the invoice. The taxable base behind them is an intermediate result and does not get written out.
- How to fix it
- Write BT-116 into cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount in UBL, or ram:BasisAmount inside ram:ApplicableTradeTax in CII. How base, rate and tax amount must agree is checked by BR-CO-17.
- What the validator checks
UBLexists(cbc:TaxableAmount)CII(ram:BasisAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:TaxSubtotal> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal>Fixed <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal>BR-46
Each VAT breakdown (BG-23) shall have a VAT category tax amount (BT-117).
- What the rule requires
- Every VAT breakdown block (BG-23) must contain the category tax amount (BT-117). The breakdown is where the tax becomes traceable.
- Why it fires
- For exempt categories a tax amount of zero reads like an absence of information and gets left out. The norm requires the zero explicitly.
- How to fix it
- Write BT-117 into cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount in UBL, or ram:CalculatedAmount inside ram:ApplicableTradeTax in CII. How base, rate and tax amount must agree is checked by BR-CO-17.
- What the validator checks
UBLexists(cbc:TaxAmount)CII(ram:CalculatedAmount)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal>Fixed <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal>BR-47
VAT breakdown with no category code (BT-118)
Each VAT breakdown (BG-23) shall be defined through a VAT category code (BT-118).
- What the rule requires
- Every VAT breakdown block must be defined through a VAT category code (BT-118) — one of the ten BR-CL-17 permits. Those are S, Z, E, AE, K, G, O, L, M and B.
- Why it fires
- Systems distinguish rates by percentage and need no category for it. 0% then stands for exempt, intra-community and reverse charge alike, though those are three different categories.
- How to fix it
- Set BT-118 in cac:TaxSubtotal/cac:TaxCategory/cbc:ID in UBL, ram:ApplicableTradeTax/ram:CategoryCode in CII. It is the same code as on the line, see BR-CO-04.
- What the validator checks
UBLexists(cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID)CII(.[upper-case(ram:TypeCode) = 'VAT']/ram:CategoryCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxCategory> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal>Fixed <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal>BR-48
Each VAT breakdown (BG-23) shall have a VAT category rate (BT-119), except if the Invoice is not subject to VAT.
- What the rule requires
- Every VAT breakdown block must carry a VAT category rate (BT-119), unless the invoice is not subject to VAT at all.
- Why it fires
- For exempt categories the rate is omitted, because there is none. The norm wants an explicit zero there — the statement “zero per cent” differs from “no value”.
- How to fix it
- Set BT-119 in cac:TaxSubtotal/cac:TaxCategory/cbc:Percent in UBL, ram:RateApplicablePercent in CII, explicitly as 0 for E, Z and AE. On the German path BR-DE-14 applies as well.
- What the validator checks
UBLexists(cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:Percent) or (cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/normalize-space(cbc:ID)='O')CII(.[upper-case(ram:TypeCode) = 'VAT']/ram:RateApplicablePercent) or (.[upper-case(ram:TypeCode) = 'VAT']/ram:CategoryCode = 'O')
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal>Fixed <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxCategory> <cbc:ID>S</cbc:ID> <cbc:Percent>19</cbc:Percent> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:TaxCategory> </cac:TaxSubtotal>BR-49
A Payment instruction (BG-16) shall specify the Payment means type code (BT-81).
- What the rule requires
- A payment instruction (BG-16) must state the payment means code (BT-81) — how the invoice is meant to be paid.
- Why it fires
- The group is created to hold the IBAN, and the IBAN seems to explain the route already. For automatic processing the code is the decisive field, not the account number.
- How to fix it
- Set a UNTDID 4461 code in cbc:PaymentMeansCode in UBL, ram:TypeCode in CII: 58 for SEPA credit transfer, 59 for SEPA direct debit, 30 for a general transfer, 48 for card payment.
- What the validator checks
UBLexists(cbc:PaymentMeansCode)CII(ram:TypeCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:PaymentMeans> <cac:PayeeFinancialAccount> <cbc:ID>DE02120300000000202051</cbc:ID> </cac:PayeeFinancialAccount> </cac:PaymentMeans>Fixed <cac:PaymentMeans> <cbc:PaymentMeansCode>58</cbc:PaymentMeansCode> <cac:PayeeFinancialAccount> <cbc:ID>DE02120300000000202051</cbc:ID> </cac:PayeeFinancialAccount> </cac:PaymentMeans>BR-50
A Payment account identifier (BT-84) shall be present if Credit transfer (BG-17) information is provided in the Invoice.
Wording in CII
A Payment account identifier (BT-84) shall be present if Credit transfer (BG-16) information is provided in the Invoice.
- What the rule requires
- Where a credit transfer is given as the payment means, the payee account identifier (BT-84) must accompany it. An instruction to transfer needs somewhere to transfer to.
- Why it fires
- The bank details sit in the footer of the PDF and are not repeated in the structured record, being already visible there to a human reader.
- How to fix it
- Write the IBAN into cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID in UBL, ram:PayeePartyCreditorFinancialAccount/ram:IBANID in CII. On the IBAN being correct, see BR-DE-19.
- What the validator checks
UBLnormalize-space(cbc:ID) != ''CIInormalize-space(ram:IBANID) != '' or normalize-space(ram:ProprietaryID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:PaymentMeans> <cbc:PaymentMeansCode>58</cbc:PaymentMeansCode> </cac:PaymentMeans>Fixed <cac:PaymentMeans> <cbc:PaymentMeansCode>58</cbc:PaymentMeansCode> <cac:PayeeFinancialAccount> <cbc:ID>DE02120300000000202051</cbc:ID> </cac:PayeeFinancialAccount> </cac:PaymentMeans>BR-51
Unmasked card number (BT-87)
In accordance with card payments security standards an invoice should never include a full card primary account number (BT-87). At the moment PCI Security Standards Council has defined that the first 6 digits and last 4 digits are the maximum number of digits to be shown.
Wording in CII
In accordance with card payments security standards an invoice should never include a full card primary account number (BT-97). At the moment PCI Security Standards Council has defined that the first 6 digits and last 4 digits are the maximum number of digits to be shown.
- What the rule requires
- A full card primary account number must never appear in an invoice — implemented as a plain length limit: at most ten characters in the field. That matches the first six and last four digits PCI DSS allows at most.
- Why it fires
- The payment system holds the number in full and the mapping copies it across unchanged, because the target field has exactly that name. A number then travels into a document that is emailed and archived for years. Note that the rule text above cites BT-97, which is the document level allowance reason. The shipped rule set contradicts itself here: the UBL copy of BR-51 says BT-87, the CII copy says BT-97. Both mean and check the card number.
- How to fix it
- Mask before writing and transmit at most the trailing digits in cac:PaymentMeans/cac:CardAccount/cbc:PrimaryAccountNumberID in UBL, ram:ApplicableTradeSettlementFinancialCard/ram:ID in CII. The masking belongs in the generation, not in a later clean-up. Count the masking characters while you do: “**** **** **** 1234” is nineteen characters and fails, though it reveals nothing. The last four digits alone are safe.
- What the validator checks
UBLstring-length(normalize-space(.))<=10CIIstring-length(normalize-space(ram:ID)) <= 10
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:CardAccount> <cbc:PrimaryAccountNumberID>4111111111111111</cbc:PrimaryAccountNumberID> <cbc:NetworkID>NA</cbc:NetworkID> </cac:CardAccount>Fixed <cac:CardAccount> <cbc:PrimaryAccountNumberID>******1111</cbc:PrimaryAccountNumberID> <cbc:NetworkID>NA</cbc:NetworkID> </cac:CardAccount>BR-52
Each Additional supporting document (BG-24) shall contain a Supporting document reference (BT-122).
- What the rule requires
- Every additional supporting document (BG-24) must carry a reference (BT-122) — the number under which the recipient can find the document.
- Why it fires
- The group is created to carry an attachment, and the attachment has a filename. No identifier is assigned to the document itself, the filename seeming description enough.
- How to fix it
- Set BT-122 in cac:AdditionalDocumentReference/cbc:ID in UBL, ram:AdditionalReferencedDocument/ram:IssuerAssignedID in CII. On filenames being distinct where there are several attachments, see BR-DE-22.
- What the validator checks
UBLnormalize-space(cbc:ID) != ''CIInormalize-space(ram:IssuerAssignedID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AdditionalDocumentReference> <cac:Attachment> <cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf" filename="stundennachweis.pdf">JVBERi0=</cbc:EmbeddedDocumentBinaryObject> </cac:Attachment> </cac:AdditionalDocumentReference>Fixed <cac:AdditionalDocumentReference> <cbc:ID>SN-2026-0042</cbc:ID> <cac:Attachment> <cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf" filename="stundennachweis.pdf">JVBERi0=</cbc:EmbeddedDocumentBinaryObject> </cac:Attachment> </cac:AdditionalDocumentReference>BR-53
If the VAT accounting currency code (BT-6) is present, then the Invoice total VAT amount in accounting currency (BT-111) shall be provided.
- What the rule requires
- Where a separate VAT accounting currency (BT-6) is given, the tax total in that currency (BT-111) must be provided too. A currency on its own states no amount.
- Why it fires
- BT-6 is set from a company code or a template while the conversion of the tax amount is never triggered — it needs a rate the invoice object does not hold.
- How to fix it
- Write the converted tax amount into a second cac:TaxTotal without a cac:TaxSubtotal, with the accounting currency as currencyID — or drop BT-6. How many such blocks are allowed is governed by PEPPOL-EN16931-R054, and their sign by R055.
- What the validator checks
UBLevery $taxcurrency in cbc:TaxCurrencyCode satisfies exists(//cac:TaxTotal/cbc:TaxAmount[@currencyID=$taxcurrency])CIInot(/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:TaxCurrencyCode) or (/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:TaxCurrencyCode and (ram:TaxTotalAmount/@currencyID = /rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:TaxCurrencyCode) and not(/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:TaxCurrencyCode = /rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceCurrencyCode))
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode> <cbc:TaxCurrencyCode>SEK</cbc:TaxCurrencyCode> <cac:TaxTotal> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> </cac:TaxSubtotal> </cac:TaxTotal>Fixed <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode> <cbc:TaxCurrencyCode>SEK</cbc:TaxCurrencyCode> <cac:TaxTotal> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> <cac:TaxSubtotal> <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount> </cac:TaxSubtotal> </cac:TaxTotal> <cac:TaxTotal> <cbc:TaxAmount currencyID="SEK">2090.00</cbc:TaxAmount> </cac:TaxTotal>BR-54
Item attribute with no value (BT-161)
Each Item attribute (BG-32) shall contain an Item attribute name (BT-160) and an Item attribute value (BT-161).
- What the rule requires
- Every item attribute (BG-32) must consist of a name (BT-160) and a value (BT-161). An attribute missing either describes nothing.
- Why it fires
- Attributes are taken from a property table in which values are allowed to be absent. The group is produced per property even where only the name is populated.
- How to fix it
- Write cac:Item/cac:AdditionalItemProperty only where both fields are populated — cbc:Name and cbc:Value in UBL, ram:ApplicableProductCharacteristic with ram:Description and ram:Value in CII. Watch out in CII: the name lives in ram:Description, not in any ram:Name — look for the obvious element and you will not find it.
- What the validator checks
UBLexists(cbc:Name) and exists(cbc:Value)CII(ram:Description) and (ram:Value)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> <cac:AdditionalItemProperty> <cbc:Name>Projektnummer</cbc:Name> </cac:AdditionalItemProperty> </cac:Item>Fixed <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> <cac:AdditionalItemProperty> <cbc:Name>Projektnummer</cbc:Name> <cbc:Value>P-2026-0815</cbc:Value> </cac:AdditionalItemProperty> </cac:Item>BR-55
Each Preceding Invoice reference (BG-3) shall contain a Preceding Invoice reference (BT-25).
- What the rule requires
- Every preceding invoice reference (BG-3) must contain the preceding invoice's number (BT-25). Without it the group refers to nothing in particular.
- Why it fires
- The group is created to carry the original invoice's date, or comes out of a correction template empty. The number sits in a different field from the one expected.
- How to fix it
- Set BT-25 in cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID in UBL, ram:InvoiceReferencedDocument/ram:IssuerAssignedID in CII. When the reference is required at all is covered by BR-DE-26.
- What the validator checks
UBLexists(cac:InvoiceDocumentReference/cbc:ID)CIInormalize-space(ram:IssuerAssignedID) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:BillingReference> <cac:InvoiceDocumentReference> <cbc:IssueDate>2026-07-15</cbc:IssueDate> </cac:InvoiceDocumentReference> </cac:BillingReference>Fixed <cac:BillingReference> <cac:InvoiceDocumentReference> <cbc:ID>RE-2026-0041</cbc:ID> <cbc:IssueDate>2026-07-15</cbc:IssueDate> </cac:InvoiceDocumentReference> </cac:BillingReference>BR-56
Each Seller tax representative party (BG-11) shall have a Seller tax representative VAT identifier (BT-63).
- What the rule requires
- Every tax representative party (BG-11) must carry its VAT identifier (BT-63). Without it the representation cannot be traced for tax purposes.
- Why it fires
- The representative is treated as one more address and mapped like an address — name, city, country. That a tax identifier is mandatory here rather than merely useful gets lost.
- How to fix it
- Set BT-63 in cac:TaxRepresentativeParty/cac:PartyTaxScheme/cbc:CompanyID with a cac:TaxScheme/cbc:ID of VAT in UBL, or the representative's ram:SpecifiedTaxRegistration/ram:ID in CII. The country prefix is required by BR-CO-09.
- What the validator checks
UBLexists(cac:PartyTaxScheme[cac:TaxScheme/(normalize-space(upper-case(cbc:ID)) = 'VAT')]/cbc:CompanyID)CIInormalize-space(ram:SpecifiedTaxRegistration/ram:ID[@schemeID='VA']) != ''
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:TaxRepresentativeParty> <cac:PartyName> <cbc:Name>Steuerkanzlei Muster</cbc:Name> </cac:PartyName> </cac:TaxRepresentativeParty>Fixed <cac:TaxRepresentativeParty> <cac:PartyName> <cbc:Name>Steuerkanzlei Muster</cbc:Name> </cac:PartyName> <cac:PartyTaxScheme> <cbc:CompanyID>DE811234567</cbc:CompanyID> <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme> </cac:PartyTaxScheme> </cac:TaxRepresentativeParty>BR-57
Deliver-to address with no country (BT-80)
Each Deliver to address (BG-15) shall contain a Deliver to country code (BT-80).
- What the rule requires
- Where a deliver-to address (BG-15) is sent, it must contain a country code (BT-80) — the same requirement as BR-09 and BR-11, for the delivery address.
- Why it fires
- The delivery address is taken from the order, where it is usually free text because it is only ever printed on a delivery note. A country code was never needed for that.
- How to fix it
- Set the ISO 3166-1 code in cac:Delivery/cac:DeliveryLocation/cac:Address/cac:Country/cbc:IdentificationCode in UBL, ram:ShipToTradeParty/ram:PostalTradeAddress/ram:CountryID in CII — or omit the group. City and post code are required by BR-DE-10 and BR-DE-11.
- What the validator checks
UBLexists(cac:Country/cbc:IdentificationCode)CII(ram:ShipToTradeParty/ram:PostalTradeAddress and normalize-space(ram:ShipToTradeParty/ram:PostalTradeAddress/ram:CountryID) != '') or not (ram:ShipToTradeParty/ram:PostalTradeAddress)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:Delivery> <cac:DeliveryLocation> <cac:Address> <cbc:CityName>Bonn</cbc:CityName> <cbc:PostalZone>53113</cbc:PostalZone> </cac:Address> </cac:DeliveryLocation> </cac:Delivery>Fixed <cac:Delivery> <cac:DeliveryLocation> <cac:Address> <cbc:CityName>Bonn</cbc:CityName> <cbc:PostalZone>53113</cbc:PostalZone> <cac:Country> <cbc:IdentificationCode>DE</cbc:IdentificationCode> </cac:Country> </cac:Address> </cac:DeliveryLocation> </cac:Delivery>BR-61
Credit transfer with no account (BT-84)
If the Payment means type code (BT-81) means SEPA credit transfer, Local credit transfer or Non-SEPA international credit transfer, the Payment account identifier (BT-84) shall be present.
- What the rule requires
- Where the payment means code means a credit transfer — SEPA, local or non-SEPA international — the payee account identifier (BT-84) must be provided.
- Why it fires
- Closely related to BR-50 but triggered differently: BR-50 applies once credit transfer data is present, BR-61 through the code. In UBL, BR-61 only checks where the code is 30 or 58 — set 58 and omit the account group altogether and this rule reports alone. In CII both look at the same two elements but test different things: BR-50 wants a non-empty value, BR-61 only that the element is there. An empty ram:IBANID therefore reports BR-50 by itself.
- How to fix it
- Write the IBAN into cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID in UBL, ram:PayeePartyCreditorFinancialAccount/ram:IBANID in CII — or set the code to the payment means actually intended.
- What the validator checks
UBL(exists(cac:PayeeFinancialAccount/cbc:ID) and ((normalize-space(cbc:PaymentMeansCode) = '30') or (normalize-space(cbc:PaymentMeansCode) = '58') )) or ((normalize-space(cbc:PaymentMeansCode) != '30') and (normalize-space(cbc:PaymentMeansCode) != '58'))CII(ram:IBANID) or (ram:ProprietaryID)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:PaymentMeans> <cbc:PaymentMeansCode>58</cbc:PaymentMeansCode> <cbc:PaymentID>RE-2026-0042</cbc:PaymentID> </cac:PaymentMeans>Fixed <cac:PaymentMeans> <cbc:PaymentMeansCode>58</cbc:PaymentMeansCode> <cbc:PaymentID>RE-2026-0042</cbc:PaymentID> <cac:PayeeFinancialAccount> <cbc:ID>DE02120300000000202051</cbc:ID> </cac:PayeeFinancialAccount> </cac:PaymentMeans>BR-62
The Seller electronic address (BT-34) shall have a Scheme identifier.
- What the rule requires
- Where the seller electronic address (BT-34) is sent, it must carry a scheme identifier. The attribute says which register the identifier comes from — without it the value is only a string.
- Why it fires
- The address is mapped as a plain string, because in the source system it is one. The scheme attribute beside it has no counterpart there and stays empty.
- How to fix it
- Set the schemeID attribute on cac:AccountingSupplierParty/cac:Party/cbc:EndpointID in UBL. CII names the element differently: schemeID goes on ram:SellerTradeParty/ram:URIUniversalCommunication/ram:URIID. PEPPOL-EN16931-R020 requires the address itself.
- What the validator checks
UBLexists(@schemeID)CIInormalize-space(rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:URIUniversalCommunication[1]/ram:URIID/@schemeID) != '' or not (rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:URIUniversalCommunication)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AccountingSupplierParty> <cac:Party> <cbc:EndpointID>rechnung@muster.de</cbc:EndpointID> <cac:PartyName> <cbc:Name>Muster GmbH</cbc:Name> </cac:PartyName> </cac:Party> </cac:AccountingSupplierParty>Fixed <cac:AccountingSupplierParty> <cac:Party> <cbc:EndpointID schemeID="EM">rechnung@muster.de</cbc:EndpointID> <cac:PartyName> <cbc:Name>Muster GmbH</cbc:Name> </cac:PartyName> </cac:Party> </cac:AccountingSupplierParty>BR-63
The Buyer electronic address (BT-49) shall have a Scheme identifier.
- What the rule requires
- Where the buyer electronic address (BT-49) is sent, it must carry a scheme identifier. The attribute says which register the identifier comes from — without it the value is only a string.
- Why it fires
- The same cause as BR-62, sharpened: for public bodies the Leitweg-ID is entered as the address, and then the scheme is 0204 rather than EM — a choice that only surfaces if the field exists at all. The formerly common 9958 is no longer in the list BR-CL-25 checks.
- How to fix it
- Set the schemeID attribute on cac:AccountingCustomerParty/cac:Party/cbc:EndpointID in UBL. CII names the element differently: schemeID goes on ram:BuyerTradeParty/ram:URIUniversalCommunication/ram:URIID. PEPPOL-EN16931-R010 requires the address itself.
- What the validator checks
UBLexists(@schemeID)CIInormalize-space(rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:URIUniversalCommunication[1]/ram:URIID/@schemeID) != '' or not (rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:URIUniversalCommunication)- Further reading
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:AccountingCustomerParty> <cac:Party> <cbc:EndpointID>rechnung@behoerde-bonn.de</cbc:EndpointID> <cac:PartyName> <cbc:Name>Behörde Bonn</cbc:Name> </cac:PartyName> </cac:Party> </cac:AccountingCustomerParty>Fixed <cac:AccountingCustomerParty> <cac:Party> <cbc:EndpointID schemeID="EM">rechnung@behoerde-bonn.de</cbc:EndpointID> <cac:PartyName> <cbc:Name>Behörde Bonn</cbc:Name> </cac:PartyName> </cac:Party> </cac:AccountingCustomerParty>BR-64
The Item standard identifier (BT-157) shall have a Scheme identifier.
- What the rule requires
- Where the item standard identifier (BT-157) is sent, it must carry a scheme identifier. The attribute says which register the identifier comes from — without it the value is only a string.
- Why it fires
- The GTIN sits in the item master as a number. That it must additionally say which numbering scheme it comes from does not follow from your own system.
- How to fix it
- Set the schemeID attribute on cac:Item/cac:StandardItemIdentification/cbc:ID in UBL. CII names the element differently: schemeID goes on ram:SpecifiedTradeProduct/ram:GlobalID. 0160 denotes a GTIN.
- What the validator checks
UBLexists(@schemeID)CIInormalize-space(ram:SpecifiedTradeProduct/ram:GlobalID/@schemeID) != '' or not (ram:SpecifiedTradeProduct/ram:GlobalID)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> <cac:StandardItemIdentification> <cbc:ID>04012345678901</cbc:ID> </cac:StandardItemIdentification> </cac:Item>Fixed <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> <cac:StandardItemIdentification> <cbc:ID schemeID="0160">04012345678901</cbc:ID> </cac:StandardItemIdentification> </cac:Item>BR-65
Item classification with no scheme (BT-158)
The Item classification identifier (BT-158) shall have a Scheme identifier.
- What the rule requires
- Where the item classification identifier (BT-158) is sent, it must carry a scheme identifier. The attribute says which register the identifier comes from — without it the value is only a string.
- Why it fires
- Commodity groups are assigned in house and copied across unchanged. Without naming the classification scheme the number means nothing to the recipient.
- How to fix it
- Set the listID attribute on cac:Item/cac:CommodityClassification/cbc:ItemClassificationCode in UBL. CII shares neither the element name nor the path: there listID goes on ram:ClassCode inside ram:DesignatedProductClassification. TST stands in for a real scheme in the example; eCl@ss and UNSPSC are the usual ones.
- What the validator checks
UBLexists(@listID)CIInormalize-space(ram:ClassCode/@listID) != '' or not (ram:ClassCode)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> <cac:CommodityClassification> <cbc:ItemClassificationCode>12345678</cbc:ItemClassificationCode> </cac:CommodityClassification> </cac:Item>Fixed <cac:Item> <cbc:Name>Beratungsleistung</cbc:Name> <cac:CommodityClassification> <cbc:ItemClassificationCode listID="TST">12345678</cbc:ItemClassificationCode> </cac:CommodityClassification> </cac:Item>BR-DE-TMP-32
Delivery or service date missing
Eine Rechnung sollte zur Angabe des Liefer-/Leistungsdatums entweder BT-72 "Actual delivery date", BG-14 "Invoicing period" oder in jeder Rechnungsposition BG-26 "Invoice line period" enthalten.Notice
- What the rule requires
- A notice, not an error: the invoice should state the delivery or service date in structured form — as the actual delivery date (BT-72), as the invoicing period (BG-14), or as a period on every single invoice line (BG-26). If all three are missing the invoice remains valid, and the validator still recommends accepting it. Only the presence of one of the elements is checked; in UBL, a cac:InvoicePeriod holding nothing but the VAT point date code (BT-8) is therefore enough, although it contains no date.
- Why it fires
- All three are optional in EN 16931, and many invoices give the time of supply — a mandatory particular under § 14(4) sentence 1 no. 6 UStG — only in free text ("Leistungsdatum entspricht Rechnungsdatum", i.e. the service date is the invoice date) or as the VAT point date (BT-7), which does not count here. The line period, for its part, only helps if every line carries one: even test invoice 01.01a from KoSIT’s XRechnung test suite draws the notice, because its "Porto + Versandkosten" line has no period.
- How to fix it
- For a single delivery, set BT-72: cac:Delivery/cbc:ActualDeliveryDate in UBL, ram:ApplicableHeaderTradeDelivery/ram:ActualDeliverySupplyChainEvent/ram:OccurrenceDateTime with a udt:DateTimeString in format="102" in CII. For a service over a period, use BG-14 with start and end — cac:InvoicePeriod with cbc:StartDate and cbc:EndDate in UBL, ram:ApplicableHeaderTradeSettlement/ram:BillingSpecifiedPeriod with ram:StartDateTime and ram:EndDateTime in CII. If lines already carry periods of their own, those must lie within BG-14, or PEPPOL-EN16931-R110 and PEPPOL-EN16931-R111 reject the invoice; an empty group does clear the notice, but fails BR-CO-19.
- What the validator checks
UBLcac:Delivery/cbc:ActualDeliveryDate or cac:InvoicePeriod or (every $line in (cac:InvoiceLine | cac:CreditNoteLine) satisfies $line/cac:InvoicePeriod)CIIram:ApplicableHeaderTradeDelivery/ram:ActualDeliverySupplyChainEvent/ram:OccurrenceDateTime or ram:ApplicableHeaderTradeSettlement/ram:BillingSpecifiedPeriod or (every $line in ram:IncludedSupplyChainTradeLineItem satisfies $line/ram:SpecifiedLineTradeSettlement/ram:BillingSpecifiedPeriod)
In the XML
A UBL fragment. The CII path is named above — same change, different element names.
Fails <cbc:Note>Leistungszeitraum: September 2026</cbc:Note> <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode> <cbc:BuyerReference>04011000-12345-03</cbc:BuyerReference>Fixed <cbc:Note>Leistungszeitraum: September 2026</cbc:Note> <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode> <cbc:BuyerReference>04011000-12345-03</cbc:BuyerReference> <cac:InvoicePeriod> <cbc:StartDate>2026-09-01</cbc:StartDate> <cbc:EndDate>2026-09-30</cbc:EndDate> </cac:InvoicePeriod>
NormAPI provides technical validation, not tax or legal advice.