Automation MCP Server Features Blog Pricing Contact
Validation rule EN 16931 core

BR-CO-15

One addition, zero tolerance: BR-CO-15 requires the invoice total with VAT (BT-112) to equal the total without VAT (BT-109) plus the invoice VAT total (BT-110). It is the simplest equation in the totals chain and, thanks to rounding, still one of the most failed.

What BR-CO-15 checks

BR-CO-15 defines the gross total: Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110). It joins the two halves of the totals arithmetic, the net chain and the VAT breakdown, into the number the buyer actually recognises as "the invoice amount". In UBL, BT-112 is the cbc:TaxInclusiveAmount of the legal monetary total.

Layer and formats

An en16931 core layer rule for every syntax and profile. Both inputs are themselves policed: BT-109 by BR-CO-13 and BT-110 by BR-CO-14, so a failure lower down normally drags BR-CO-15 (and BR-CO-16) into the report with it. A lone BR-CO-15 failure points squarely at BT-112 itself.

Why it fails in practice

  • Gross imported from the source system. The ERP's gross amount was rounded along a different path than the XML's net and VAT figures, and the three no longer add up.
  • VAT recomputed at the wrong granularity. BT-110 is refreshed per category while BT-112 still holds net times a blended rate.
  • Manual edits. A corrected VAT amount or net total is patched into the document without recomputing the gross.

How to fix it

Never source BT-112 independently: derive it as the sum of the emitted BT-109 and BT-110.

<cac:LegalMonetaryTotal>
  <cbc:TaxExclusiveAmount currencyID="EUR">970.00</cbc:TaxExclusiveAmount>
  <cbc:TaxInclusiveAmount currencyID="EUR">1154.30</cbc:TaxInclusiveAmount>
  ...
</cac:LegalMonetaryTotal>

In an InvoiceXML create request the field is totals.grandTotalAmount; leave invoice.totals out and the API derives it from the computed net total and VAT breakdown server-side.

The raw rejection vs the InvoiceXML finding

The Schematron report:

<svrl:failed-assert id="BR-CO-15" flag="fatal"
    location="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]">
  <svrl:text>[BR-CO-15]-Invoice total amount with VAT (BT-112) =
    Invoice total amount without VAT (BT-109) + Invoice total
    VAT amount (BT-110).</svrl:text>
</svrl:failed-assert>

The InvoiceXML finding for the same failure:

{
  "rule": "BR-CO-15",
  "layer": "en16931",
  "line": null,
  "message": "The total amount with VAT must equal the total without VAT plus the total VAT amount. Check totals.",
  "btCodes": ["BT-112", "BT-109", "BT-110"],
  "fields": ["totals.grandTotalAmount", "totals.taxBasisTotalAmount", "totals.taxTotalAmount"],
  "raw": "[BR-CO-15] EN16931: Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110)."
}

The message is safe to show an end customer, and fields names all three request fields in the equation for your UI to highlight. Clients can localize by the stable rule key without losing the mapping.

Check or generate documents with correct totals

Validate a file in the UBL validator or the XRechnung validator. Generation via POST /v1/create/ubl or POST /v1/create/facturx keeps the chain consistent by construction, with every document validated before delivery. The wider arithmetic is covered in EN 16931 explained.

  • BR-CO-10, the line sum (BT-106)
  • 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), this page
  • BR-CO-16, the amount due (BT-115)

Frequently asked questions

The equation is one addition. How does anyone fail it?

Because the three numbers are computed in different places. BT-109 comes from the net chain, BT-110 from the VAT breakdown, and BT-112 is often taken from the source system's gross amount, which was rounded along a different path. If gross was computed as net times 1.19 and rounded once, while the breakdown rounded per category, the addition no longer holds.

What if my invoice has no VAT amount at all?

For a fully exempt, reverse charge or zero-rated document the VAT total is 0.00 and BT-112 simply equals BT-109. An absent BT-110 counts as zero; declaring BT-112 different from BT-109 in that case fails the rule.

Is BR-CO-15 about the amount due?

No, that is one link later. BR-CO-16 derives the amount due for payment (BT-115) from BT-112 minus the paid amount plus rounding. BR-CO-15 stops at the gross total; prepayments do not appear in its equation.

Can documents created via the API fail this rule?

Not in practice. Omit invoice.totals in a /v1/create/* request and BT-112 is computed server-side as BT-109 plus BT-110, with the whole document validated before delivery. The rule bites on documents arriving from other systems.