Architecture Overview
The ubl-builder library mirrors the UBL 2.1 schema structure through TypeScript classes organized into three main layers:
Layer 1: Schema Documents
The top-level document classes represent complete UBL documents.
Invoice Class
The primary document type in this library:
import { Invoice } from 'ubl-builder' ;
const invoice = new Invoice ( 'SETP990000001' , {
timestamp: Date . now (),
enviroment: '2' ,
issuer: { /* ... */ },
software: { /* ... */ },
});
Location: src/ubl21/schemaDocuments/Invoice.ts
The Invoice class provides over 50 setter methods corresponding to UBL elements:
setUBLVersionID() - UBL schema version (e.g., “UBL 2.1”)
setID() - Invoice number
setIssueDate() - Issue date in YYYY-MM-DD format
setIssueTime() - Issue time with timezone
setDocumentCurrencyCode() - Currency code (e.g., “COP”, “USD”)
setAccountingSupplierParty() - Supplier/seller information
setAccountingCustomerParty() - Customer/buyer information
addInvoiceLine() - Add line items
addTaxTotal() - Add tax calculations
setLegalMonetaryTotal() - Set monetary totals
All methods return this for fluent chaining.
Internal Data Management
The Invoice class stores data in an internal children object:
// From Invoice.ts:134
private children : IGenericKeyValue < any > = {};
setID ( value : string | UdtIdentifier , attributes = {}): Invoice {
this . children . id = value instanceof UdtIdentifier
? value
: new UdtIdentifier ( value , attributes );
return this ;
}
This ensures type safety and proper XML serialization.
The children object maintains the correct UBL element order automatically using the INVOICE_CHILDREN_MAP configuration.
Layer 2: Common Aggregate Components
Aggregate components are reusable complex structures that group related elements.
Party Component
Represents an organization or individual:
import { Party , PartyTaxScheme , PartyLegalEntity } from 'ubl-builder' ;
const party = new Party ({
partyTaxSchemes: [],
partyLegalEntities: [],
postalAddress: postalAddress ,
});
party . addPartyTaxScheme ( new PartyTaxScheme ({
registrationName: 'ACME Corporation' ,
companyID: '900123456' ,
taxScheme: taxScheme ,
}));
party . addPartyLegalEntity ( new PartyLegalEntity ({
registrationName: 'ACME Corporation S.A.S.' ,
companyID: '900123456' ,
}));
Location: src/ubl21/CommonAggregateComponents/PartyTypeGroup.ts
TaxTotal Component
Handles tax calculations:
import { TaxTotal , TaxSubtotal , TaxCategory , TaxScheme } from 'ubl-builder' ;
const taxTotal = new TaxTotal ({
taxAmount: { content: '190.00' , attributes: { currencyID: 'COP' } },
taxSubtotals: [
new TaxSubtotal ({
taxableAmount: { content: '1000.00' , attributes: { currencyID: 'COP' } },
taxAmount: { content: '190.00' , attributes: { currencyID: 'COP' } },
taxCategory: new TaxCategory ({
percent: '19.00' ,
taxScheme: new TaxScheme ({ id: '01' , name: 'IVA' }),
}),
}),
],
});
invoice . addTaxTotal ( taxTotal );
Location: src/ubl21/CommonAggregateComponents/TaxTotalTypeGroup.ts
GenericAggregateComponent Base Class
Most aggregate components extend GenericAggregateComponent:
// From GenericAggregateComponent.ts:18
export default class GenericAggregateComponent {
private paramsMap : IGenericKeyValue < ParamsMapValues > = {};
protected attributes : IGenericKeyValue < any > = {};
constructor ( content : any , paramsMap : IGenericKeyValue < ParamsMapValues >) {
this . paramsMap = paramsMap ;
this . assignContent ( content );
}
parseToJson () {
// Converts attributes to JSON preserving UBL element order
}
}
Key features:
Parameter mapping: Validates allowed attributes and their cardinality
Order preservation: Ensures elements appear in correct UBL sequence
Type conversion: Automatically instantiates typed objects from raw values
The paramsMap defines element metadata: const ParamsMap = {
taxAmount: {
order: 1 ,
attributeName: 'cbc:TaxAmount' ,
min: 1 ,
max: 1 ,
classRef: UdtAmount
},
// ...
};
Layer 3: Unqualified Data Types (UDT)
UBL’s strongly-typed primitive values.
Core UDT Types
UdtAmount Monetary values with currency new UdtAmount ( '1000.00' , { currencyID: 'COP' })
UdtIdentifier Unique identifiers with schemes new UdtIdentifier ( '900123456' , {
schemeName: '31' // NIT
})
UdtCode Coded values from codelists new UdtCode ( '01' , {
listName: 'Tax Category'
})
UdtText Text with language attributes new UdtText ( 'Invoice' , {
languageID: 'es'
})
Type Hierarchy
UDT types inherit from Core Component Types (CCT), which inherit from XSD base types:
// UdtAmount.ts:20
export class UdtAmount extends CctAmountType {
constructor ( content : string , attributes ?: UdtAmountAttributes ) {
super ( content , attributes );
}
}
// CctAmount.ts:14
export class CctAmountType extends XsdDecimal {
constructor ( content : string , attributes ?: AllowedAttributes ) {
super ( content , attributes );
}
}
// XsdDecimal extends XsdAnySimpleType
XSD Base Types
The foundation layer implements XML Schema primitive types:
// XsdAnySimpleType.ts:4
export default class XsdAnySimpleType implements IXsdAnySimpleType {
content : string | number | boolean ;
attributes : IDictionary < string > = {};
parseToJson () : any {
return { '#text' : this . content };
}
}
Available XSD types:
XsdString - Text values
XsdDecimal - Numeric values
XsdDate - Date values (YYYY-MM-DD)
XsdTime - Time values with timezone
XsdBoolean - True/false values
XsdBase64Binary - Binary data
XsdAnyURI - URI references
Always use the appropriate UDT wrapper instead of raw strings or numbers. This ensures proper XML serialization and attribute handling. // ❌ Wrong
invoice . setID ( 'INV-001' );
// ✅ Correct
invoice . setID ( new UdtIdentifier ( 'INV-001' ));
// ✅ Also correct - library handles conversion
invoice . setID ( 'INV-001' ); // Converted internally to UdtIdentifier
Data Flow Example
Here’s how data flows through the layers:
Create Invoice
const invoice = new Invoice ( 'INV-001' , options );
Add Aggregate Components
invoice . addTaxTotal ( new TaxTotal ({
taxAmount: new UdtAmount ( '190.00' , { currencyID: 'COP' }),
taxSubtotals: [ /* ... */ ],
}));
Components Store UDT Types
// TaxTotal internally stores:
this . attributes . taxAmount = new UdtAmount ( '190.00' , ... );
Serialize to JSON
// GenericAggregateComponent.parseToJson()
{
'cbc:TaxAmount' : {
'#text' : '190.00' ,
'@currencyID' : 'COP'
}
}
Generate XML
< cbc:TaxAmount currencyID = "COP" > 190.00 </ cbc:TaxAmount >
Extension Components
UBL extensions allow country-specific customizations:
import { UBLExtensions , DianExtensions } from 'ubl-builder' ;
// DIAN-specific extensions for Colombia
const extensions = new UBLExtensions ();
extensions . addUBLExtension ( new UBLExtensionType ({
extensionContent: new DianExtensions ({
invoiceControl: invoiceControl ,
softwareProvider: softwareProvider ,
// ...
}),
}));
invoice . setUBLExtensions ( extensions );
Location: src/ubl21/extensionComponents/
The library automatically handles DIAN extensions when you provide issuer and software options to the Invoice constructor.
Common Patterns
Builder Pattern
All setters return this for method chaining:
invoice
. setUBLVersionID ( 'UBL 2.1' )
. setID ( 'INV-001' )
. setIssueDate ( '2026-03-06' )
. setDocumentCurrencyCode ( 'COP' )
. setAccountingSupplierParty ( supplier )
. setAccountingCustomerParty ( customer );
Array Management
Components with multiple occurrences use add methods:
// Add multiple invoice lines
invoice . addInvoiceLine ( line1 );
invoice . addInvoiceLine ( line2 );
invoice . addInvoiceLine ( line3 );
// Add multiple tax totals (e.g., VAT + withholding)
invoice . addTaxTotal ( vatTotal );
invoice . addTaxTotal ( withholdingTotal );
Parameter Flexibility
Most methods accept either raw values or typed objects:
// Using raw values
invoice . setID ( 'INV-001' );
// Using typed object
invoice . setID ( new UdtIdentifier ( 'INV-001' , {
schemeName: 'Invoice Number' ,
}));
// Both are equivalent - library handles conversion
Next Steps
XML Generation Learn how objects are serialized to valid UBL XML
API Reference Explore all available classes and methods