Saltar a contenido

4. Instalación del módulo e-CF

4.1. Estructura del módulo

accounting/
├── models.py                       # SaleInvoice, ECFSequence, EInvoiceReceived, etc.
├── ecf/
│   ├── __init__.py
│   ├── signing.py                  # firmar_ecf (XAdES-BES)
│   ├── validation.py               # validar XSD oficial DGII
│   ├── dgii_client.py              # token, envío, consulta TrackID
│   ├── qr.py                       # generar QR timbre fiscal
│   ├── receiver.py                 # parsear e-CF entrante + ACR
│   └── _xml.py                     # safe parser (XXE-safe)
├── services/
│   ├── sequence_service.py         # asignar e-NCF transaccional
│   ├── tracking_service.py         # poll TrackID
│   ├── receptor_validator.py       # validar RNC contra Padrón
│   ├── error_classifier.py
│   └── dgii_versioning.py
└── ecf_dgii_endpoints.py           # endpoints públicos que DGII llama

Modelos críticos (accounting/models.py):

  • SaleInvoice con campos:
  • ecf_tipo (E31/E32/E33/E34/E41/E43/E44/E45/E46/E47)
  • ecf_ambiente (CERT/PROD)
  • ecf_estado (BORRADOR → GENERADO → VALIDADO → FIRMADO → EN_COLA → ENVIADO → EN_PROCESO → ACEPTADO/RECHAZADO)
  • ecf_encf (string E + 12 dígitos)
  • ecf_trackid (UUID devuelto por DGII)
  • ecf_xml_hash, ecf_intentos, ecf_fecha_envio
  • ECFSequence: rango DGII (numero_desde/numero_hasta), fecha_autorizacion/fecha_vencimiento, ambiente, activo
  • ECFEnvioDGII: bitácora inmutable (triggers SQL en migración 0028_ecfenvio_immutable_triggers)
  • EInvoiceReceived, ECFValidationError, ECFTrackingLog

4.2. Certificado digital DGII

DGII RD emite certificados a través de Cámara de Comercio de Santo Domingo (CCPSD) o Camara TC. Se entrega un archivo .p12 con clave privada + certificado público + cadena de confianza.

mkdir -p /opt/tienda/secrets
chown tienda:tienda /opt/tienda/secrets
chmod 700 /opt/tienda/secrets

# Subir el .p12 vía SCP:
scp ccpsd_130768021.p12 root@76.13.100.236:/opt/tienda/secrets/

# Permisos restrictivos
chown tienda:tienda /opt/tienda/secrets/ccpsd_130768021.p12
chmod 400 /opt/tienda/secrets/ccpsd_130768021.p12

Descargar cadena CCPSD (autoridad emisora):

cd /opt/tienda/secrets
wget https://digi.camarasantodomingo.do/CCPSD_cadena.crt
chmod 444 CCPSD_cadena.crt

Verificar contenido y vigencia:

openssl pkcs12 -in /opt/tienda/secrets/ccpsd_*.p12 -nokeys -info -passin pass:<PASSWORD>
# Buscar: "notAfter" — debe ser futura.

Buena práctica: rotar el certificado 30 días antes del vencimiento. Configurar un monitor (accounting/services/cert_monitor.py o cron) que alerte si quedan <60 días.

4.3. Variables .env DGII

Ya descritas en sección 3.3. Validación rápida:

cd /opt/tienda && set -a && . .env && set +a && source .venv/bin/activate && \
python manage.py ecf_validate_config

Este comando verifica: - .p12 legible y con password correcto - Vigencia certificado - Cadena CCPSD presente - ALLOWED_HOSTS incluye el FQDN - SECRET_KEY no es de DEBUG - Conectividad TCP a DGII

4.4. Carga de secuencias e-CF

DGII autoriza rangos numéricos para cada tipo de e-CF (ej. E31 desde 1 hasta 50000). Se cargan con:

python manage.py shell
from accounting.models import ECFSequence
from django.utils import timezone
from datetime import date

ECFSequence.objects.create(
    tipo_encf="E31",
    numero_desde=1,
    numero_hasta=50000,
    siguiente=1,
    ambiente="CERT",
    fecha_autorizacion=date(2026, 5, 7),
    fecha_vencimiento=date(2027, 5, 7),
    activo=True,
)
# Repetir para E32, E33, E34, E41, E43, E44, E45, E46, E47

Al pasar a PROD: crear nuevas filas con ambiente="PROD" y los rangos PROD autorizados por DGII, y desactivar (activo=False) las de CERT.

4.5. Firma XAdES-BES con signxml

El módulo accounting/ecf/signing.py ya implementa la firma. Comprobaciones del estándar DGII:

  • Algoritmo: RSA-SHA256
  • Canonicalización: c14n exclusivo
  • Transformación: enveloped-signature + c14n
  • KeyInfo: incluye certificado completo + cadena CCPSD
  • Política: ETSI TS 101 903 v1.3.2 (XAdES-BES)

Test de firma manual:

from accounting.ecf.signing import firmar_ecf, cargar_certificado
cert = cargar_certificado()
xml_firmado = firmar_ecf("<ECF>...</ECF>", cert)
# Verificar con: xmlsec1 verify --pubkey-cert-pem <cert> <archivo.xml>

4.6. Generación XML conforme a XSD oficial

mkdir -p /opt/tienda/dgii_schemas
cd /opt/tienda/dgii_schemas
wget https://dgii.gov.do/.../eCF_v1.0.xsd  # URL oficial DGII actual

Pipeline en el ERP:

generar_ecf_xml_oficial_base(invoice)
  → estructura conforme a XSD
validar_completo(xml)
  → lxml.etree validate contra XSD
firmar_ecf(xml, cert)
  → XAdES-BES

Validación CLI:

python manage.py dgii_validate_xml --invoice-id <ID>