Skip to main content

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:
1

Create Invoice

const invoice = new Invoice('INV-001', options);
2

Add Aggregate Components

invoice.addTaxTotal(new TaxTotal({
  taxAmount: new UdtAmount('190.00', { currencyID: 'COP' }),
  taxSubtotals: [/* ... */],
}));
3

Components Store UDT Types

// TaxTotal internally stores:
this.attributes.taxAmount = new UdtAmount('190.00', ...);
4

Serialize to JSON

// GenericAggregateComponent.parseToJson()
{
  'cbc:TaxAmount': {
    '#text': '190.00',
    '@currencyID': 'COP'
  }
}
5

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

Build docs developers (and LLMs) love