HORARIOS pasa a 5 bloques que siguen cubriendo las 24 horas: 00:00-06:30 cada
30 minutos (antes 00:00-05:00 cada 60 s y 05:00-06:30 cada 30 min) y
18:30-24:00 cada 60 minutos (antes dos bloques iguales). El día no cambia.
De madrugada un correo puede esperar hasta 30 minutos y los reintentos por
SAP fuera de línea, uno por ciclo, van igual de espaciados.
Se ordena el formato de la lista, test_horarios fija el primer y el último
minuto de cada bloque (y que nunca se devuelva None en el día), y se
actualizan docs/main.md, la guía de despliegue y el CHANGELOG.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
La comparación abs(a - b) < 0.01 no era fiable por la coma flotante: el mismo
céntimo se aceptaba o rechazaba según el importe (431.41 contra 431.40 se
rechazaba y 624.39 contra 624.38 se aceptaba). pipeline_utils.importes_coinciden
redondea la diferencia a 2 decimales, lo que muestra un comprobante, y admite
hasta 1 céntimo. Se usa en la validación de precios y en la comparación del
valor referencial. Con datos reales de SAP (49 líneas) ningún resultado cambió.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- HORARIOS cubre las 24 horas: se agregan 21:30-24:00 (cada 60 min) y
05:00-06:30 (cada 30 min); antes el demonio dormía hasta las 06:30 en esos
tramos. Se corrigen los comentarios, que no coincidían con los valores.
tests/test_horarios.py garantiza que no haya huecos ni solapes.
- La carpeta de resultados se resuelve junto al código, no relativa al cwd
(un servicio de Windows puede arrancar desde cualquier carpeta).
- copiar_a_red no crea carpetas locales cuando RUTA_RED_FACTURACION no es una
ruta absoluta en el sistema operativo (una UNC fuera de Windows); antes el
draft podía referenciar archivos que no estaban en el recurso compartido.
- El log muestra la versión del proyecto (pyproject.toml) al arrancar.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- sapb1_manager.buscar_factura_existente busca la factura (proveedor, tipo,
serie, número) en facturas de compra no anuladas y en drafts; si existe no
se crea otro draft y se responde "ya está registrada" sin pisar el registro
de una aceptación anterior.
- SapNoDisponible (subclase de ConnectionError) distingue una caída (timeout,
sin conexión, HTTP 5xx) de un rechazo normal de SAP (HTTP 4xx). Una caída
de Service Layer con HANA sin resultados ya no se lee como "guía no existe".
- Con SAP o HANA caídos se avisa una sola vez al proveedor con la plantilla
"En cola" y el correo se reintenta en los ciclos siguientes (hasta 24), sin
repetir avisos; al agotarse se avisa al equipo.
- La lógica por factura pasa a _procesar_factura; lo ya resuelto dentro de un
correo no se repite al reintentarlo.
- seguimiento: columna intentos (con migración), registrar_intento y el
estado de notificación en_espera.
- database_utilities.DB_NAME pasa a ser absoluto (junto al código).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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>
plantilla/ con los dos HTML de correo y ejemplos.json (6 escenarios ficticios);
plantillas.py renderiza el subconjunto de Mustache que usan (variables con
escape de HTML, secciones e invertidas), en modo estricto: un dato que falta
da error en vez de un correo con huecos. tools/previsualizar_plantillas.py
genera las vistas previas y envía correos de prueba (--enviar). En la
plantilla de rechazo el RUC, la fila Comprobante y la tabla de guías son
opcionales, para el rechazo de un correo sin comprobante.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
El commit ee64e5f del remoto cambió el DueDate de las cuotas en pipeline.py,
que ya se había dividido en módulos; al hacer el rebase se tomó la versión
dividida y aquí se reaplica el mismo cambio en draft_sap.build_sap_draft
(también se quita la línea comentada de ItemDescription). Se actualizan las
pruebas y la nota de construir_draft.md.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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>
Los scripts manuales pasan a tools/ y los correos de ejemplo a
tests/fixtures/. Suite de 95 pruebas: caracterización de la extracción sobre
los 46 correos (resultados esperados en tests/golden/, fuera de la carpeta
que descargar_correos_prueba borra), unitarias de draft_sap, notificaciones,
documentos, adjuntos y config_validator, y flujo completo de
_procesar_un_correo con SAP/HANA/Outlook simulados. pytest como dependencia
dev y configuración en pyproject.toml.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Dependencias directas con versiones fijas (incluye hdbcli, se quita el alias
dotenv y las transitivas), build-backend corregido a setuptools.build_meta y
py-modules declarados para el layout plano.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- hana_manager.get_delivery ahora busca en ODLN y, si no encuentra, en OWTR
- sapb1_manager resuelve StockTransfers -> Orders (via U_IDC_NUMOV) para
validar precios de flete cuando la guia es un traslado de inventario
- se corrige el orden de parametros tipo/serie/numero (sin cruces) en
get_delivery, get_delivery_notes y _get_delivery_entries
- se agrega sapb1_manager.obtener_condicion_pago (BusinessPartners +
PaymentTermsTypes) y pipeline._calcular_fecha_vencimiento para que
DocDueDate del draft sea fecha de contabilizacion + dias de la condicion
de pago del proveedor
- se agrega variante de regex para "VALOR REFER" truncado en sunat_schema.py
- se actualiza documentacion (docs/) reflejando estos cambios
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>