Skip to content

BR-CO-17Fix a VAT breakdown amount that does not match taxable amount times rate

Each VAT breakdown tax amount must agree with its taxable amount times its rate. The check tolerates a difference below 1.00; the correct figure does not.

EN 16931Fatal: the document is invalidVATTotals

The short answer

BR-CO-17 fails when the cbc:TaxAmount of a cac:TaxSubtotal does not agree with that subtotal's cbc:TaxableAmount multiplied by its cac:TaxCategory/cbc:Percent. The correct value is taxable amount x rate / 100, rounded to two decimals. Recalculate it, then carry the corrected figure into the VAT total and the totals that depend on it.

The rule runs on every VAT breakdown whatever its category. On a Standard rated breakdown the same arithmetic is checked again by BR-S-09, so the two are normally reported together.

What the rule checks

For a breakdown with a non-zero rate, the rule calculates taxable amount x rate / 100, rounds that to two decimals, and compares it with the tax amount. The comparison is not exact: it passes when the two differ by less than 1.00 and fails at a difference of exactly 1.00 or more. On 25.00 at 20%, where the calculated amount is 5.00, tax amounts of 4.01 and 5.99 pass and 4.00 and 6.00 fail.

Passing that tolerance does not make the figure right. A breakdown of 5.90 on 25.00 at 20% validates when the totals follow it, and the invoice still states 0.90 too much VAT. Treat the tolerance as slack in the checker, not as a rounding allowance you can use.

When the rate is 0, or the breakdown has no cbc:Percent at all, the tax amount only has to round to zero as a whole number: 0.40 gets past this rule and 0.50 does not. The category rules are stricter; a Zero rated breakdown with 0.40 of tax is still rejected by BR-Z-09.

Both amounts are compared as absolute values, so the rule does not look at the sign. A tax amount of -5.00 on a taxable amount of 25.00 passes it.

Only the three values inside the breakdown are read. Whether the document VAT total agrees with the breakdowns is a separate check, BR-CO-14.

TermMeaningUBL element
BT-117VAT category tax amountcac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount
BT-116VAT category taxable amountcac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount
BT-119VAT category ratecac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent

How an integration ends up here

Possible causes, from the shape of the rule rather than from measured usage:

  • VAT is calculated per line and the line results are summed into the breakdown, so it drifts away from taxable amount x rate. Pennies of drift stay inside the tolerance; a larger gap usually means a line was taxed at a different rate from the one the breakdown declares.
  • The tax amount comes from a ledger posting while the taxable amount and rate are rebuilt from the lines, and the two were produced at different times or from different data.
  • The rate is written as a fraction, 0.2 instead of 20, so the calculated amount is a hundredth of what was intended.
  • The taxable amount was changed by a document-level allowance or charge and the tax amount was not recalculated.
  • A gross amount, or the tax of a different category, is mapped into cbc:TaxAmount.

How to fix it

  1. For each cac:TaxSubtotal, take cbc:TaxableAmount and cac:TaxCategory/cbc:Percent as sent.
  2. Calculate taxable amount x rate / 100 in decimal arithmetic and round the result to two decimals. Write it to the subtotal cbc:TaxAmount.
  3. If the calculated figure disagrees with what your ledger holds, find out which of the taxable amount, the rate or the posted tax is wrong before sending. Do not nudge the amount until it falls inside the tolerance.
  4. Set cac:TaxTotal/cbc:TaxAmount to the sum of the breakdown tax amounts (BR-CO-14), then recalculate cbc:TaxInclusiveAmount (BR-CO-15) and cbc:PayableAmount (BR-CO-16).

The recorded example has one breakdown: 25.00 taxable at 20%.

Calculated tax: 25.00 x 20 / 100 = 5.00
Tax amount sent: 6.50
Difference: 1.50, not below 1.00, so the rule fails
Corrected: tax amount 5.00, VAT total 5.00, total with VAT 25.00 + 5.00 = 30.00

Validate your corrected invoice

Before and after

These are fragments, not complete documents. The complete synthetic documents they come from are linked below.

Fragment of the failing invoice: 6.50 of VAT on 25.00 at 20%, with the totals built on the wrong figure

<cac:TaxTotal>
  <cbc:TaxAmount currencyID="GBP">6.50</cbc:TaxAmount>
  <cac:TaxSubtotal>
    <cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
    <cbc:TaxAmount currencyID="GBP">6.50</cbc:TaxAmount>
    <cac:TaxCategory>
      <cbc:ID>S</cbc:ID>
      <cbc:Percent>20</cbc:Percent>
      <cac:TaxScheme>
        <cbc:ID>VAT</cbc:ID>
      </cac:TaxScheme>
    </cac:TaxCategory>
  </cac:TaxSubtotal>
</cac:TaxTotal>

<cac:LegalMonetaryTotal>
  <cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
  <cbc:TaxExclusiveAmount currencyID="GBP">25.00</cbc:TaxExclusiveAmount>
  <cbc:TaxInclusiveAmount currencyID="GBP">31.50</cbc:TaxInclusiveAmount>
  <!-- allowance, charge, prepaid and rounding totals (all 0.00) omitted from this fragment -->
  <cbc:PayableAmount currencyID="GBP">31.50</cbc:PayableAmount>
</cac:LegalMonetaryTotal>

Fragment of the corrected invoice: the breakdown is 5.00 and the totals follow

<cac:TaxTotal>
  <cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
  <cac:TaxSubtotal>
    <cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
    <cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
    <cac:TaxCategory>
      <cbc:ID>S</cbc:ID>
      <cbc:Percent>20</cbc:Percent>
      <cac:TaxScheme>
        <cbc:ID>VAT</cbc:ID>
      </cac:TaxScheme>
    </cac:TaxCategory>
  </cac:TaxSubtotal>
</cac:TaxTotal>

<cac:LegalMonetaryTotal>
  <cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
  <cbc:TaxExclusiveAmount currencyID="GBP">25.00</cbc:TaxExclusiveAmount>
  <cbc:TaxInclusiveAmount currencyID="GBP">30.00</cbc:TaxInclusiveAmount>
  <!-- allowance, charge, prepaid and rounding totals (all 0.00) omitted from this fragment -->
  <cbc:PayableAmount currencyID="GBP">30.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>

Four values differ: the breakdown cbc:TaxAmount and the document cac:TaxTotal/cbc:TaxAmount go from 6.50 to 5.00, and cbc:TaxInclusiveAmount and cbc:PayableAmount go from 31.50 to 30.00. The failing document also reports BR-S-09, because the breakdown is Standard rated and that rule repeats the calculation for the category. No totals rule fires, because the failing totals were built consistently on the wrong 6.50. Had only the breakdown been wrong, BR-CO-14 would be reported as well.

What the validator reported

Recorded on phive 12.1.0 / phive-rules-peppol 4.5.6 / Saxon-HE 12.10, the engine behind the free validator, using synthetic data. A recorded result is regression evidence for these documents; it is not a certification.

Where it applies

  • Applies to UBL Invoice and CreditNote alike. A credit note with the same 6.50 on 25.00 at 20% reports the same two rules.
  • Every cac:TaxSubtotal inside a cac:TaxTotal is checked, in any VAT category. The category-specific counterparts, such as BR-S-09 for Standard rated and BR-Z-09 for Zero rated, run in addition.
  • Amounts are compared in the currency they are written in. The rule does not convert currencies.
  • A breakdown that passes this rule can still be wrong in its taxable amount. For Standard rated breakdowns BR-S-08 checks that against the lines, allowances and charges.

Scope and source

Written for Peppol BIS Billing 3.0.21 (May 2026), EN 16931 1.3.16, as applied to UBL 2.1 Invoice and CreditNote documents. Other profiles, syntaxes and releases can define this identifier differently. Guidance version 2026-09-20.1: source checked 2026-09-20, explanation last updated 2026-09-20.

The official definition of BR-CO-17 carries the normative wording and test. This page is our explanation of it, not a copy.

Guidance does not change the engine verdict. Fixing this finding does not mean the document passes every layer, and validation does not certify legal or tax compliance or transmit a document over Peppol.