# PEPPOL-EN16931-R054: Send a tax total without a breakdown only with a VAT accounting currency

A `cac:TaxTotal` with no `cac:TaxSubtotal` must appear exactly once when `cbc:TaxCurrencyCode` is given, and not at all when it is absent.

- Layer: Peppol BIS Billing
- Severity: fatal (the document is invalid)
- Topics: VAT, Totals
- Official definition: https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-peppol/PEPPOL-EN16931-R054/
- Explanation last updated: 2026-09-24

## 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:TaxCurrencyCode` depends 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

1. Establish whether the seller accounts for VAT in a currency other than the invoice currency.
2. If so, write that currency code to `cbc:TaxCurrencyCode`, straight after `cbc:DocumentCurrencyCode`, and keep exactly one extra `cac:TaxTotal` containing only `cbc:TaxAmount` with that code as its `currencyID`.
3. If not, delete every `cac:TaxTotal` that has no `cac:TaxSubtotal`. The VAT total in the invoice currency belongs in the tax total that holds the breakdown.
4. Do not give the accounting-currency total a breakdown, and do not write it twice.

## 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

```xml
<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

```xml
<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 XML](https://ironfang.uk/finance/rule-examples/PEPPOL-EN16931-R054-invalid.xml)
  - [Download the corrected XML](https://ironfang.uk/finance/rule-examples/tax-currency-valid.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 `Invoice` and `CreditNote`. A credit note with a EUR tax total and no `cbc:TaxCurrencyCode` reported this rule alone when tried.
- Reported by the Peppol layer. Beside it, `BR-53` and `PEPPOL-EN16931-R055` appear when the accounting currency is declared but its total is missing, and `PEPPOL-EN16931-R005` when the declared accounting currency is the invoice currency itself.
- A document whose only `cac:TaxTotal` has no breakdown reports this rule with `BR-CO-18`, `PEPPOL-EN16931-R053` and 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](https://ironfang.uk/docs/finance/rules/BR-53.md)
- [PEPPOL-EN16931-R053 is the counterpart for the tax total that holds the breakdown, which must appear exactly once](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R053.md)
- [PEPPOL-EN16931-R005 rejects an accounting currency equal to the invoice currency and brings this rule with it](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R005.md)
- [PEPPOL-EN16931-R055 compares the sign of the two VAT totals once both are present](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R055.md)

## 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](https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-peppol/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.

## Links

- [This rule as a web page](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R054)
- [Free Peppol invoice validator](https://ironfang.uk/tools/peppol-validator)
- [Rule index](https://ironfang.uk/docs/finance/rules.md)
- [Ironfang Finance API docs](https://ironfang.uk/docs/finance)
- The same rule is available to MCP clients as the tool `finance.rule.get` on https://mcp.ironfang.uk/mcp
