se divide pipeline.py en módulos (adjuntos, documentos, persistencia, draft_sap, notificaciones)
Las funciones anidadas de _procesar_un_correo pasan a funciones de módulo con parámetros explícitos; pipeline.py queda solo como orquestador. copiar_a_red lee las variables de entorno al ejecutarse. Se actualizan docs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ee64e5f773
commit
7d2f3212c0
9 files changed
+1309
-1228
No files matched your search
@@ -0,0 +1,117 @@
|
||||
"""Cuerpos HTML de los correos de notificación."""
|
||||
|
||||
|
||||
|
||||
|
||||
# ============================================================================
|
||||
def build_validation_html(resultados: list[dict]) -> str:
|
||||
"""
|
||||
Genera una tabla HTML con el resultado de la validación de precios
|
||||
para incluir en el cuerpo del email de notificación.
|
||||
|
||||
Args:
|
||||
resultados: Lista de resultados de ``validate_prices``.
|
||||
|
||||
Returns:
|
||||
str — Código HTML de la tabla con columnas:
|
||||
Guía, Estado, Draft Price, SAP, Almacén, Resultado.
|
||||
|
||||
Example:
|
||||
Con un resultado ``ok=True`` y ``grflete="09T001-127773"``::
|
||||
|
||||
html = build_validation_html(resultados)
|
||||
# → "<table ...><tr><td>09T001-127773</td><td>✅</td>...</tr></table>"
|
||||
"""
|
||||
rows = ""
|
||||
for r in resultados:
|
||||
icono = "✅" if r["ok"] else "❌"
|
||||
dp = f"{r['draft_price']:.6f}" if r["draft_price"] is not None else "-"
|
||||
sap_price_str = f"{r['sap_price']:.6f}" if r["sap_price"] is not None else "-"
|
||||
rows += f"<tr><td>{r['grflete']}</td><td>{icono}</td><td>{dp}</td><td>{sap_price_str}</td><td>{r['warehouse_code']}</td><td>{r['label']}</td></tr>"
|
||||
return (
|
||||
"<table border='1' cellpadding='6' cellspacing='0' style='border-collapse:collapse;'>"
|
||||
"<tr><th>Guía</th><th>Estado</th><th>Draft Price</th><th>SAP</th><th>Almacén</th><th>Resultado</th></tr>"
|
||||
f"{rows}</table>"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
def build_valor_referencial_html(factura: dict, guias_valor_ref: list[dict]) -> str:
|
||||
"""
|
||||
Genera una tabla HTML comparando el valor referencial del XML de la
|
||||
factura contra el valor referencial obtenido en SAP
|
||||
(``Sp_ObtenerValorReferencial``) por cada guía de remisión, y el
|
||||
total acumulado.
|
||||
|
||||
Args:
|
||||
factura: Diccionario de la factura (usa ``lineas[]`` para sumar
|
||||
``valor_referencial`` extraído del XML).
|
||||
guias_valor_ref: Resultado de ``obtener_valor_referencial_sap``.
|
||||
|
||||
Returns:
|
||||
str — Tabla HTML con una fila por guía, el total SAP, el valor
|
||||
del XML y un ícono de coincidencia.
|
||||
"""
|
||||
valor_xml = sum(l.get("valor_referencial") or 0 for l in factura.get("lineas", []))
|
||||
total_sap = sum(g["valor_sap"] for g in guias_valor_ref)
|
||||
|
||||
rows = "".join(
|
||||
f"<tr><td>09{g['serie']}-{g['numero']}</td><td>{g['valor_sap']:.2f}</td></tr>"
|
||||
for g in guias_valor_ref
|
||||
)
|
||||
coincide = abs(valor_xml - total_sap) < 0.01
|
||||
icono = "✅" if coincide else "❌"
|
||||
|
||||
return (
|
||||
"<table border='1' cellpadding='6' cellspacing='0' style='border-collapse:collapse;'>"
|
||||
"<tr><th>Guía de Remisión</th><th>Valor Referencial SAP</th></tr>"
|
||||
f"{rows}"
|
||||
f"<tr><td><b>Total SAP</b></td><td><b>{total_sap:.2f}</b></td></tr>"
|
||||
f"<tr><td><b>Valor Referencial XML</b></td><td><b>{valor_xml:.2f}</b></td></tr>"
|
||||
f"<tr><td colspan='2'>{icono} {'Coincide' if coincide else 'No coincide'}</td></tr>"
|
||||
"</table>"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
def build_notification_body(status: str, factura: dict, validation_table: str, valor_referencial_table: str, **kwargs) -> str:
|
||||
"""
|
||||
Construye el cuerpo HTML del email de notificación del resultado
|
||||
del proceso SAP.
|
||||
|
||||
Args:
|
||||
status: ``"created"`` (draft creado), ``"error"`` (error al
|
||||
crear), o cualquier otro valor (validación falló, draft no
|
||||
creado).
|
||||
factura: Diccionario de la factura (se usa ``serie``,
|
||||
``numero``, ``razon_social_emisor``).
|
||||
validation_table: Tabla HTML generada por
|
||||
``build_validation_html``.
|
||||
valor_referencial_table: Tabla HTML generada por
|
||||
``build_valor_referencial_html``.
|
||||
**kwargs: Argumentos adicionales:
|
||||
- ``doc_entry`` (int): DocEntry del draft creado.
|
||||
- ``error`` (str): Mensaje de error si ocurrió.
|
||||
|
||||
Returns:
|
||||
str — Cuerpo HTML completo del email.
|
||||
|
||||
Example:
|
||||
Draft creado exitosamente::
|
||||
|
||||
body = build_notification_body(
|
||||
"created", factura, tabla_html, valref_html, doc_entry=2021
|
||||
)
|
||||
# → "<p>✅ Draft SAP B1 creado exitosamente.</p>..."
|
||||
"""
|
||||
base = (
|
||||
f"<p><b>Factura:</b> {factura.get('serie')}-{factura.get('numero')}</p>"
|
||||
f"<p><b>Emisor:</b> {factura.get('razon_social_emisor', '')}</p>"
|
||||
)
|
||||
valref_section = f"<h3>Valor Referencial (XML vs SAP)</h3>{valor_referencial_table}"
|
||||
if status == "created":
|
||||
return f"<p>✅ Draft SAP B1 creado exitosamente.</p><p><b>DocEntry:</b> {kwargs.get('doc_entry')}</p>{base}<h3>Validación de precios</h3>{validation_table}{valref_section}"
|
||||
elif status == "error":
|
||||
return f"<p>❌ Error al crear Draft SAP B1.</p>{base}<p><b>Error:</b> {kwargs.get('error')}</p><h3>Validación de precios</h3>{validation_table}{valref_section}"
|
||||
else:
|
||||
return f"<p>⏭️ Draft NO creado. Los precios de las guías no coinciden con SAP.</p>{base}<h3>Validación de precios</h3>{validation_table}{valref_section}"
|
||||
Reference in new issue
Block a user