The short answer
PEPPOL-EN16931-R054 counts the cac:TaxTotal elements directly under the root that contain no cac:TaxSubtotal. With cbc:TaxCurrencyCode present there must be exactly one, carrying the VAT total in the accounting currency; without it there must be none. In the recorded example a EUR tax total is present but the code declaring EUR as the accounting currency is not, so either add cbc:TaxCurrencyCode or remove the extra total.
Which of the two is right is a business fact, not something the validator can decide. Keep the second total only if the seller really accounts for VAT in that currency.
What the rule checks
The rule runs once per document. A tax total is counted when it is a direct child of the root and has no cac:TaxSubtotal child at all; the tax total that holds the VAT breakdown is never counted.
The expected count is 1 when cbc:TaxCurrencyCode exists and 0 when it does not. The currency of the counted total is not examined here, so a spare total in the invoice currency counts the same as one in the accounting currency; when tried without cbc:TaxCurrencyCode, a duplicate GBP total was reported by this rule and by BR-CO-15.
Two totals without a breakdown fail even when both are in the declared currency. A duplicated EUR total beside a cbc:TaxCurrencyCode of EUR reported this rule alone when tried.
The accounting-currency total must not carry a breakdown of its own. When tried, giving the EUR total a cac:TaxSubtotal left no total without a breakdown, so this rule fired, and PEPPOL-EN16931-R053 (two totals with a breakdown), PEPPOL-EN16931-R051 (EUR amounts where the invoice currency is required) and BR-S-08 came with it.
| Term | Meaning | UBL element |
|---|---|---|
| BT-6 | VAT accounting currency code | cbc:TaxCurrencyCode |
| BT-111 | Invoice total VAT amount in accounting currency | cac:TaxTotal/cbc:TaxAmount (in the tax total without cac:TaxSubtotal) |
| BG-23 | VAT breakdown | cac:TaxTotal/cac:TaxSubtotal |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- The converted VAT total is written whenever the source record holds one, while
cbc:TaxCurrencyCodedepends on a separate setting that is off. - The VAT total is emitted twice: once in the tax total with the breakdown and again as a bare tax total in the invoice currency.
- The accounting currency is declared, but the serialiser only ever emits one
cac:TaxTotal, so the converted amount never appears. - The converted VAT is broken down by category like the main total instead of being sent as a single amount.
How to fix it
- Establish whether the seller accounts for VAT in a currency other than the invoice currency.
- If so, write that currency code to
cbc:TaxCurrencyCode, straight aftercbc:DocumentCurrencyCode, and keep exactly one extracac:TaxTotalcontaining onlycbc:TaxAmountwith that code as itscurrencyID. - If not, delete every
cac:TaxTotalthat has nocac:TaxSubtotal. The VAT total in the invoice currency belongs in the tax total that holds the breakdown. - Do not give the accounting-currency total a breakdown, and do not write it twice.
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: a EUR tax total without a breakdown, but no VAT accounting currency declared
<cbc:DocumentCurrencyCode>GBP</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>BUYER-REF-001</cbc:BuyerReference>
<!-- parties omitted from this fragment -->
<cac:TaxTotal>
<cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
<!-- VAT breakdown omitted from this fragment -->
</cac:TaxTotal>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">5.80</cbc:TaxAmount>
</cac:TaxTotal>Fragment of the corrected invoice: EUR is declared as the VAT accounting currency
<cbc:DocumentCurrencyCode>GBP</cbc:DocumentCurrencyCode>
<cbc:TaxCurrencyCode>EUR</cbc:TaxCurrencyCode>
<cbc:BuyerReference>BUYER-REF-001</cbc:BuyerReference>
<!-- parties omitted from this fragment -->
<cac:TaxTotal>
<cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
<!-- VAT breakdown omitted from this fragment -->
</cac:TaxTotal>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">5.80</cbc:TaxAmount>
</cac:TaxTotal>The corrected invoice adds <cbc:TaxCurrencyCode>EUR</cbc:TaxCurrencyCode> after the document currency; both tax totals are identical in the two documents. The failing document reports only PEPPOL-EN16931-R054. With no accounting currency declared, BR-53 and PEPPOL-EN16931-R055 have nothing to test, and PEPPOL-EN16931-R051 leaves tax totals without a breakdown out of its currency check, so the stray EUR amount is caught by this rule alone. Removing the EUR total instead would also have passed.
What the validator reported
- The failing invoice reports PEPPOL-EN16931-R054. The corrected document passes every layer with no findings.Download the failing XMLDownload the corrected XML
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
InvoiceandCreditNote. A credit note with a EUR tax total and nocbc:TaxCurrencyCodereported this rule alone when tried. - Reported by the Peppol layer. Beside it,
BR-53andPEPPOL-EN16931-R055appear when the accounting currency is declared but its total is missing, andPEPPOL-EN16931-R005when the declared accounting currency is the invoice currency itself. - A document whose only
cac:TaxTotalhas no breakdown reports this rule withBR-CO-18,PEPPOL-EN16931-R053and the category rule for its lines: that single total counts as one without a breakdown.
Related rules
- BR-53 requires the accounting-currency VAT total that this rule counts
- PEPPOL-EN16931-R053 is the counterpart for the tax total that holds the breakdown, which must appear exactly once
- PEPPOL-EN16931-R005 rejects an accounting currency equal to the invoice currency and brings this rule with it
- PEPPOL-EN16931-R055 compares the sign of the two VAT totals once both are present
- Browse every rule in the reference
- Background: How Peppol invoice validation actually works
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-24.1: source checked 2026-09-24, explanation last updated 2026-09-24.
The official definition of PEPPOL-EN16931-R054 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.
