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 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 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 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) ──
|
// ── Ordenamiento de tablas (detecta número/soles, fecha dd-mm-yyyy o texto) ──
|
||||||
function _valorOrden(v) {
|
function _valorOrden(v) {
|
||||||
@@ -356,13 +359,15 @@ export default function Ventas() {
|
|||||||
</>}
|
</>}
|
||||||
|
|
||||||
{modal && (
|
{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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ModalDetalle({ vendedor, ano, mes, onClose }) {
|
function ModalDetalle({ vendedor, ano, mes, sede = "TODOS", programa = "TODOS", onClose }) {
|
||||||
const [tipo, setTipo] = useState(TIPOS[0]);
|
const [tipo, setTipo] = useState(TIPOS[0]);
|
||||||
const [filas, setFilas] = useState([]);
|
const [filas, setFilas] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -378,15 +383,35 @@ function ModalDetalle({ vendedor, ano, mes, onClose }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let activo = true;
|
let activo = true;
|
||||||
setLoading(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); } })
|
.then((res) => { if (activo) { setFilas(res.filas || []); setLoading(false); } })
|
||||||
.catch(() => { if (activo) { setFilas([]); setLoading(false); } });
|
.catch(() => { if (activo) { setFilas([]); setLoading(false); } });
|
||||||
return () => { activo = false; };
|
return () => { activo = false; };
|
||||||
}, [vendedor, ano, mes, tipo]);
|
}, [vendedor, ano, mes, tipo, sede, programa]);
|
||||||
|
|
||||||
// Columnas redimensionables (igual que las tablas principales)
|
// Columnas redimensionables (igual que las tablas principales)
|
||||||
const colsDet = useColumnasAjustables([200, 260, 120, 130, 120, 120, 130, 120]);
|
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 (
|
return (
|
||||||
<Modal title={`👤 ${vendedor}`} onClose={onClose} width={1500}>
|
<Modal title={`👤 ${vendedor}`} onClose={onClose} width={1500}>
|
||||||
@@ -405,14 +430,23 @@ function ModalDetalle({ vendedor, ano, mes, onClose }) {
|
|||||||
</th>
|
</th>
|
||||||
))}</tr></thead>
|
))}</tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{filasOrd.map((f, i) => (
|
{filasOrd.map((f, i) => {
|
||||||
<tr key={i}>
|
const esTotal = String(f[0]).toUpperCase() === "TOTAL GENERAL";
|
||||||
{f.slice(0, HEADERS_DETALLE.length).map((v, j) => (
|
return (
|
||||||
<td key={j} className={j === 1 ? "col-name" : ""}
|
<tr key={i}>
|
||||||
style={{textAlign:"center", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{v}</td>
|
{IDX_DETALLE.map((idx, j) => {
|
||||||
))}
|
const base = { textAlign:"center", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis" };
|
||||||
</tr>
|
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>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>}
|
</div>}
|
||||||
|
|||||||
Reference in New Issue
Block a user