# 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