Files
descarga_factura_transporte/docs/respuesta_proveedor.md
T
GeorgeandClaude Sonnet 5.5 f0ad6aa78e se valida si la factura ya existe en SAP y se avisa cuando SAP está fuera de línea
- 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>
2026-10-22 12:08:38 -05:00

8.5 KiB

respuesta_proveedor.py — Respuesta al proveedor

Cuando alguien envía un correo con el XML y el PDF de una factura, el sistema valida y responde a esa misma dirección con la plantilla de aceptado o de rechazado (plantilla/plantilla-aceptado.html y plantilla-rechazado.html). Aparte, el equipo recibe un correo técnico (tablas de SAP, JSON adjuntos).

Piezas: respuesta_proveedor.py (contexto, protecciones, envío), observaciones.py (motivos de rechazo y sus textos), plantillas.py (renderizador) y seguimiento.py (código de seguimiento y registro).

Qué se responde

Situación Respuesta al remitente
Draft creado en SAP Aceptado
Precios que no coinciden, guía inexistente, línea sin guía, cantidad cero Rechazado con una observación por motivo
La factura ya existe en SAP (factura de compra no anulada o draft) Rechazado: "Esta factura ya está registrada". No se crea otro draft
Correo con adjuntos pero sin PDF ni XML válido, o con PDF y sin XML, o sin ninguna factura (tipo 01) Rechazado sin comprobante: no muestra proveedor, número ni tabla de guías
SAP o HANA no responden Aviso "En cola" (una sola vez): se reintentará solo, no hace falta reenviar. Ver más abajo
Fallo inesperado, o un rechazo normal de SAP al crear el draft (HTTP 4xx) Nada: es una falla nuestra. Solo se avisa al equipo
Correo sin adjuntos Nada

Una respuesta por comprobante: un correo con 3 facturas genera 3 respuestas. La aceptación significa "draft creado en SAP". La diferencia de valor referencial XML vs SAP es solo informativa y no rechaza.

Guía "no encontrada" y caídas. get_delivery_notes ya no confunde una caída con una guía inexistente: si Service Layer no responde y HANA no aporta nada, lanza SapNoDisponible. Además, antes de rechazar por guía no encontrada se comprueba que HANA responde; si no, es "servicio fuera de línea" y no un rechazo.

¿La factura ya existe en SAP?

Antes de validar precios, sapb1_manager.buscar_factura_existente busca el comprobante por proveedor (PL + RUC), tipo, serie y número (U_BPP_MDTD/U_BPP_MDSD/U_BPP_MDCD) en las facturas de compra no anuladas y en los drafts de factura de compra. Si existe, no se crea otro draft y se responde con la observación factura_ya_registrada. Si el comprobante ya estaba aceptado en comprobantes, ese registro (estado y doc_entry) no se sobrescribe.

La plantilla de rechazo dice "No pudimos registrar su factura… Corríjalas y envíenos el comprobante nuevamente", que no encaja del todo con un duplicado: la observación aclara que no hace falta reenviarla. Si prefiere una plantilla propia para este caso, es un cambio pequeño.

Servicio fuera de línea y reintentos

SapNoDisponible (subclase de ConnectionError) se lanza solo ante una caída: timeout, sin conexión, HTTP 5xx o login imposible por esos motivos. Un HTTP 4xx (rechazo de SAP) o credenciales inválidas siguen siendo un ConnectionError normal y no se tratan como caída.

Cuando una factura se encuentra con una caída:

  1. Se suma un intento en comprobantes.intentos y el resultado queda error con la observación servicio_no_disponible.
  2. Solo en el primer intento se envía al proveedor la plantilla plantilla-fuera-de-linea.html ("En cola, la procesaremos automáticamente, no necesita reenviarla") y se avisa al equipo. La notificación queda en_espera. Los reintentos siguientes no repiten ningún aviso.
  3. El correo se elimina de emails, de modo que el siguiente ciclo del demonio lo vuelve a tomar.
  4. Cuando SAP vuelve, se procesa con normalidad y el proveedor recibe el aceptado o rechazado.
  5. Tras MAX_REINTENTOS (24) intentos se deja de reintentar, se avisa al equipo ("Reintentos agotados") y el proveedor no recibe nada más.

Al reintentar un correo, lo que ya se resolvió dentro de él (otra factura del mismo correo) no se repite ni se responde dos veces. Sin seguimiento (si falla la base de datos) no se puede contar intentos, así que una caída se trata como error interno y no se reintenta.

Modo prueba y modo real

NOTIFICACION_MODO en el .env:

Modo A quién va la respuesta Asunto
prueba (por defecto) Al equipo (NOTIFICACION_EQUIPO), nunca al remitente [PRUEBA → remitente] Aceptado · Factura F002-486 · ABR-…
real Al remitente, con copia oculta al equipo Aceptado · Factura F002-486 · ABR-…

Cualquier otro valor se trata como prueba. Al arrancar, config_validator deja en el log una advertencia con el modo activo. Pasar a real es una decisión explícita: no ocurre solo.

Protecciones: cuándo NO se responde

El sistema contesta solo y a terceros, así que no responde a:

Caso Motivo registrado
Remitente vacío o desconocido remitente desconocido
Dirección sin dominio válido (por ejemplo test@local) dirección no válida
El propio buzón del sistema el remitente es el propio buzón
no-reply, noreply, do-not-reply, no-responder, mailer-daemon, postmaster, bounce dirección automática
Mensajes automáticos (respuestas automáticas, rebotes, listas) mensaje automático
En correos sin comprobante: asunto con Re:/RV: o que cita un código ABR-… parece una respuesta a una notificación nuestra
En correos sin comprobante: más de 3 respuestas al mismo remitente en 24 h límite de respuestas

Cada omisión queda en comprobantes como notificacion_estado = omitida, con el motivo.

Qué ve el proveedor

  • Importe por guía: el importe de la línea aparece una vez, en su primera guía; las demás dicen "incluida en la línea". Todas las guías de una línea llevan el mismo resultado, porque la validación es por línea.
  • Rechazo por precio: se muestran "Importe en su factura" e "Importe esperado" (el de SAP).
  • Pago estimado: solo si hay condición de pago (fecha de vencimiento posterior a la de contabilización).
  • Sin adjuntos internos: la respuesta no lleva archivos.

Observaciones (observaciones.py)

Los textos son un borrador para revisar; se cambian en ese archivo sin tocar la lógica.

Código Cuándo
precio_no_coincide El total de la línea del XML no coincide con la suma de SAP
guia_no_encontrada SAP no devuelve datos para la guía
linea_sin_guia Una línea no indica su guía de remisión
cantidad_cero Una línea tiene cantidad cero
factura_ya_registrada La factura ya existe en SAP
sin_pdf_xml El correo trae adjuntos, pero ninguno es PDF ni XML
falta_xml Hay PDF y ningún XML
sin_factura El XML no contiene una factura electrónica (tipo 01)

Configuración (.env)

Variable Para qué Por defecto
NOTIFICACION_MODO prueba o real prueba
NOTIFICACION_EQUIPO Direcciones internas (coma o ;): correo técnico, y respuestas en modo prueba jlopez@aberio.com.pe, gcalle@pucp.pe
NOTIFICACION_CONTACTO Dirección del botón "Escribir a Aberio" el buzón EMAIL
LOGO_URL URL pública (https) del logo vacío: sin logo

El buzón EMAIL es también el envio_correo al que se pide reenviar el comprobante corregido.

Cómo probar

  1. Vista previa y envío de ejemplos: python tools/previsualizar_plantillas.py (ver su ayuda). Los ejemplos están en plantilla/ejemplos.json.
  2. Piloto en modo prueba: con NOTIFICACION_MODO=prueba el flujo completo corre y las respuestas llegan solo al equipo, indicando en el asunto a quién habrían ido. El correo técnico también.
  3. Pasar a real: cuando el equipo valide los correos, cambiar a NOTIFICACION_MODO=real. Revisar antes que LOGO_URL tenga valor.

Limitaciones conocidas

  • Un fallo de envío no se reintenta. Queda fallida en comprobantes, pero el correo ya está marcado como procesado. (Solo se reintentan los correos cuando SAP está caído.)
  • Los reintentos de SAP caído dependen de que el correo siga dentro de la ventana --dias del demonio (2 días por defecto).
  • Un error que no es una caída (por ejemplo un HTTP 400 de SAP al crear el draft) no se reintenta: se avisa al equipo.
  • La respuesta va a la dirección from del mensaje. Si alguien reenvía la factura de un proveedor, la respuesta le llega a quien reenvió.
  • No se prueba contra el logo real ni contra Outlook de escritorio: hay que revisar los correos a mano.