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:
@@ -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)