Files
descarga_factura_transporte/docs/seguimiento.md
T
GeorgeandClaude Sonnet 5.5 4deb9d4e4d se responde al remitente con las plantillas de aceptado/rechazado
El sistema contesta a quien envió el correo con la plantilla que corresponda.
Por defecto (NOTIFICACION_MODO=prueba) la respuesta va solo al equipo con el
asunto "[PRUEBA → remitente]"; en modo real va al remitente con copia oculta
al equipo. El correo técnico (tablas de SAP, JSON) pasa a ir solo al equipo.

- respuesta_proveedor.py: contextos de las plantillas, envío y protecciones
  (remitente desconocido o sin dominio, propio buzón, no-reply, mensajes
  automáticos, respuestas a notificaciones, límite de 3 por remitente/24 h).
- observaciones.py: motivos de rechazo con título, filas y cómo corregir
  (borrador de textos); también para correos sin comprobante válido.
- Un error de SAP/HANA o un fallo inesperado no se le comunica al proveedor y
  ya no tumba el ciclo; "guía no encontrada" exige que HANA responda.
- outlook_utilities: es_mensaje_automatico y copia oculta en send_email.
- seguimiento: notificacion_destinatario (con migración) y
  contar_respuestas_correo.
- config_validator anuncia el modo activo; documentación y pruebas.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 08:55:54 -05:00

4.6 KiB

seguimiento.py — Códigos de seguimiento y resultado por comprobante

Cada comprobante que llega por correo recibe un código de seguimiento (ABR-AAMMDD-NNNNN, por ejemplo ABR-261019-00042) que se le comunicará al proveedor y que permite reconocer el comprobante si lo reenvía. El registro también guarda el resultado del proceso y el estado de la notificación.

Los datos viven en la tabla comprobantes de emails.db. El esquema lo crea database_utilities.init_db() con CREATE TABLE IF NOT EXISTS, por lo que una base existente se actualiza sola sin tocar la tabla emails.

Identidad

  • Un comprobante se identifica por (RUC emisor, tipo, serie, número). Si vuelve a llegar, en el mismo correo o en otro, conserva su código.
  • Un correo que no trae ningún comprobante identificable (sin adjuntos, sin XML válido…) se registra aparte con registrar_correo: una fila por correo, con RUC, serie y número en NULL.
  • El código sale del id de la fila (ABR- + fecha + id con 5 dígitos), así que es único por construcción, sin reintentos ni contadores aparte.

Tabla comprobantes

Columna Contenido
codigo_seguimiento Código único del comprobante
email_id Último correo en el que llegó
ruc_emisor, tipo, serie, numero Identidad del comprobante (NULL en las filas de correo sin comprobante)
estado pendiente, aceptado, rechazado o error
observaciones JSON con los motivos de rechazo o el detalle del error
doc_entry DocEntry del draft creado en SAP
veces_recibido Cuántos correos distintos trajeron este comprobante
notificacion_estado, notificacion_error, notificacion_fecha pendiente, enviada, fallida u omitida, y su motivo
notificacion_destinatario Direcciones a las que se envió de verdad (en modo prueba no son las del remitente)
creado_en, actualizado_en Marcas de tiempo

Funciones

Función Qué hace
registrar_comprobante(email_id, ruc_emisor, tipo, serie, numero) Crea el registro o recupera el existente. Devuelve la fila más reenvio (True si ya había llegado en otro correo). Reprocesar el mismo correo no cambia nada.
registrar_correo(email_id) Igual, para un correo sin comprobante. Idempotente por correo.
obtener(codigo) Devuelve el registro o None.
actualizar_resultado(codigo, estado, observaciones=None, doc_entry=None) Guarda el resultado. ValueError si el estado no es válido, LookupError si no existe el código.
registrar_notificacion(codigo, estado, error=None, destinatario=None) Guarda el estado de la notificación y a quién se envió. Mismas excepciones.
contar_respuestas_correo(remitente, horas=24) Respuestas enviada a ese remitente en correos sin comprobante: el freno contra bucles de respuesta_proveedor.

Uso en el pipeline

En pipeline._procesar_un_correo, cada factura se registra al empezar (_registrar_comprobante) y su resultado se guarda al terminar (_registrar_resultado):

Resultado del proceso estado Detalle guardado
Draft creado en SAP aceptado doc_entry
Precios no coinciden con SAP rechazado [{"codigo": "precios_no_coinciden"}]
Error al crear el draft error [{"codigo": "error_sap", "detalle": "..."}]

init_db agrega la columna notificacion_destinatario a una tabla creada antes sin ella, sin tocar los datos.

El seguimiento nunca impide procesar la factura: si falla la base de datos, se registra un WARNING y el flujo continúa. Si el comprobante ya había llegado antes, se registra un WARNING con las veces recibidas y el estado anterior, pero no se bloquea la creación de otro draft.

Observaciones guardadas

  • Rechazo de una factura: la lista de observaciones completa (codigo, titulo, filas, como_corregir; ver observaciones.py).
  • Error: [{"codigo": "error_interno", "detalle": "..."}].
  • Correo sin comprobante: la observación del motivo (sin_pdf_xml, falta_xml, sin_factura).

Las notificaciones las registra respuesta_proveedor (enviada, fallida u omitida con su motivo).

Notas

  • database_utilities.DB_NAME es "emails.db", una ruta relativa al directorio de trabajo. Si el demonio se lanza desde otra carpeta, usará (y creará) otra base y los códigos ya emitidos no se encontrarían. Hoy funciona porque se ejecuta desde la raíz del proyecto.
  • Las pruebas redirigen DB_NAME a una base temporal (fixture automático en tests/conftest.py) para no escribir en emails.db.