Actualizar backend/core/data_manager.py

This commit is contained in:
2026-10-09 13:24:19 -05:00
parent bc5c9a8399
commit d8498dfd34

View File

@@ -43,6 +43,7 @@ class DataManager:
self.github_cronograma_url = f"{base}/SQL_QUERY_2/BASE_CRONOGRAMA_2.query" self.github_cronograma_url = f"{base}/SQL_QUERY_2/BASE_CRONOGRAMA_2.query"
self.github_facturas_url = f"{base}/SQL_QUERY_2/BASE_FACTURAS_2.query" self.github_facturas_url = f"{base}/SQL_QUERY_2/BASE_FACTURAS_2.query"
self.github_matriculas_url = f"{base}/SQL_QUERY_2/BASE_MATRICULADOS_2.query" self.github_matriculas_url = f"{base}/SQL_QUERY_2/BASE_MATRICULADOS_2.query"
self.github_notascredito_url = f"{base}/SQL_QUERY_2/BASE_NOTASCREDITO_2.query"
# --- Variables de estado --- # --- Variables de estado ---
self.config_data = {} self.config_data = {}
@@ -57,6 +58,7 @@ class DataManager:
self.query_matriculas_sql = "" self.query_matriculas_sql = ""
self.query_cronograma_sql = "" self.query_cronograma_sql = ""
self.query_facturas_sql = "" self.query_facturas_sql = ""
self.query_notascredito_sql = ""
# Caché en memoria de las consultas ANUALES de cobranza. Evita re-ejecutar # Caché en memoria de las consultas ANUALES de cobranza. Evita re-ejecutar
# la consulta del año COMPLETO en cada combinación de filtros (mes/sectorista/ # la consulta del año COMPLETO en cada combinación de filtros (mes/sectorista/
@@ -64,6 +66,7 @@ class DataManager:
# consulta tiene éxito y se limpia en cada refresco (manual o ciclo 15 min). # consulta tiene éxito y se limpia en cada refresco (manual o ciclo 15 min).
self._cache_cronograma = {} # str(ano) -> filas self._cache_cronograma = {} # str(ano) -> filas
self._cache_facturas = {} # str(ano) -> filas self._cache_facturas = {} # str(ano) -> filas
self._cache_notascredito = {} # str -> filas (rango completo)
self.cargar_toda_configuracion() self.cargar_toda_configuracion()
@@ -87,6 +90,7 @@ class DataManager:
"query_matriculas_sql": self.github_matriculas_url, "query_matriculas_sql": self.github_matriculas_url,
"query_cronograma_sql": self.github_cronograma_url, "query_cronograma_sql": self.github_cronograma_url,
"query_facturas_sql": self.github_facturas_url, "query_facturas_sql": self.github_facturas_url,
"query_notascredito_sql": self.github_notascredito_url,
} }
with ThreadPoolExecutor(max_workers=15) as ex: with ThreadPoolExecutor(max_workers=15) as ex:
json_futs = {k: ex.submit(self._get_json, url, default) for k,(url,default) in json_tasks.items()} json_futs = {k: ex.submit(self._get_json, url, default) for k,(url,default) in json_tasks.items()}
@@ -129,7 +133,7 @@ class DataManager:
def get_connection(self): def get_connection(self):
import pyodbc import pyodbc
conn_str = ( conn_str = (
f"DRIVER={{ODBC Driver 17 for SQL Server}};" f"DRIVER={{SQL Server}};"
f"SERVER={self.server};" f"SERVER={self.server};"
f"DATABASE={self.database};" f"DATABASE={self.database};"
f"UID={self.username};" f"UID={self.username};"
@@ -699,13 +703,37 @@ class DataManager:
print(f"❌ Error facturas cobranza: {e}") print(f"❌ Error facturas cobranza: {e}")
return [] return []
def ejecutar_consulta_notascredito(self):
"""Notas de crédito (rango completo, cacheado). El query no tiene año."""
en_cache = self._cache_notascredito.get("all")
if en_cache is not None:
return en_cache
try:
if not self.query_notascredito_sql:
return []
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute(self.query_notascredito_sql)
columns = [col[0] for col in cursor.description]
results = [dict(zip(columns, row)) for row in cursor.fetchall()]
conn.close()
self._cache_notascredito["all"] = results
return results
except Exception as e:
print(f"❌ Error notas de crédito: {e}")
return []
def traer_vendedor_cod_detalle(self, matriculas, ano=None, mes=None): def traer_vendedor_cod_detalle(self, matriculas, ano=None, mes=None):
"""Dada una lista de num_matricula (POR RETIRAR, pocas), devuelve por cada una: """Dada una lista de num_matricula (POR RETIRAR, pocas), devuelve por cada una:
{num_matricula, dsc_vendedor, cod_detalle}. Consulta directa y ligera {num_matricula, dsc_vendedor, cod_detalle, ...}. Consulta ligera.
(filtra solo esas matrículas). NO toca cobranza ni sus cachés.""" Las cuotas vencidas se cuentan HASTA EL FIN DEL MES (ano, mes) del filtro,
NO hasta la fecha de hoy (así septiembre no cambia al llegar octubre).
Si no se pasa ano/mes, usa la fecha de hoy (comportamiento anterior)."""
mats = [str(m).strip() for m in (matriculas or []) if str(m).strip()] mats = [str(m).strip() for m in (matriculas or []) if str(m).strip()]
if not mats: if not mats:
return {} return {}
# Fecha de corte: primer día del mes SIGUIENTE al filtro (vencidas < corte).
# ini_mes = primer día del mes del filtro (para separar Anterior vs Curso).
corte_venc = "CAST(GETDATE() AS DATE)" corte_venc = "CAST(GETDATE() AS DATE)"
ini_mes = "CAST(GETDATE() AS DATE)" ini_mes = "CAST(GETDATE() AS DATE)"
if ano and mes: if ano and mes:
@@ -777,20 +805,20 @@ class DataManager:
WHERE c.cod_localidad = m.cod_localidad WHERE c.cod_localidad = m.cod_localidad
AND c.num_matricula = m.num_matricula AND c.num_matricula = m.num_matricula
AND c.num_refinanciamiento = m.num_refinanciamiento) AS imp_total_pagado, AND c.num_refinanciamiento = m.num_refinanciamiento) AS imp_total_pagado,
ISNULL((SELECT SUM(c.imp_total - ISNULL(c.imp_dscto, 0)) ISNULL((SELECT SUM(c.imp_total - ISNULL(c.imp_dscto, 0))
FROM sgede_cronograma_matricula c FROM sgede_cronograma_matricula c
WHERE c.cod_localidad = m.cod_localidad WHERE c.cod_localidad = m.cod_localidad
AND c.num_matricula = m.num_matricula AND c.num_matricula = m.num_matricula
AND c.num_refinanciamiento = 1), 0) AS inv_neta, AND c.num_refinanciamiento = 1), 0) AS inv_neta,
m.cod_moneda AS cod_moneda, m.cod_moneda AS cod_moneda,
ISNULL((SELECT TOP 1 comp.imp_tc ISNULL((SELECT TOP 1 comp.imp_tc
FROM vtaca_comprobante comp FROM vtaca_comprobante comp
INNER JOIN sgevi_cuotas_x_comprobante cxc INNER JOIN sgevi_cuotas_x_comprobante cxc
ON comp.cod_localidad = cxc.cod_localidad ON comp.cod_localidad = cxc.cod_localidad
AND comp.num_correlativo = cxc.num_correlativo AND comp.num_correlativo = cxc.num_correlativo
WHERE cxc.cod_localidad_c = m.cod_localidad WHERE cxc.cod_localidad_c = m.cod_localidad
AND cxc.num_matricula = m.num_matricula AND cxc.num_matricula = m.num_matricula
ORDER BY comp.fch_emision DESC), 1) AS imp_tc ORDER BY comp.fch_emision DESC), 1) AS imp_tc
FROM sgeca_matricula m FROM sgeca_matricula m
INNER JOIN sgema_alumno a ON m.cod_alumno = a.cod_alumno INNER JOIN sgema_alumno a ON m.cod_alumno = a.cod_alumno
WHERE m.num_matricula IN ({placeholders}) WHERE m.num_matricula IN ({placeholders})
@@ -801,13 +829,15 @@ class DataManager:
conn.close() conn.close()
out = {} out = {}
for f in filas: for f in filas:
# Si la matrícula está en DÓLARES, convertir montos a soles (× imp_tc).
_moneda = str(f.get("cod_moneda") or "SOL").strip().upper() _moneda = str(f.get("cod_moneda") or "SOL").strip().upper()
try: _tc = float(f.get("imp_tc") or 1.0) try: _tc = float(f.get("imp_tc") or 1.0)
except: _tc = 1.0 except: _tc = 1.0
if _moneda != "DOL" or _tc <= 0: _tc = 1.0 if _moneda != "DOL" or _tc <= 0:
_tc = 1.0
_mult = _tc if _moneda == "DOL" else 1.0 _mult = _tc if _moneda == "DOL" else 1.0
_pag = float(f.get("imp_total_pagado") or 0) * _mult
_inv = float(f.get("inv_neta") or 0) * _mult _inv = float(f.get("inv_neta") or 0) * _mult
_pag = float(f.get("imp_total_pagado") or 0) * _mult
_mva = float(f.get("monto_venc_ant") or 0) * _mult _mva = float(f.get("monto_venc_ant") or 0) * _mult
_mvc = float(f.get("monto_venc_cur") or 0) * _mult _mvc = float(f.get("monto_venc_cur") or 0) * _mult
out[str(f.get("num_matricula", "")).strip()] = { out[str(f.get("num_matricula", "")).strip()] = {
@@ -833,6 +863,7 @@ class DataManager:
if ano is None: if ano is None:
self._cache_cronograma.clear() self._cache_cronograma.clear()
self._cache_facturas.clear() self._cache_facturas.clear()
self._cache_notascredito.clear()
else: else:
self._cache_cronograma.pop(str(ano), None) self._cache_cronograma.pop(str(ano), None)
self._cache_facturas.pop(str(ano), None) self._cache_facturas.pop(str(ano), None)