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>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-22 08:55:54 -05:00
1 parent af9e3961e5
commit 4deb9d4e4d
22 files changed
+1660 -146

No files matched your search

+15
View File
@@ -21,6 +21,9 @@ OBLIGATORIAS = {
"SAP HANA": ["HANA_SERVER", "HANA_USER", "HANA_PASSWORD", "HANA_SCHEMA"],
}
# Modos de ``NOTIFICACION_MODO``: ver respuesta_proveedor.py
MODOS_NOTIFICACION = ("prueba", "real")
# Variable de verificación SSL → servicio al que aplica. Cada módulo solo
# la activa con el valor "true" (cualquier otro valor, o ausente, la desactiva).
VERIFICACION_SSL = {
@@ -55,6 +58,18 @@ def validar_configuracion() -> tuple[list[str], list[str]]:
"y U_VS_ANEXO_PDF/XML irán vacíos en el draft"
)
modo = os.getenv("NOTIFICACION_MODO", "").strip().lower()
if modo and modo not in MODOS_NOTIFICACION:
advertencias.append(f"NOTIFICACION_MODO='{modo}' no es válido (usa {' o '.join(MODOS_NOTIFICACION)}); se usará prueba")
elif modo == "real":
advertencias.append("Modo de notificación REAL: las respuestas se envían a quien envía el correo")
else:
advertencias.append("Modo de notificación PRUEBA: las respuestas van al equipo, no a quien envía el correo")
if not os.getenv("NOTIFICACION_EQUIPO", "").strip():
advertencias.append("NOTIFICACION_EQUIPO no definida: se usa la lista por defecto del equipo")
if not os.getenv("LOGO_URL", "").strip():
advertencias.append("LOGO_URL no definida: los correos al proveedor saldrán sin logo")
nivel = os.getenv("LOG_LEVEL", "").strip()
if nivel and not isinstance(getattr(logging, nivel.upper(), None), int):
advertencias.append(f"LOG_LEVEL='{nivel}' no es un nivel válido; se usará INFO")