Files
dashboard-completo/backend/modules/saldo_pendiente/processor.py
2026-06-27 11:58:05 -05:00

162 lines
6.8 KiB
Python

# modules/saldo_pendiente/processor.py
from datetime import datetime
class SaldoProcessor:
"""
Procesador de lógica de negocio para Saldos Pendientes.
Filtra estrictamente por estado ALU/PRE, saldos positivos,
vendedores permitidos y FECHA DE VENCIMIENTO (No mostrar futuro).
"""
def __init__(self, data_manager):
self.data_manager = data_manager
def obtener_datos_procesados(self, ano, mes, tipo_cuota):
"""
Recupera datos crudos y aplica los filtros de negocio.
"""
# 1. Traer la data cruda desde DataManager
raw_data = self.data_manager.ejecutar_consulta_saldos_pendientes(ano, mes)
datos_limpios = []
# 2. Obtener fecha actual para saber qué es "futuro"
ahora = datetime.now()
ano_actual = ahora.year
mes_actual = ahora.month
# 3. Cargar la "Lista Blanca" de vendedores desde el JSON
config = self.data_manager.config_data
lista_raw = config.get("lista_pendientes", [])
# RESPALDO DE EMERGENCIA
if not lista_raw:
lista_raw = [
"AGUILAR U. JUAN CARLOS",
"CHAVEZ P. DIANA",
"HUAMAN C. ALONSO AGUSTIN",
"LA ROSA C. VERONICA ASTRID",
"LAZARO Q. DIEGO ARTURO",
"MONTOYA D. CARMEN ISABEL",
"PERALTA C. ALMENDRA LUCIA"
]
# Normalizamos la lista
lista_permitidos = set([str(v).strip().upper() for v in lista_raw])
# 4. Mapeo de columnas según la selección
mapa_columnas = {
"1° Cuota": ("imp_saldo_cuota1", "fch_venc_cuota1"),
"2° Cuota": ("imp_saldo_cuota2", "fch_venc_cuota2"),
"3° Cuota": ("imp_saldo_cuota3", "fch_venc_cuota3"),
}
col_saldo_target, col_venc_target = mapa_columnas.get(tipo_cuota, (None, None))
if not col_saldo_target:
return []
for row in raw_data:
# =================================================================
# 🛑 FILTRO 1: VENDEDOR PERMITIDO
# =================================================================
vendedor_actual = str(row.get('dsc_vendedor', '')).strip().upper()
if lista_permitidos:
if vendedor_actual not in lista_permitidos:
# Búsqueda parcial por si hay errores de espacios
encontrado = False
for permitido in lista_permitidos:
v_norm = vendedor_actual.replace(" ", "")
p_norm = permitido.replace(" ", "")
if p_norm in v_norm:
encontrado = True
break
if not encontrado:
continue
# =================================================================
# 🛑 FILTRO 2: ESTADO (ALU/PRE)
# =================================================================
estado = str(row.get('estado_matricula', '')).strip().upper()
if estado not in ('ALU', 'PRE'):
continue
# =================================================================
# 🛑 FILTRO 3: FECHA DE VENCIMIENTO (NO MOSTRAR FUTURO)
# =================================================================
# Este filtro aplica PRINCIPALMENTE para 2° Cuota en adelante.
# (Aunque la lógica es válida para todas, la 1° suele ser inmediata).
if tipo_cuota != "1° Cuota":
fch_venc_raw = row.get(col_venc_target)
es_futuro = False
if fch_venc_raw:
try:
# Convertir a objeto fecha si es texto
if isinstance(fch_venc_raw, str):
# Asumimos formato SQL YYYY-MM-DD
f_obj = datetime.strptime(fch_venc_raw[:10], '%Y-%m-%d')
else:
f_obj = fch_venc_raw # Ya es datetime
venc_ano = f_obj.year
venc_mes = f_obj.month
# LÓGICA DE TIEMPO:
# Si el año de vencimiento es mayor al actual -> ES FUTURO
if venc_ano > ano_actual:
es_futuro = True
# Si es el mismo año, pero el mes es mayor al actual -> ES FUTURO
elif venc_ano == ano_actual and venc_mes > mes_actual:
es_futuro = True
except Exception as e:
# Si falla la fecha, asumimos que no es futuro para no ocultar por error
pass
# Si la cuota vence en el futuro (ej: Marzo cuando estamos en Febrero), LA OCULTAMOS.
if es_futuro:
continue
# =================================================================
# 🛑 FILTRO 4: SALDO > 0
# =================================================================
try:
val = row.get(col_saldo_target, 0)
saldo_a_mostrar = float(val) if val is not None else 0.0
except:
saldo_a_mostrar = 0.0
if saldo_a_mostrar <= 0.1:
continue
# --- FORMATEO PARA VISUALIZACIÓN ---
def format_date(val):
if not val: return ""
if isinstance(val, str): return val[:10]
if isinstance(val, datetime): return val.strftime('%d-%m-%Y')
return str(val)
f_inicio = format_date(row.get('fch_inicio'))
f_matr = format_date(row.get('fch_matricula'))
f_vencimiento = format_date(row.get(col_venc_target))
# --- CONSTRUCCIÓN DE LA FILA FINAL ---
fila = {
'MATRICULA': row.get('num_matricula'),
'VENDEDOR': row.get('dsc_vendedor'),
'ALUMNO': row.get('dsc_alumno'),
'PROGRAMA': row.get('dsc_promocion'),
'FECHA INICIO': f_inicio,
'FECHA MATR.': f_matr,
'SALDO MAT.': float(row.get('imp_saldo_matricula', 0)),
f'SALDO {tipo_cuota.upper()}': saldo_a_mostrar,
'VENCIMIENTO': f_vencimiento, # Aquí se verá la fecha (ej: 14-02-2026)
'INV. NETA': float(row.get('INV_NETA', 0)),
}
datos_limpios.append(fila)
return datos_limpios