Skip to main content

Python SDK for Digifact FEL Guatemala e-invoicing API

Project description

Digifact FEL Guatemala — SDK para Python

SDK en Python para la API de facturación electrónica en línea (FEL) de Guatemala de Digifact.

Instalación

pip install digifact-sdk

O desde el código fuente:

pip install -e sdk/python/

Inicio rápido

from digifact_sdk import DigifactClient

client = DigifactClient(
    taxid="12345678",
    username="FELUSER",
    password="secret",
    environment="test",  # o "production"
)

# FACT CF — consumidor final, el IVA se calcula automáticamente
result = client.invoice(
    buyer="CF",
    items=[{"description": "Consultoría", "qty": 1, "price": 100.00}]
)
print(result.auth_number)

# FACT a NIT — el nombre del receptor se consulta automáticamente en SAT
result = client.invoice(
    buyer="12345678",
    items=[
        {"description": "Laptop", "qty": 1, "price": 5000.00, "type": "Bien"},
        {"description": "Soporte anual", "qty": 1, "price": 500.00},
    ]
)

# FACT a receptor con CUI
result = client.invoice(
    buyer={"taxid": "3730617490101", "type": "CUI", "name": "Juan Pérez"},
    items=[{"description": "Producto", "qty": 2, "price": 50.00}]
)

# Receptor NIT con datos explícitos (sin consulta automática)
result = client.invoice(
    buyer={
        "taxid":    "12345678",
        "name":     "EMPRESA EJEMPLO S.A.",
        "address":  "6 AV 6-48 ZONA 9",
        "city":     "01009",
        "district": "GUATEMALA",
        "state":    "GUATEMALA",
        "country":  "GT",
        "email":    "facturacion@empresa.com",  # opcional
    },
    items=[{"description": "Producto", "qty": 1, "price": 100.00}]
)

# FCAM (Factura Cambiaria)
result = client.invoice(
    buyer="12345678",
    items=[{"description": "Servicio", "qty": 1, "price": 500.00}],
    doc_type="FCAM",
    payment_terms=[{"date": "2026-04-18", "amount": 500.00}]
)

# Nota de crédito (NCRE)
result = client.credit_note(
    buyer="12345678",
    items=[{"description": "Devolución", "qty": 1, "price": 100.00}],
    origin={
        "auth_number": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "date": "2026-03-18",
        "series": "XXXXXXXX",
        "number": "123456",
    },
    reason="Producto defectuoso"
)

# Nota de débito (NDEB)
result = client.debit_note(
    buyer="12345678",
    items=[{"description": "Cargo adicional", "qty": 1, "price": 50.00}],
    origin={...},
    reason="Cargo por entrega express"
)

# Anular un DTE
result = client.cancel(
    auth_number="XXXXXXXX-...",
    receiver_id="CF",
    issue_datetime="2026-03-18 21:40:14",
    reason="Error en monto"
)

# Nota de crédito total
result = client.credit_note_total(
    auth_number="XXXXXXXX-...",
    issue_datetime="2026-03-18 21:40:14",
    reason="Nota de crédito total"
)

# Consulta de NIT
info = client.lookup_nit("12345678")
print(info["name"])

# Obtener DTE
doc = client.get_dte("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")

Tipos de documento

Tipo Descripción IVA
FACT Factura estándar
FCAM Factura Cambiaria (con abonos)
NDEB Nota de débito
NCRE Nota de crédito
NABN Nota de abono No
FESP Factura especial
RDON Recibo por donación No
FPEQ Factura pequeño contribuyente No
RECI Recibo universitario No
CCA Cobro por cuenta ajena

Cálculo del IVA

Los precios incluyen IVA (es lo que paga el cliente):

total_linea    = qty × price
base_imponible = total_linea / 1.12
monto_iva      = total_linea − base_imponible

Todos los montos se formatean como cadenas con 6 decimales.

Campos del ítem

{
    "description": str,          # requerido
    "price": float | Decimal,    # requerido — precio unitario, incluye IVA
    "qty": float | Decimal,      # opcional, por defecto 1
    "type": str,                  # opcional: "Servicio" (por defecto) | "Bien"
    "unit_of_measure": str,       # opcional, por defecto "UNI"
    "discount": float | None,     # opcional — descuento de la línea
}

Facturas de combustible (FACT Combustible)

Las facturas de combustible emiten IVA y un impuesto PETROLEO según la especificación de SAT. Los ítems sin petroleo_amount se tratan como ítems regulares (sólo IVA) y pueden coexistir en la misma factura.

Opción A — tarifas fijadas al inicializar el cliente (recomendada para gasolineras)

# Fijar tarifas PETROLEO una sola vez por tipo de combustible (Q/galón, según MEM o factura del proveedor)
client = DigifactClient(
    taxid="12345678",
    username="FELUSER",
    password="secret",
    petroleo_rates={"1": 4.70, "2": 4.60, "4": 1.30},  # SUPER / REGULAR / DIESEL
)

# Sólo hace falta petroleo_code — petroleo_amount se completa automáticamente
result = client.fuel_invoice(
    buyer="CF",
    items=[
        {"description": "GASOLINA SUPER",    "qty": 30, "price": 35.00, "petroleo_code": "1", "type": "Bien"},
        {"description": "GASOLINA REGULAR",  "qty": 20, "price": 34.00, "petroleo_code": "2", "type": "Bien"},
        {"description": "GASOLINA DIESEL",   "qty": 50, "price": 32.00, "petroleo_code": "4", "type": "Bien"},
        # Ítems regulares (sin petroleo_code): sólo IVA, pueden coexistir
        {"description": "FILTRO DE ACEITE",    "qty": 1, "price": 45.00,  "type": "Bien"},
        {"description": "SET DE CANDELAS NGK", "qty": 1, "price": 400.00, "type": "Bien"},
    ],
)
print(result.auth_number)

Opción B — monto explícito por ítem

result = client.fuel_invoice(
    buyer="CF",
    items=[
        {"description": "GASOLINA SUPER",   "qty": 1, "price": 35.00, "petroleo_amount": 4.70, "petroleo_code": "1", "type": "Bien"},
        {"description": "GASOLINA DIESEL",  "qty": 1, "price": 32.00, "petroleo_amount": 1.30, "petroleo_code": "4", "type": "Bien"},
    ],
)

Campos del ítem de combustible

Campo Tipo Por defecto Descripción
description str requerido Descripción de la línea
price float|Decimal requerido Precio unitario completo al consumidor (incluye PETROLEO + IVA). Es lo que paga el cliente en la bomba. Si la factura del proveedor muestra un precio unitario sin PETROLEO/IDP (p. ej. 37.99), suma la tarifa IDP por unidad: price = 37.99 + 4.70 = 42.69.
qty float|Decimal 1 Cantidad
type str "Servicio" "Bien" o "Servicio"
unit_of_measure str "UNI" Código de unidad de SAT
petroleo_amount float|Decimal Impuesto PETROLEO por unidad; omitir para ítems sólo-IVA
petroleo_code str "1" "1"=SUPER, "2"=REGULAR, "4"=DIESEL. Obligatorio cuando se omite petroleo_amount y petroleo_rates está configurado; lanza DigifactValidationError si el código no está en el diccionario de tarifas.

Configuración de frases (TipoFrase / CodigoEscenario)

Todo DTE (excepto FESP) debe llevar un par TipoFrase + CodigoEscenario. El SDK elige valores por defecto adecuados, por lo que no hace falta configurar nada en el caso común.

Orden de precedencia: argumentos por llamada → globales del constructor (tipo_frase / escenario) → tabla de valores por defecto.

Tabla de valores por defecto:

DTE Afiliación TipoFrase CodigoEscenario Notas
FESP Sin bloque AdditionlInfo
FPEQ PEQ 2 1 Pequeño contribuyente
RDON cualquiera 4 4 Donaciones
RECI cualquiera 4 5 Recibos (universidades)
NABN cualquiera 1 1 Abonos
FACT / FCAM / NCRE / NDEB GEN 1 1 Por defecto: ISR régimen sobre utilidades trimestrales
FACT / FCAM / NCRE / NDEB PEQ 2 1
FACT / FCAM / NCRE / NDEB EXE 4 1 Exento

Tanto tipo_frase como escenario se pueden sobreescribir de forma independiente — por llamada (como argumentos con nombre) o globalmente al construir el cliente. Cuando se omiten, cada uno cae al global del constructor y luego a la tabla de valores por defecto.

# Sobreescritura por llamada (uno o ambos)
client.invoice("CF", items, escenario="1")
client.invoice("CF", items, tipo_frase="2", escenario="1")

# Funciona igual en los demás métodos de DTE
client.credit_note("12345678", items, origin, "...", tipo_frase="2", escenario="1")
client.fuel_invoice("CF", items, tipo_frase="2", escenario="1")

# O globalmente al construir el cliente (p. ej. GEN + ISR régimen opcional simplificado)
client = DigifactClient(
    taxid="12345678", username="FELUSER", password="...",
    afiliacion_iva="GEN",
    tipo_frase="1",   # opcional — la tabla ya devuelve "1" para GEN
    escenario="2",    # ISR régimen opcional simplificado (sobreescribe el "1" por defecto)
)

Para descubrir el par correcto en un caso particular, revisa las afiliaciones del RTU en el portal de SAT.

Ejecutar las pruebas

# Pruebas unitarias (no requieren credenciales)
python -m pytest tests/ -v

# Pruebas de integración
export DIGIFACT_TAXID=12345678
export DIGIFACT_USERNAME=FELUSER
export DIGIFACT_PASSWORD=tu_contraseña
python -m pytest tests/ -v

Variables de entorno

Variable Descripción
DIGIFACT_TAXID NIT del emisor (p. ej. 12345678)
DIGIFACT_USERNAME Usuario (p. ej. FELUSER)
DIGIFACT_PASSWORD Contraseña de la cuenta

Manejo de errores

from digifact_sdk import (
    DigifactError,          # base
    DigifactAuthError,      # fallo de autenticación
    DigifactApiError,       # error HTTP / de API
    DigifactValidationError, # rechazo de SAT
    DigifactNitNotFoundError, # NIT no encontrado
)

try:
    result = client.invoice("CF", [...])
except DigifactValidationError as exc:
    print(f"SAT rechazó: {exc}")
    print(f"Código: {exc.code}")
    print(f"Respuesta: {exc.raw}")
except DigifactError as exc:
    print(f"Error del SDK: {exc}")

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

digifact_sdk-2.2.2.tar.gz (27.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

digifact_sdk-2.2.2-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file digifact_sdk-2.2.2.tar.gz.

File metadata

  • Download URL: digifact_sdk-2.2.2.tar.gz
  • Upload date:
  • Size: 27.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for digifact_sdk-2.2.2.tar.gz
Algorithm Hash digest
SHA256 f5bd4a4542f73daffc9caeb1b1c589fe1bbe99bae101b831df54f8ed3e199640
MD5 76e16e9388ff439eb4af3d56384eccf2
BLAKE2b-256 15e3af45b6d4936e86d0e3782e1a81b8859b980d7f17261c7e7d161c42c0480e

See more details on using hashes here.

Provenance

The following attestation bundles were made for digifact_sdk-2.2.2.tar.gz:

Publisher: publish.yml on aalonzolu/digifact

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file digifact_sdk-2.2.2-py3-none-any.whl.

File metadata

  • Download URL: digifact_sdk-2.2.2-py3-none-any.whl
  • Upload date:
  • Size: 21.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for digifact_sdk-2.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 08482f97fcb8597d90128b09c65efb9fdccd1a1bb089db12969601a89fb099a6
MD5 aa69cb37253e6484f04e75c23c784d23
BLAKE2b-256 fde7fcccc20ffdd10cfdd41758cc371a68473d8d21f774e89d35260cda1bad83

See more details on using hashes here.

Provenance

The following attestation bundles were made for digifact_sdk-2.2.2-py3-none-any.whl:

Publisher: publish.yml on aalonzolu/digifact

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page