Skip to main content

Overview

Tax calculations in UBL invoices involve a hierarchical structure:
  1. Tax Scheme: The taxation system (e.g., IVA, INC, ICA)
  2. Tax Category: The tax category with rate and exemptions
  3. Tax Subtotal: The tax calculated for a category
  4. Tax Total: The sum of all tax subtotals for a scheme
This guide covers how to properly configure and calculate taxes for Colombian and international invoices.

Tax Structure

Basic Tax Configuration

1

Create Tax Scheme

Define the taxation system:
import { TaxScheme } from 'ubl-builder';

// IVA (VAT) - ID 01
const ivaScheme = new TaxScheme({
  id: '01',
  name: 'IVA'
});

// INC (National Consumption Tax) - ID 04
const incScheme = new TaxScheme({
  id: '04',
  name: 'INC'
});

// ICA (Industry and Commerce Tax) - ID 03
const icaScheme = new TaxScheme({
  id: '03',
  name: 'ICA'
});
Colombian tax scheme IDs:
  • 01: IVA (Value Added Tax)
  • 02: IC (Consumption Tax)
  • 03: ICA (Industry and Commerce Tax)
  • 04: INC (National Consumption Tax)
  • 05: ReteIVA (VAT Withholding)
  • 06: ReteRenta (Income Withholding)
  • 07: ReteICA (ICA Withholding)
2

Create Tax Category

Define the tax category with rate:
import { TaxCategory } from 'ubl-builder';

const taxCategory = new TaxCategory({
  id: '01', // Category ID
  percent: '19', // Tax rate
  taxScheme: ivaScheme
});

// Access and modify
taxCategory.setPercent('19');
const rate = taxCategory.getPercent(); // Returns '19'
const scheme = taxCategory.getTaxScheme(); // Returns TaxScheme
3

Create Tax Subtotal

Calculate tax for a specific category:
import { TaxSubtotal } from 'ubl-builder';

const taxSubtotal = new TaxSubtotal({
  taxableAmount: '1000.00', // Base amount
  taxAmount: '190.00', // 19% of 1000
  percent: '19',
  taxCategory: taxCategory
});

// Access amounts
const taxable = taxSubtotal.getTaxableAmount(); // '1000.00'
const tax = taxSubtotal.getTaxAmount(); // '190.00'
const percent = taxSubtotal.getPercent(); // '19'
4

Create Tax Total

Sum all subtotals for a tax scheme:
import { TaxTotal } from 'ubl-builder';

const taxTotal = new TaxTotal({
  taxAmount: '190.00', // Total tax for this scheme
  taxSubtotals: [taxSubtotal]
});

// Access and modify
const total = taxTotal.getTaxAmount(); // '190.00'
taxTotal.setTaxAmount('200.00');
5

Add to Invoice

Add tax totals to the invoice:
// Add IVA tax total
invoice.addTaxTotal(taxTotal);

// Add additional tax totals
invoice.addTaxTotal({
  taxAmount: '50.00',
  taxSubtotals: [/* INC subtotals */]
});

Colombian Tax Examples

IVA (VAT) - Standard Rate

import { TaxTotal, TaxSubtotal, TaxCategory, TaxScheme } from 'ubl-builder';

// Calculate 19% IVA on 1,000,000 COP
const ivaTotal = new TaxTotal({
  taxAmount: '190000.00',
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '190000.00',
      percent: '19',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '19',
        taxScheme: new TaxScheme({
          id: '01',
          name: 'IVA'
        })
      })
    })
  ]
});

invoice.addTaxTotal(ivaTotal);

IVA - Multiple Rates

// Mixed rates: 19% and 5% IVA
const mixedIvaTotal = new TaxTotal({
  taxAmount: '240000.00', // 190,000 + 50,000
  taxSubtotals: [
    // 19% on 1,000,000
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '190000.00',
      percent: '19',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '19',
        taxScheme: new TaxScheme({ id: '01', name: 'IVA' })
      })
    }),
    // 5% on 1,000,000
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '50000.00',
      percent: '5',
      taxCategory: new TaxCategory({
        id: '02',
        percent: '5',
        taxScheme: new TaxScheme({ id: '01', name: 'IVA' })
      })
    })
  ]
});

invoice.addTaxTotal(mixedIvaTotal);

INC (National Consumption Tax)

// 8% INC on luxury goods
const incTotal = new TaxTotal({
  taxAmount: '80000.00',
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '80000.00',
      percent: '8',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '8',
        taxScheme: new TaxScheme({
          id: '04',
          name: 'INC'
        })
      })
    })
  ]
});

invoice.addTaxTotal(incTotal);

ICA (Industry and Commerce Tax)

// Variable ICA rate (e.g., 0.966% for services in Bogotá)
const icaTotal = new TaxTotal({
  taxAmount: '9660.00',
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '9660.00',
      percent: '0.966',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '0.966',
        taxScheme: new TaxScheme({
          id: '03',
          name: 'ICA'
        })
      })
    })
  ]
});

invoice.addTaxTotal(icaTotal);

Zero-Rated (Exempt) Items

// 0% IVA for exempt goods
const exemptIvaTotal = new TaxTotal({
  taxAmount: '0.00',
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '500000.00',
      taxAmount: '0.00',
      percent: '0',
      taxCategory: new TaxCategory({
        id: '04', // Exempt category
        percent: '0',
        taxScheme: new TaxScheme({ id: '01', name: 'IVA' }),
        taxExemptionReasonCode: 'E',
        taxExemptionReason: 'Exempt goods per Article 424'
      })
    })
  ]
});

invoice.addTaxTotal(exemptIvaTotal);

Multiple Tax Schemes

Combine different taxes on the same invoice:
// Product: 1,000,000 COP base
// IVA 19%: 190,000
// INC 8%: 80,000
// Total: 1,270,000

// Add IVA
invoice.addTaxTotal({
  taxAmount: '190000.00',
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '190000.00',
      percent: '19',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '19',
        taxScheme: new TaxScheme({ id: '01', name: 'IVA' })
      })
    })
  ]
});

// Add INC
invoice.addTaxTotal({
  taxAmount: '80000.00',
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '80000.00',
      percent: '8',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '8',
        taxScheme: new TaxScheme({ id: '04', name: 'INC' })
      })
    })
  ]
});

// Set monetary totals
import { LegalMonetaryTotal } from 'ubl-builder';

invoice.setLegalMonetaryTotal({
  lineExtensionAmount: '1000000.00',
  taxExclusiveAmount: '1000000.00',
  taxInclusiveAmount: '1270000.00',
  payableAmount: '1270000.00'
});

Tax Calculation Helpers

Calculate Tax Amount

function calculateTax(baseAmount: number, rate: number): string {
  const taxAmount = baseAmount * (rate / 100);
  return taxAmount.toFixed(2);
}

const base = 1000000;
const ivaRate = 19;
const ivaAmount = calculateTax(base, ivaRate); // '190000.00'

Calculate Total with Tax

function calculateTotal(baseAmount: number, taxRate: number): {
  base: string;
  tax: string;
  total: string;
} {
  const tax = baseAmount * (taxRate / 100);
  const total = baseAmount + tax;
  
  return {
    base: baseAmount.toFixed(2),
    tax: tax.toFixed(2),
    total: total.toFixed(2)
  };
}

const result = calculateTotal(1000000, 19);
// { base: '1000000.00', tax: '190000.00', total: '1190000.00' }

Find Tax by Scheme ID

The Invoice class provides a helper to retrieve tax amounts:
// After adding tax totals to invoice
const ivaAmount = invoice.findTaxTotalById('01'); // Returns '190000.00'
const incAmount = invoice.findTaxTotalById('04'); // Returns '80000.00'
const icaAmount = invoice.findTaxTotalById('03'); // Returns '0.00' if not set

// Get as number
const ivaNumber = invoice.findTaxTotalById('01', false); // Returns 190000

Invoice Line Taxes

Add tax information to individual invoice lines:
import { InvoiceLine, Item, Price, TaxTotal } from 'ubl-builder';

const line = new InvoiceLine({
  id: '1',
  invoicedQuantity: '10',
  lineExtensionAmount: '1000000.00',
  item: new Item({
    description: 'Professional Services',
    sellersItemIdentification: { id: 'SRV-001' }
  }),
  price: new Price({
    priceAmount: '100000.00',
    baseQuantity: '1'
  }),
  taxTotals: [
    new TaxTotal({
      taxAmount: '190000.00',
      taxSubtotals: [
        new TaxSubtotal({
          taxableAmount: '1000000.00',
          taxAmount: '190000.00',
          percent: '19',
          taxCategory: new TaxCategory({
            id: '01',
            percent: '19',
            taxScheme: new TaxScheme({ id: '01', name: 'IVA' })
          })
        })
      ]
    })
  ]
});

invoice.addInvoiceLine(line);

Withholding Taxes

For withholding tax (retenciones):
import { WithholdingTaxTotal } from 'ubl-builder';

// ReteIVA - 15% withholding on IVA
const reteIvaTotal = new WithholdingTaxTotal({
  taxAmount: '28500.00', // 15% of 190,000 IVA
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '190000.00',
      taxAmount: '28500.00',
      percent: '15',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '15',
        taxScheme: new TaxScheme({
          id: '05',
          name: 'ReteIVA'
        })
      })
    })
  ]
});

// Note: Withholding taxes reduce the payable amount
const payableAmount = 1190000 - 28500; // 1,161,500

Advanced Tax Configuration

Tiered Tax Rates

// Progressive tax rate based on amount tiers
const tieredTaxSubtotal = new TaxSubtotal({
  taxableAmount: '5000000.00',
  taxAmount: '250000.00',
  percent: '5',
  tierRange: '0-10000000',
  tierRatePercent: '5',
  taxCategory: new TaxCategory({
    id: '01',
    percent: '5',
    taxScheme: new TaxScheme({ id: '01', name: 'IVA' })
  })
});

Per-Unit Tax

import { UdtMeasure } from 'ubl-builder';

// Tax per unit (e.g., per liter, per kilogram)
const perUnitTaxSubtotal = new TaxSubtotal({
  taxableAmount: '0.00',
  taxAmount: '5000.00', // 100 units × 50 per unit
  baseUnitMeasure: new UdtMeasure('100', { unitCode: 'LTR' }),
  perUnitAmount: '50.00',
  taxCategory: new TaxCategory({
    id: '01',
    percent: '0',
    taxScheme: new TaxScheme({ id: '02', name: 'IC' })
  })
});

Testing Tax Calculations

import { TaxTotal, TaxSubtotal } from 'ubl-builder';

// Create tax total
const taxTotal = new TaxTotal({ taxAmount: '19.00' } as any);

// Test getter
expect(taxTotal.getTaxAmount()).toBe('19.00');

// Test setter
taxTotal.setTaxAmount('20.00');
expect(taxTotal.getTaxAmount()).toBe('20.00');

// Test calculation
const subtotal1 = new TaxSubtotal({ taxAmount: '10' } as any);
const subtotal2 = new TaxSubtotal({ taxAmount: '2.5' } as any);
const total = new TaxTotal({ 
  taxAmount: '0', 
  taxSubtotals: [subtotal1, subtotal2] 
} as any);

// Note: calculateTotalTaxAmount() has a known issue
// It concatenates strings instead of adding numbers
const calculated = total.calculateTotalTaxAmount();
// Current behavior returns '0102.5' instead of '12.5'
The calculateTotalTaxAmount() method has a known issue where it concatenates strings instead of adding numbers. Always set taxAmount explicitly instead of relying on this method.

Complete Example

import {
  Invoice,
  TaxTotal,
  TaxSubtotal,
  TaxCategory,
  TaxScheme,
  LegalMonetaryTotal
} from 'ubl-builder';

// Create invoice (simplified)
const invoice = new Invoice('SETP990000001', {
  /* options */
} as any);

// Add IVA 19%
invoice.addTaxTotal({
  taxAmount: '190000.00',
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '190000.00',
      percent: '19',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '19',
        taxScheme: new TaxScheme({
          id: '01',
          name: 'IVA'
        })
      })
    })
  ]
});

// Add INC 8%
invoice.addTaxTotal({
  taxAmount: '80000.00',
  taxSubtotals: [
    new TaxSubtotal({
      taxableAmount: '1000000.00',
      taxAmount: '80000.00',
      percent: '8',
      taxCategory: new TaxCategory({
        id: '01',
        percent: '8',
        taxScheme: new TaxScheme({
          id: '04',
          name: 'INC'
        })
      })
    })
  ]
});

// Set monetary totals
invoice.setLegalMonetaryTotal({
  lineExtensionAmount: '1000000.00',
  taxExclusiveAmount: '1000000.00',
  taxInclusiveAmount: '1270000.00',
  payableAmount: '1270000.00'
});

// Retrieve tax amounts
const iva = invoice.findTaxTotalById('01'); // '190000.00'
const inc = invoice.findTaxTotalById('04'); // '80000.00'
const ica = invoice.findTaxTotalById('03'); // '0.00'

Best Practices

Tax Calculation

  • Always calculate tax amounts before creating tax objects
  • Use proper decimal precision (2 decimals for COP)
  • Set taxAmount explicitly; don’t rely on calculateTotalTaxAmount()
  • Verify tax amounts match legal requirements
  • Include all applicable taxes for your jurisdiction

Tax Categories

  • Use correct tax scheme IDs (‘01’ for IVA, ‘04’ for INC, etc.)
  • Set appropriate tax category IDs for different rates
  • Include exemption reason codes when applicable
  • Document tax exemptions with reason text

Testing

  • Test calculations with known values
  • Verify tax totals sum correctly
  • Check rounding behavior
  • Test edge cases (zero rates, exempt items)
  • Validate against official tax tables

Next Steps

Creating Invoices

Complete guide to invoice creation

DIAN Extensions

Colombian e-invoicing compliance

Build docs developers (and LLMs) love