Anatomy of a UBL invoice¶
UBL — the OASIS Universal Business Language — is one of the two XML syntaxes
that can carry an EN16931 invoice (the other is UN/CEFACT CII). A UBL
invoice is a single XML document rooted at <Invoice>, and once you know how it is
organised it reads like a paper invoice: header fields, the two parties, the tax
and total blocks, then the lines.
This page walks a small but complete invoice and shows how the business terms of the EN16931 model map onto the elements.
Three namespaces¶
Every UBL invoice mixes three namespaces, and you will see their prefixes on nearly every element:
| Prefix | Namespace | Holds |
|---|---|---|
| (default) | …:ubl:schema:xsd:Invoice-2 |
the document root <Invoice> only |
cbc |
…:CommonBasicComponents-2 |
leaf fields — a single value (cbc:ID, cbc:IssueDate) |
cac |
…:CommonAggregateComponents-2 |
aggregates — groups of other elements (cac:AccountingSupplierParty) |
The split is the whole mental model: cbc is a value, cac is a container.
A cac element holds more cac and cbc children; a cbc element holds text.
A complete invoice¶
- BT-24, Specification identifier. The single most important field: it
declares which rulebook the document claims to follow.
urn:cen.eu:en16931:2017is plain EN16931; a Peppol invoice puts a longer Peppol URN here (see Peppol and CIUS profiles). This is the field rule BR-01 checks. - BT-1, Invoice number.
- BT-3, Invoice type code —
380is "commercial invoice", a value from code list UNCL1001 (see Genericode). - BG-4, Seller. A
cacaggregate wrapping acac:Party, itself wrapping name and address aggregates —cacnesting all the way down to thecbcleaves. - BG-22 / BT-110, total VAT. The
currencyIDattribute restates the currency on every amount. - BG-22, document totals. The four amounts a rule like BR-CO-10 ties together arithmetically.
- BG-25, an invoice line — quantity, line amount, the item, and a unit price.
Business terms vs elements¶
EN16931 does not describe XML at all. It defines a semantic model: numbered
business terms (BT) and business groups (BG) — BT-1 is "Invoice number", BG-4
is "Seller" — with no mention of cbc or cac. The UBL binding is the mapping
from those numbers to concrete elements:
| EN16931 | Means | UBL element |
|---|---|---|
| BT-24 | Specification identifier | cbc:CustomizationID |
| BT-1 | Invoice number | cbc:ID |
| BT-2 | Issue date | cbc:IssueDate |
| BT-5 | Currency | cbc:DocumentCurrencyCode |
| BG-4 | Seller | cac:AccountingSupplierParty |
| BG-25 | Invoice line | cac:InvoiceLine |
| BT-131 | Line net amount | cac:InvoiceLine/cbc:LineExtensionAmount |
| BT-106 | Sum of line net amounts | cac:LegalMonetaryTotal/cbc:LineExtensionAmount |
The same model, a different syntax
In the CII syntax, BT-1 is ram:ID inside rsm:ExchangedDocument, not
cbc:ID. The business term is identical; only the binding differs. This is
exactly why EN16931's Schematron is written as an
abstract pattern bound twice —
one rulebook, two syntaxes.
Reading the invoice with XPath¶
Because it is just XML, every XPath technique applies. The totals rule BR-CO-10 — document line total equals the sum of the lines — is one expression:
And pulling a human-readable summary is ordinary XSLT:
<xsl:value-of select="cbc:ID"/> —
<xsl:value-of select="cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name"/>:
<xsl:value-of select="cac:LegalMonetaryTotal/cbc:PayableAmount"/>
<xsl:value-of select="cac:LegalMonetaryTotal/cbc:PayableAmount/@currencyID"/>
INV-001 — Contoso Wholesale AB: 27.25 EUR
Next¶
A well-formed invoice is only the start — it still has to pass. Next: The validation pipeline — the layered checks every invoice runs, and which layer catches what.