Estructura inicial del backend

This commit is contained in:
Panchito
2026-06-26 12:28:49 -05:00
commit c0ee88b153
62 changed files with 13144 additions and 0 deletions

221
modules/cobranza/logic.py Normal file
View File

@@ -0,0 +1,221 @@
# modules/cobranza/logic.py
import pandas as pd
from .processor import CobranzaProcessor
class CobranzaLogic:
def __init__(self, data_manager):
self.data_manager = data_manager
self.processor = CobranzaProcessor(data_manager)
def obtener_lista_sectoristas(self, ano, mes):
return self.processor.obtener_lista_sectoristas(ano, mes)
def obtener_datos_tabla(self, ano, mes, sectorista="TODOS", agrupacion="SEDE"):
datos_brutos = self.processor.obtener_datos_procesados(ano, mes, sectorista, agrupacion)
sheet_data = []
t_cta_ant = t_cta_cur = t_cta_tot = t_cob_ant = t_cob_cur = t_cob_tot = t_saldo = 0.0
for d in datos_brutos:
grupo = d.get('GRUPO', '')
frecuencia = d.get('FRECUENCIA', '-')
num_cuota = d.get('NUM_CUOTA', '-')
fecha_venc = d.get('FCH_VENC_MODA', '-')
cta_ant = float(d.get('CTA_COB_ANT', 0.0))
cta_cur = float(d.get('CTA_COB_MES_CURSO', 0.0))
cta_tot = float(d.get('TOTAL_CTA_COB', 0.0))
cob_ant = float(d.get('COB_ANT', 0.0))
r1 = float(d.get('RATIO_1', 0.0))
cob_cur = float(d.get('COB_MES_CURSO', 0.0))
r2 = float(d.get('RATIO_2', 0.0))
cob_tot = float(d.get('TOTAL_COBRADO', 0.0))
r3 = float(d.get('RATIO_3', 0.0))
saldo = float(d.get('SALDO', 0.0))
opciones = d.get('OPCIONES', ' ≡ ▼ ')
t_cta_ant += cta_ant
t_cta_cur += cta_cur
t_cta_tot += cta_tot
t_cob_ant += cob_ant
t_cob_cur += cob_cur
t_cob_tot += cob_tot
t_saldo += saldo
avance_ant = f"{r1:.0f}%" if cta_ant > 0 else ""
avance_cur = f"{r2:.0f}%" if cta_cur > 0 else ""
avance_tot = f"{r3:.0f}%" if cta_tot > 0 else ""
if agrupacion == "PROGRAMA":
fila = [
grupo, frecuencia, num_cuota, fecha_venc,
f"S/ {cta_ant:,.0f}", f"S/ {cob_ant:,.0f}", avance_ant,
f"S/ {cta_cur:,.0f}", f"S/ {cob_cur:,.0f}", avance_cur,
f"S/ {cta_tot:,.0f}", f"S/ {cob_tot:,.0f}", avance_tot,
f"S/ {saldo:,.0f}", opciones
]
else:
fila = [
grupo,
f"S/ {cta_ant:,.0f}", f"S/ {cob_ant:,.0f}", avance_ant,
f"S/ {cta_cur:,.0f}", f"S/ {cob_cur:,.0f}", avance_cur,
f"S/ {cta_tot:,.0f}", f"S/ {cob_tot:,.0f}", avance_tot,
f"S/ {saldo:,.0f}", opciones
]
sheet_data.append(fila)
r_tot_1 = (t_cob_ant / t_cta_ant) * 100 if t_cta_ant > 0 else 0.0
r_tot_2 = (t_cob_cur / t_cta_cur) * 100 if t_cta_cur > 0 else 0.0
r_tot_3 = (t_cob_tot / t_cta_tot) * 100 if t_cta_tot > 0 else 0.0
if agrupacion == "PROGRAMA":
fila_total = [
"TOTAL GENERAL", "-", "-", "-",
f"S/ {t_cta_ant:,.0f}", f"S/ {t_cob_ant:,.0f}", f"{r_tot_1:.0f}%",
f"S/ {t_cta_cur:,.0f}", f"S/ {t_cob_cur:,.0f}", f"{r_tot_2:.0f}%",
f"S/ {t_cta_tot:,.0f}", f"S/ {t_cob_tot:,.0f}", f"{r_tot_3:.0f}%",
f"S/ {t_saldo:,.0f}", ""
]
else:
fila_total = [
"TOTAL GENERAL",
f"S/ {t_cta_ant:,.0f}", f"S/ {t_cob_ant:,.0f}", f"{r_tot_1:.0f}%",
f"S/ {t_cta_cur:,.0f}", f"S/ {t_cob_cur:,.0f}", f"{r_tot_2:.0f}%",
f"S/ {t_cta_tot:,.0f}", f"S/ {t_cob_tot:,.0f}", f"{r_tot_3:.0f}%",
f"S/ {t_saldo:,.0f}", ""
]
sheet_data.append(fila_total)
return sheet_data
def obtener_detalle_programa_formateado(self, ano, mes, sectorista, programa):
datos_brutos = self.processor.obtener_detalle_programa(ano, mes, sectorista, programa)
sheet_data = []
t_cta_ant = t_cta_cur = t_cta_tot = t_cob_ant = t_cob_cur = t_cob_tot = t_saldo = 0.0
for d in datos_brutos:
mat = d.get('MATRICULA', '')
alumno = d.get('ALUMNO', '')
num_cuota = d.get('NUM_CUOTA', '-')
fch_venc = d.get('FCH_VENC', '-')
cta_ant = float(d.get('CTA_COB_ANT', 0.0))
cta_cur = float(d.get('CTA_COB_MES_CURSO', 0.0))
cta_tot = float(d.get('TOTAL_CTA_COB', 0.0))
cob_ant = float(d.get('COB_ANT', 0.0))
cob_cur = float(d.get('COB_MES_CURSO', 0.0))
cob_tot = float(d.get('TOTAL_COBRADO', 0.0))
saldo = float(d.get('SALDO', 0.0))
t_cta_ant += cta_ant; t_cta_cur += cta_cur; t_cta_tot += cta_tot
t_cob_ant += cob_ant; t_cob_cur += cob_cur; t_cob_tot += cob_tot; t_saldo += saldo
r1 = (cob_ant / cta_ant) * 100 if cta_ant > 0 else 0.0
r2 = (cob_cur / cta_cur) * 100 if cta_cur > 0 else 0.0
r3 = (cob_tot / cta_tot) * 100 if cta_tot > 0 else 0.0
avance_ant = f"{r1:.0f}%" if cta_ant > 0 else ""
avance_cur = f"{r2:.0f}%" if cta_cur > 0 else ""
avance_tot = f"{r3:.0f}%" if cta_tot > 0 else ""
fila = [
mat, alumno, num_cuota, fch_venc,
f"S/ {cta_ant:,.0f}", f"S/ {cob_ant:,.0f}", avance_ant,
f"S/ {cta_cur:,.0f}", f"S/ {cob_cur:,.0f}", avance_cur,
f"S/ {cta_tot:,.0f}", f"S/ {cob_tot:,.0f}", avance_tot,
f"S/ {saldo:,.0f}"
]
sheet_data.append(fila)
r_tot_1 = (t_cob_ant / t_cta_ant) * 100 if t_cta_ant > 0 else 0.0
r_tot_2 = (t_cob_cur / t_cta_cur) * 100 if t_cta_cur > 0 else 0.0
r_tot_3 = (t_cob_tot / t_cta_tot) * 100 if t_cta_tot > 0 else 0.0
fila_total = [
"TOTAL", "GENERAL", "-", "-",
f"S/ {t_cta_ant:,.0f}", f"S/ {t_cob_ant:,.0f}", f"{r_tot_1:.0f}%",
f"S/ {t_cta_cur:,.0f}", f"S/ {t_cob_cur:,.0f}", f"{r_tot_2:.0f}%",
f"S/ {t_cta_tot:,.0f}", f"S/ {t_cob_tot:,.0f}", f"{r_tot_3:.0f}%",
f"S/ {t_saldo:,.0f}"
]
sheet_data.append(fila_total)
return sheet_data
def exportar_detalle_excel(self, programa, headers, datos):
if not datos: raise ValueError("No hay datos para exportar")
prog_limpio = "".join([c if c.isalnum() else "_" for c in str(programa)])[:30]
archivo = f"Detalle_Cobranza_{prog_limpio}.xlsx"
df = pd.DataFrame(datos, columns=[h.replace('\n', ' ') for h in headers])
try:
with pd.ExcelWriter(archivo, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='Detalle')
except:
df.to_excel(archivo, index=False)
return archivo
def exportar_reporte_global(self, ano, mes, sectorista):
datos_brutos = self.processor.obtener_detalle_programa(ano, mes, sectorista, "TODOS")
if not datos_brutos: raise ValueError("No hay datos para exportar en este mes.")
filas_excel = []
for d in datos_brutos:
cta_ant = float(d.get('CTA_COB_ANT', 0.0))
cob_ant = float(d.get('COB_ANT', 0.0))
cta_cur = float(d.get('CTA_COB_MES_CURSO', 0.0))
cob_cur = float(d.get('COB_MES_CURSO', 0.0))
cta_tot = float(d.get('TOTAL_CTA_COB', 0.0))
cob_tot = float(d.get('TOTAL_COBRADO', 0.0))
filas_excel.append({
"ALUMNO": d.get('ALUMNO', ''),
"PROGRAMA": d.get('PROGRAMA', '-'),
"FRECUENCIA": d.get('FRECUENCIA', '-'),
"NUM CUOTA": d.get('NUM_CUOTA', '-'),
"FCH VENCIMIENTO": d.get('FCH_VENC', '-'),
"CUENTA PENDIENTE": cta_ant,
"COBRADO PENDIENTE": cob_ant,
"AVANCE % ": (cob_ant / cta_ant) if cta_ant > 0 else 0.0,
"CUENTA EN CURSO": cta_cur,
"COBRADO EN CURSO": cob_cur,
"AVANCE % ": (cob_cur / cta_cur) if cta_cur > 0 else 0.0,
"TOTAL CUENTA": cta_tot,
"TOTAL COBRADO": cob_tot,
"AVANCE %": (cob_tot / cta_tot) if cta_tot > 0 else 0.0,
"SALDO": float(d.get('SALDO', 0.0))
})
df = pd.DataFrame(filas_excel)
archivo = f"Reporte_Cobranza_Global_{ano}_{mes}.xlsx"
try:
with pd.ExcelWriter(archivo, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='Reporte Global')
worksheet = writer.sheets['Reporte Global']
from openpyxl.styles import PatternFill, Font, Alignment
fill_header = PatternFill(start_color="12345F", end_color="12345F", fill_type="solid")
font_header = Font(color="FFFFFF", bold=True)
for cell in worksheet[1]:
cell.fill = fill_header
cell.font = font_header
cell.alignment = Alignment(horizontal="center", vertical="center")
for row in range(2, len(filas_excel) + 2):
for col in [6, 7, 9, 10, 12, 13, 15]:
worksheet.cell(row=row, column=col).number_format = '"S/" #,##0.00'
for col in [8, 11, 14]:
worksheet.cell(row=row, column=col).number_format = '0%'
for col in worksheet.columns:
max_len = 0
col_letter = col[0].column_letter
for cell in col:
if cell.value:
max_len = max(max_len, len(str(cell.value)))
worksheet.column_dimensions[col_letter].width = min(max_len + 2, 40)
except Exception:
df.to_excel(archivo, index=False)
return archivo