# 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 | | 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 | | Error de SAP o de HANA, o un fallo inesperado | **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 de HANA.** `get_delivery_notes` devuelve lista vacía tanto si la guía no existe como si SAP/HANA fallan. Antes de rechazar por guía no encontrada se comprueba que HANA responde; si no, se trata como error interno y no se le dice nada al proveedor. ## 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 | | `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. - **Si SAP está caído, el correo tampoco se reintenta.** Se avisa al equipo y el correo queda procesado (con estado `error`). - **Un comprobante ya aceptado que se reenvía** genera otro draft y otra respuesta "Aceptado"; hoy solo se advierte en el log. - 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.