fase 1: registro de comprobantes con código de seguimiento

Nueva tabla comprobantes (creada en init_db) y módulo seguimiento.py: cada
comprobante recibe un código ABR-AAMMDD-NNNNN que conserva si reaparece en
otro correo, más su resultado (aceptado/rechazado/error) y el estado de la
notificación. El pipeline lo registra por factura sin bloquear el flujo si el
seguimiento falla. Las pruebas usan una BD temporal para no tocar emails.db.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-20 22:03:38 -05:00
1 parent 9d295d6485
commit 297cfde0e1
9 files changed
+563 -6

No files matched your search

+39
View File
@@ -21,11 +21,46 @@ import persistencia
import draft_sap
import notificaciones
import sapb1_manager
import seguimiento
from pipeline_utils import log
logger = logging.getLogger(__name__)
# ============================================================================
# SEGUIMIENTO (códigos y resultado por comprobante)
# ============================================================================
def _registrar_comprobante(email: dict, factura: dict) -> dict | None:
"""
Registra la factura en ``seguimiento`` y devuelve su registro (con el código
de seguimiento), o None si falla: el seguimiento nunca debe impedir
procesar la factura.
"""
try:
registro = seguimiento.registrar_comprobante(
email["id"], factura.get("ruc_emisor") or "", factura.get("tipo", "01"),
factura.get("serie", ""), factura.get("numero", ""),
)
except Exception as ex:
log(f"⚠️ No se pudo registrar el comprobante en seguimiento: {ex}", severity=logging.WARNING)
return None
log(f"🔖 Código de seguimiento: {registro['codigo_seguimiento']}")
if registro["reenvio"]:
log(f"⚠️ Comprobante ya recibido antes ({registro['veces_recibido']} veces, estado: {registro['estado']})", severity=logging.WARNING)
return registro
def _registrar_resultado(registro: dict | None, estado: str, **kwargs) -> None:
"""Guarda el resultado del comprobante; si falla, solo lo registra en el log."""
if not registro:
return
try:
seguimiento.actualizar_resultado(registro["codigo_seguimiento"], estado, **kwargs)
except Exception as ex:
log(f"⚠️ No se pudo guardar el resultado en seguimiento: {ex}", severity=logging.WARNING)
# ============================================================================
# PASO 1: Autenticación Microsoft Graph
# ============================================================================
@@ -153,6 +188,7 @@ def _procesar_un_correo(email: dict, token: str, email_addr: str, download_folde
numero = factura.get("numero", "?")
log(f"📄 Factura: {serie}-{numero}")
registro = _registrar_comprobante(email, factura)
# 12. Validar precios (consulta SAP DNs, NO requiere draft)
todas_coinciden, resultados_val = draft_sap.validate_prices(factura)
@@ -179,12 +215,15 @@ def _procesar_un_correo(email: dict, token: str, email_addr: str, download_folde
sap_result = sapb1_manager.create_draft(draft)
doc_entry = sap_result.get("DocEntry")
log(f"✅ Draft SAP B1 creado: {serie}-{numero} → DocEntry {doc_entry}")
_registrar_resultado(registro, "aceptado", doc_entry=doc_entry)
html_body = notificaciones.build_notification_body("created", factura, validation_table, valor_referencial_table, doc_entry=doc_entry)
except Exception as ex:
log(f"❌ Error creando draft {serie}-{numero}: {ex}", severity=logging.ERROR)
_registrar_resultado(registro, "error", observaciones=[{"codigo": "error_sap", "detalle": str(ex)}])
html_body = notificaciones.build_notification_body("error", factura, validation_table, valor_referencial_table, error=ex)
else:
log(f"⏭️ Draft NO creado: {serie}-{numero} — validación falló")
_registrar_resultado(registro, "rechazado", observaciones=[{"codigo": "precios_no_coinciden"}])
html_body = notificaciones.build_notification_body("skipped", factura, validation_table, valor_referencial_table)
# 16. Enviar notificación al remitente original, con copia al equipo