Actualizar frontend/src/pages/Cobranza.jsx
This commit is contained in:
@@ -155,9 +155,8 @@ export default function Cobranza() {
|
||||
}, [alumnos, esPrograma, sede, frec]);
|
||||
|
||||
function grupoDe(a) {
|
||||
if (agrupacion === "SEDE") return a.sede;
|
||||
if (agrupacion === "ASESOR") return ""; // el sectorista ya filtra; no hay sub-grupo por alumno
|
||||
return a.programa; // PROGRAMA
|
||||
// El programa detallado del alumno, en TODOS los filtros (Sede, Asesor, Programa).
|
||||
return a.programa;
|
||||
}
|
||||
|
||||
// Exportar el DETALLE DE ALUMNOS (rápido: usa el dataset ya cargado, sin N consultas)
|
||||
@@ -169,10 +168,7 @@ export default function Cobranza() {
|
||||
|
||||
const toDate = (v) => {
|
||||
if (!v || v === "-") return "";
|
||||
const s = String(v).trim().replace(/\//g,"-");
|
||||
const p = s.split("-");
|
||||
if (p.length === 3 && p[2].length === 4) return new Date(+p[2], +p[1]-1, +p[0]);
|
||||
return String(v);
|
||||
return String(v).trim();
|
||||
};
|
||||
const pct = (cob, cta) => cta > 0 ? cob/cta : null;
|
||||
|
||||
@@ -210,7 +206,6 @@ export default function Cobranza() {
|
||||
const st = cellStyle(c);
|
||||
if ([5,6,8,9,11,12,14].includes(c)) ws[ref] = { v:Number(v)||0, t:"n", z:moneyFmt, ...(st?{s:st}:{}) };
|
||||
else if ([7,10,13].includes(c)) ws[ref] = (v===null) ? { v:"-", t:"s", ...(st?{s:st}:{}) } : { v:Number(v), t:"n", z:pctFmt, ...(st?{s:st}:{}) };
|
||||
else if (c===4 && v instanceof Date) ws[ref] = { v, t:"d", z:dateFmt };
|
||||
else ws[ref] = { v: v ?? "", t:"s" };
|
||||
});
|
||||
});
|
||||
@@ -560,7 +555,11 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, onClose })
|
||||
return () => { activo = false; };
|
||||
}, [grupo, ano, mes, sectorista, agrupacion]);
|
||||
|
||||
const COLS = ["MATRÍCULA","ALUMNO","N° CUOTA","F. VENC.","CTA ANT.","COB ANT.","% ANT","CTA MES","COB MES","% MES","CTA TOT","COB TOT","% TOT","SALDO"];
|
||||
const COLS = ["PROGRAMA","ALUMNO","N° CUOTA","F. VENC.","CTA ANT.","COB ANT.","% ANT","CTA MES","COB MES","% MES","CTA TOT","COB TOT","% TOT","SALDO"];
|
||||
|
||||
// Columnas redimensionables (igual que la tabla principal): PROGRAMA y ALUMNO más anchas
|
||||
const anchosDet = COLS.map((h, i) => i === 0 ? 240 : (i === 1 ? 220 : (h.startsWith("%") ? 80 : 110)));
|
||||
const cols = useColumnasAjustables(anchosDet);
|
||||
|
||||
// Resumen para las 3 tarjetas (siempre visibles) — suma del detalle de alumnos
|
||||
const resumen = useMemo(() => {
|
||||
@@ -574,6 +573,17 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, onClose })
|
||||
return { cAnt,obAnt,cMes,obMes,cTot,obTot };
|
||||
}, [filas]);
|
||||
|
||||
// Conteo de alumnos: total, ya pagaron (saldo 0) y por pagar (saldo > 0)
|
||||
const conteo = useMemo(() => {
|
||||
let total=0, pagaron=0, deben=0;
|
||||
filas.forEach((f) => {
|
||||
if (String(f[0]).toUpperCase() === "TOTAL") return;
|
||||
total++;
|
||||
if (toMonto(f[13]) > 0.01) deben++; else pagaron++;
|
||||
});
|
||||
return { total, pagaron, deben };
|
||||
}, [filas]);
|
||||
|
||||
return (
|
||||
<Modal title={`📊 Estado de Cuenta — ${grupo}`} onClose={onClose} width={1500}>
|
||||
{loading ? <Loader text="Cargando estado de cuenta..." /> :
|
||||
@@ -582,9 +592,28 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, onClose })
|
||||
<div style={{marginBottom:16}}>
|
||||
<TarjetasResumen r={resumen} />
|
||||
</div>
|
||||
<div className="table-wrap">
|
||||
<table className="cob-table" style={{minWidth:1000}}>
|
||||
<thead><tr>{COLS.map((c)=><th key={c}>{c}</th>)}</tr></thead>
|
||||
{/* Etiquetas de conteo de alumnos */}
|
||||
<div style={{display:"flex",gap:10,marginBottom:14,flexWrap:"wrap"}}>
|
||||
<span style={{display:"inline-flex",alignItems:"center",gap:6,padding:"6px 14px",
|
||||
borderRadius:999,background:"#eff6ff",color:"#1e40af",fontSize:13,fontWeight:700,
|
||||
border:"1px solid #bfdbfe"}}>
|
||||
👥 Total: {conteo.total} alumno{conteo.total!==1?"s":""}
|
||||
</span>
|
||||
<span style={{display:"inline-flex",alignItems:"center",gap:6,padding:"6px 14px",
|
||||
borderRadius:999,background:"#ecfdf5",color:"#047857",fontSize:13,fontWeight:700,
|
||||
border:"1px solid #a7f3d0"}}>
|
||||
✅ Pagaron: {conteo.pagaron}
|
||||
</span>
|
||||
<span style={{display:"inline-flex",alignItems:"center",gap:6,padding:"6px 14px",
|
||||
borderRadius:999,background:"#fef2f2",color:"#b91c1c",fontSize:13,fontWeight:700,
|
||||
border:"1px solid #fecaca"}}>
|
||||
⏳ Por pagar: {conteo.deben}
|
||||
</span>
|
||||
</div>
|
||||
<div className="table-wrap" style={{overflowX:"auto"}}>
|
||||
<table className="cob-table" {...cols.tableProps}>
|
||||
<cols.ColGroup />
|
||||
<thead><tr>{COLS.map((c,i)=><th key={c} style={{position:"relative"}}>{c}<cols.Resizer index={i} /></th>)}</tr></thead>
|
||||
<tbody>
|
||||
{filas.filter((f)=>String(f[0]).toUpperCase()!=="TOTAL").map((f, i) => {
|
||||
const saldo = toMonto(f[13]);
|
||||
@@ -593,10 +622,12 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, onClose })
|
||||
const antVacio = !String(f[6]||"").trim() || ["-","—"].includes(String(f[6]).trim());
|
||||
const mesVacio = !String(f[9]||"").trim() || ["-","—"].includes(String(f[9]).trim());
|
||||
const totVacio = !String(f[12]||"").trim() || ["-","—"].includes(String(f[12]).trim());
|
||||
const programa = f[14] || "-"; // programa del alumno (extra del backend)
|
||||
return (
|
||||
<tr key={i} className={deuda?"row-deuda":""}>
|
||||
{f.map((v, j) => {
|
||||
let val = v;
|
||||
{f.slice(0, 14).map((v, j) => {
|
||||
// Columna 0: mostrar PROGRAMA en vez de la matrícula
|
||||
let val = (j===0) ? programa : v;
|
||||
if ((j===4||j===5||j===6) && antVacio) val = "—";
|
||||
if ((j===7||j===8||j===9) && mesVacio) val = "—";
|
||||
if ((j===10||j===11||j===12) && totVacio) val = "—";
|
||||
@@ -606,11 +637,15 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, onClose })
|
||||
if ([4,5,6].includes(j)) cart = "cart-ant";
|
||||
else if ([7,8,9].includes(j)) cart = "cart-mes";
|
||||
else if ([10,11,12].includes(j)) cart = "cart-tot";
|
||||
const clss = ((j===1?"col-name":"") + (cart?` ${cart}`:"")).trim();
|
||||
const clss = (((j===0||j===1)?"col-name":"") + (cart?` ${cart}`:"")).trim();
|
||||
const esTexto = (j===0||j===1);
|
||||
return (
|
||||
<td key={j} className={clss}
|
||||
style={{textAlign:j===1?"left":(isMonto?"right":"center"),
|
||||
title={esTexto ? String(val) : undefined}
|
||||
style={{textAlign:esTexto?"left":(isMonto?"right":"center"),
|
||||
whiteSpace:"nowrap",
|
||||
overflow:esTexto?"hidden":undefined,
|
||||
textOverflow:esTexto?"ellipsis":undefined,
|
||||
color:j===13&&deuda?"#dc2626":undefined,
|
||||
fontWeight:j===13?700:undefined}}>
|
||||
{isPct ? (val==="—"?<span style={{color:"#cbd5e1"}}>—</span>:<MiniBar valStr={val} />) : fmtMoneda(val)}
|
||||
|
||||
Reference in New Issue
Block a user