Actualizar frontend/src/pages/Ventas.jsx
This commit is contained in:
@@ -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>}
|
||||
|
||||
Reference in New Issue
Block a user