From 143932158d0c6ffcc6650d4a0a38ebdc687b44b1 Mon Sep 17 00:00:00 2001 From: Administrator Date: Tue, 22 Sep 2026 10:47:28 -0500 Subject: [PATCH] Actualizar backend/main.py --- backend/main.py | 87 +++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/backend/main.py b/backend/main.py index b6ea4ad..49a821a 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4,7 +4,7 @@ API REST del Dashboard — FastAPI. Reutiliza la lógica de negocio existente en modules/ y core/. Cada endpoint devuelve JSON; el frontend (React) lo consume. """ -from fastapi import FastAPI, Query, HTTPException +from fastapi import FastAPI, Query, HTTPException, Body from fastapi.middleware.cors import CORSMiddleware from datetime import datetime import uvicorn @@ -34,7 +34,7 @@ def _startup(): except Exception as e: print(f"[startup] Precarga falló: {e}") # Refresco cada 4 min en segundo plano (TTL del caché es 5 min) - start_background_refresh(services.refrescar_todo, interval=1800) + start_background_refresh(services.refrescar_todo, interval=1800) # 30 min (con horario) # Historial de estado de asesores: revisa Chatwoot cada 5 min y guarda los # cambios (ON/OFF) en historial_estado_asesor. Solo entre 05:00 y 23:59. @@ -44,13 +44,28 @@ def _startup(): while True: try: h = _dt.now().hour - if 5 <= h <= 23: + if 5 <= h <= 23: # 05:00 a 23:59 (fuera de eso NO revisa) services.registrar_estado_asesores() except Exception as e: print(f"[historial asesores loop] {e}") - _t.sleep(300) + _t.sleep(300) # 5 minutos threading.Thread(target=_loop_asesores, daemon=True).start() + # Auto-offline diario: 1 vez al día entre 05:00 y 06:00, desactiva el + # auto_offline de todos los asesores que lo tengan activado. + def _loop_auto_offline(): + ultimo_dia = None + while True: + try: + ahora = _dt.now() + if 5 <= ahora.hour < 6 and ultimo_dia != ahora.date(): + services.desactivar_auto_offline_todos() + ultimo_dia = ahora.date() + except Exception as e: + print(f"[auto_offline loop] {e}") + _t.sleep(300) # revisa cada 5 min si ya es la hora + threading.Thread(target=_loop_auto_offline, daemon=True).start() + # ── Salud / utilidades ───────────────────────────────────────────────────── @app.get("/api/health") @@ -140,9 +155,10 @@ def get_ventas_detalle( tipo: str = Query("Venta Inscritos"), sede: str = Query("TODOS"), programa: str = Query("TODOS"), + excluir_ret: bool = Query(False), ): try: - return services.ventas_detalle(vendedor, ano, mes, tipo, sede, programa) + return services.ventas_detalle(vendedor, ano, mes, tipo, sede, programa, excluir_ret) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @@ -181,6 +197,36 @@ def get_cobranza_detalle( raise HTTPException(status_code=500, detail=str(e)) +# ── RETIROS ──────────────────────────────────────────────────────────────── +@app.get("/api/retiros") +def get_retiros(ano: int = Query(2026), mes: int = Query(8)): + try: + return services.retiros(ano, mes) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.post("/api/retiros/info/guardar") +def post_retiros_info(payload: dict = Body(...)): + try: + mat = payload.get("matricula", "") + ano = payload.get("ano") + mes = payload.get("mes") + campos = {k: v for k, v in payload.items() + if k not in ("matricula", "ano", "mes", "usuario")} + return services.retiros_info_guardar(mat, ano, mes, campos, payload.get("usuario")) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.post("/api/retiros/snapshot/guardar") +def post_retiros_snapshot(payload: dict = Body(...)): + try: + return services.retiros_snapshot_guardar( + payload.get("ano"), payload.get("mes"), + payload.get("filas", []), payload.get("usuario")) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + # ── RENTABILIDAD ─────────────────────────────────────────────────────────── @app.get("/api/rentabilidad") def get_rentabilidad( @@ -317,36 +363,6 @@ def get_cobranza_detalle_todos(ano: int, mes: int, sectorista: str = "TODOS"): raise HTTPException(status_code=500, detail=str(e)) -# ── RETIROS ──────────────────────────────────────────────────────────────── -@app.get("/api/retiros") -def get_retiros(ano: int = Query(2026), mes: int = Query(8)): - try: - return services.retiros(ano, mes) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - -@app.post("/api/retiros/info/guardar") -def post_retiros_info(payload: dict = Body(...)): - try: - mat = payload.get("matricula", "") - ano = payload.get("ano") - mes = payload.get("mes") - campos = {k: v for k, v in payload.items() - if k not in ("matricula", "ano", "mes", "usuario")} - return services.retiros_info_guardar(mat, ano, mes, campos, payload.get("usuario")) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - -@app.post("/api/retiros/snapshot/guardar") -def post_retiros_snapshot(payload: dict = Body(...)): - try: - return services.retiros_snapshot_guardar( - payload.get("ano"), payload.get("mes"), - payload.get("filas", []), payload.get("usuario")) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - # ── USUARIOS ─────────────────────────────────────────────────────────────── @app.get("/api/alumno/estado") def get_alumno_estado(matricula: str, ano: int = Query(...), mes: int = Query(...)): @@ -567,7 +583,6 @@ def set_asesor_estado(agent_id: int = Query(...), online: bool = Query(...)): except Exception as e: raise HTTPException(status_code=500, detail=str(e)) - @app.get("/api/asesores/historial") def get_asesor_historial(email: str = Query(...)): try: