Feat Se Añade Total a Canillas Page
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 1m48s

This commit is contained in:
2025-11-10 15:07:36 -03:00
parent 74f07df960
commit 7f1fadfc84

View File

@@ -88,7 +88,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
setLoadingFiltersDropdown(false); // Detiene el spinner de los filtros setLoadingFiltersDropdown(false); // Detiene el spinner de los filtros
return; return;
} }
setLoadingFiltersDropdown(true); setLoadingFiltersDropdown(true);
setError(null); setError(null);
try { try {
@@ -130,14 +130,14 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
const cargarMovimientos = useCallback(async () => { const cargarMovimientos = useCallback(async () => {
if (!puedeVer) { if (!puedeVer) {
setError("No tiene permiso para ver esta sección."); setError("No tiene permiso para ver esta sección.");
setLoading(false); setLoading(false);
return; return;
} }
if (!filtroFecha || !filtroIdCanillitaSeleccionado) { if (!filtroFecha || !filtroIdCanillitaSeleccionado) {
if (loading) setLoading(false); if (loading) setLoading(false);
setMovimientos([]); setMovimientos([]);
return; return;
} }
setLoading(true); setError(null); setApiErrorMessage(null); setLoading(true); setError(null); setApiErrorMessage(null);
try { try {
@@ -164,37 +164,37 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
useEffect(() => { useEffect(() => {
if (filtroFecha && filtroIdCanillitaSeleccionado) { if (filtroFecha && filtroIdCanillitaSeleccionado) {
cargarMovimientos(); cargarMovimientos();
} else { } else {
setMovimientos([]); setMovimientos([]);
if (loading) setLoading(false); if (loading) setLoading(false);
} }
}, [cargarMovimientos, filtroFecha, filtroIdCanillitaSeleccionado]); }, [cargarMovimientos, filtroFecha, filtroIdCanillitaSeleccionado]);
const handleOpenModal = (item?: EntradaSalidaCanillaDto) => { const handleOpenModal = (item?: EntradaSalidaCanillaDto) => {
if (!puedeCrear && !item) { if (!puedeCrear && !item) {
setApiErrorMessage("No tiene permiso para registrar nuevos movimientos."); setApiErrorMessage("No tiene permiso para registrar nuevos movimientos.");
return; return;
} }
if (item && !puedeModificar) { if (item && !puedeModificar) {
setApiErrorMessage("No tiene permiso para modificar movimientos."); setApiErrorMessage("No tiene permiso para modificar movimientos.");
return; return;
} }
if (item) { if (item) {
setEditingMovimiento(item); setEditingMovimiento(item);
setPrefillModalData(null); setPrefillModalData(null);
} else { } else {
const canillitaSeleccionado = destinatariosDropdown.find( const canillitaSeleccionado = destinatariosDropdown.find(
c => c.idCanilla === Number(filtroIdCanillitaSeleccionado) c => c.idCanilla === Number(filtroIdCanillitaSeleccionado)
); );
setEditingMovimiento(null); setEditingMovimiento(null);
setPrefillModalData({ setPrefillModalData({
fecha: filtroFecha, fecha: filtroFecha,
idCanilla: filtroIdCanillitaSeleccionado, idCanilla: filtroIdCanillitaSeleccionado,
nombreCanilla: canillitaSeleccionado?.nomApe, nombreCanilla: canillitaSeleccionado?.nomApe,
idPublicacion: filtroIdPublicacion idPublicacion: filtroIdPublicacion
}); });
} }
setApiErrorMessage(null); setApiErrorMessage(null);
setModalOpen(true); setModalOpen(true);
@@ -224,7 +224,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
}); });
}; };
const handleSelectAllForLiquidar = (event: React.ChangeEvent<HTMLInputElement>) => { const handleSelectAllForLiquidar = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.checked) { if (event.target.checked) {
const newSelectedIds = new Set(movimientos.filter(m => !m.liquidado).map(m => m.idParte)); const newSelectedIds = new Set(movimientos.filter(m => !m.liquidado).map(m => m.idParte));
setSelectedIdsParaLiquidar(newSelectedIds); setSelectedIdsParaLiquidar(newSelectedIds);
} else { } else {
@@ -248,17 +248,17 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
const fechaLiquidacionDate = new Date(fechaLiquidacionDialog + 'T00:00:00Z'); const fechaLiquidacionDate = new Date(fechaLiquidacionDialog + 'T00:00:00Z');
let fechaMovimientoMasReciente: Date | null = null; let fechaMovimientoMasReciente: Date | null = null;
selectedIdsParaLiquidar.forEach(idParte => { selectedIdsParaLiquidar.forEach(idParte => {
const movimiento = movimientos.find(m => m.idParte === idParte); const movimiento = movimientos.find(m => m.idParte === idParte);
if (movimiento && movimiento.fecha) { if (movimiento && movimiento.fecha) {
const movFecha = new Date(movimiento.fecha.split('T')[0] + 'T00:00:00Z'); const movFecha = new Date(movimiento.fecha.split('T')[0] + 'T00:00:00Z');
if (fechaMovimientoMasReciente === null || movFecha.getTime() > (fechaMovimientoMasReciente as Date).getTime()) { if (fechaMovimientoMasReciente === null || movFecha.getTime() > (fechaMovimientoMasReciente as Date).getTime()) {
fechaMovimientoMasReciente = movFecha; fechaMovimientoMasReciente = movFecha;
}
} }
}
}); });
if (fechaMovimientoMasReciente !== null && fechaLiquidacionDate.getTime() < (fechaMovimientoMasReciente as Date).getTime()) { if (fechaMovimientoMasReciente !== null && fechaLiquidacionDate.getTime() < (fechaMovimientoMasReciente as Date).getTime()) {
setApiErrorMessage(`La fecha de liquidación (${fechaLiquidacionDate.toLocaleDateString('es-AR', {timeZone: 'UTC'})}) no puede ser inferior a la fecha del movimiento más reciente a liquidar (${(fechaMovimientoMasReciente as Date).toLocaleDateString('es-AR', {timeZone: 'UTC'})}).`); setApiErrorMessage(`La fecha de liquidación (${fechaLiquidacionDate.toLocaleDateString('es-AR', { timeZone: 'UTC' })}) no puede ser inferior a la fecha del movimiento más reciente a liquidar (${(fechaMovimientoMasReciente as Date).toLocaleDateString('es-AR', { timeZone: 'UTC' })}).`);
return; return;
} }
setApiErrorMessage(null); setApiErrorMessage(null);
setLoading(true); setLoading(true);
@@ -277,9 +277,9 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
if (movimientoParaTicket && !movimientoParaTicket.canillaEsAccionista) { if (movimientoParaTicket && !movimientoParaTicket.canillaEsAccionista) {
console.log("Liquidación exitosa, generando ticket para canillita NO accionista:", movimientoParaTicket.idCanilla); console.log("Liquidación exitosa, generando ticket para canillita NO accionista:", movimientoParaTicket.idCanilla);
await handleImprimirTicketLiquidacion( await handleImprimirTicketLiquidacion(
movimientoParaTicket.idCanilla, movimientoParaTicket.idCanilla,
movimientoParaTicket.fecha, movimientoParaTicket.fecha,
false false
); );
} else if (movimientoParaTicket && movimientoParaTicket.canillaEsAccionista) { } else if (movimientoParaTicket && movimientoParaTicket.canillaEsAccionista) {
console.log("Liquidación exitosa para accionista. No se genera ticket automáticamente."); console.log("Liquidación exitosa para accionista. No se genera ticket automáticamente.");
@@ -344,9 +344,13 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
}; };
const displayData = movimientos.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); const displayData = movimientos.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
const totalARendirVisible = useMemo(() => const totalARendirVisible = useMemo(() =>
displayData.filter(m => !m.liquidado).reduce((sum, item) => sum + item.montoARendir, 0) displayData.filter(m => !m.liquidado).reduce((sum, item) => sum + item.montoARendir, 0)
, [displayData]); , [displayData]);
const montoARendirAll = useMemo(() =>
movimientos.reduce((sum, item) => sum + item.montoARendir, 0)
, [movimientos]);
if (!puedeVer) { if (!puedeVer) {
return ( return (
@@ -367,7 +371,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
<Paper sx={{ p: 2, mb: 2 }}> <Paper sx={{ p: 2, mb: 2 }}>
<Typography variant="h6" gutterBottom>Filtros <FilterListIcon fontSize="small" /></Typography> <Typography variant="h6" gutterBottom>Filtros <FilterListIcon fontSize="small" /></Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, alignItems: 'center', mb: 2 }}> <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, alignItems: 'center', mb: 2 }}>
<TextField label="Fecha" type="date" size="small" value={filtroFecha} <TextField label="Fecha" type="date" size="small" value={filtroFecha}
onChange={(e) => setFiltroFecha(e.target.value)} onChange={(e) => setFiltroFecha(e.target.value)}
InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }} InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }}
required required
@@ -398,9 +402,9 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
onChange={(e) => setFiltroIdCanillitaSeleccionado(e.target.value as number | string)} onChange={(e) => setFiltroIdCanillitaSeleccionado(e.target.value as number | string)}
> >
<MenuItem value=""><em>Seleccione uno</em></MenuItem> <MenuItem value=""><em>Seleccione uno</em></MenuItem>
{destinatariosDropdown.map(c => <MenuItem key={c.idCanilla} value={c.idCanilla}>{c.nomApe} {c.legajo ? `(Leg: ${c.legajo})`: ''}</MenuItem>)} {destinatariosDropdown.map(c => <MenuItem key={c.idCanilla} value={c.idCanilla}>{c.nomApe} {c.legajo ? `(Leg: ${c.legajo})` : ''}</MenuItem>)}
</Select> </Select>
{!filtroIdCanillitaSeleccionado && <Typography component="p" color="error" variant="caption" sx={{ml:1.5, fontSize:'0.65rem'}}>Selección obligatoria</Typography>} {!filtroIdCanillitaSeleccionado && <Typography component="p" color="error" variant="caption" sx={{ ml: 1.5, fontSize: '0.65rem' }}>Selección obligatoria</Typography>}
</FormControl> </FormControl>
<FormControl size="small" sx={{ minWidth: 180, flexGrow: 1 }} disabled={loadingFiltersDropdown}> <FormControl size="small" sx={{ minWidth: 180, flexGrow: 1 }} disabled={loadingFiltersDropdown}>
@@ -411,15 +415,15 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
</Select> </Select>
</FormControl> </FormControl>
</Box> </Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap:2 }}> <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 2 }}>
{puedeCrear && ( {puedeCrear && (
<Button <Button
variant="contained" variant="contained"
startIcon={<AddIcon />} startIcon={<AddIcon />}
onClick={() => handleOpenModal()} onClick={() => handleOpenModal()}
disabled={!filtroFecha || !filtroIdCanillitaSeleccionado} disabled={!filtroFecha || !filtroIdCanillitaSeleccionado}
> >
Registrar Movimiento Registrar Movimiento
</Button> </Button>
)} )}
{puedeLiquidar && numSelectedToLiquidate > 0 && movimientos.some(m => selectedIdsParaLiquidar.has(m.idParte) && !m.liquidado) && ( {puedeLiquidar && numSelectedToLiquidate > 0 && movimientos.some(m => selectedIdsParaLiquidar.has(m.idParte) && !m.liquidado) && (
@@ -429,32 +433,39 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
)} )}
</Box> </Box>
</Paper> </Paper>
{!filtroFecha && <Alert severity="info" sx={{my:1}}>Por favor, seleccione una fecha.</Alert>} {!filtroFecha && <Alert severity="info" sx={{ my: 1 }}>Por favor, seleccione una fecha.</Alert>}
{filtroFecha && !filtroIdCanillitaSeleccionado && <Alert severity="info" sx={{my:1}}>Por favor, seleccione un {filtroTipoDestinatario === 'canillitas' ? 'canillita' : 'accionista'}.</Alert>} {filtroFecha && !filtroIdCanillitaSeleccionado && <Alert severity="info" sx={{ my: 1 }}>Por favor, seleccione un {filtroTipoDestinatario === 'canillitas' ? 'canillita' : 'accionista'}.</Alert>}
{loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>} {loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>}
{error && !loading && !apiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{error}</Alert>} {error && !loading && !apiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{error}</Alert>}
{apiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{apiErrorMessage}</Alert>} {apiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{apiErrorMessage}</Alert>}
{loadingTicketPdf && ( <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', my: 2 }}> <CircularProgress size={20} sx={{ mr: 1 }} /> <Typography variant="body2">Cargando ticket...</Typography> </Box> )} {loadingTicketPdf && (<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', my: 2 }}> <CircularProgress size={20} sx={{ mr: 1 }} /> <Typography variant="body2">Cargando ticket...</Typography> </Box>)}
{!loading && movimientos.length > 0 && ( {!loading && movimientos.length > 0 && (
<Paper sx={{ p: 1.5, mb: 2, mt:1, backgroundColor: 'grey.100' }}> <Paper sx={{ p: 1.5, mb: 2, mt: 1, backgroundColor: 'grey.100' }}>
<Box sx={{display: 'flex', justifyContent: 'flex-end', alignItems: 'center'}}> <Box sx={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}>
<Typography variant="subtitle1" sx={{mr:2}}> <Typography variant="subtitle1" sx={{ mr: 1 }}>
Total a Liquidar: Total:
</Typography> </Typography>
<Typography variant="h6" sx={{fontWeight: 'bold', color: 'error.main'}}> <Typography variant="h6" sx={{ paddingRight: '5px', fontWeight: 'bold', color: 'text.main' }}>
{totalARendirVisible.toLocaleString('es-AR', { style: 'currency', currency: 'ARS' })} {montoARendirAll.toLocaleString('es-AR', { style: 'currency', currency: 'ARS' })}
</Typography> </Typography>
</Box> -
<Typography variant="subtitle1" sx={{ mr: 1, paddingLeft: '5px', }}>
Total a Liquidar:
</Typography>
<Typography variant="h6" sx={{ fontWeight: 'bold', color: totalARendirVisible > 0 ? 'error.main' : 'green' }}>
{totalARendirVisible.toLocaleString('es-AR', { style: 'currency', currency: 'ARS' })}
</Typography>
</Box>
</Paper> </Paper>
)} )}
{!loading && !error && puedeVer && filtroFecha && filtroIdCanillitaSeleccionado && ( {!loading && !error && puedeVer && filtroFecha && filtroIdCanillitaSeleccionado && (
<TableContainer component={Paper}> <TableContainer component={Paper}>
<Table size="small"> <Table size="small">
<TableHead> <TableHead>
<TableRow> <TableRow>
{puedeLiquidar && ( {puedeLiquidar && (
@@ -545,10 +556,10 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
</TableContainer> </TableContainer>
)} )}
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}> <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}>
{puedeModificar && selectedRow && !selectedRow.liquidado && ( {puedeModificar && selectedRow && !selectedRow.liquidado && (
<MenuItem onClick={() => { handleOpenModal(selectedRow); handleMenuClose(); }}><EditIcon fontSize="small" sx={{ mr: 1 }} /> Modificar</MenuItem>)} <MenuItem onClick={() => { handleOpenModal(selectedRow); handleMenuClose(); }}><EditIcon fontSize="small" sx={{ mr: 1 }} /> Modificar</MenuItem>)}
{selectedRow && selectedRow.liquidado && puedeLiquidar && ( {selectedRow && selectedRow.liquidado && puedeLiquidar && (
<MenuItem <MenuItem
onClick={() => { onClick={() => {
@@ -560,7 +571,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
selectedRow.canillaEsAccionista selectedRow.canillaEsAccionista
); );
} }
handleMenuClose(); handleMenuClose();
}} }}
disabled={loadingTicketPdf} disabled={loadingTicketPdf}
> >
@@ -572,9 +583,9 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
{selectedRow && ( {selectedRow && (
((!selectedRow.liquidado && puedeEliminar) || (selectedRow.liquidado && puedeEliminarLiquidados)) ((!selectedRow.liquidado && puedeEliminar) || (selectedRow.liquidado && puedeEliminarLiquidados))
) && ( ) && (
<MenuItem onClick={() => {if (selectedRow) handleDelete(selectedRow.idParte);}}> <MenuItem onClick={() => { if (selectedRow) handleDelete(selectedRow.idParte); }}>
<DeleteIcon fontSize="small" sx={{ mr: 1 }} /> <DeleteIcon fontSize="small" sx={{ mr: 1 }} />
Eliminar Eliminar
</MenuItem> </MenuItem>
)} )}
</Menu> </Menu>
@@ -589,7 +600,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => {
clearErrorMessage={() => setApiErrorMessage(null)} clearErrorMessage={() => setApiErrorMessage(null)}
/> />
<Dialog open={openLiquidarDialog} onClose={handleCloseLiquidarDialog}> <Dialog open={openLiquidarDialog} onClose={handleCloseLiquidarDialog}>
<DialogTitle>Confirmar Liquidación</DialogTitle> <DialogTitle>Confirmar Liquidación</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>