Error codes · EN 16931

BR-CO-17Fixable here

VAT category tax amount must equal taxable amount times rate

This is the rejection, word for word: „[BR-CO-17] BR-CO-17 fires when the VAT category tax amount (BT-117) on a TaxSubtotal does not equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119) divided by 100, rounded to two decimals. It's the single-category sibling of BR-CO-14 (sum of category tax amounts equals document VAT total) and one of the most common Peppol calculation failures.“

The schematron checks, for each cac:TaxTotal/cac:TaxSubtotal: `xs:decimal(cbc:TaxAmount) = round((xs:decimal(cbc:TaxableAmount) * xs:decimal(cac:TaxCategory/cbc:Percent)) div 100 * 100) div 100`. Failures arise from: (1) Banker's rounding vs. half-up rounding mismatches — financial systems often use round-half-to-even, schematron uses round-half-up. (2) Floating-point accumulation when category amounts are computed by summing line-level VAT instead of (taxable_amount * rate / 100). (3) Currency precision drift — emitting four decimals on TaxableAmount and two on TaxAmount creates rounding errors. (4) Multi-rate invoices where one TaxSubtotal is correct and another is off by 0.01. (5) Wrong VAT rate cast (e.g. 19 vs. 19.00 vs. 0.19) by a templating engine. (6) Negative TaxableAmount on credit notes flipped sign on TaxAmount differently.

What to have readyThe missing value from your purchase order, contract or bookkeeping.
What we doStep 1: For every cac:TaxTotal/cac:TaxSubtotal, read cbc:TaxableAmount (BT-116), cac:TaxCategory/cbc:Percent (BT-119), cbc:TaxAmount (BT-117). Step 2: Compute the expected TaxAmount as `round_half_up(TaxableAmount * Percent / 100, 2)`. Use Decimal arithmetic, not float — `Decimal(taxable) * Decimal(percent) / Decimal('100')` in Python with `ROUND_HALF_UP`. Step 3: Compare to the emitted TaxAmount. If different by exactly 0.01 it's a rounding-mode bug. If different by more, the TaxableAmount or rate is wrong. Step 4: Rewrite cbc:TaxAmount with the recomputed value, keeping the currencyID attribute and two-decimal formatting (`'{:.2f}'.format(value)`). Step 5: Recompute downstream totals: BT-110 (cbc:TaxAmount on cac:TaxTotal — sum of subtotal TaxAmount) and BT-112 (cbc:TaxInclusiveAmount). Step 6: Re-validate. BR-CO-17 should clear, and BR-CO-14 should also pass if the totals are recomputed correctly. Step 7: If the input data was wrong (rate cast as integer, taxable amount truncated), fix the source mapping — auto-fixing the symptom hides a recurring bug.
If you enter it yourself in your invoicing software
Before
<cac:TaxTotal>
  <cbc:TaxAmount currencyID="EUR">190.01</cbc:TaxAmount>
  <cac:TaxSubtotal>
    <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount>
    <cbc:TaxAmount currencyID="EUR">190.01</cbc:TaxAmount>
    <cac:TaxCategory>
      <cbc:ID>S</cbc:ID>
      <cbc:Percent>19.00</cbc:Percent>
      <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
    </cac:TaxCategory>
  </cac:TaxSubtotal>
</cac:TaxTotal>
After
<cac:TaxTotal>
  <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount>
  <cac:TaxSubtotal>
    <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount>
    <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount>
    <cac:TaxCategory>
      <cbc:ID>S</cbc:ID>
      <cbc:Percent>19.00</cbc:Percent>
      <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
    </cac:TaxCategory>
  </cac:TaxSubtotal>
</cac:TaxTotal>
What the finding looks likeExample
FindingValue missing · BR-CO-17
From youThe missing value
ThenPassed
ProofSHA-256 and /verify link after the check