Saltar a contenido

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