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

BR-CO-17

Inside every VAT breakdown group the arithmetic must hold: BR-CO-17 requires the VAT category tax amount (BT-117) to equal the taxable amount (BT-116) times the category rate (BT-119) divided by 100, rounded to two decimals. It is the per-category multiplication behind the whole VAT side of the invoice.

What BR-CO-17 checks

BR-CO-17 is the multiplication inside each VAT breakdown group: VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals. An EN 16931 invoice carries one breakdown group per category and rate; this rule makes each group internally consistent, so the buyer can recompute the VAT from the declared base and rate and get the declared amount.

Layer and formats

An en16931 core layer rule for every syntax and profile. It works hand in hand with BR-CO-14, which sums the groups into the invoice VAT total, and with the per-category families (BR-S-08 for standard rate, BR-AE, BR-E and friends) that police which lines feed which group.

Why it fails in practice

  • Per-line VAT rounding. VAT is computed and rounded on every line, then summed into the group; the group's own base times rate differs by a cent from that sum.
  • Float arithmetic. Binary floating point turns 19% of 105.55 into 20.054499999, and the wrong rounding direction surfaces exactly at the second decimal.
  • Rate drift. The group declares 19% while the amounts were computed with 16% (or a temporary rate) somewhere upstream, so the multiplication cannot hold.

How to fix it

Compute VAT per category with decimal arithmetic: sum the category's net amounts into BT-116, multiply by the rate once, round once, and emit that as BT-117.

<cac:TaxSubtotal>
  <cbc:TaxableAmount currencyID="EUR">970.00</cbc:TaxableAmount>
  <cbc:TaxAmount currencyID="EUR">184.30</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>

In an InvoiceXML create request, leave invoice.vatBreakdowns out and the API builds the groups from the line VAT information server-side; supplied groups are validated so an inconsistency comes back as a finding, not as a broken document.

The raw rejection vs the InvoiceXML finding

The Schematron report:

<svrl:failed-assert id="BR-CO-17" flag="fatal"
    location="/*:Invoice[namespace-uri()='...']/cac:TaxTotal[1]/cac:TaxSubtotal[1]">
  <svrl:text>[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.</svrl:text>
</svrl:failed-assert>

The InvoiceXML finding for the same failure:

{
  "rule": "BR-CO-17",
  "layer": "en16931",
  "line": null,
  "message": "The VAT category tax amount must equal the taxable amount multiplied by the VAT rate, divided by 100. Check VAT calculations.",
  "btCodes": ["BT-117", "BT-116", "BT-119"],
  "fields": null,
  "raw": "[BR-CO-17] EN16931: VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals."
}

The message is ready for an end user and the BT codes point at the breakdown fields involved. No rule-code lookup tables in your integration, and localization stays a per-rule concern for clients that want it.

Check or generate documents with consistent VAT

Run a file through the UBL validator or the XRechnung validator to see the per-group findings. Generation via POST /v1/create/ubl or POST /v1/create/facturx computes each group with a single rounding step server-side. The category model is covered in EN 16931 explained.

  • BR-CO-14, the sum of these groups against the invoice VAT total
  • BR-S-08, which lines must feed a standard-rate group's taxable amount
  • BR-AE-10 and BR-E-10, the reason requirements of the 0% categories

Frequently asked questions

How exactly is the expected amount rounded?

Multiply the category's taxable amount by the rate, divide by 100, and round the result to two decimals, half up. The comparison allows only that value. Summing per-line VAT amounts that were each rounded individually produces a different number on long invoices, which is the classic way to fail this rule.

How does BR-CO-17 relate to BR-CO-14?

BR-CO-17 checks each breakdown group internally; BR-CO-14 checks that the invoice's VAT total equals the sum over the groups. Fix BR-CO-17 first: once every group multiplies correctly, BR-CO-14 usually follows by summing the corrected amounts.

Does the rule apply to 0% categories?

Yes, trivially: for zero-rated, exempt, reverse charge and export groups the rate or the computation yields 0.00, and BT-117 must be exactly 0.00. A leftover tax amount in such a group fails both this rule and the category's own amount rule (for example BR-AE-09).

Can documents created via the API fail this rule?

Not in practice. Omit invoice.vatBreakdowns in a /v1/create/* request and the groups are computed server-side, per category with one rounding step, then validated before delivery. The rule bites on documents arriving from other systems.