{
  "id": "BR-O-04",
  "country": "EU",
  "syntax": "ubl",
  "group": "not-subject",
  "severity": "fatal",
  "severity_note": "fatal, from the Flag column of the Peppol BIS Billing 3.0 UBL rules index retrieved 2026-10-18.",
  "spec": "Peppol BIS Billing 3.0, May 2026 release, EN 16931 model bound to UBL",
  "statement": "An Invoice that contains a Document level charge (BG-21) where the Document level charge VAT category code (BT-102) is \"Not subject to VAT\" shall not contain the Seller VAT identifier (BT-31), the Seller tax representative VAT identifier (BT-63) or the Buyer VAT identifier (BT-48).",
  "statement_lang": "en",
  "context": "/ubl:Invoice | /cn:CreditNote",
  "test": "(exists((/ubl:Invoice|/cn:CreditNote)/cac:AllowanceCharge[cbc:ChargeIndicator=true()]/cac:TaxCategory[normalize-space(cbc:ID) = 'O'][cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']) and (not(//cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme[cac:TaxScheme/(normalize-space(upper-case(cbc:ID)) = 'VAT')]/cbc:CompanyID) and not(//cac:TaxRepresentativeParty/cac:PartyTaxScheme[cac:TaxScheme/(normalize-space(upper-case(cbc:ID)) = 'VAT')]/cbc:CompanyID) and not(//cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme[cac:TaxScheme/(normalize-space(upper-case(cbc:ID)) = 'VAT')]/cbc:CompanyID))) or not(exists((/ubl:Invoice|/cn:CreditNote)/cac:AllowanceCharge[cbc:ChargeIndicator=true()]/cac:TaxCategory[normalize-space(cbc:ID) = 'O'][cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']))",
  "meaning": "If a document-level charge uses VAT category \"Not subject to VAT\", the invoice must not contain the seller VAT identifier (BT-31), the seller tax representative VAT identifier (BT-63), or the buyer VAT identifier (BT-48). The official message names BG-21 and BT-102. It says \"shall not contain\". It does not name BT-32 or BT-47. The published test looks for a charge whose charge indicator is true, whose category code is O, and whose tax scheme is VAT, and it requires those three company identifiers to be absent. The category comparison is normalize-space(cbc:ID) = 'O' with spaces around =. The charge tax-scheme comparison is cbc:ID)='VAT' with no space before =. Each identifier check writes (normalize-space(upper-case(cbc:ID)) = 'VAT') with spaces around =. Both branches call exists. The negative branch is not(exists(...)).",
  "fix": "On a charge coded O, leave out BT-31, BT-63, and BT-48.",
  "example_fail": "A charge uses category code O, and BT-31, BT-63, or BT-48 is present.",
  "example_pass": "A charge uses O, and BT-31, BT-63, and BT-48 are absent.",
  "example_note": "Illustration of the condition in the official statement. Not an example published by the source.",
  "source_url": "https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-O-04/",
  "source_retrieved": "2026-10-18",
  "last_verified": "2026-10-18"
}
