From ab2ef167f9b2d1d3641b34a57a1c1887cf9002f2 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Sun, 26 Jul 2026 17:59:42 -0500 Subject: [PATCH] feat: add Resumen Diario TNS page as submodule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Groups envios by factura+tipo per day and shows estado_final (exitoso if any send succeeded, error persistente if all failed). Supports inline reenvío and JSON detail modal. Added to sidebar under Historial TNS and as TNS submodule link. Co-Authored-By: Claude Sonnet 4.6 --- app/routes/logs.py | 56 +++++++- app/templates/base.html | 8 +- app/templates/resumen.html | 274 +++++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 app/templates/resumen.html diff --git a/app/routes/logs.py b/app/routes/logs.py index a3cf046..8d987a9 100644 --- a/app/routes/logs.py +++ b/app/routes/logs.py @@ -1,5 +1,5 @@ import json as json_lib -from datetime import datetime +from datetime import datetime, date as date_cls import httpx from fastapi import APIRouter, Request, Depends from fastapi.responses import JSONResponse @@ -199,6 +199,60 @@ async def reenviar_envio(envio_id: int, request: Request, user: dict = Depends(g return JSONResponse({"success": new_status != "error", "message": msg}) +@router.get("/resumen") +async def resumen_page( + request: Request, + user: dict = Depends(get_current_user), + fecha: str = "", + tipo: str = "", +): + if not fecha: + fecha = date_cls.today().isoformat() + + conn = get_connection() + + where_extra = "" + params_q: list = [fecha] + if tipo: + where_extra = "AND tipo = ?" + params_q.append(tipo) + + rows = conn.execute(f""" + SELECT + factura, tipo, contrato, cedula, idrecepcion, + COUNT(*) AS intentos, + SUM(CASE WHEN status != 'error' THEN 1 ELSE 0 END) AS exitos, + MIN(created_at) AS primer_envio, + MAX(created_at) AS ultimo_envio, + MAX(CASE WHEN status = 'error' THEN id END) AS ultimo_error_id, + MAX(CASE WHEN status != 'error' THEN id END) AS exitoso_id, + CASE WHEN SUM(CASE WHEN status != 'error' THEN 1 ELSE 0 END) > 0 + THEN MAX(CASE WHEN status != 'error' THEN status END) + ELSE 'error' + END AS estado_final + FROM envios + WHERE DATE(created_at) = ? {where_extra} + GROUP BY factura, tipo, contrato, cedula, idrecepcion + ORDER BY estado_final, tipo, factura + """, params_q).fetchall() + + conn.close() + + total = len(rows) + exitosos = sum(1 for r in rows if r["estado_final"] != "error") + errores = sum(1 for r in rows if r["estado_final"] == "error") + + return request.app.state.templates.TemplateResponse("resumen.html", { + "request": request, "user": user, + "fecha": fecha, + "filtro_tipo": tipo, + "rows": rows, + "total": total, + "exitosos": exitosos, + "errores": errores, + }) + + @router.get("/detalle/{envio_id}") async def detalle_envio(envio_id: int, request: Request, user: dict = Depends(get_current_user)): conn = get_connection() diff --git a/app/templates/base.html b/app/templates/base.html index af2b933..29000e7 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -61,10 +61,13 @@ TNS 5 - {% if cur == '/envios/tns' %} + {% if cur in ('/envios/tns', '/logs/resumen') %}

Terceros · Transacción · Ventas

Prueba RDA · Automatización

+ + Resumen Diario +
{% endif %} @@ -84,6 +87,9 @@ Historial TNS + + Resumen Diario + Actividad diff --git a/app/templates/resumen.html b/app/templates/resumen.html new file mode 100644 index 0000000..cb38765 --- /dev/null +++ b/app/templates/resumen.html @@ -0,0 +1,274 @@ +{% extends "base.html" %} +{% block title %}Resumen Diario{% endblock %} +{% block header %}Resumen Diario TNS{% endblock %} +{% block content %} + + +
+
+ + {{ total }} + facturas procesadas +
+
+ + {{ exitosos }} + exitosas +
+
+ + {{ errores }} + error persistente +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + + + +
+
+
+
+ + +
+
+ + {% if total %}{{ total }} registro{{ 's' if total != 1 }} — {{ fecha }}{% else %}Sin registros para {{ fecha }}{% endif %} + + + + Estado final: si hubo al menos un envío exitoso, se marca como exitoso + +
+ + {% if rows %} +
+ + + + + + + + + + + + + + + + + {% for r in rows %} + + + + + + + + + + + + + {% endfor %} + +
TipoFacturaIDRECEP.ContratoCédulaIntentosEstado finalPrimer envíoÚltimo envío
+ + {{ r.tipo }} + + {{ r.factura or '—' }}{{ r.idrecepcion or '—' }}{{ r.contrato or '—' }}{{ r.cedula or '—' }} + {{ r.intentos }} + {% if r.exitos > 0 and r.intentos > r.exitos %} + ({{ r.exitos }} ok) + {% endif %} + + {% if r.estado_final == 'success' %} + ✓ Enviado + {% elif r.estado_final == 'warning' %} + ! Ya existe + {% else %} + ✗ Error persistente + {% endif %} + {{ r.primer_envio[:16] if r.primer_envio else '—' }}{{ r.ultimo_envio[:16] if r.ultimo_envio else '—' }} + {% set ver_id = r.exitoso_id or r.ultimo_error_id %} + {% if ver_id %} + + {% endif %} + {% if r.estado_final == 'error' and r.ultimo_error_id %} + + {% endif %} +
+
+ {% else %} +
+ +

Sin envíos registrados para {{ fecha }}

+
+ {% endif %} +
+ + + + + +{% endblock %}