2026-10-14 22:28:48 -05:00
"""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:
2026-10-22 12:08:38 -05:00
status: ``"created"`` (draft creado), ``"duplicate"`` (la factura ya
existe en SAP), ``"error"`` (error al
2026-10-14 22:28:48 -05:00
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 } "
2026-10-22 12:08:38 -05:00
elif status == "duplicate" :
existente = kwargs . get ( "existente" ) or {}
return f "<p>⏭️ Draft NO creado: la factura ya existe en SAP ( { existente . get ( 'entidad' ) } { existente . get ( 'DocEntry' ) } ).</p> { base } "
2026-10-14 22:28:48 -05:00
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 } "