From f031714f4226e9147a9f6ad1e5b2a5a89cdef6b3 Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 10 Sep 2026 14:07:16 -0500 Subject: [PATCH] Actualizar backend/services.py --- backend/services.py | 59 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/backend/services.py b/backend/services.py index 1111297..502b104 100644 --- a/backend/services.py +++ b/backend/services.py @@ -765,12 +765,18 @@ def retiros(ano="2026", mes=8): """Apartado RETIROS: alumnos marcados POR RETIRAR en (ano, mes), con sus datos del cronograma de cobranza. El estado POR RETIRAR y la info manual son de ese mes.""" def _load(): + # ¿Hay SNAPSHOT guardado (GUARDAR TOTAL) de este mes? Si hay al menos una + # fila guardada, se muestra ese snapshot congelado (todas las columnas). + snap = retiros_snapshot_obtener(ano, mes) + if snap.get("filas"): + return {"filas": snap["filas"], "guardado": True, + "guardado_en": snap.get("guardado_en")} try: mats_ret = set(alumnos_por_retirar(ano, mes).get("matriculas", [])) except Exception: mats_ret = set() if not mats_ret: - return {"filas": []} + return {"filas": [], "guardado": False} dm = get_dm() proc = CobranzaLogic(dm).processor # para clasificar SEDE por programa (REPLACE_META) crono = dm.ejecutar_consulta_cronograma_cobranza(str(ano)) or [] @@ -841,7 +847,7 @@ def retiros(ano="2026", mes=8): "detalle_retiro": inf.get("detalle_retiro") or "", }) filas.sort(key=lambda x: x["fch_matricula"], reverse=True) - return {"filas": filas} + return {"filas": filas, "guardado": False} return cache_get_or_set("retiros", (str(ano), str(mes)), _load) @@ -886,6 +892,54 @@ def retiros_info_obtener(ano, mes): out[str(r.get("matricula", "")).strip()] = r return out +def retiros_snapshot_obtener(ano, mes): + """Lee el SNAPSHOT completo guardado (GUARDAR TOTAL) de (ano, mes) y lo devuelve + con los MISMOS nombres de campo que usa la tabla principal de Retiros. + Solo cuenta como snapshot si la fila tiene datos completos (alumno/programa).""" + sb = _sb() + try: + res = (sb.table("retiros_info").select("*") + .eq("ano", int(ano)).eq("mes", int(mes)).execute()) + except Exception: + return {"filas": []} + filas = [] + guardado_en = None + for r in (res.data or []): + if not (r.get("alumno") or r.get("programa")): + continue + if not guardado_en and r.get("actualizado_en"): + guardado_en = str(r.get("actualizado_en"))[:16].replace("T", " ") + filas.append({ + "matricula": str(r.get("matricula", "")).strip(), + "sede": r.get("sede") or "", + "fch_matricula": r.get("fch_matricula") or "", + "asesor": r.get("sectorista") or "", + "vendedor": r.get("asesor_comercial") or "", + "programa": r.get("programa") or "", + "cod_detalle": r.get("cod_detalle") or "", + "fch_inicio": r.get("fch_inicio") or "", + "fch_retiro": r.get("fch_retiro") or "", + "alumno": r.get("alumno") or "", + "num_cuotas_pagadas": r.get("num_cuotas_pagadas") or 0, + "num_cuotas_vencidas": r.get("num_cuotas_vencidas") or 0, + "fch_vencimiento": r.get("fch_vencimiento") or "", + "monto_venc_ant": r.get("monto_venc_ant") or 0, + "monto_venc_cur": r.get("monto_venc_cur") or 0, + "imp_total_pagado": r.get("imp_total_pagado") or 0, + "monto_dejo_pagar": r.get("monto_dejo_pagar") or 0, + "n_asistencia": r.get("n_asistencia"), + "n_faltas": r.get("n_faltas"), + "ultimo_dia_asist": r.get("ultimo_dia_asist") or "", + "hasta_sesion": r.get("hasta_sesion") or "", + "fch_ult_resp": r.get("fch_ult_resp") or "", + "fch_envio_correo": r.get("fch_envio_correo") or "", + "retiro_wsp_class": r.get("retiro_wsp_class") or "", + "motivo_retiro": r.get("motivo_retiro") or "", + "detalle_retiro": r.get("detalle_retiro") or "", + }) + filas.sort(key=lambda x: x["fch_matricula"], reverse=True) + return {"filas": filas, "guardado_en": guardado_en} + def retiros_info_guardar(matricula, ano, mes, campos, usuario=None): """INFO: guarda SOLO las 9 columnas manuales de un alumno en (matricula, ano, mes).""" sb = _sb() @@ -929,6 +983,7 @@ def retiros_snapshot_guardar(ano, mes, filas, usuario=None): "fch_vencimiento": f.get("fch_vencimiento") or None, "monto_venc_ant": f.get("monto_venc_ant"), "monto_venc_cur": f.get("monto_venc_cur"), + "imp_total_pagado": f.get("imp_total_pagado"), "monto_dejo_pagar": f.get("monto_dejo_pagar"), **_ri_manual_payload(f), "actualizado_en": now,