Skip to main content

Overview

The electronic invoice system supports dual-currency invoicing, displaying amounts in both Venezuelan Bolívares (BSD) and US Dollars (USD). This is essential for businesses operating in Venezuela’s multi-currency economy.

Supported Currencies

BSD - Bolívares

Primary CurrencyVenezuelan Bolívares - the legal tender and official invoicing currency. All tax calculations and official amounts use BSD.

USD - US Dollars

Secondary CurrencyUS Dollars - reference currency for international pricing and customer convenience. Displayed in TotalesOtraMoneda section.

Currency Structure in JSON

Each invoice contains two parallel sets of totals:
{
  "Encabezado": {
    "IdentificacionDocumento": {
      "Moneda": "BSD"  // Primary currency
    },
    "Totales": {
      // All amounts in BSD
      "Subtotal": "3874.56",
      "TotalIVA": "619.93",
      "TotalAPagar": "4494.49"
    },
    "TotalesOtraMoneda": {
      "Moneda": "USD",
      "TipoCambio": "154.9825",  // Exchange rate
      // All amounts in USD
      "Subtotal": "25.0",
      "TotalIVA": "4.0",
      "TotalAPagar": "29.0"
    }
  }
}

Exchange Rate Application

The exchange rate (Tasa) is read from the Excel input and stored in the invoice:
main.py:174-176
"TotalesOtraMoneda": {
    "Moneda": "USD",
    "TipoCambio": str(row["Tasa"]),
    ...
}

Exchange Rate Field

TipoCambio
string
required
Exchange rate from USD to BSDExample: "154.9825" means 1 USD = 154.9825 BSDSource: Excel column TasaFormat: Decimal string with 4 decimal places (standard Venezuelan exchange rate precision)

Calculation Relationship

BSD Amount = USD Amount × TipoCambio

Example:
  25.0 USD × 154.9825 = 3874.5625 BSD
  Rounded to: 3874.56 BSD
The exchange rate is provided in the input Excel file, not calculated by the system. This allows for using official, agreed-upon rates for invoicing.

Amount Extraction from Excel

The system reads currency amounts from specific Excel columns:
main.py:79-84
monto_bs = round(float(row["bolivares"]), 2)
monto_usd = round(float(row["Total"]), 0)
bolivares_sin_iva = round(float(row["bolivares sin iva"]), 2)
precio_sin_iva = round(float(row["precio sin iva"]), 2)
iva_bs = round(monto_bs - bolivares_sin_iva, 2)
iva_usd = round(monto_usd - precio_sin_iva, 2)

Excel Column Mapping

Excel ColumnCurrencyDescriptionDecimal Places
bolivaresBSDTotal amount with IVA2
bolivares sin ivaBSDSubtotal before IVA2
TotalUSDTotal amount with IVA0 (rounded)
precio sin ivaUSDSubtotal before IVA2
Tasa-Exchange rate4-6
Important Rounding Rules:
  • BSD amounts: Always 2 decimal places
  • USD amounts: Rounded to whole numbers (0 decimals) for totals
  • USD amounts: 2 decimal places for subtotals and tax base
  • Exchange rates: Preserve original precision (typically 4 decimals)

IVA (VAT) Calculation

IVA is calculated separately for each currency by subtracting the base amount from the total.

BSD IVA Calculation

main.py:83
iva_bs = round(monto_bs - bolivares_sin_iva, 2)
Example:
Total (with IVA):     4494.49 BSD
Subtotal (base):     -3874.56 BSD
                     ───────────
IVA (16%):            619.93 BSD

USD IVA Calculation

main.py:84
iva_usd = round(monto_usd - precio_sin_iva, 2)
Example:
Total (with IVA):     29.0 USD
Subtotal (base):     -25.0 USD
                     ──────────
IVA (16%):            4.0 USD

IVA Rate

The system uses a fixed 16% IVA rate, which is Venezuela’s standard VAT rate:
main.py:155
"AlicuotaImp": "16.00"
main.py:219
"TasaIVA": "16"

IVA Verification

Verification Formula:
Calculated IVA ≈ Subtotal × 0.16

BSD Example:
3874.56 × 0.16 = 619.9296 → rounds to 619.93 ✓

USD Example:
25.0 × 0.16 = 4.0 ✓

Totales Structure (Primary Currency)

The Totales section contains all monetary amounts in the primary currency (BSD):
main.py:137-172
"Totales": {
    "NroItems": "1",
    "MontoGravadoTotal": str(monto_bs),
    "MontoExentoTotal": "0.00",
    "MontoPercibidoTotal": "0.00",
    "SubtotalAntesDescuento": str(bolivares_sin_iva),
    "TotalDescuento": None,
    "TotalRecargos": None,
    "Subtotal": str(bolivares_sin_iva),
    "TotalIVA": str(iva_bs),
    "MontoTotalConIVA": str(monto_bs),
    "TotalAPagar": str(monto_bs),
    "MontoEnLetras": monto_a_letras_b(monto_bs),
    ...
}

Key Amount Fields (BSD)

SubtotalAntesDescuento
string
Subtotal before any discounts (pre-tax)Source: bolivares_sin_iva from ExcelExample: "3874.56"
Subtotal
string
Subtotal after discounts, before taxIn this system, same as SubtotalAntesDescuento (no discounts applied)Example: "3874.56"
TotalIVA
string
Total IVA amountCalculation: MontoTotalConIVA - SubtotalExample: "619.93"
MontoGravadoTotal
string
Total taxable amount (includes IVA)Same as: MontoTotalConIVAExample: "4494.49"
MontoTotalConIVA
string
Total amount including IVASource: bolivares from ExcelExample: "4494.49"
TotalAPagar
string
Final amount to paySame as: MontoTotalConIVA (no additional charges)Example: "4494.49"

TotalesOtraMoneda Structure (Secondary Currency)

The TotalesOtraMoneda section mirrors the primary totals structure in USD:
main.py:174-198
"TotalesOtraMoneda": {
    "Moneda": "USD",
    "TipoCambio": str(row["Tasa"]),
    "MontoGravadoTotal": str(precio_sin_iva),
    "MontoPercibidoTotal": "0.00",
    "MontoExentoTotal": "0.00",
    "Subtotal": str(precio_sin_iva),
    "TotalAPagar": str(monto_usd),
    "TotalIVA": str(iva_usd),
    "MontoTotalConIVA": str(monto_usd),
    "MontoEnLetras": monto_a_letras(monto_usd),
    "SubtotalAntesDescuento": str(precio_sin_iva),
    ...
}

Key Amount Fields (USD)

TipoCambio
string
required
Exchange rate from USD to BSDExample: "154.9825"
Subtotal
string
Subtotal before tax (USD)Source: precio_sin_iva from ExcelExample: "25.0"
TotalIVA
string
IVA amount (USD)Calculation: monto_usd - precio_sin_ivaExample: "4.0"
TotalAPagar
string
Final amount to pay (USD)Source: Total from Excel (rounded to 0 decimals)Example: "29.0"

Amount in Words

Both currency sections include amounts written in Spanish words for legal compliance.

BSD Amount to Words

main.py:36-49
def monto_a_letras_b(monto):
    try:
        monto_str = str(monto)
        if '.' in monto_str:
            partes = monto_str.split('.')
            entero = int(partes[0])
            decimal_str = partes[1]
            entero_letras = num2words(entero, lang='es')
            decimal_letras = " ".join(num2words(int(d), lang='es') for d in decimal_str)
            return f"{entero_letras} punto {decimal_letras}"
        else:
            return num2words(int(monto), lang='es')
    except:
        return None
Example:
Input:  4494.49
Output: "cuatro mil cuatrocientos noventa y cuatro punto cuatro nueve"
This function converts each decimal digit individually (“punto cuatro nueve” instead of “punto cuarenta y nueve”).

USD Amount to Words

main.py:51-71
def monto_a_letras(monto, moneda="dolares"):
    try:
        monto = float(monto)
        entero = int(monto)
        decimales = int(round((monto - entero) * 100))
        entero_letras = num2words(entero, lang='es')
        
        if moneda.lower() == "bolivares":
            if decimales > 0:
                return f"{entero_letras} con {num2words(decimales, lang='es')} céntimos"
            else:
                return f"{entero_letras} bolívares"
        elif moneda.lower() == "dolares":
            if decimales > 0:
                return f"{entero_letras} con {num2words(decimales, lang='es')} centavos"
            else:
                return f"{entero_letras}"
        ...
    except:
        return None
Example:
Input:  29.0 (USD)
Output: "veintinueve"

Input:  29.45 (USD)
Output: "veintinueve con cuarenta y cinco centavos"
This function treats decimals as cents and converts them as a whole number.
{
  "Totales": {
    "TotalAPagar": "4494.49",
    "MontoEnLetras": "cuatro mil cuatrocientos noventa y cuatro punto cuatro nueve"
  }
}

Tax Breakdown (ImpuestosSubtotal)

Both currency sections include a detailed tax breakdown:

BSD Tax Breakdown

main.py:152-159
"ImpuestosSubtotal": [
    {
        "CodigoTotalImp": "G",
        "AlicuotaImp": "16.00",
        "BaseImponibleImp": str(bolivares_sin_iva),
        "ValorTotalImp": str(iva_bs)
    }
]
Example:
{
  "CodigoTotalImp": "G",
  "AlicuotaImp": "16.00",
  "BaseImponibleImp": "3874.56",
  "ValorTotalImp": "619.93"
}

USD Tax Breakdown

main.py:190-197
"ImpuestosSubtotal": [
    {
        "CodigoTotalImp": "G",
        "AlicuotaImp": "16.00",
        "BaseImponibleImp": str(precio_sin_iva),
        "ValorTotalImp": str(iva_usd)
    }
]
Example:
{
  "CodigoTotalImp": "G",
  "AlicuotaImp": "16.00",
  "BaseImponibleImp": "25.0",
  "ValorTotalImp": "4.0"
}
Tax Code Meanings:
  • "G" = Gravado (Taxable)
  • "E" = Exento (Exempt) - not used in this system
  • "AlicuotaImp" = Tax rate (always 16.00% for Venezuelan IVA)

Payment Method Currency

The payment method always uses the primary currency (BSD):
main.py:160-169
"FormasPago": [
    {
        "Descripcion": row["Forma de Pago"],
        "Fecha": fecha_pago,
        "Forma": "01",
        "Monto": str(monto_bs),
        "Moneda": "BSD",
        "TipoCambio": "0.00"
    }
]
Why BSD for payments?Venezuelan tax law requires invoices to be denominated and paid in Bolívares. The USD amounts in TotalesOtraMoneda are for reference only. The legal payment obligation is always in BSD.

Complete Currency Example

Here’s a complete example showing both currencies side-by-side:
{
  "DocumentoElectronico": {
    "Encabezado": {
      "IdentificacionDocumento": {
        "NumeroDocumento": "6746",
        "Moneda": "BSD"
      },
      "Totales": {
        "SubtotalAntesDescuento": "3874.56",
        "Subtotal": "3874.56",
        "TotalIVA": "619.93",
        "MontoGravadoTotal": "4494.49",
        "MontoTotalConIVA": "4494.49",
        "TotalAPagar": "4494.49",
        "MontoEnLetras": "cuatro mil cuatrocientos noventa y cuatro punto cuatro nueve",
        "ImpuestosSubtotal": [
          {
            "CodigoTotalImp": "G",
            "AlicuotaImp": "16.00",
            "BaseImponibleImp": "3874.56",
            "ValorTotalImp": "619.93"
          }
        ],
        "FormasPago": [
          {
            "Monto": "4494.49",
            "Moneda": "BSD"
          }
        ]
      },
      "TotalesOtraMoneda": {
        "Moneda": "USD",
        "TipoCambio": "154.9825",
        "SubtotalAntesDescuento": "25.0",
        "Subtotal": "25.0",
        "TotalIVA": "4.0",
        "MontoGravadoTotal": "25.0",
        "MontoTotalConIVA": "29.0",
        "TotalAPagar": "29.0",
        "MontoEnLetras": "veintinueve",
        "ImpuestosSubtotal": [
          {
            "CodigoTotalImp": "G",
            "AlicuotaImp": "16.00",
            "BaseImponibleImp": "25.0",
            "ValorTotalImp": "4.0"
          }
        ]
      }
    }
  }
}

Calculation Verification

1

USD Base Amount

Subtotal: 25.0 USD
2

USD IVA Calculation

25.0 × 0.16 = 4.0 USD
3

USD Total

25.0 + 4.0 = 29.0 USD
4

Convert to BSD (Subtotal)

25.0 × 154.9825 = 3874.5625 BSD → rounds to 3874.56 BSD
5

Convert to BSD (IVA)

4.0 × 154.9825 = 619.93 BSD
6

BSD Total

3874.56 + 619.93 = 4494.49 BSD
Consistency Check Passed:
  • IVA rate is 16% in both currencies ✓
  • Exchange rate applied correctly ✓
  • Rounding follows currency conventions ✓

Build docs developers (and LLMs) love