What BR-CO-10 checks
BR-CO-10 is pure arithmetic: Sum of Invoice line net amount (BT-106) = Σ Invoice line net amount (BT-131). The document declares a sum over its own lines, and the validator recomputes it. In UBL, BT-106 is cbc:LineExtensionAmount inside cac:LegalMonetaryTotal, and each BT-131 is the cbc:LineExtensionAmount of an invoice line; CII has the same pair under SpecifiedTradeSettlementHeaderMonetarySummation and the line settlement. There is no tolerance: a single cent of drift fails the rule.
Layer and formats
The finding carries "layer": "en16931". BR-CO-10 fires in every format and profile, from plain UBL to XRechnung, Peppol BIS and the CII inside hybrid PDFs. It is the head of a chain of calculation rules: BR-CO-13 derives the total without VAT from the lines, BR-CO-14 ties the VAT total to the breakdown, BR-CO-15 and BR-CO-16 carry the arithmetic to the amount due. An error in one line ripples through all of them, so fix the chain bottom-up.
Why it fails in practice
- Round-then-sum vs sum-then-round. Lines are rounded to two decimals individually while the header total was computed from unrounded values, or vice versa.
- Floating-point arithmetic. Binary floats accumulate errors that surface exactly at the second decimal; totals computed with
doubleinstead of decimal types drift by a cent. - Filtered or edited lines. A line was removed or a quantity corrected after the totals were computed, and the header block was never refreshed.
How to fix it
Recompute BT-106 as the sum of the BT-131 values you actually emit, using decimal arithmetic, and refresh it whenever a line changes. In an InvoiceXML create request the simplest fix is to not send totals at all: leave invoice.totals out and the API computes totals.sumOfLineNetAmounts and the rest of the chain from invoice.lines server-side. If you do supply totals, they are validated and any mismatch comes back as a finding before a broken document ever leaves the API.
The raw rejection vs the InvoiceXML finding
The Schematron report states the equation and leaves the debugging to you:
<svrl:failed-assert id="BR-CO-10" flag="fatal"
location="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]">
<svrl:text>[BR-CO-10]-Sum of Invoice line net amount (BT-106) =
Σ Invoice line net amount (BT-131).</svrl:text>
</svrl:failed-assert>
The InvoiceXML finding for the same failure:
{
"rule": "BR-CO-10",
"layer": "en16931",
"line": null,
"message": "The sum of line net amounts does not match the total of all line amounts. Recalculate line totals.",
"btCodes": ["BT-106", "BT-131"],
"fields": ["totals.sumOfLineNetAmounts"],
"raw": "[BR-CO-10] EN16931: Sum of Invoice line net amount (BT-106) = Σ Invoice line net amount (BT-131)."
}
The message reads like something you can show a customer, and fields points at the request field to correct. Because the message table is keyed by rule code, clients can localize by rule and keep the field mapping.
Check or generate documents with correct totals
Validate incoming files with the UBL validator or the XRechnung validator. If your own documents fail the totals chain, that first red report is usually the argument for moving generation to the API too: with POST /v1/create/ubl or POST /v1/create/facturx the chain is computed server-side from your line items and cannot drift. The arithmetic rules are walked through in EN 16931 explained.
The totals chain
- BR-CO-10, the line sum (BT-106), this page
- BR-CO-11, the allowance sum (BT-107)
- BR-CO-13, the total without VAT (BT-109)
- BR-CO-14, the VAT total against the breakdown (BT-110)
- BR-CO-15, the total with VAT (BT-112), and BR-CO-16, the amount due (BT-115)
- BR-16, at least one invoice line must exist for the sum to mean anything