Attestwire › Rule reference › BR-CO-10
BR-CO-10 The line net amounts must add up to the stated total
noun · EN 16931 · fatal · BT-106
the sum of your lines is not the sum of your lines.
Your invoice was refused because the line amounts do not add up to the subtotal the invoice states. Usually the difference is a cent, and usually it comes from rounding at the wrong point. The fix is to round each line to two decimals first, then add. In the standard the stated subtotal is field 106, written BT-106, and BR-CO-10 is the rule that compares it with the lines.
- Business term
BT-106— Sum of invoice line net amounts- Severity
fatal- Applies to
All profiles
The fix
Round once per line, to two decimals, keep the rounded figure, then add. If you are generating the invoice from this library, the simplest fix is to stop stating the subtotal at all: drop declaredTotals.lineExtensionAmount and let the totals be computed from the lines, which makes a mismatch impossible. State the totals when you are checking a document somebody else produced, which is what the parsers do.
What the rule requires
The subtotal of the invoice lines, the sum of invoice line net amounts (BT-106), has to equal the individual line net amounts (BT-131) added together. Nothing else goes into it: document-level allowances and charges are not subtracted or added here, they land in BR-CO-13 one step later.
The order of operations is what bites. EN 16931 sums rounded line amounts: round each line to two decimals, then add. Rounding only at the end drifts by a cent or two on a long invoice, and a cent is a failure, because this comparison is exact.
This rule only has something to compare when your input states the subtotal. Pass declaredTotals.lineExtensionAmount and we check it; leave it out and the library computes the figure, in which case there is nothing to disagree with.
Sum of Invoice line net amount (BT-106) = Σ Invoice line net amount (BT-131).
Failing and passing
validateInput({
// ...
lines: [{ id: "1", description: "Consulting", quantity: 10,
unitCode: "HUR", unitPrice: 150,
vatCategory: "S", vatRate: 19 }],
declaredTotals: { lineExtensionAmount: 1499.99 }, // <- BR-CO-10
});
// -> { valid: false, errors: [ { rule: "BR-CO-10", field: "BT-106", ... } ] }validateInput({
// ...
lines: [{ id: "1", description: "Consulting", quantity: 10,
unitCode: "HUR", unitPrice: 150,
vatCategory: "S", vatRate: 19 }],
declaredTotals: { lineExtensionAmount: 1500.00 },
});
// -> { valid: true, errors: [] }
// Omitting declaredTotals entirely also passes: the library
// computes BT-106 and writes it into the XML for you.Why it exists
The line subtotal is the anchor the whole totals block hangs off. BT-109 starts from it, BT-112 is built on BT-109, and BT-115 is built on BT-112, so a cent wrong here walks all the way down to the amount the buyer pays, and you get four findings for one mistake.
It is also the cheapest integrity check on a document that has passed through several systems: if the lines and the header disagree, something in between changed one of them.
What the library returns
The full error returned by @attestwire/en16931. It includes the rule ID,
affected field and suggested correction. Developers can use this object in their application:
{
"rule": "BR-CO-10",
"field": "BT-106",
"severity": "fatal",
"message": "BR-CO-10 requires the sum of invoice line net amounts (BT-106) to equal Σ BT-131. You declared 1.00 for BT-106, but the invoice lines compute to 1500.00 — a difference of -1499.00 EUR. This is a pure addition check, so a mismatch means either a line is missing from your total or a line amount was rounded differently.",
"fix": "Either correct the line data so it sums to your declared figure, or drop declaredTotals.lineExtensionAmount and let the library compute it. Note that EN 16931 sums *rounded* line amounts: round each line to 2 decimals first, then add — rounding only the final sum drifts by a cent or two on long invoices.",
"example": "\"declaredTotals\": { \"lineExtensionAmount\": 1500.00 }",
"docsUrl": "https://attestwire.com/rules/BR-CO-10"
}How our check differs
This rule needs a stated figure to fire. If you build an InvoiceInput by hand and omit declaredTotals, BR-CO-10 cannot report anything, because there is no second opinion to compare against. It fires on documents you read: parseUbl and parseCiiInvoice fill declaredTotals with what the file said.
A green result on a hand-built input therefore says nothing about your own totals code, because the comparison never ran.
One finding per document, not one per line. When the document states each line’s own BT-131, that stated-against-stated comparison owns this id, which is what KoSIT does, and the derived-value check stands down rather than emitting a second BR-CO-10 with a different delta.
A line whose own arithmetic is wrong but which is consistent with the header comes back as PEPPOL-EN16931-R120, per line, and is not a BR-CO-10 violation.
See also: BR-CO-13 — the same lines, with document allowances and charges applied; PEPPOL-EN16931-R120 — a line whose own net amount does not multiply out.