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:
1 parent
9d295d6485
commit
297cfde0e1
9 files changed
+563
-6
No files matched your search
@@ -4,8 +4,10 @@ BD simulados. Usa el correo de prueba 001 (factura F002-2450, una línea de
|
||||
624.38 con la guía T001-128912).
|
||||
"""
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sqlite3
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -14,6 +16,7 @@ import hana_manager
|
||||
import outlook_utilities
|
||||
import pipeline
|
||||
import sapb1_manager
|
||||
import seguimiento
|
||||
from conftest import FIXTURES
|
||||
|
||||
CASO = "001_correo"
|
||||
@@ -144,3 +147,78 @@ def test_guarda_resultados_y_draft_en_disco(entorno):
|
||||
nombres = os.listdir(entorno.salida)
|
||||
assert any(n.startswith("xml_resultado_F002-2450") for n in nombres)
|
||||
assert any(n.startswith("draft_sapb1_F002-2450") for n in nombres)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Seguimiento
|
||||
# ============================================================================
|
||||
def _registros():
|
||||
conn = sqlite3.connect(database_utilities.DB_NAME)
|
||||
conn.row_factory = sqlite3.Row
|
||||
try:
|
||||
return [dict(f) for f in conn.execute("SELECT * FROM comprobantes ORDER BY id")]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_draft_creado_deja_el_comprobante_aceptado(entorno):
|
||||
_procesar(entorno)
|
||||
|
||||
(registro,) = _registros()
|
||||
assert registro["estado"] == "aceptado"
|
||||
assert registro["doc_entry"] == 777
|
||||
assert registro["codigo_seguimiento"].startswith("ABR-")
|
||||
assert (registro["serie"], registro["numero"], registro["tipo"]) == ("F002", "2450", "01")
|
||||
assert registro["ruc_emisor"] == "10060028686"
|
||||
|
||||
|
||||
def test_precios_que_no_coinciden_dejan_el_comprobante_rechazado(entorno):
|
||||
entorno.line_total_sap = TOTAL_LINEA + 50
|
||||
|
||||
_procesar(entorno)
|
||||
|
||||
(registro,) = _registros()
|
||||
assert registro["estado"] == "rechazado"
|
||||
assert registro["doc_entry"] is None
|
||||
assert seguimiento.obtener(registro["codigo_seguimiento"])["observaciones"] == [{"codigo": "precios_no_coinciden"}]
|
||||
|
||||
|
||||
def test_error_al_crear_draft_deja_el_comprobante_en_error(entorno):
|
||||
entorno.error_al_crear = RuntimeError("Service Layer caído")
|
||||
|
||||
_procesar(entorno)
|
||||
|
||||
(registro,) = _registros()
|
||||
assert registro["estado"] == "error"
|
||||
obs = seguimiento.obtener(registro["codigo_seguimiento"])["observaciones"]
|
||||
assert obs == [{"codigo": "error_sap", "detalle": "Service Layer caído"}]
|
||||
|
||||
|
||||
def test_comprobante_reenviado_conserva_el_codigo_y_se_advierte(entorno, caplog):
|
||||
_procesar(entorno)
|
||||
(primero,) = _registros()
|
||||
|
||||
entorno.descarga = entorno.descarga.parent / "descarga2" # otra descarga, otro correo
|
||||
with caplog.at_level(logging.WARNING, logger="pipeline"):
|
||||
pipeline._procesar_un_correo(
|
||||
{"id": "otro-correo", "fecha": "2026-10-19 11:00:00", "subject": "Reenvío", "sender": "p@e.com"},
|
||||
"token", "buzon@aberio.com", str(entorno.descarga), str(entorno.salida),
|
||||
)
|
||||
|
||||
(registro,) = _registros() # sigue habiendo una sola fila
|
||||
assert registro["codigo_seguimiento"] == primero["codigo_seguimiento"]
|
||||
assert registro["veces_recibido"] == 2
|
||||
assert any("ya recibido antes" in r.getMessage() for r in caplog.records)
|
||||
|
||||
|
||||
def test_si_falla_el_seguimiento_igual_se_crea_el_draft(entorno, monkeypatch):
|
||||
def roto(*a, **k):
|
||||
raise sqlite3.OperationalError("database is locked")
|
||||
|
||||
monkeypatch.setattr(seguimiento, "registrar_comprobante", roto)
|
||||
monkeypatch.setattr(seguimiento, "actualizar_resultado", roto)
|
||||
|
||||
_procesar(entorno)
|
||||
|
||||
assert len(entorno.drafts) == 1
|
||||
assert len(entorno.correos) == 1
|
||||
Reference in new issue
Block a user