Actualizar backend/core/data_manager.py
This commit is contained in:
@@ -58,8 +58,10 @@ class DataManager:
|
|||||||
self.query_cronograma_sql = ""
|
self.query_cronograma_sql = ""
|
||||||
self.query_facturas_sql = ""
|
self.query_facturas_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).
|
# la consulta del año COMPLETO en cada combinación de filtros (mes/sectorista/
|
||||||
|
# agrupación), que era lo que saturaba el SQL Server. Se llena solo cuando la
|
||||||
|
# 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
|
||||||
|
|
||||||
@@ -99,28 +101,20 @@ class DataManager:
|
|||||||
self.pendientes_data = set()
|
self.pendientes_data = set()
|
||||||
|
|
||||||
def _get_json(self, url, default=None):
|
def _get_json(self, url, default=None):
|
||||||
# 3 intentos, timeout 15s. Si falla, avisa en consola y devuelve el default.
|
try:
|
||||||
for intento in range(1, 4):
|
r = requests.get(url, timeout=4)
|
||||||
try:
|
r.raise_for_status()
|
||||||
r = requests.get(url, timeout=15)
|
return r.json()
|
||||||
r.raise_for_status()
|
except:
|
||||||
return r.json()
|
return default or {}
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ [GitHub] intento {intento}/3 fallo JSON {url}: {e}")
|
|
||||||
print(f"❌ [GitHub] NO se pudo descargar (JSON): {url}")
|
|
||||||
return default or {}
|
|
||||||
|
|
||||||
def _get_text(self, url):
|
def _get_text(self, url):
|
||||||
# 3 intentos, timeout 15s. Si falla, avisa en consola y devuelve texto vacio.
|
try:
|
||||||
for intento in range(1, 4):
|
r = requests.get(url, timeout=4)
|
||||||
try:
|
r.raise_for_status()
|
||||||
r = requests.get(url, timeout=15)
|
return r.text
|
||||||
r.raise_for_status()
|
except:
|
||||||
return r.text
|
return ""
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ [GitHub] intento {intento}/3 fallo TEXT {url}: {e}")
|
|
||||||
print(f"❌ [GitHub] NO se pudo descargar (TEXT): {url}")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def _cargar_pendientes(self):
|
def _cargar_pendientes(self):
|
||||||
# Ya cargado en paralelo dentro de cargar_toda_configuracion
|
# Ya cargado en paralelo dentro de cargar_toda_configuracion
|
||||||
@@ -135,7 +129,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}};" # <--- ¡ESTE ES EL CAMBIO MÁGICO!
|
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};"
|
||||||
@@ -637,7 +631,8 @@ class DataManager:
|
|||||||
ventas_por_vendedor[vendedor]['monto_pc'] += inv_neta
|
ventas_por_vendedor[vendedor]['monto_pc'] += inv_neta
|
||||||
ventas_por_vendedor[vendedor]['cantidad_pc'] += 1
|
ventas_por_vendedor[vendedor]['cantidad_pc'] += 1
|
||||||
|
|
||||||
# COLUMNA 4 y 5 — MES PASADO: matrícula anterior + pagó 1° cuota en el mes del filtro
|
# VENTA PENDIENTES: SOLO de tu lista BASE_PENDIENTES y que pagaron su 1ª
|
||||||
|
# cuota EN EL MES/AÑO DEL FILTRO (pago_en_fecha_correcta). Si pagó otro mes, no.
|
||||||
if matricula_int in set_lista_pendientes and pago_en_fecha_correcta:
|
if matricula_int in set_lista_pendientes and pago_en_fecha_correcta:
|
||||||
ventas_por_vendedor[vendedor]['monto_pendientes'] += inv_neta
|
ventas_por_vendedor[vendedor]['monto_pendientes'] += inv_neta
|
||||||
ventas_por_vendedor[vendedor]['cantidad_pendientes'] += 1
|
ventas_por_vendedor[vendedor]['cantidad_pendientes'] += 1
|
||||||
@@ -680,68 +675,6 @@ class DataManager:
|
|||||||
print(f"❌ Error cronograma cobranza: {e}")
|
print(f"❌ Error cronograma cobranza: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def traer_vendedor_cod_detalle(self, matriculas):
|
|
||||||
"""Para el apartado RETIROS: por cada num_matricula (solo las POR RETIRAR),
|
|
||||||
trae dsc_vendedor, cod_detalle, dsc_alumno, N° cuotas pagadas/vencidas,
|
|
||||||
fecha de vencimiento más antigua, imp_total_pagado e inv_neta. Consulta ligera."""
|
|
||||||
try:
|
|
||||||
mats = [str(m).strip() for m in (matriculas or []) if str(m).strip()]
|
|
||||||
if not mats:
|
|
||||||
return {}
|
|
||||||
placeholders = ",".join(["?"] * len(mats))
|
|
||||||
query = f"""
|
|
||||||
SELECT m.num_matricula,
|
|
||||||
(SELECT t.dsc_apellido_paterno + ' ' + SUBSTRING(t.dsc_apellido_materno,1,1) + '. ' + t.dsc_nombres
|
|
||||||
FROM rhuma_trabajador t WHERE t.cod_trabajador = m.cod_vendedor) AS dsc_vendedor,
|
|
||||||
m.cod_periodo AS cod_detalle,
|
|
||||||
(a.dsc_apellido_paterno + ' ' + a.dsc_apellido_materno + ', ' + a.dsc_nombres) AS dsc_alumno,
|
|
||||||
(SELECT COUNT(1) FROM sgede_cronograma_matricula c
|
|
||||||
WHERE c.cod_localidad=m.cod_localidad AND c.num_matricula=m.num_matricula
|
|
||||||
AND c.num_refinanciamiento=m.num_refinanciamiento
|
|
||||||
AND c.cod_tipo_cuota='ARM' AND c.cod_estado='CAN') AS num_cuotas_pagadas,
|
|
||||||
(SELECT COUNT(1) FROM sgede_cronograma_matricula c
|
|
||||||
WHERE c.cod_localidad=m.cod_localidad AND c.num_matricula=m.num_matricula
|
|
||||||
AND c.num_refinanciamiento=m.num_refinanciamiento
|
|
||||||
AND c.cod_tipo_cuota='ARM' AND c.cod_estado<>'CAN'
|
|
||||||
AND c.fch_vencimiento < CAST(GETDATE() AS DATE)) AS num_cuotas_vencidas,
|
|
||||||
(SELECT MIN(c.fch_vencimiento) FROM sgede_cronograma_matricula c
|
|
||||||
WHERE c.cod_localidad=m.cod_localidad AND c.num_matricula=m.num_matricula
|
|
||||||
AND c.num_refinanciamiento=m.num_refinanciamiento
|
|
||||||
AND c.cod_tipo_cuota='ARM' AND c.cod_estado<>'CAN'
|
|
||||||
AND c.fch_vencimiento < CAST(GETDATE() AS DATE)) AS fch_venc_mas_antigua,
|
|
||||||
(SELECT SUM(c.imp_total_pagado) FROM sgede_cronograma_matricula c
|
|
||||||
WHERE c.cod_localidad=m.cod_localidad AND c.num_matricula=m.num_matricula
|
|
||||||
AND c.num_refinanciamiento=m.num_refinanciamiento) AS imp_total_pagado,
|
|
||||||
ISNULL((SELECT SUM(c.imp_total - ISNULL(c.imp_dscto,0)) FROM sgede_cronograma_matricula c
|
|
||||||
WHERE c.cod_localidad=m.cod_localidad AND c.num_matricula=m.num_matricula
|
|
||||||
AND c.num_refinanciamiento=1),0) AS inv_neta
|
|
||||||
FROM sgeca_matricula m
|
|
||||||
INNER JOIN sgema_alumno a ON m.cod_alumno = a.cod_alumno
|
|
||||||
WHERE m.num_matricula IN ({placeholders})
|
|
||||||
"""
|
|
||||||
conn = self.get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute(query, mats)
|
|
||||||
out = {}
|
|
||||||
for row in cursor.fetchall():
|
|
||||||
mat = str(row[0]).strip()
|
|
||||||
fvenc = row[6]
|
|
||||||
out[mat] = {
|
|
||||||
"dsc_vendedor": (row[1] or "").strip() if row[1] else "",
|
|
||||||
"cod_detalle": row[2] if row[2] is not None else "",
|
|
||||||
"dsc_alumno": (row[3] or "").strip() if row[3] else "",
|
|
||||||
"num_cuotas_pagadas": int(row[4] or 0),
|
|
||||||
"num_cuotas_vencidas": int(row[5] or 0),
|
|
||||||
"fch_venc_mas_antigua": (str(fvenc)[:10] if fvenc else ""),
|
|
||||||
"imp_total_pagado": float(row[7] or 0),
|
|
||||||
"inv_neta": float(row[8] or 0),
|
|
||||||
}
|
|
||||||
conn.close()
|
|
||||||
return out
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error traer_vendedor_cod_detalle: {e}")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def ejecutar_consulta_facturas_cobranza(self, ano):
|
def ejecutar_consulta_facturas_cobranza(self, ano):
|
||||||
clave = str(ano)
|
clave = str(ano)
|
||||||
en_cache = self._cache_facturas.get(clave)
|
en_cache = self._cache_facturas.get(clave)
|
||||||
@@ -762,6 +695,84 @@ class DataManager:
|
|||||||
print(f"❌ Error facturas cobranza: {e}")
|
print(f"❌ Error facturas cobranza: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def traer_vendedor_cod_detalle(self, matriculas):
|
||||||
|
"""Dada una lista de num_matricula (POR RETIRAR, pocas), devuelve por cada una:
|
||||||
|
{num_matricula, dsc_vendedor, cod_detalle}. Consulta directa y ligera
|
||||||
|
(filtra solo esas matrículas). NO toca cobranza ni sus cachés."""
|
||||||
|
mats = [str(m).strip() for m in (matriculas or []) if str(m).strip()]
|
||||||
|
if not mats:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
conn = self.get_connection()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
placeholders = ",".join("?" for _ in mats)
|
||||||
|
sql = f"""
|
||||||
|
SELECT
|
||||||
|
m.num_matricula,
|
||||||
|
(SELECT t.dsc_apellido_paterno + ' ' +
|
||||||
|
SUBSTRING(t.dsc_apellido_materno, 1, 1) + '. ' + t.dsc_nombres
|
||||||
|
FROM rhuma_trabajador t
|
||||||
|
WHERE t.cod_trabajador = m.cod_vendedor) AS dsc_vendedor,
|
||||||
|
m.cod_periodo AS cod_detalle,
|
||||||
|
a.dsc_apellido_paterno + ' ' + a.dsc_apellido_materno + ', ' + a.dsc_nombres AS dsc_alumno,
|
||||||
|
(SELECT COUNT(1)
|
||||||
|
FROM sgede_cronograma_matricula c
|
||||||
|
WHERE c.cod_localidad = m.cod_localidad
|
||||||
|
AND c.num_matricula = m.num_matricula
|
||||||
|
AND c.num_refinanciamiento = m.num_refinanciamiento
|
||||||
|
AND c.cod_tipo_cuota = 'ARM'
|
||||||
|
AND c.cod_estado = 'CAN') AS num_cuotas_pagadas,
|
||||||
|
(SELECT COUNT(1)
|
||||||
|
FROM sgede_cronograma_matricula c
|
||||||
|
WHERE c.cod_localidad = m.cod_localidad
|
||||||
|
AND c.num_matricula = m.num_matricula
|
||||||
|
AND c.num_refinanciamiento = m.num_refinanciamiento
|
||||||
|
AND c.cod_tipo_cuota = 'ARM'
|
||||||
|
AND c.cod_estado <> 'CAN'
|
||||||
|
AND c.fch_vencimiento < CAST(GETDATE() AS DATE)) AS num_cuotas_vencidas,
|
||||||
|
(SELECT MIN(c.fch_vencimiento)
|
||||||
|
FROM sgede_cronograma_matricula c
|
||||||
|
WHERE c.cod_localidad = m.cod_localidad
|
||||||
|
AND c.num_matricula = m.num_matricula
|
||||||
|
AND c.num_refinanciamiento = m.num_refinanciamiento
|
||||||
|
AND c.cod_tipo_cuota = 'ARM'
|
||||||
|
AND c.cod_estado <> 'CAN'
|
||||||
|
AND c.fch_vencimiento < CAST(GETDATE() AS DATE)) AS fch_venc_mas_antigua,
|
||||||
|
(SELECT SUM(c.imp_total_pagado)
|
||||||
|
FROM sgede_cronograma_matricula c
|
||||||
|
WHERE c.cod_localidad = m.cod_localidad
|
||||||
|
AND c.num_matricula = m.num_matricula
|
||||||
|
AND c.num_refinanciamiento = m.num_refinanciamiento) AS imp_total_pagado,
|
||||||
|
ISNULL((SELECT SUM(c.imp_total - ISNULL(c.imp_dscto, 0))
|
||||||
|
FROM sgede_cronograma_matricula c
|
||||||
|
WHERE c.cod_localidad = m.cod_localidad
|
||||||
|
AND c.num_matricula = m.num_matricula
|
||||||
|
AND c.num_refinanciamiento = 1), 0) AS inv_neta
|
||||||
|
FROM sgeca_matricula m
|
||||||
|
INNER JOIN sgema_alumno a ON m.cod_alumno = a.cod_alumno
|
||||||
|
WHERE m.num_matricula IN ({placeholders})
|
||||||
|
"""
|
||||||
|
cursor.execute(sql, mats)
|
||||||
|
cols = [c[0] for c in cursor.description]
|
||||||
|
filas = [dict(zip(cols, row)) for row in cursor.fetchall()]
|
||||||
|
conn.close()
|
||||||
|
out = {}
|
||||||
|
for f in filas:
|
||||||
|
out[str(f.get("num_matricula", "")).strip()] = {
|
||||||
|
"dsc_vendedor": str(f.get("dsc_vendedor") or "").strip(),
|
||||||
|
"cod_detalle": str(f.get("cod_detalle") or "").strip(),
|
||||||
|
"dsc_alumno": str(f.get("dsc_alumno") or "").strip(),
|
||||||
|
"num_cuotas_pagadas": int(f.get("num_cuotas_pagadas") or 0),
|
||||||
|
"num_cuotas_vencidas": int(f.get("num_cuotas_vencidas") or 0),
|
||||||
|
"fch_venc_mas_antigua": str(f.get("fch_venc_mas_antigua") or "")[:10],
|
||||||
|
"imp_total_pagado": float(f.get("imp_total_pagado") or 0),
|
||||||
|
"inv_neta": float(f.get("inv_neta") or 0),
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error traer_vendedor_cod_detalle: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
def limpiar_cache_cobranza(self, ano=None):
|
def limpiar_cache_cobranza(self, ano=None):
|
||||||
"""Vacía el caché anual de cobranza para forzar re-lectura desde SQL.
|
"""Vacía el caché anual de cobranza para forzar re-lectura desde SQL.
|
||||||
Se llama en cada refresco (manual o ciclo de 15 min)."""
|
Se llama en cada refresco (manual o ciclo de 15 min)."""
|
||||||
|
|||||||
Reference in New Issue
Block a user