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>
11 KiB
11 KiB
Inventario del código
Archivos
| Archivo | Líneas | Rol |
|---|---|---|
main.py |
137 | Demonio orquestador (loop de horarios) |
pipeline.py |
272 | Orquestador del pipeline (autenticar, descargar correos, procesar un correo) |
pipeline_utils.py |
27 | log() (logging con indentación de árbol y severity) y ruta_carpeta() |
adjuntos.py |
82 | Descarga de adjuntos (Graph) y filtrado PDF/XML |
documentos.py |
372 | Extracción PDF/XML, asignación de guías, copia a red |
persistencia.py |
163 | Guardado de JSON de resultados y de drafts |
draft_sap.py |
414 | Validación de precios contra SAP, valor referencial y construcción del draft |
notificaciones.py |
117 | Cuerpos HTML de los correos de notificación |
sunat_schema.py |
722 | Parseo de XML SUNAT (Invoice, DespatchAdvice) |
pdf_utilities.py |
471 | Parseo de PDFs (factura, guías) |
sapb1_manager.py |
401 | Comunicación con SAP B1 Service Layer |
outlook_utilities.py |
249 | Comunicación con Microsoft Graph |
hana_manager.py |
141 | Comunicación con SAP HANA directa (fallback de sapb1_manager y valor referencial) |
database_utilities.py |
150 | SQLite local (cache de correos) |
tools/*_prueba.py, tools/completar_guias_pipeline.py, tools/explorar_notes_xml.py |
— | Scripts manuales sobre tests/fixtures/correos_prueba/ (anteriores a la suite pytest) |
tools/consulta_rapida.py |
120 | Script auxiliar para reprocesar correos por ID (sets en tools/ids_prueba.json) |
seguimiento.py |
187 | Códigos de seguimiento y resultado por comprobante (tabla comprobantes) |
config_validator.py |
83 | Valida el .env al arrancar (obligatorias, ruta de red, avisos de SSL) |
email_utilities.py |
55 | Utilitarios de adjuntos (ZIP, filtrado) |
logger_manager.py |
150 | Logging a archivo rotado (30 días) + consola + captura de print() |
Documentación por módulo: seguimiento.py → seguimiento.md, outlook_utilities.py → outlook_utilities.md, hana_manager.py → hana_manager.md, validación y draft → validar_precios.md y construir_draft.md.
Dependencias entre archivos
main.py
├── logger_manager.py
└── pipeline.py
├── sunat_schema.py
├── pdf_utilities.py
├── sapb1_manager.py
│ └── hana_manager.py
├── hana_manager.py (valor referencial, Sp_ObtenerValorReferencial)
├── outlook_utilities.py
├── email_utilities.py
└── database_utilities.py
Módulos que pipeline.py orquesta (todos usan pipeline_utils.py):
pipeline.py
├── adjuntos.py → outlook_utilities
├── documentos.py → sunat_schema, pdf_utilities
├── persistencia.py → sunat_schema, pdf_utilities
├── draft_sap.py → sapb1_manager, hana_manager
└── notificaciones.py
tools/consulta_rapida.py
├── logger_manager.py
├── database_utilities.py
└── pipeline.py (_procesar_un_correo, step_authenticate)
Flujo de datos
main()
└── run_pipeline()
├── step_authenticate() → token MS Graph
├── step_fetch_emails() → emails[]
└── _procesar_un_correo() (por cada email)
├── BD: verificar duplicado
├── adjuntos.descargar_adjuntos() → archivos
├── email_utilities.descomprimir_adjuntos() ZIPs
├── adjuntos.filtrar_y_limpiar() → solo PDF/XML
├── documentos.procesar_pdfs() → pdf_cache
│ └── pdf_utilities.extraer_datos_pdf()
├── documentos.procesar_xmls(pdf_cache) → documentos[]
│ ├── sunat_schema.extraer_datos_xml()
│ └── documentos.copiar_a_red() → copia PDF/XML a ruta de red (RUTA_RED_FACTURACION)
├── documentos.completar_guias(documentos, pdf_cache) → completa guías faltantes desde PDF
├── documentos.validar_guias(documentos) → advierte guías con datos incompletos
├── persistencia.guardar_resultado(documentos)
├── draft_sap.validate_prices(factura) → (ok, resultados)
│ └── sapb1_manager.get_delivery_notes()
│ ├── SL (Service Layer) GET /DeliveryNotes
│ ├── HANA fallback (get_delivery: ODLN, luego OWTR)
│ └── si vino de OWTR: GET StockTransfers(DocEntry)
│ → U_IDC_NUMOV → GET Orders(U_IDC_NUMOV)
├── draft_sap.obtener_valor_referencial_sap(factura) → guias_valor_ref
│ └── hana_manager.obtener_valor_referencial() (Sp_ObtenerValorReferencial)
├── draft_sap.build_sap_draft(factura, resultados) → draft (incluye U_VS_ANEXO_PDF/XML)
│ └── sapb1_manager.obtener_condicion_pago(CardCode) → DocDueDate
├── persistencia.guardar_draft_json(draft)
├── sapb1_manager.create_draft(draft)
└── outlook_utilities.send_email(notificación)
Problemas conocidos hasta ahora
| # | Problema | Archivo | Estado |
|---|---|---|---|
| 1 | _extraer_serie_numero_desde_texto no limpiaba leading zeros |
sunat_schema.py |
✅ Corregido |
| 2 | Al asignar guías por línea, si hay +refs que líneas, las sobrantes se perdían | pipeline.py |
✅ Corregido |
| 3 | SL sin fallback a HANA | sapb1_manager.py |
✅ Corregido |
| 4 | Ctrl+C mostraba traceback | main.py |
✅ Corregido |
| 5 | .env.dev vs .env inconsistente |
sapb1_manager.py |
✅ Corregido |
| 6 | HANA_HOST vs HANA_SERVER en .env |
hana_manager.py |
✅ Corregido |
| 7 | Columna Cancelled vs CANCELED en HANA |
hana_manager.py |
✅ Corregido |
| 8 | Bloque if __name__ == "__main__" borraba un correo real de la BD al ejecutar el módulo directo |
database_utilities.py |
✅ Corregido |
| 9 | extraer_datos_xml solo procesaba guías tipo 31; tipo 09 (remitente) se descartaba pese a estar documentado |
sunat_schema.py |
✅ Corregido |
| 10 | DatosComprobante.__str__ usaba ref.id (campo que ya no existe en DocumentoReferencia) |
sunat_schema.py |
✅ Corregido |
| 11 | Log de error HTTP con print("%s", ...) no interpolaba el mensaje |
sapb1_manager.py |
✅ Corregido |
| 12 | Fuga de conexión HANA si cursor() fallaba (quedaba fuera del try) |
hana_manager.py |
✅ Corregido |
| 13 | razon_social_emisor se llenaba con el nombre del receptor en el JSON de depuración del PDF |
pipeline.py |
✅ Corregido |
| 14 | Typo VERIFI_SSL en vez de VERIFY_SSL |
outlook_utilities.py / .env |
✅ Corregido |
| 15 | Conexiones SQLite sin try/finally (fuga si fallaba una consulta a mitad de camino) |
database_utilities.py |
✅ Corregido |
| 16 | Código muerto sin llamadores (quitar_correos_ya_registrados_en_base_de_datos, procesar_adjuntos_email) |
email_utilities.py |
✅ Corregido (eliminado) |
| 17 | logger_utilities.py (redirección global de stdout, sin niveles ni rotación real) reemplazado por logger_manager.py (logging estándar, rotación diaria 30 días, captura de print()) |
logger_manager.py |
✅ Corregido |
| 18 | task()/last_task() de logger_manager.py no devolvían el sub-logger (with ... as sub daba siempre None) |
logger_manager.py |
✅ Corregido |
| 19 | DOWNLOAD_FOLDER duplicado en .env |
.env |
✅ Corregido |
| 20 | Guías tipo 09 no se encontraban si el documento estaba en OWTR (Traslado de Inventario) en vez de ODLN |
hana_manager.py |
✅ Corregido (fallback ODLN → OWTR) |
| 21 | Parámetros tipo/serie cruzados al llamar get_delivery/get_delivery_notes (mapeaban al revés a U_BPP_MDSD/U_BPP_MDTD) |
hana_manager.py, sapb1_manager.py, pipeline.py |
✅ Corregido (orden natural tipo, serie, numero) |
| 22 | StockTransfers (OWTR) no tiene líneas FL*, por lo que la validación de precios fallaba siempre para esas guías |
sapb1_manager.py |
✅ Corregido (resuelve la Orden de Venta vía U_IDC_NUMOV y valida contra Orders(DocEntry)) |
| 23 | DocDueDate del draft usaba fecha_emision del XML, sin considerar la condición de pago del proveedor |
pipeline.py, sapb1_manager.py |
✅ Corregido (DocDueDate = fecha de contabilización + días de PaymentTermsTypes) |
| 24 | Patrón regex de valor referencial no reconocía la variante truncada "VALOR REFER" (ej. "VALOR REFER:S/ 1431.16") | sunat_schema.py |
✅ Corregido |
Funciones por módulo
Antes eran funciones anidadas en
_procesar_un_correo; ahora son funciones de módulo que reciben explícitamentedownload_folder,output_folder, etc.pipeline.pysolo las orquesta.
| Módulo | Función | Propósito |
|---|---|---|
pipeline_utils |
ruta_carpeta |
Resuelve una ruta relativa al directorio del proyecto |
pipeline_utils |
log |
Imprime con indentación de árbol (niveles 1-4) |
adjuntos |
descargar_adjuntos |
Descarga adjuntos vía Graph API |
adjuntos |
filtrar_y_limpiar |
Filtra PDF/XML, elimina el resto |
documentos |
copiar_a_red |
Copia PDF/XML a la ruta de red de Facturación Electrónica (RUTA_RED_FACTURACION) |
documentos |
procesar_pdfs |
Extrae datos de PDFs, renombra, genera cache |
documentos |
procesar_xmls |
Procesa XMLs, cruza con PDF, asigna guías, copia a red |
documentos |
completar_guias |
Completa guías faltantes en líneas XML desde el PDF cache |
documentos |
validar_guias |
Advierte (log) guías con datos incompletos |
persistencia |
guardar_resultado |
Guarda JSON con datos extraídos (XML y PDF) |
persistencia |
guardar_draft_json |
Guarda draft JSON para depuración |
draft_sap |
calcular_fecha_vencimiento |
Calcula DocDueDate = fecha de contabilización + días de la condición de pago (PaymentTermsTypes) |
draft_sap |
build_sap_draft |
Construye payload del draft SAP B1 |
draft_sap |
validate_prices |
Valida precios contra DeliveryNotes / Orders (SAP) |
draft_sap |
obtener_valor_referencial_sap |
Consulta Sp_ObtenerValorReferencial en HANA por guía |
notificaciones |
build_validation_html |
Genera tabla HTML de validación de precios |
notificaciones |
build_valor_referencial_html |
Genera tabla HTML comparando valor referencial XML vs SAP |
notificaciones |
build_notification_body |
Arma cuerpo del email de notificación |