Skip to main content

Overview

The go-siat SDK uses a fluent builder pattern to construct invoices safely and efficiently. The builder pattern ensures:
  • Type safety with opaque interfaces
  • Prevention of direct manipulation of internal structures
  • Clear, readable invoice construction code
  • Compile-time validation of required fields

Invoice Structure

A complete invoice consists of three main components:
  1. Factura - The root invoice container
  2. Cabecera - The header with issuer, customer, and transaction details
  3. Detalle - Individual line items (products/services)
FacturaCompraVenta (Invoice)
├── Cabecera (Header)
│   ├── Issuer information
│   ├── Customer information
│   └── Transaction totals
└── Detalle[] (Line Items)
    ├── Product/service details
    ├── Quantities and prices
    └── Subtotals

Building an Invoice

1. Create Cabecera (Header)

The header contains issuer, customer, and transaction information:
cabecera := models.CompraVenta.NewCabecera().
    // Issuer information
    WithNitEmisor(123456789).
    WithRazonSocialEmisor("Mi Empresa S.A.").
    WithMunicipio("La Paz").
    WithDireccion("Av. Principal 123").
    WithTelefono("+591 2 1234567").
    
    // Invoice identification
    WithNumeroFactura(1).
    WithCuf("GENERATED_CUF").
    WithCufd("DAILY_CUFD").
    WithCodigoSucursal(0).
    WithCodigoPuntoVenta(0).
    WithFechaEmision("2024-01-15T14:30:00.000").
    
    // Customer information
    WithNombreRazonSocial("JUAN PEREZ").
    WithCodigoTipoDocumentoIdentidad(1).
    WithNumeroDocumento("5544332").
    WithComplemento("1A").
    WithCodigoCliente("CLI-001").
    
    // Payment information
    WithCodigoMetodoPago(1).
    WithMontoTotal(100.0).
    WithMontoTotalSujetoIva(100.0).
    WithCodigoMoneda(1).
    WithTipoCambio(1.0).
    WithMontoTotalMoneda(100.0).
    
    // Additional fields
    WithLeyenda("Ley N° 453: El proveedor deberá suministrar el servicio...").
    WithUsuario("admin").
    WithCodigoDocumentoSector(1).
    Build()

2. Create Detalle (Line Items)

Add one or more line items describing the products or services:
detalle1 := models.CompraVenta.NewDetalle().
    WithActividadEconomica("461000").
    WithCodigoProductoSin("12345").
    WithCodigoProducto("PROD-001").
    WithDescripcion("Producto de prueba").
    WithCantidad(2.0).
    WithUnidadMedida(57).
    WithPrecioUnitario(50.0).
    WithSubTotal(100.0).
    Build()

detalle2 := models.CompraVenta.NewDetalle().
    WithActividadEconomica("461000").
    WithCodigoProductoSin("67890").
    WithCodigoProducto("PROD-002").
    WithDescripcion("Servicio adicional").
    WithCantidad(1.0).
    WithUnidadMedida(62).
    WithPrecioUnitario(25.0).
    WithMontoDescuento(5.0).
    WithSubTotal(20.0).
    Build()

3. Assemble the Invoice

Combine the header and line items into a complete invoice:
factura := models.CompraVenta.NewFactura().
    WithCabecera(cabecera).
    AddDetalle(detalle1).
    AddDetalle(detalle2).
    Build()

Cabecera Builder Methods

Complete reference for all CabeceraBuilder methods:

Issuer Information

WithNitEmisor
func(int64)
required
Tax identification number (NIT) of the issuing company
WithRazonSocialEmisor
func(string)
required
Legal name of the issuing company
WithMunicipio
func(string)
required
Municipality where the issuer is located (e.g., “La Paz”, “Santa Cruz”)
WithDireccion
func(string)
required
Physical address of the issuer
WithTelefono
func(string)
Contact phone number (optional)

Invoice Identification

WithNumeroFactura
func(int64)
required
Sequential invoice number
WithCuf
func(string)
required
CUF (Código Único de Factura) - unique invoice code generated using models.CompraVenta.GenerarCUF()
WithCufd
func(string)
required
CUFD (Código Único de Factura Diario) - daily invoice code
WithCodigoSucursal
func(int)
required
Branch/office code registered in SIAT
WithCodigoPuntoVenta
func(int)
required
Point of sale code
WithFechaEmision
func(string)
required
Emission date and time in format: "2006-01-02T15:04:05.000"
WithCodigoDocumentoSector
func(int)
required
Document sector type code from SIAT catalogs

Customer Information

WithNombreRazonSocial
func(string)
Customer’s name or legal name (optional)
WithCodigoTipoDocumentoIdentidad
func(int)
required
Document type code:
  • 1 = CI (Cédula de Identidad)
  • 2 = CEX (Cédula de Extranjero)
  • 3 = Pasaporte
  • 4 = NIT
  • 5 = Otro
WithNumeroDocumento
func(string)
required
Customer’s document number
WithComplemento
func(string)
Document complement (e.g., “1A”) - optional
WithCodigoCliente
func(string)
required
Internal customer code in your system

Payment Information

WithCodigoMetodoPago
func(int)
required
Payment method code:
  • 1 = Efectivo
  • 2 = Tarjeta
  • 3 = Cheque
  • 4 = Otro
WithNumeroTarjeta
func(int64)
Card number (last 4 digits) - required if payment method is card
WithMontoTotal
func(float64)
required
Total invoice amount including taxes
WithMontoTotalSujetoIva
func(float64)
required
Total amount subject to VAT
WithCodigoMoneda
func(int)
required
Currency code:
  • 1 = Boliviano (BOB)
  • 2 = Dólar (USD)
  • 3 = Euro (EUR)
WithTipoCambio
func(float64)
required
Exchange rate (use 1.0 for BOB)
WithMontoTotalMoneda
func(float64)
required
Total amount in the specified currency
WithMontoGiftCard
func(float64)
Gift card amount applied (optional)
WithDescuentoAdicional
func(float64)
Additional discount amount (optional)

Additional Fields

WithLeyenda
func(string)
required
Legal legend text (e.g., “Ley N° 453: El proveedor deberá suministrar el servicio…”)
WithUsuario
func(string)
required
Username of the person issuing the invoice
WithCodigoExcepcion
func(int64)
Exception code for offline invoicing (optional)
WithCafc
func(string)
CAFC code for offline contingency mode (optional)

Detalle Builder Methods

Complete reference for all DetalleBuilder methods:
WithActividadEconomica
func(string)
required
Economic activity code associated with the product/service
WithCodigoProductoSin
func(string)
required
SIN product code from SIAT catalogs
WithCodigoProducto
func(string)
required
Internal product code in your system
WithDescripcion
func(string)
required
Product or service description
WithCantidad
func(float64)
required
Quantity sold
WithUnidadMedida
func(int)
required
Unit of measure code from SIAT catalogs:
  • 57 = Unidad
  • 58 = Kilogramo
  • 59 = Metro
  • 62 = Servicio
  • (see SIAT catalogs for complete list)
WithPrecioUnitario
func(float64)
required
Unit price of the product/service
WithSubTotal
func(float64)
required
Line item subtotal (quantity × unit price - discount)
WithMontoDescuento
func(float64)
Discount amount applied to this line item (optional)
WithNumeroSerie
func(string)
Serial number for serialized products (optional)
WithNumeroImei
func(string)
IMEI number for mobile devices (optional)

Complete Example

Here’s a complete example building an invoice with multiple line items:
package main

import (
    "encoding/xml"
    "fmt"
    "log"
    "time"

    "github.com/ron86i/go-siat/pkg/models"
)

func main() {
    // Generate CUF
    nit := int64(123456789)
    fechaEmision := time.Now()
    cuf, err := models.CompraVenta.GenerarCUF(
        nit, fechaEmision, 0, 1, 1, 1, 1, 1, 0, "XYZ789",
    )
    if err != nil {
        log.Fatal(err)
    }

    // Build header
    cabecera := models.CompraVenta.NewCabecera().
        WithNitEmisor(nit).
        WithRazonSocialEmisor("ACME Corporation S.A.").
        WithMunicipio("La Paz").
        WithDireccion("Av. 16 de Julio #1234").
        WithTelefono("+591 2 1234567").
        WithNumeroFactura(1001).
        WithCuf(cuf).
        WithCufd("CUFD123456789").
        WithCodigoSucursal(0).
        WithCodigoPuntoVenta(0).
        WithFechaEmision(fechaEmision.Format("2006-01-02T15:04:05.000")).
        WithNombreRazonSocial("Juan Pérez").
        WithCodigoTipoDocumentoIdentidad(1).
        WithNumeroDocumento("12345678").
        WithComplemento("1A").
        WithCodigoCliente("CUST-001").
        WithCodigoMetodoPago(1).
        WithMontoTotal(250.0).
        WithMontoTotalSujetoIva(250.0).
        WithCodigoMoneda(1).
        WithTipoCambio(1.0).
        WithMontoTotalMoneda(250.0).
        WithLeyenda("Ley N° 453: El proveedor deberá suministrar el servicio...").
        WithUsuario("admin").
        WithCodigoDocumentoSector(1).
        Build()

    // Build line items
    detalle1 := models.CompraVenta.NewDetalle().
        WithActividadEconomica("461000").
        WithCodigoProductoSin("12345").
        WithCodigoProducto("LAPTOP-001").
        WithDescripcion("Laptop Dell Inspiron 15").
        WithCantidad(1.0).
        WithUnidadMedida(57).
        WithPrecioUnitario(200.0).
        WithSubTotal(200.0).
        WithNumeroSerie("SN123456789").
        Build()

    detalle2 := models.CompraVenta.NewDetalle().
        WithActividadEconomica("461000").
        WithCodigoProductoSin("67890").
        WithCodigoProducto("MOUSE-001").
        WithDescripcion("Mouse inalámbrico").
        WithCantidad(2.0).
        WithUnidadMedida(57).
        WithPrecioUnitario(30.0).
        WithMontoDescuento(10.0).
        WithSubTotal(50.0).
        Build()

    // Assemble invoice
    factura := models.CompraVenta.NewFactura().
        WithCabecera(cabecera).
        AddDetalle(detalle1).
        AddDetalle(detalle2).
        Build()

    // Marshal to XML
    xmlData, err := xml.MarshalIndent(factura, "", "  ")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(xmlData))
}

Working with Optional Fields

Some fields use the Nilable[T] type internally to support SIAT’s requirement for explicit nil values. The builders handle this automatically:
// Optional string field
cabecera := models.CompraVenta.NewCabecera().
    WithTelefono("+591 2 1234567"). // Sets value
    // ... other fields
    Build()

// Optional numeric field
detalle := models.CompraVenta.NewDetalle().
    WithMontoDescuento(15.5). // Sets value
    // ... other fields
    Build()

// Omitting optional fields is fine - they will be nil in the XML
detalle := models.CompraVenta.NewDetalle().
    // No WithMontoDescuento() call
    // ... other fields
    Build()

XML Output

The built invoice marshals to XML compatible with SIAT’s XSD schema:
<facturaElectronicaCompraVenta 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="facturaElectronicaCompraVenta.xsd">
  <cabecera>
    <nitEmisor>123456789</nitEmisor>
    <razonSocialEmisor>Mi Empresa S.A.</razonSocialEmisor>
    <municipio>La Paz</municipio>
    <telefono>+591 2 1234567</telefono>
    <!-- ... more fields ... -->
  </cabecera>
  <detalle>
    <actividadEconomica>461000</actividadEconomica>
    <codigoProductoSin>12345</codigoProductoSin>
    <!-- ... more fields ... -->
  </detalle>
</facturaElectronicaCompraVenta>

Next Steps

After building your invoice:
  1. Marshal to XML: Use xml.Marshal(factura)
  2. Sign: Use models.CompraVenta.SignXML()
  3. Compress: Use gzip compression
  4. Hash: Calculate SHA256 hash
  5. Submit: Use RecepcionFactura service method
See the RecepcionFactura page for the complete submission workflow.

RecepcionFactura

Submit invoices to SIAT

Overview

CompraVenta service overview

Build docs developers (and LLMs) love