Calculation and cross-field rules — when the totals do not add up
BR-CO does not check individual fields but how they relate: that the totals reconcile, that a coded reason and a written one mean the same thing, that two dates do not contradict each other.
23 rules · 19 explained · Rule set v2026-08-31
Rounding is the usual cause
The total rules — BR-CO-10 through BR-CO-17 — rarely fail because somebody calculated wrongly. They fail because of when the rounding happened. Keep line amounts internally at four decimal places, round them to two only when writing the XML, and build the total from the unrounded values, and you produce an invoice whose lines do not add up to its own total.
The norm requires the other order: round first, then sum. That is one line in the mapping and the difference between an accepted and a rejected invoice.
Entries that exclude each other
The other half of the family checks pairs where exactly one may be set, or both must say the same thing — the tax point as a date or as a code, an allowance reason as text and as a code. Systems that fill both fields “to be safe” breach these for exactly that reason.
No amount of recalculating helps here; a decision in the mapping does. Which of the two is the source, and the other stays empty.
All calculation and cross-field rules
With the official rule text. Rules with a written explanation carry their cause and fix directly beneath.
- BR-CO-03
Value added tax point date (BT-7) and Value added tax point date code (BT-8) are mutually exclusive.
- What the rule requires
- The invoice may state the VAT point either as a date (BT-7) or as a code (BT-8), never as both. The two fields answer the same question: when the VAT becomes chargeable.
- Why it fires
- Mappings fill BT-7 from the delivery date and then set BT-8 to a default as well, because both fields exist in the target schema and nothing marks them as either/or.
- How to fix it
- Pick one. With a concrete date, send BT-7 (cbc:TaxPointDate in UBL, ram:TaxPointDate in CII) and omit BT-8; otherwise send only the code in BT-8 (cac:InvoicePeriod/cbc:DescriptionCode in UBL, ram:DueDateTypeCode in CII).
- BR-CO-04
Each Invoice line (BG-25) shall be categorized with an Invoiced item VAT category code (BT-151).
- What the rule requires
- Every invoice line (BG-25) must carry a VAT category code (BT-151) — S for the standard rate, E for exempt, Z for zero-rated, AE for reverse charge, and so on.
- Why it fires
- The code sits in the header, where the tax breakdown is built anyway, and is forgotten at line level — particularly on invoices with a single rate, where repeating it looks redundant.
- How to fix it
- Set BT-151 on every line: cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:ID in UBL, ram:ApplicableTradeTax/ram:CategoryCode at line level in CII. Every line must name it, even where the rate is the same throughout.
- BR-CO-05
Document level allowance reason code (BT-98) and Document level allowance reason (BT-97) shall indicate the same type of allowance.never fires
- BR-CO-06
Document level charge reason code (BT-105) and Document level charge reason (BT-104) shall indicate the same type of charge.never fires
- BR-CO-07
Invoice line allowance reason code (BT-140) and Invoice line allowance reason (BT-139) shall indicate the same type of allowance reason.never fires
- BR-CO-08
Invoice line charge reason code (BT-145) and Invoice line charge reason (BT-144) shall indicate the same type of charge reason.never fires
- BR-CO-09
The Seller VAT identifier (BT-31), the Seller tax representative VAT identifier (BT-63) and the Buyer VAT identifier (BT-48) shall have a prefix in accordance with ISO code ISO 3166-1 alpha-2 by which the country of issue may be identified. Nevertheless, Greece may use the prefix ‘EL’.
- What the rule requires
- The VAT identifiers of the seller (BT-31), the tax representative (BT-63) and the buyer (BT-48) must begin with the ISO 3166-1 alpha-2 country code. Greece is permitted to use “EL” instead.
- Why it fires
- Master data usually holds the number without the prefix, because the country lives in a field of its own. The mapping then copies the digits alone — “811234567” where “DE811234567” is required.
- How to fix it
- Prepend the country code and strip any spaces: cac:PartyTaxScheme/cbc:CompanyID with a cac:TaxScheme/cbc:ID of VAT in UBL, ram:SpecifiedTaxRegistration/ram:ID with schemeID="VA" in CII.
- BR-CO-10
Sum of Invoice line net amount (BT-106) = Σ Invoice line net amount (BT-131).
- What the rule requires
- The sum of invoice line net amounts (BT-106) must equal the sum of the individual line net amounts (BT-131) exactly.
- Why it fires
- This appears when line amounts are rounded individually and then added, while the total is computed from unrounded values. The two routes disagree.
- How to fix it
- Round each line amount to two decimal places and sum exactly those rounded values, not the raw ones.
- BR-CO-11
Sum of allowances on document level (BT-107) = Σ Document level allowance amount (BT-92).
- What the rule requires
- The sum of allowances at document level (BT-107) must equal the sum of the individual allowance amounts (BT-92) exactly.
- Why it fires
- The total is taken from your own invoice object while the individual allowances are written through a filter — an allowance of zero skipped, say, or a discount also folded into the line.
- How to fix it
- Build BT-107 from exactly the allowances the document contains: cbc:AllowanceTotalAmount inside cac:LegalMonetaryTotal as the sum of every cac:AllowanceCharge whose cbc:ChargeIndicator is false in UBL, ram:AllowanceTotalAmount in CII.
- BR-CO-12
Sum of charges on document level (BT-108) = Σ Document level charge amount (BT-99).
- BR-CO-13
Invoice total amount without VAT (BT-109) = Σ Invoice line net amount (BT-131) - Sum of allowances on document level (BT-107) + Sum of charges on document level (BT-108).
- What the rule requires
- The invoice total without VAT (BT-109) must equal the sum of all line net amounts (BT-131) minus document-level allowances (BT-107) plus document-level charges (BT-108).
- Why it fires
- Usually document-level allowances or charges are shown but forgotten when computing BT-109 — or the discount is already baked into the line prices and then subtracted a second time.
- How to fix it
- Compute BT-109 strictly as Σ BT-131 − BT-107 + BT-108 from exactly the values written in the document. A discount belongs either in the line or at document level — never in both.
- BR-CO-14
Invoice total VAT amount (BT-110) = Σ VAT category tax amount (BT-117).
- What the rule requires
- The invoice total VAT amount (BT-110) must equal the sum of the tax amounts of all VAT categories (BT-117).
- Why it fires
- Classic when BT-110 is computed from individually rounded per-line taxes while the category amounts come from the taxable bases — the two routes drift apart by rounding differences.
- How to fix it
- Compute BT-117 per category from its taxable base first, then form BT-110 as the exact sum of those category amounts — never as a separate, independent calculation.
- BR-CO-15
Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110).
- What the rule requires
- The invoice total with VAT (BT-112) must equal the total without VAT (BT-109) plus the total VAT amount (BT-110), exactly.
- Why it fires
- BT-112 is often copied from the internal system while BT-109 and BT-110 are recalculated — a single cent of rounding difference between the two worlds triggers the error.
- How to fix it
- Always derive BT-112 as BT-109 + BT-110 from the two document values instead of writing an independently calculated gross amount. Use decimal arithmetic, never float.
- BR-CO-16
Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) -Paid amount (BT-113) +Rounding amount (BT-114).
- BR-CO-17
VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals.
- BR-CO-18
An Invoice shall at least have one VAT breakdown group (BG-23).
- What the rule requires
- The invoice must contain at least one VAT breakdown group (BG-23) — one block per tax category, carrying the taxable amount, the tax amount, the category and the rate.
- Why it fires
- On an exempt invoice the block looks superfluous and gets left out. The rule wants it regardless: “no tax” is itself a statement, and it belongs in the breakdown.
- How to fix it
- Write one cac:TaxTotal/cac:TaxSubtotal in UBL, or one ram:ApplicableTradeTax in CII, per tax category — including E or Z, with a tax amount of zero and the exemption reason.
- BR-CO-19
If Invoicing period (BG-14) is used, the Invoicing period start date (BT-73) or the Invoicing period end date (BT-74) shall be filled, or both.
- What the rule requires
- If an invoicing period (BG-14) is sent, at least one date must be in it: the start (BT-73), the end (BT-74), or both. An empty period states nothing.
- Why it fires
- The group is created unconditionally in the mapping and populated afterwards. Where the source system holds neither date — a one-off service, say — an empty shell is left behind in the document.
- How to fix it
- Create the group only when at least one date exists: cac:InvoicePeriod with cbc:StartDate and/or cbc:EndDate in UBL, ram:BillingSpecifiedPeriod with ram:StartDateTime and/or ram:EndDateTime in CII.
- BR-CO-20
If Invoice line period (BG-26) is used, the Invoice line period start date (BT-134) or the Invoice line period end date (BT-135) shall be filled, or both.
- What the rule requires
- If a line period (BG-26) is sent on an invoice line, at least one date must be in it: the start (BT-134), the end (BT-135), or both. The line-level counterpart to BR-CO-19.
- Why it fires
- The same mistake as in the header, only more often: lines are produced in a loop and the period group is created for every one of them, though only some lines actually have a period.
- How to fix it
- Emit cac:InvoicePeriod inside cac:InvoiceLine only when a date exists — and in CII, ram:BillingSpecifiedPeriod inside the line, on the same condition.
- BR-CO-21
Each Document level allowance (BG-20) shall contain a Document level allowance reason (BT-97) or a Document level allowance reason code (BT-98), or both.
- What the rule requires
- Every document-level allowance (BG-20) must be justified — in plain text (BT-97), as a code (BT-98), or both. An amount with no reason is something the recipient cannot check.
- Why it fires
- The amount lives in the invoice object and the reason only in the PDF rendering. What survives into the structured data is a deduction of 50.00 with no explanation attached to it.
- How to fix it
- Supply at least one of the two: cbc:AllowanceChargeReason or cbc:AllowanceChargeReasonCode inside cac:AllowanceCharge in UBL, ram:Reason or ram:ReasonCode inside ram:SpecifiedTradeAllowanceCharge in CII.
- BR-CO-22
Each Document level charge (BG-21) shall contain a Document level charge reason (BT-104) or a Document level charge reason code (BT-105), or both.
- 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.
- Why it fires
- Charges often appear late in the process, out of a shipping provider for instance, and are passed through as a bare amount. The recipient receives a surcharge nobody can attribute, and invoices with unexplained surcharges sit unpaid.
- How to fix it
- Add cbc:AllowanceChargeReason or cbc:AllowanceChargeReasonCode to the cac:AllowanceCharge whose cbc:ChargeIndicator is true in UBL, or ram:Reason or ram:ReasonCode to the ram:SpecifiedTradeAllowanceCharge in CII.
- BR-CO-23
Each Invoice line allowance (BG-27) shall contain an Invoice line allowance reason (BT-139) or an Invoice line allowance reason code (BT-140), or both.
- What the rule requires
- Every allowance on an invoice line (BG-27) must be justified — in plain text (BT-139), as a code (BT-140), or both.
- Why it fires
- Line discounts come out of a pricing engine that rarely carries more than a percentage. The reason frequently does not exist in the source system at all, so there is nothing available to map.
- How to fix it
- Carry the discount type through from pricing, or set a default reason that is actually true. It is written as cbc:AllowanceChargeReason or cbc:AllowanceChargeReasonCode inside the line's cac:AllowanceCharge in UBL, ram:Reason or ram:ReasonCode in CII.
- BR-CO-24
Each Invoice line charge (BG-28) shall contain an Invoice line charge reason (BT-144) or an Invoice line charge reason code (BT-145), or both.
- What the rule requires
- Every charge on an invoice line (BG-28) must carry a reason — in plain text (BT-144), as a code (BT-145), or both.
- Why it fires
- The rarest of the four cases, and therefore the one most likely to have no field in the mapping at all: the amount is passed through because the total needs it, and the reason is dropped.
- How to fix it
- Write cbc:AllowanceChargeReason or cbc:AllowanceChargeReasonCode into the line's cac:AllowanceCharge with cbc:ChargeIndicator true in UBL, or ram:Reason or ram:ReasonCode into the line's ram:SpecifiedTradeAllowanceCharge in CII.
- BR-CO-26
In order for the buyer to automatically identify a supplier, the Seller identifier (BT-29), the Seller legal registration identifier (BT-30) and/or the Seller VAT identifier (BT-31) shall be present.
- What the rule requires
- So the buyer can identify the supplier automatically, at least one seller identifier must be present: a seller identifier (BT-29), the legal registration identifier (BT-30), or the VAT identifier (BT-31).
- Why it fires
- The seller is sent as a name and an address, because that is enough for a human. It is not enough for an automatic match against the recipient's supplier master data — spellings change, identifiers do not.
- How to fix it
- Send at least one of them: cac:PartyIdentification/cbc:ID (BT-29), cac:PartyLegalEntity/cbc:CompanyID (BT-30) or cac:PartyTaxScheme/cbc:CompanyID (BT-31) in UBL; ram:SellerTradeParty/ram:ID, ram:SpecifiedLegalOrganization/ram:ID or ram:SpecifiedTaxRegistration/ram:ID in CII.
NormAPI provides technical validation, not tax or legal advice.