Actualizar frontend/src/pages/Cobranza.jsx
This commit is contained in:
@@ -339,8 +339,8 @@ export default function Cobranza() {
|
|||||||
// al menos un grupo expandido, o siempre en PROGRAMA.
|
// al menos un grupo expandido, o siempre en PROGRAMA.
|
||||||
const conColsExtra = esPrograma || (expandidas.size > 0);
|
const conColsExtra = esPrograma || (expandidas.size > 0);
|
||||||
const headers = !conColsExtra
|
const headers = !conColsExtra
|
||||||
? ["GRUPO","CTA X COB. MES ANT.","COB. MES ANT.","% ANT","CTA X COB EN CURSO","COB. EN CURSO","% MES","CTA X COB TOTAL","COB. TOTAL","% TOTAL","SALDO","SALDO POR RETIRAR","OPC."]
|
? ["GRUPO","CTA X COB. MES ANT.","COB. MES ANT.","% ANT","CTA X COB EN CURSO","COB. EN CURSO","% MES","CTA X COB TOTAL","COB. TOTAL","% TOTAL","SALDO","RETIROS","DIFERENCIA","OPC."]
|
||||||
: [(esPrograma?"PROGRAMA":"GRUPO"),"FREC.","N° CUOTA","VENCIMIENTO","CTA X COB. MES ANT.","COB. MES ANT.","% ANT","CTA X COB EN CURSO","COB. EN CURSO","% MES","CTA X COB TOTAL","COB. TOTAL","% TOTAL","SALDO","SALDO POR RETIRAR","OPC."];
|
: [(esPrograma?"PROGRAMA":"GRUPO"),"FREC.","N° CUOTA","VENCIMIENTO","CTA X COB. MES ANT.","COB. MES ANT.","% ANT","CTA X COB EN CURSO","COB. EN CURSO","% MES","CTA X COB TOTAL","COB. TOTAL","% TOTAL","SALDO","RETIROS","DIFERENCIA","OPC."];
|
||||||
|
|
||||||
// Anchos para columnas redimensionables (1ª columna más ancha, resto estándar)
|
// Anchos para columnas redimensionables (1ª columna más ancha, resto estándar)
|
||||||
const anchosCob = headers.map((h, i) => i === 0 ? (conColsExtra ? 280 : 200) : (h === "FREC." || h.startsWith("%") || h === "OPC." ? 80 : 120));
|
const anchosCob = headers.map((h, i) => i === 0 ? (conColsExtra ? 280 : 200) : (h === "FREC." || h.startsWith("%") || h === "OPC." ? 80 : 120));
|
||||||
@@ -598,7 +598,7 @@ export default function Cobranza() {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{datos.map((fRaw, ri) => {
|
{datos.map((fRaw, ri) => {
|
||||||
const f = filaLayout(fRaw); // layout normalizado (15 o 12 cols)
|
const f = filaLayout(fRaw); // layout normalizado (15 o 12 cols)
|
||||||
const nCols = headers.length - 1; // sin OPC
|
const nCols = headers.length - 2; // sin RETIROS (calculada) ni OPC
|
||||||
const sedeKey = String(f[0]).toUpperCase();
|
const sedeKey = String(f[0]).toUpperCase();
|
||||||
const abiertaSede = !esPrograma && expandidas.has(sedeKey);
|
const abiertaSede = !esPrograma && expandidas.has(sedeKey);
|
||||||
// Hijos a mostrar al expandir: en SEDE los programas de la sede;
|
// Hijos a mostrar al expandir: en SEDE los programas de la sede;
|
||||||
@@ -609,7 +609,14 @@ export default function Cobranza() {
|
|||||||
const av = !String(fila[LAY.ant[2]]||"").trim() || ["-","—"].includes(String(fila[LAY.ant[2]]).trim());
|
const av = !String(fila[LAY.ant[2]]||"").trim() || ["-","—"].includes(String(fila[LAY.ant[2]]).trim());
|
||||||
const mv = !String(fila[LAY.mes[2]]||"").trim() || ["-","—"].includes(String(fila[LAY.mes[2]]).trim());
|
const mv = !String(fila[LAY.mes[2]]||"").trim() || ["-","—"].includes(String(fila[LAY.mes[2]]).trim());
|
||||||
const tv = !String(fila[LAY.tot[2]]||"").trim() || ["-","—"].includes(String(fila[LAY.tot[2]]).trim());
|
const tv = !String(fila[LAY.tot[2]]||"").trim() || ["-","—"].includes(String(fila[LAY.tot[2]]).trim());
|
||||||
return fila.slice(0, nCols).map((v, ci) => {
|
// RETIROS (calculada) = SALDO − SALDO POR RETIRAR de la propia fila.
|
||||||
|
const retiros = toMonto(fila[LAY.saldo]) - toMonto(fila[LAY.saldoRet]);
|
||||||
|
const celdaRetiros = (
|
||||||
|
<td key="retiros" style={{textAlign:"center",whiteSpace:"nowrap",color:"#7c3aed",fontWeight:700, backgroundColor: bg}}>
|
||||||
|
{"S/ " + retiros.toLocaleString("es-PE",{maximumFractionDigits:0})}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
const celdas = fila.slice(0, nCols).map((v, ci) => {
|
||||||
const isPct = LAY.ant[2]===ci || LAY.mes[2]===ci || LAY.tot[2]===ci;
|
const isPct = LAY.ant[2]===ci || LAY.mes[2]===ci || LAY.tot[2]===ci;
|
||||||
let val = v;
|
let val = v;
|
||||||
if ((ci===LAY.ant[0]||ci===LAY.ant[1]||ci===LAY.ant[2]) && av) val = "—";
|
if ((ci===LAY.ant[0]||ci===LAY.ant[1]||ci===LAY.ant[2]) && av) val = "—";
|
||||||
@@ -642,6 +649,7 @@ export default function Cobranza() {
|
|||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
return [...celdas, celdaRetiros];
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -688,6 +696,7 @@ export default function Cobranza() {
|
|||||||
if (ci===LAY.tot[2]) return ratio(T.oTot,T.cTot);
|
if (ci===LAY.tot[2]) return ratio(T.oTot,T.cTot);
|
||||||
if (ci===LAY.saldo) return money(T.saldo);
|
if (ci===LAY.saldo) return money(T.saldo);
|
||||||
if (ci===LAY.saldoRet) return money(T.saldoRet);
|
if (ci===LAY.saldoRet) return money(T.saldoRet);
|
||||||
|
if (ci===LAY.saldoRet+1) return money(T.saldo - T.saldoRet); // RETIROS (calculada)
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@@ -703,10 +712,12 @@ export default function Cobranza() {
|
|||||||
boxShadow:`inset -1px 0 0 0 #cbd5e1${ci===FIJAS_MAIN-1?", 3px 0 5px -2px rgba(0,0,0,0.18)":""}, inset 0 1px 0 0 #cbd5e1` }
|
boxShadow:`inset -1px 0 0 0 #cbd5e1${ci===FIJAS_MAIN-1?", 3px 0 5px -2px rgba(0,0,0,0.18)":""}, inset 0 1px 0 0 #cbd5e1` }
|
||||||
: { position:"sticky", bottom:0, zIndex:9, backgroundColor:"#eff6ff",
|
: { position:"sticky", bottom:0, zIndex:9, backgroundColor:"#eff6ff",
|
||||||
boxShadow:"inset 0 1px 0 0 #cbd5e1" };
|
boxShadow:"inset 0 1px 0 0 #cbd5e1" };
|
||||||
|
const esRetiros = ci===LAY.saldoRet+1;
|
||||||
return (
|
return (
|
||||||
<td key={ci} className={ci===0?"col-name":""}
|
<td key={ci} className={ci===0?"col-name":""}
|
||||||
style={{textAlign: ci===0?"left":"center",whiteSpace:"nowrap",
|
style={{textAlign: ci===0?"left":"center",whiteSpace:"nowrap",
|
||||||
color: ci===LAY.saldoRet ? "#dc2626" : undefined, ...st}}>
|
color: ci===LAY.saldoRet ? "#dc2626" : (esRetiros ? "#7c3aed" : undefined),
|
||||||
|
fontWeight: esRetiros ? 700 : undefined, ...st}}>
|
||||||
{isPct ? <MiniBar valStr={val} /> : val}
|
{isPct ? <MiniBar valStr={val} /> : val}
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
@@ -841,8 +852,8 @@ function Tarjetas({ kpis, retiro, loading }) {
|
|||||||
<Row k="Cta x Cobrar" v={`S/ ${cta.toLocaleString("es-PE",{maximumFractionDigits:0})}`} />
|
<Row k="Cta x Cobrar" v={`S/ ${cta.toLocaleString("es-PE",{maximumFractionDigits:0})}`} />
|
||||||
<Row k="Cobrado" v={`S/ ${cob.toLocaleString("es-PE",{maximumFractionDigits:0})}`} />
|
<Row k="Cobrado" v={`S/ ${cob.toLocaleString("es-PE",{maximumFractionDigits:0})}`} />
|
||||||
<Row k="Saldo" v={`S/ ${saldo.toLocaleString("es-PE",{maximumFractionDigits:0})}`} bold color={saldo>0?"#dc2626":"#10b981"} />
|
<Row k="Saldo" v={`S/ ${saldo.toLocaleString("es-PE",{maximumFractionDigits:0})}`} bold color={saldo>0?"#dc2626":"#10b981"} />
|
||||||
<Row k="Saldo por Retirar" v={`S/ ${(saldoRetiro||0).toLocaleString("es-PE",{maximumFractionDigits:0})}`} color="#dc2626" />
|
<Row k="Retiros" v={`S/ ${(saldoRetiro||0).toLocaleString("es-PE",{maximumFractionDigits:0})}`} color="#dc2626" />
|
||||||
<Row k="Retiros" v={`S/ ${(saldo-(saldoRetiro||0)).toLocaleString("es-PE",{maximumFractionDigits:0})}`} bold color="#7c3aed" />
|
<Row k="Diferencia" v={`S/ ${(saldo-(saldoRetiro||0)).toLocaleString("es-PE",{maximumFractionDigits:0})}`} bold color="#7c3aed" />
|
||||||
{cta>0 && (
|
{cta>0 && (
|
||||||
<div style={{marginTop:10}}>
|
<div style={{marginTop:10}}>
|
||||||
<div style={{display:"flex",justifyContent:"space-between",marginBottom:4}}>
|
<div style={{display:"flex",justifyContent:"space-between",marginBottom:4}}>
|
||||||
@@ -892,8 +903,8 @@ function TarjetasResumen({ r, retiro }) {
|
|||||||
<Row k="Cta x Cobrar" v={`S/ ${cta.toLocaleString("es-PE",{maximumFractionDigits:0})}`} />
|
<Row k="Cta x Cobrar" v={`S/ ${cta.toLocaleString("es-PE",{maximumFractionDigits:0})}`} />
|
||||||
<Row k="Cobrado" v={`S/ ${cob.toLocaleString("es-PE",{maximumFractionDigits:0})}`} />
|
<Row k="Cobrado" v={`S/ ${cob.toLocaleString("es-PE",{maximumFractionDigits:0})}`} />
|
||||||
<Row k="Saldo" v={`S/ ${saldo.toLocaleString("es-PE",{maximumFractionDigits:0})}`} bold color={saldo>0?"#dc2626":"#10b981"} />
|
<Row k="Saldo" v={`S/ ${saldo.toLocaleString("es-PE",{maximumFractionDigits:0})}`} bold color={saldo>0?"#dc2626":"#10b981"} />
|
||||||
<Row k="Saldo por Retirar" v={`S/ ${(saldoRetiro||0).toLocaleString("es-PE",{maximumFractionDigits:0})}`} color="#dc2626" />
|
<Row k="Retiros" v={`S/ ${(saldoRetiro||0).toLocaleString("es-PE",{maximumFractionDigits:0})}`} color="#dc2626" />
|
||||||
<Row k="Retiros" v={`S/ ${(saldo-(saldoRetiro||0)).toLocaleString("es-PE",{maximumFractionDigits:0})}`} bold color="#7c3aed" />
|
<Row k="Diferencia" v={`S/ ${(saldo-(saldoRetiro||0)).toLocaleString("es-PE",{maximumFractionDigits:0})}`} bold color="#7c3aed" />
|
||||||
{cta>0 && (
|
{cta>0 && (
|
||||||
<div style={{marginTop:8}}>
|
<div style={{marginTop:8}}>
|
||||||
<div style={{display:"flex",justifyContent:"space-between",marginBottom:4}}>
|
<div style={{display:"flex",justifyContent:"space-between",marginBottom:4}}>
|
||||||
@@ -1136,7 +1147,7 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, progFiltro
|
|||||||
};
|
};
|
||||||
useEffect(() => { cargarPorRetirar(); }, [ano, mes]);
|
useEffect(() => { cargarPorRetirar(); }, [ano, mes]);
|
||||||
|
|
||||||
const COLS = ["PROGRAMA","ALUMNO","N° CUOTA","F. VENC.","CTA ANT.","COB ANT.","% ANT","CTA MES","COB MES","% MES","CTA TOT","COB TOT","% TOT","SALDO","INFO"];
|
const COLS = ["PROGRAMA","ALUMNO","N° CUOTA","F. VENC.","CTA ANT.","COB ANT.","% ANT","CTA MES","COB MES","% MES","CTA TOT","COB TOT","% TOT","SALDO","DIFERENCIA","INFO"];
|
||||||
|
|
||||||
// Alumno seleccionado para el pop-up de INFO (estado + observaciones)
|
// Alumno seleccionado para el pop-up de INFO (estado + observaciones)
|
||||||
const [infoAlumno, setInfoAlumno] = useState(null);
|
const [infoAlumno, setInfoAlumno] = useState(null);
|
||||||
@@ -1256,7 +1267,7 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, progFiltro
|
|||||||
Ver solo por retirar
|
Ver solo por retirar
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="table-wrap" style={{overflow:"auto", maxHeight:"60vh"}}>
|
<div className="table-wrap" style={{overflow:"auto", maxHeight:"52vh"}}>
|
||||||
<table className="cob-table" {...cols.tableProps}>
|
<table className="cob-table" {...cols.tableProps}>
|
||||||
<cols.ColGroup />
|
<cols.ColGroup />
|
||||||
<thead><tr>{COLS.map((c,i)=>{
|
<thead><tr>{COLS.map((c,i)=>{
|
||||||
@@ -1264,7 +1275,7 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, progFiltro
|
|||||||
const stH = fija
|
const stH = fija
|
||||||
? stickyCol(i,{esHeader:true,bg:"#334155"})
|
? stickyCol(i,{esHeader:true,bg:"#334155"})
|
||||||
: { position:"sticky", top:0, zIndex:12, backgroundColor:"#334155" };
|
: { position:"sticky", top:0, zIndex:12, backgroundColor:"#334155" };
|
||||||
const ordenable = i !== 14;
|
const ordenable = i !== 14 && i !== 15; // RETIROS (14, calculada) e INFO (15) no ordenables
|
||||||
return <th key={c} onClick={()=>clickOrden(i)}
|
return <th key={c} onClick={()=>clickOrden(i)}
|
||||||
style={{...stH, position:stH.position||"relative", cursor: ordenable?"pointer":"default", userSelect:"none"}}>
|
style={{...stH, position:stH.position||"relative", cursor: ordenable?"pointer":"default", userSelect:"none"}}>
|
||||||
{c}{ordenable && _flecha(ordCol===i, ordDir)}<cols.Resizer index={i} /></th>;
|
{c}{ordenable && _flecha(ordCol===i, ordDir)}<cols.Resizer index={i} /></th>;
|
||||||
@@ -1282,6 +1293,9 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, progFiltro
|
|||||||
const totVacio = !String(f[12]||"").trim() || ["-","—"].includes(String(f[12]).trim());
|
const totVacio = !String(f[12]||"").trim() || ["-","—"].includes(String(f[12]).trim());
|
||||||
const programa = f[14] || "-"; // programa del alumno (extra del backend)
|
const programa = f[14] || "-"; // programa del alumno (extra del backend)
|
||||||
const retira = porRetirar.has(String(f[0])); // alumno POR RETIRAR
|
const retira = porRetirar.has(String(f[0])); // alumno POR RETIRAR
|
||||||
|
// RETIROS (calculada) = SALDO − SALDO POR RETIRAR. Si el alumno está POR
|
||||||
|
// RETIRAR, todo su saldo es "por retirar" ⇒ RETIROS = 0; si no, = su saldo.
|
||||||
|
const retirosFila = retira ? 0 : saldo;
|
||||||
return (
|
return (
|
||||||
<tr key={i} className={deuda?"row-deuda":""}
|
<tr key={i} className={deuda?"row-deuda":""}
|
||||||
style={retira ? { textDecoration:"line-through", textDecorationColor:"#dc2626",
|
style={retira ? { textDecoration:"line-through", textDecorationColor:"#dc2626",
|
||||||
@@ -1315,6 +1329,10 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, progFiltro
|
|||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{/* RETIROS (calculada) = SALDO − SALDO POR RETIRAR */}
|
||||||
|
<td style={{textAlign:"center",whiteSpace:"nowrap",color:"#7c3aed",fontWeight:700}}>
|
||||||
|
{"S/ " + retirosFila.toLocaleString("es-PE",{maximumFractionDigits:0})}
|
||||||
|
</td>
|
||||||
{/* Botón INFO por alumno (estado + observaciones) */}
|
{/* Botón INFO por alumno (estado + observaciones) */}
|
||||||
<td style={{textAlign:"center"}}>
|
<td style={{textAlign:"center"}}>
|
||||||
<button
|
<button
|
||||||
@@ -1337,6 +1355,10 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, progFiltro
|
|||||||
if (alumnos.length===0) return null;
|
if (alumnos.length===0) return null;
|
||||||
const sum = (j)=>alumnos.reduce((s,f)=>s+toMonto(f[j]),0);
|
const sum = (j)=>alumnos.reduce((s,f)=>s+toMonto(f[j]),0);
|
||||||
const t = {4:sum(4),5:sum(5),7:sum(7),8:sum(8),10:sum(10),11:sum(11),13:sum(13)};
|
const t = {4:sum(4),5:sum(5),7:sum(7),8:sum(8),10:sum(10),11:sum(11),13:sum(13)};
|
||||||
|
// RETIROS total (calculada) = SALDO total − SALDO POR RETIRAR total.
|
||||||
|
// SALDO POR RETIRAR = saldo de los alumnos marcados POR RETIRAR.
|
||||||
|
const saldoPorRetirarTot = alumnos.reduce((s,f)=> s + (porRetirar.has(String(f[0])) ? toMonto(f[13]) : 0), 0);
|
||||||
|
const retirosTot = t[13] - saldoPorRetirarTot;
|
||||||
const r = (cob,cta)=>cta>0?`${Math.round(cob/cta*100)}%`:"";
|
const r = (cob,cta)=>cta>0?`${Math.round(cob/cta*100)}%`:"";
|
||||||
const celda = (j)=>{
|
const celda = (j)=>{
|
||||||
if (j===0) return "TOTAL GENERAL";
|
if (j===0) return "TOTAL GENERAL";
|
||||||
@@ -1369,6 +1391,10 @@ function ModalEstadoCuenta({ grupo, ano, mes, sectorista, agrupacion, progFiltro
|
|||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{/* RETIROS total (calculada) */}
|
||||||
|
<td style={{position:"sticky",bottom:0,zIndex:8,backgroundColor:"#eff6ff",padding:"11px 8px",textAlign:"center",whiteSpace:"nowrap",color:"#7c3aed",fontWeight:700,boxShadow:"inset 0 1px 0 0 #cbd5e1"}}>
|
||||||
|
{"S/ " + retirosTot.toLocaleString("es-PE",{maximumFractionDigits:0})}
|
||||||
|
</td>
|
||||||
<td style={{position:"sticky",bottom:0,zIndex:8,backgroundColor:"#eff6ff",boxShadow:"inset 0 1px 0 0 #cbd5e1"}}></td>
|
<td style={{position:"sticky",bottom:0,zIndex:8,backgroundColor:"#eff6ff",boxShadow:"inset 0 1px 0 0 #cbd5e1"}}></td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user