Actualizar frontend/src/pages/Ventas.jsx

This commit is contained in:
2026-09-28 16:36:57 -05:00
parent 29621ab483
commit 012a4e14ec

View File

@@ -12,7 +12,10 @@ const SEDES = ["LIMA", "AREQUIPA", "PIURA", "TRUJILLO"];
const PROGRAMAS = ["SEMINARIOS", "OTROS", "TEAC", "TERC"];
const COLS = ["VENDEDOR","INSCRITOS","VENTA TOTAL","INSCRITOS P.C DEL MES","VENTA P.C DEL MES","INSCRITOS MES ANTERIOR","VENTA MES ANTERIOR","AVANCE P.C TOTAL","OPC."];
const TIPOS = ["Venta Inscritos", "Venta P.C", "Venta Pendientes"];
const HEADERS_DETALLE = ["VENDEDOR","ALUMNO","F. MATRÍCULA","F. CANCELACIÓN","TIPO","CUOTA PROM.","DESC. ESPECIAL","INV. NETA"];
const HEADERS_DETALLE = ["ALUMNO","PROGRAMA","F. MATRÍCULA","F. CANCELACIÓN","TIPO","CUOTA PROM.","DESC. ESPECIAL","INV. NETA"];
// Índice en la fila del backend para cada columna del detalle (arriba).
// Fila: [0]vend [1]alumno [2]f.mat [3]f.canc [4]tipo [5]cuota [6]desc [7]inv [8]programa
const IDX_DETALLE = [1, 8, 2, 3, 4, 5, 6, 7];
// ── Ordenamiento de tablas (detecta número/soles, fecha dd-mm-yyyy o texto) ──
function _valorOrden(v) {
@@ -356,13 +359,15 @@ export default function Ventas() {
</>}
{modal && (
<ModalDetalle vendedor={modal.vendedor} ano={ano} mes={mes} onClose={() => setModal(null)} />
<ModalDetalle vendedor={modal.vendedor} ano={ano} mes={mes}
sede={sedesKey || "TODOS"} programa={programasKey || "TODOS"}
onClose={() => setModal(null)} />
)}
</div>
);
}
function ModalDetalle({ vendedor, ano, mes, onClose }) {
function ModalDetalle({ vendedor, ano, mes, sede = "TODOS", programa = "TODOS", onClose }) {
const [tipo, setTipo] = useState(TIPOS[0]);
const [filas, setFilas] = useState([]);
const [loading, setLoading] = useState(true);
@@ -378,15 +383,35 @@ function ModalDetalle({ vendedor, ano, mes, onClose }) {
useEffect(() => {
let activo = true;
setLoading(true);
api.ventasDetalle(vendedor, ano, mes, tipo, "TODOS", "TODOS", true)
api.ventasDetalle(vendedor, ano, mes, tipo, sede, programa, true)
.then((res) => { if (activo) { setFilas(res.filas || []); setLoading(false); } })
.catch(() => { if (activo) { setFilas([]); setLoading(false); } });
return () => { activo = false; };
}, [vendedor, ano, mes, tipo]);
}, [vendedor, ano, mes, tipo, sede, programa]);
// Columnas redimensionables (igual que las tablas principales)
const colsDet = useColumnasAjustables([200, 260, 120, 130, 120, 120, 130, 120]);
const filasOrd = useMemo(() => _ordenarFilas(filas, ordCol, ordDir), [filas, ordCol, ordDir]);
const filasOrd = useMemo(() => {
const _n = (v) => { const n = parseFloat(String(v ?? "").replace("S/","").replace(/,/g,"").trim()); return isNaN(n)?0:n; };
const _m = (v) => "S/ " + Math.round(v).toLocaleString("es-PE");
// Ignorar el TOTAL que venga del backend y recalcularlo siempre desde los alumnos
// (al filtrar por sede/programa el backend NO envía la fila TOTAL).
const alumnos = filas.filter((f) => String(f[0]).toUpperCase() !== "TOTAL GENERAL");
const ordenadas = _ordenarFilas(alumnos, ordCol == null ? null : IDX_DETALLE[ordCol], ordDir);
if (alumnos.length === 0) return ordenadas;
// Fila TOTAL: INV.NETA[7] y DESC[6] suman; CUOTA PROM[5] promedio de los que tienen valor.
let sInv = 0, sDesc = 0, sCuota = 0, nCuota = 0;
alumnos.forEach((f) => {
sInv += _n(f[7]); sDesc += _n(f[6]);
const c = _n(f[5]); if (c > 0) { sCuota += c; nCuota += 1; }
});
const total = [];
total[0] = "TOTAL GENERAL";
total[5] = nCuota ? _m(sCuota / nCuota) : "";
total[6] = sDesc ? _m(sDesc) : "";
total[7] = _m(sInv);
return [...ordenadas, total];
}, [filas, ordCol, ordDir]);
return (
<Modal title={`👤 ${vendedor}`} onClose={onClose} width={1500}>
@@ -405,14 +430,23 @@ function ModalDetalle({ vendedor, ano, mes, onClose }) {
</th>
))}</tr></thead>
<tbody>
{filasOrd.map((f, i) => (
<tr key={i}>
{f.slice(0, HEADERS_DETALLE.length).map((v, j) => (
<td key={j} className={j === 1 ? "col-name" : ""}
style={{textAlign:"center", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{v}</td>
))}
</tr>
))}
{filasOrd.map((f, i) => {
const esTotal = String(f[0]).toUpperCase() === "TOTAL GENERAL";
return (
<tr key={i}>
{IDX_DETALLE.map((idx, j) => {
const base = { textAlign:"center", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis" };
if (esTotal) {
const st = { ...base, background:"#eff6ff", fontWeight:700, color:"#1e40af", borderTop:"2px solid #bfdbfe" };
return <td key={j} style={st}>{j === 0 ? "TOTAL GENERAL" : f[idx]}</td>;
}
return (
<td key={j} className={j === 0 ? "col-name" : ""} style={base}>{f[idx]}</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>}