The Conversion System provides bidirectional transformation between GOBL (JSON-based business document format) and UBL 2.1 (XML-based Universal Business Language format). The system implements two independent but complementary pipelines that enable round-trip conversion while supporting multiple European e-invoicing standards.
Core Functions:
| Function | Direction | Input Type | Output Type | Purpose |
|---|---|---|---|---|
ubl.Parse() | UBL → GOBL | []byte (XML) | any (Go struct) | Parse UBL XML to Go structures |
ubl.Convert() | GOBL → UBL | *gobl.Envelope | any (Go struct) | Convert GOBL to UBL structures |
ubl.ConvertInvoice() | GOBL → UBL | *gobl.Envelope | *Invoice | Type-safe invoice conversion |
ubl.Bytes() | Serialization | any (Go struct) | []byte (XML) | Serialize UBL structures to XML |
Invoice.Convert() | UBL → GOBL | *Invoice | *gobl.Envelope | Method for parsing invoices |
Key System Features:
Invoice structCustomizationID and ProfileID during UBL→GOBL parsingRelated Pages:
Sources: ubl.go1-177 invoice.go1-215 context.go1-177
The conversion system consists of four primary API functions that coordinate with internal processing functions to transform documents between formats. The architecture is organized into three layers: external formats, public APIs, and internal processing.
High-Level System Architecture
Component Responsibilities:
| Component | File | Responsibility |
|---|---|---|
ubl.Parse() | ubl.go47-88 | Entry point for UBL→GOBL conversion |
ubl.Convert() | ubl.go90-120 | Entry point for GOBL→UBL conversion |
ubl.ConvertInvoice() | invoice.go203-215 | Type-safe wrapper for invoice conversion |
ubl.Bytes() | ubl.go168-176 | XML serialization with header |
extractRootNamespace() | ubl.go150-166 | Detect document type from namespace |
ensureAddons() | ubl.go122-148 | Validate and add required addons |
ublInvoice() | invoice.go101-194 | Main GOBL→UBL transformation orchestrator |
Invoice.Convert() | invoice_parse.go30-54 | Main UBL→GOBL transformation orchestrator |
FindContext() | context.go61-88 | Detect context from CustomizationID/ProfileID |
Sources: ubl.go1-177 invoice.go1-215 invoice_parse.go1-157 context.go1-177
The system implements two independent pipelines with different processing logic. The GOBL→UBL pipeline is prescriptive (enforcing requirements), while the UBL→GOBL pipeline is descriptive (detecting characteristics).
GOBL to UBL Conversion Pipeline
UBL to GOBL Parsing Pipeline
Pipeline Characteristics:
| Characteristic | GOBL → UBL | UBL → GOBL |
|---|---|---|
| Context handling | Prescriptive (specified via option) | Descriptive (detected from metadata) |
| Addon handling | Enforced before conversion | Not processed |
| Tax handling | Removes included taxes | Detects from TaxTotal structures |
| Validation | Validates invoice after addon addition | No validation |
| Entry function | ubl.Convert() | ubl.Parse() |
| Main orchestrator | ublInvoice() | Invoice.Convert() |
For detailed implementation of each pipeline, see:
Sources: ubl.go47-120 invoice.go101-194 invoice_parse.go30-157
The system currently supports UBL Invoice and CreditNote documents through a unified Invoice struct invoice.go28-97 The struct serves both document types because their structures are nearly identical in UBL 2.1, differing primarily in namespace and type code fields.
Namespace to Structure Mapping
| UBL Namespace | Go Struct | Type Code Field |
|---|---|---|
urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 | Invoice | InvoiceTypeCode |
urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2 | Invoice | CreditNoteTypeCode |
During parsing, the extractRootNamespace() function ubl.go150-166 reads the root element namespace to determine which document type is being processed. Both types unmarshal into the same Invoice struct with different type code fields populated.
Document Type Code Mapping
The system maps between UBL type codes (UNTDID 1001) and GOBL invoice types:
| UBL Type Code | Description | GOBL Type | Tags |
|---|---|---|---|
380 | Commercial invoice | standard | - |
381 | Credit note | credit-note | - |
383 | Debit note | debit-note | - |
384 | Corrective invoice | corrective | - |
325 | Proforma invoice | proforma | - |
389 | Self-billed invoice | standard | self-billed |
326 | Partial invoice | standard | partial |
261 | Self-billed credit note | credit-note | self-billed |
Type codes are processed through invoiceTypeMap type.go11-34 during conversion and typeCodeParse() during parsing. Some type codes result in tags being added to the invoice rather than changing the type itself.
Sources: invoice.go14-24 invoice.go28-97 ubl.go150-166 type.go11-34
The conversion system uses XML namespaces to identify document types and properly parse/generate UBL documents. All UBL documents use standardized namespace URIs from the OASIS UBL 2.1 specification.
Namespace Constants
The system defines the following namespace constants:
The extractRootNamespace function ubl.go150-166 reads the XML document to determine its type by examining the root element's namespace. This namespace determines which struct to unmarshal into.
During parsing, xmlctx.Unmarshal ubl.go56-64 is configured with a namespace map that assigns prefixes to all required UBL namespaces:
""): The main document namespace (Invoice or CreditNote)cbc: Common Basic Componentscac: Common Aggregate Componentsqdt: Qualified Data Typesudt: Unqualified Data Typesccts: Core Component Type Schemaxsi: XML Schema InstanceDuring generation, these namespaces are written as attributes on the root Invoice element invoice.go29-38
Sources: ubl.go14-18 ubl.go150-166 ubl.go56-64 invoice.go29-38
Before converting GOBL to UBL, the system performs two critical pre-processing steps: addon enforcement and tax normalization. These steps ensure the GOBL invoice conforms to UBL requirements.
Pre-processing Steps
Addon Requirements by Context
Each Context defines required addons in its Addons field:
| Context | Required Addon | Purpose |
|---|---|---|
ContextEN16931 | en16931.V2017 | European standard EN 16931 compliance |
ContextPeppol | en16931.V2017 | Peppol BIS 3.0 compliance |
ContextPeppolSelfBilled | en16931.V2017 | Peppol self-billing compliance |
ContextXRechnung | xrechnung.V3 | German XRechnung 3.0 compliance |
ContextPeppolFranceCIUS | ctc.Flow2V1 | French CIUS compliance |
ContextPeppolFranceExtended | facturx.V1 | French extended profile compliance |
The ensureAddons() function ubl.go122-148 automatically adds missing addons and recalculates the invoice. If the invoice cannot be calculated or validated with the required addons, an error is returned.
Tax Normalization
UBL does not support tax-inclusive pricing (where taxes are already incorporated into line prices). The RemoveIncludedTaxes() method ubl.go112-114 converts any included taxes to excluded taxes. If this conversion fails (e.g., due to rounding issues), an error is returned and conversion halts.
Sources: ubl.go107-148 context.go109-177
The conversion system defines specific error types for different failure scenarios:
Error Types
| Error | Condition | Source |
|---|---|---|
ErrUnknownDocumentType | XML namespace not recognized during parsing | ubl.go19 |
ErrUnsupportedDocumentType | Document type not supported for conversion | ubl.go23 |
| Addon calculation error | Required addon cannot be added or invoice fails validation | ubl.go142-146 |
| Included tax error | Invoice has included taxes that cannot be removed | ubl.go112-114 |
Sources: ubl.go16-24 ubl.go47-88 ubl.go90-120
The Bytes function converts any UBL document structure to XML bytes with proper formatting.
Serialization Process
The function ubl.go170-176:
xml.MarshalIndent() with 2-space indentation<?xml version="1.0" encoding="UTF-8"?>)The resulting XML includes:
Sources: ubl.go168-176