7. Ajustes del flujo de negocio¶
7.1. Numeración transaccional¶
En accounting/services/sequence_service.py:
@transaction.atomic
def asignar_encf(invoice, ambiente):
seq = (ECFSequence.objects
.select_for_update()
.filter(tipo_encf=invoice.ecf_tipo,
ambiente=ambiente,
activo=True)
.first())
if not seq:
raise SinSecuencia(...)
if seq.siguiente > seq.numero_hasta:
raise SecuenciaAgotada(...)
if seq.fecha_vencimiento < timezone.now().date():
raise SecuenciaVencida(...)
invoice.ecf_encf = f"{invoice.ecf_tipo}{seq.siguiente:010d}"
invoice.ecf_ambiente = ambiente
invoice.save(update_fields=["ecf_encf", "ecf_ambiente"])
seq.siguiente += 1
seq.save(update_fields=["siguiente"])
select_for_update() evita race conditions concurrentes.
7.2. Captura de TrackID en SaleInvoice¶
invoice.ecf_trackid = resp_json["trackId"]
invoice.ecf_estado = "ENVIADO"
invoice.ecf_fecha_envio = timezone.now()
invoice.ecf_intentos += 1
invoice.save(update_fields=[
"ecf_trackid", "ecf_estado", "ecf_fecha_envio", "ecf_intentos",
])
ECFEnvioDGII.objects.create(
invoice=invoice,
trackid=invoice.ecf_trackid,
payload_xml_hash=hashlib.sha256(xml_firmado).hexdigest(),
response_http_code=resp.status_code,
response_body=resp.text[:65535],
)
7.3. Vista de creación de facturas¶
El form de SaleInvoice debe exponer:
- ecf_tipo (select con choices E31..E47)
- ecf_ambiente (radio CERT/PROD, default desde settings)
- Botón "Generar XML preview" → llama invoice_ecf_xml_oficial_preview antes de firmar
- Botón "Enviar a DGII" → invoca pipeline completo
- Botón "Consultar estado" → llama consultar_estado_por_trackid