|
|
|
|
@@ -1,9 +1,11 @@
|
|
|
|
|
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
|
|
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
|
|
|
import {
|
|
|
|
|
Box, Typography, TextField, Button, Paper, IconButton, Menu, MenuItem, Chip,
|
|
|
|
|
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TablePagination,
|
|
|
|
|
CircularProgress, Alert, FormControl, InputLabel, Select, FormControlLabel, Checkbox
|
|
|
|
|
Alert, FormControl, InputLabel, Select, FormControlLabel, Checkbox
|
|
|
|
|
} from '@mui/material';
|
|
|
|
|
import { DataGrid, type GridColDef, type GridRenderCellParams } from '@mui/x-data-grid';
|
|
|
|
|
import { esES } from '@mui/x-data-grid/locales';
|
|
|
|
|
|
|
|
|
|
import AddIcon from '@mui/icons-material/Add';
|
|
|
|
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
|
|
|
import EditIcon from '@mui/icons-material/Edit';
|
|
|
|
|
@@ -26,8 +28,8 @@ import type { TipoBobinaDto } from '../../models/dtos/Impresion/TipoBobinaDto';
|
|
|
|
|
import type { PlantaDto } from '../../models/dtos/Impresion/PlantaDto';
|
|
|
|
|
import type { EstadoBobinaDto } from '../../models/dtos/Impresion/EstadoBobinaDto';
|
|
|
|
|
import type { UpdateFechaRemitoLoteDto } from '../../models/dtos/Impresion/UpdateFechaRemitoLoteDto';
|
|
|
|
|
import StockBobinaFechaRemitoModal from '../../components/Modals/Impresion/StockBobinaFechaRemitoModal';
|
|
|
|
|
|
|
|
|
|
import StockBobinaFechaRemitoModal from '../../components/Modals/Impresion/StockBobinaFechaRemitoModal';
|
|
|
|
|
import StockBobinaIngresoFormModal from '../../components/Modals/Impresion/StockBobinaIngresoFormModal';
|
|
|
|
|
import StockBobinaEditFormModal from '../../components/Modals/Impresion/StockBobinaEditFormModal';
|
|
|
|
|
import StockBobinaCambioEstadoModal from '../../components/Modals/Impresion/StockBobinaCambioEstadoModal';
|
|
|
|
|
@@ -46,16 +48,23 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null);
|
|
|
|
|
|
|
|
|
|
// Estados de los filtros
|
|
|
|
|
// --- Estados de los filtros ---
|
|
|
|
|
const [filtroTipoBobina, setFiltroTipoBobina] = useState<number | string>('');
|
|
|
|
|
const [filtroNroBobina, setFiltroNroBobina] = useState('');
|
|
|
|
|
const [filtroPlanta, setFiltroPlanta] = useState<number | string>('');
|
|
|
|
|
const [filtroEstadoBobina, setFiltroEstadoBobina] = useState<number | string>('');
|
|
|
|
|
const [filtroRemito, setFiltroRemito] = useState('');
|
|
|
|
|
|
|
|
|
|
// Filtro Fechas Remito
|
|
|
|
|
const [filtroFechaHabilitado, setFiltroFechaHabilitado] = useState<boolean>(false);
|
|
|
|
|
const [filtroFechaDesde, setFiltroFechaDesde] = useState<string>(new Date().toISOString().split('T')[0]);
|
|
|
|
|
const [filtroFechaHasta, setFiltroFechaHasta] = useState<string>(new Date().toISOString().split('T')[0]);
|
|
|
|
|
|
|
|
|
|
// Nuevo Filtro: Fechas Estado
|
|
|
|
|
const [filtroFechaEstadoHabilitado, setFiltroFechaEstadoHabilitado] = useState<boolean>(false);
|
|
|
|
|
const [filtroFechaEstadoDesde, setFiltroFechaEstadoDesde] = useState<string>(new Date().toISOString().split('T')[0]);
|
|
|
|
|
const [filtroFechaEstadoHasta, setFiltroFechaEstadoHasta] = useState<string>(new Date().toISOString().split('T')[0]);
|
|
|
|
|
|
|
|
|
|
// Estados para datos de dropdowns
|
|
|
|
|
const [tiposBobina, setTiposBobina] = useState<TipoBobinaDto[]>([]);
|
|
|
|
|
const [plantas, setPlantas] = useState<PlantaDto[]>([]);
|
|
|
|
|
@@ -69,9 +78,7 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
const [loteModalOpen, setLoteModalOpen] = useState(false);
|
|
|
|
|
const [fechaRemitoModalOpen, setFechaRemitoModalOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
// Estados para la paginación y el menú de acciones
|
|
|
|
|
const [page, setPage] = useState(0);
|
|
|
|
|
const [rowsPerPage, setRowsPerPage] = useState(25);
|
|
|
|
|
// Menú de acciones
|
|
|
|
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
|
|
|
const [selectedBobinaForRowMenu, setSelectedBobinaForRowMenu] = useState<StockBobinaDto | null>(null);
|
|
|
|
|
|
|
|
|
|
@@ -82,8 +89,6 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
const puedeModificarDatos = isSuperAdmin || tienePermiso("IB004");
|
|
|
|
|
const puedeEliminar = isSuperAdmin || tienePermiso("IB005");
|
|
|
|
|
|
|
|
|
|
const lastOpenedMenuButtonRef = useRef<HTMLButtonElement | null>(null);
|
|
|
|
|
|
|
|
|
|
const fetchFiltersDropdownData = useCallback(async () => {
|
|
|
|
|
setLoadingFiltersDropdown(true);
|
|
|
|
|
try {
|
|
|
|
|
@@ -123,13 +128,18 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
idPlanta: filtroPlanta ? Number(filtroPlanta) : null,
|
|
|
|
|
idEstadoBobina: filtroEstadoBobina ? Number(filtroEstadoBobina) : null,
|
|
|
|
|
remitoFilter: filtroRemito || null,
|
|
|
|
|
// Fechas Remito
|
|
|
|
|
fechaDesde: filtroFechaHabilitado ? filtroFechaDesde : null,
|
|
|
|
|
fechaHasta: filtroFechaHabilitado ? filtroFechaHasta : null,
|
|
|
|
|
// Fechas Estado (Nuevos parametros, asegurar que el backend los reciba)
|
|
|
|
|
fechaEstadoDesde: filtroFechaEstadoHabilitado ? filtroFechaEstadoDesde : null,
|
|
|
|
|
fechaEstadoHasta: filtroFechaEstadoHabilitado ? filtroFechaEstadoHasta : null,
|
|
|
|
|
};
|
|
|
|
|
const data = await stockBobinaService.getAllStockBobinas(params);
|
|
|
|
|
setStock(data);
|
|
|
|
|
if (data.length === 0) {
|
|
|
|
|
setError("No se encontraron resultados con los filtros aplicados.");
|
|
|
|
|
// No setteamos error bloqueante, solo aviso visual si se desea, o dejar tabla vacía.
|
|
|
|
|
// setError("No se encontraron resultados con los filtros aplicados.");
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error(err);
|
|
|
|
|
@@ -137,10 +147,14 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, [puedeVer, filtroTipoBobina, filtroNroBobina, filtroPlanta, filtroEstadoBobina, filtroRemito, filtroFechaHabilitado, filtroFechaDesde, filtroFechaHasta]);
|
|
|
|
|
}, [
|
|
|
|
|
puedeVer,
|
|
|
|
|
filtroTipoBobina, filtroNroBobina, filtroPlanta, filtroEstadoBobina, filtroRemito,
|
|
|
|
|
filtroFechaHabilitado, filtroFechaDesde, filtroFechaHasta,
|
|
|
|
|
filtroFechaEstadoHabilitado, filtroFechaEstadoDesde, filtroFechaEstadoHasta
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const handleBuscarClick = () => {
|
|
|
|
|
setPage(0);
|
|
|
|
|
cargarStock();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -150,14 +164,19 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
setFiltroPlanta('');
|
|
|
|
|
setFiltroEstadoBobina('');
|
|
|
|
|
setFiltroRemito('');
|
|
|
|
|
|
|
|
|
|
setFiltroFechaHabilitado(false);
|
|
|
|
|
setFiltroFechaDesde(new Date().toISOString().split('T')[0]);
|
|
|
|
|
setFiltroFechaHasta(new Date().toISOString().split('T')[0]);
|
|
|
|
|
|
|
|
|
|
setFiltroFechaEstadoHabilitado(false);
|
|
|
|
|
setFiltroFechaEstadoDesde(new Date().toISOString().split('T')[0]);
|
|
|
|
|
setFiltroFechaEstadoHasta(new Date().toISOString().split('T')[0]);
|
|
|
|
|
|
|
|
|
|
setStock([]);
|
|
|
|
|
setError(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//const handleOpenIngresoModal = () => { setApiErrorMessage(null); setIngresoModalOpen(true); };
|
|
|
|
|
const handleCloseIngresoModal = () => setIngresoModalOpen(false);
|
|
|
|
|
const handleSubmitIngresoModal = async (data: CreateStockBobinaDto) => {
|
|
|
|
|
setApiErrorMessage(null);
|
|
|
|
|
@@ -208,7 +227,7 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
setApiErrorMessage(null);
|
|
|
|
|
try {
|
|
|
|
|
await stockBobinaService.actualizarFechaRemitoLote(data);
|
|
|
|
|
cargarStock(); // Recargar la grilla para ver el cambio
|
|
|
|
|
cargarStock();
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
const msg = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al actualizar la fecha del remito.';
|
|
|
|
|
setApiErrorMessage(msg);
|
|
|
|
|
@@ -216,77 +235,115 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// --- Handlers Menú Acciones ---
|
|
|
|
|
const handleMenuOpen = (event: React.MouseEvent<HTMLButtonElement>, bobina: StockBobinaDto) => {
|
|
|
|
|
event.stopPropagation(); // Evitar selección de fila al abrir menú
|
|
|
|
|
setAnchorEl(event.currentTarget);
|
|
|
|
|
setSelectedBobinaForRowMenu(bobina);
|
|
|
|
|
lastOpenedMenuButtonRef.current = event.currentTarget;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 1. handleMenuClose ahora solo cierra el menú. No limpia el estado de la bobina seleccionada.
|
|
|
|
|
const handleMenuClose = () => {
|
|
|
|
|
setAnchorEl(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 2. Handlers para abrir modales. Abren el modal y cierran el menú.
|
|
|
|
|
const handleOpenEditModal = () => {
|
|
|
|
|
setEditModalOpen(true);
|
|
|
|
|
handleMenuClose();
|
|
|
|
|
};
|
|
|
|
|
const handleOpenEditModal = () => { setEditModalOpen(true); handleMenuClose(); };
|
|
|
|
|
const handleOpenCambioEstadoModal = () => { setCambioEstadoModalOpen(true); handleMenuClose(); };
|
|
|
|
|
const handleOpenFechaRemitoModal = () => { setFechaRemitoModalOpen(true); handleMenuClose(); };
|
|
|
|
|
|
|
|
|
|
const handleOpenCambioEstadoModal = () => {
|
|
|
|
|
setCambioEstadoModalOpen(true);
|
|
|
|
|
handleMenuClose();
|
|
|
|
|
};
|
|
|
|
|
const handleCloseEditModal = () => { setEditModalOpen(false); setSelectedBobinaForRowMenu(null); };
|
|
|
|
|
const handleCloseCambioEstadoModal = () => { setCambioEstadoModalOpen(false); setSelectedBobinaForRowMenu(null); };
|
|
|
|
|
const handleCloseFechaRemitoModal = () => { setFechaRemitoModalOpen(false); setSelectedBobinaForRowMenu(null); };
|
|
|
|
|
|
|
|
|
|
const handleOpenFechaRemitoModal = () => {
|
|
|
|
|
setFechaRemitoModalOpen(true);
|
|
|
|
|
handleMenuClose();
|
|
|
|
|
};
|
|
|
|
|
// --- Definición de Columnas DataGrid ---
|
|
|
|
|
const columns = useMemo<GridColDef<StockBobinaDto>[]>(() => [
|
|
|
|
|
{ field: 'nroBobina', headerName: 'Nro. Bobina', width: 130 },
|
|
|
|
|
{ field: 'nombreTipoBobina', headerName: 'Tipo', width: 200, flex: 1 },
|
|
|
|
|
{ field: 'peso', headerName: 'Peso (Kg)', width: 100, align: 'right', headerAlign: 'right', type: 'number' },
|
|
|
|
|
{ field: 'nombrePlanta', headerName: 'Planta', width: 120 },
|
|
|
|
|
{
|
|
|
|
|
field: 'nombreEstadoBobina',
|
|
|
|
|
headerName: 'Estado',
|
|
|
|
|
width: 130,
|
|
|
|
|
renderCell: (params) => {
|
|
|
|
|
const idEstado = params.row.idEstadoBobina;
|
|
|
|
|
let color: "success" | "primary" | "error" | "default" = "default";
|
|
|
|
|
if (idEstado === ID_ESTADO_DISPONIBLE) color = "success";
|
|
|
|
|
else if (idEstado === ID_ESTADO_UTILIZADA) color = "primary";
|
|
|
|
|
else if (idEstado === ID_ESTADO_DANADA) color = "error";
|
|
|
|
|
|
|
|
|
|
// 3. Handlers para cerrar modales. Cierran el modal y AHORA limpian el estado de la bobina seleccionada.
|
|
|
|
|
const handleCloseEditModal = () => {
|
|
|
|
|
setEditModalOpen(false);
|
|
|
|
|
setSelectedBobinaForRowMenu(null);
|
|
|
|
|
};
|
|
|
|
|
return <Chip label={params.value} size="small" color={color} variant="outlined" />;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ field: 'remito', headerName: 'Remito', width: 120 },
|
|
|
|
|
{
|
|
|
|
|
field: 'fechaRemito',
|
|
|
|
|
headerName: 'F. Remito',
|
|
|
|
|
width: 110,
|
|
|
|
|
type: 'date',
|
|
|
|
|
valueGetter: (value: string) => {
|
|
|
|
|
if (!value) return null;
|
|
|
|
|
const datePart = value.toString().split('T')[0];
|
|
|
|
|
const [year, month, day] = datePart.split('-');
|
|
|
|
|
return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
|
|
|
},
|
|
|
|
|
valueFormatter: (value: Date) => {
|
|
|
|
|
return value ? value.toLocaleDateString('es-AR') : '-';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'fechaEstado',
|
|
|
|
|
headerName: 'F. Estado',
|
|
|
|
|
width: 110,
|
|
|
|
|
type: 'date',
|
|
|
|
|
valueGetter: (value: string) => {
|
|
|
|
|
if (!value) return null;
|
|
|
|
|
const datePart = value.toString().split('T')[0];
|
|
|
|
|
const [year, month, day] = datePart.split('-');
|
|
|
|
|
return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
|
|
|
},
|
|
|
|
|
valueFormatter: (value: Date) => {
|
|
|
|
|
return value ? value.toLocaleDateString('es-AR') : '-';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
const handleCloseCambioEstadoModal = () => {
|
|
|
|
|
setCambioEstadoModalOpen(false);
|
|
|
|
|
setSelectedBobinaForRowMenu(null);
|
|
|
|
|
};
|
|
|
|
|
{ field: 'nombrePublicacion', headerName: 'Publicación', width: 150 },
|
|
|
|
|
{ field: 'nombreSeccion', headerName: 'Sección', width: 120 },
|
|
|
|
|
{ field: 'obs', headerName: 'Obs.', width: 200, flex: 1 },
|
|
|
|
|
{
|
|
|
|
|
field: 'acciones',
|
|
|
|
|
headerName: 'Acciones',
|
|
|
|
|
width: 80,
|
|
|
|
|
sortable: false,
|
|
|
|
|
filterable: false,
|
|
|
|
|
align: 'right',
|
|
|
|
|
renderCell: (params: GridRenderCellParams<StockBobinaDto>) => {
|
|
|
|
|
const b = params.row;
|
|
|
|
|
const disabled = !(puedeModificarDatos) &&
|
|
|
|
|
!(puedeCambiarEstado) &&
|
|
|
|
|
!((b.idEstadoBobina === ID_ESTADO_DISPONIBLE || b.idEstadoBobina === ID_ESTADO_DANADA) && puedeEliminar);
|
|
|
|
|
|
|
|
|
|
const handleCloseFechaRemitoModal = () => {
|
|
|
|
|
setFechaRemitoModalOpen(false);
|
|
|
|
|
setSelectedBobinaForRowMenu(null);
|
|
|
|
|
};
|
|
|
|
|
if (disabled) return null;
|
|
|
|
|
|
|
|
|
|
const handleChangePage = (_event: unknown, newPage: number) => setPage(newPage);
|
|
|
|
|
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
setRowsPerPage(parseInt(event.target.value, 10)); setPage(0);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const displayData = stock.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
|
|
|
|
|
|
|
|
|
|
const formatDate = (dateString?: string | null) => {
|
|
|
|
|
if (!dateString) return '-';
|
|
|
|
|
const date = new Date(dateString);
|
|
|
|
|
if (isNaN(date.getTime())) return '-';
|
|
|
|
|
|
|
|
|
|
const options: Intl.DateTimeFormatOptions = {
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
month: '2-digit',
|
|
|
|
|
day: '2-digit',
|
|
|
|
|
timeZone: 'UTC'
|
|
|
|
|
};
|
|
|
|
|
return new Intl.DateTimeFormat('es-AR', options).format(date);
|
|
|
|
|
};
|
|
|
|
|
return (
|
|
|
|
|
<IconButton onClick={(e) => handleMenuOpen(e, b)} size="small">
|
|
|
|
|
<MoreVertIcon fontSize="small" />
|
|
|
|
|
</IconButton>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
], [puedeModificarDatos, puedeCambiarEstado, puedeEliminar]);
|
|
|
|
|
|
|
|
|
|
if (!puedeVer) return <Box sx={{ p: 2 }}><Alert severity="error">{error || "Acceso denegado."}</Alert></Box>;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Box sx={{ p: 1 }}>
|
|
|
|
|
<Box sx={{ p: 2 }}>
|
|
|
|
|
<Typography variant="h5" gutterBottom>Stock de Bobinas</Typography>
|
|
|
|
|
|
|
|
|
|
{/* Panel de Filtros */}
|
|
|
|
|
<Paper sx={{ p: 2, mb: 2 }}>
|
|
|
|
|
<Typography variant="h6" gutterBottom>Filtros</Typography>
|
|
|
|
|
|
|
|
|
|
{/* Fila 1: Filtros generales */}
|
|
|
|
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, mb: 2 }}>
|
|
|
|
|
<FormControl size="small" sx={{ minWidth: 180, flexGrow: 1 }}>
|
|
|
|
|
<InputLabel>Tipo Bobina</InputLabel>
|
|
|
|
|
@@ -312,24 +369,32 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
</FormControl>
|
|
|
|
|
<TextField label="Remito" size="small" value={filtroRemito} onChange={(e) => setFiltroRemito(e.target.value)} sx={{ minWidth: 150, flexGrow: 1 }} />
|
|
|
|
|
</Box>
|
|
|
|
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, mb: 2 }}>
|
|
|
|
|
|
|
|
|
|
{/* Fila 2: Filtros de Fechas */}
|
|
|
|
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 4, mb: 2, alignItems: 'center' }}>
|
|
|
|
|
{/* Fechas Remito */}
|
|
|
|
|
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', border: '1px dashed #ccc', p: 1, borderRadius: 1 }}>
|
|
|
|
|
<FormControlLabel
|
|
|
|
|
control={<Checkbox checked={filtroFechaHabilitado} onChange={(e) => setFiltroFechaHabilitado(e.target.checked)} />}
|
|
|
|
|
label="Filtrar por Fechas de Remitos"
|
|
|
|
|
label="Filtrar por Fecha Remito"
|
|
|
|
|
/>
|
|
|
|
|
<TextField label="Fecha Desde" type="date" size="small" value={filtroFechaDesde} onChange={(e) => setFiltroFechaDesde(e.target.value)} InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }} disabled={!filtroFechaHabilitado} />
|
|
|
|
|
<TextField label="Fecha Hasta" type="date" size="small" value={filtroFechaHasta} onChange={(e) => setFiltroFechaHasta(e.target.value)} InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }} disabled={!filtroFechaHabilitado} />
|
|
|
|
|
<TextField label="Desde" type="date" size="small" value={filtroFechaDesde} onChange={(e) => setFiltroFechaDesde(e.target.value)} InputLabelProps={{ shrink: true }} sx={{ minWidth: 140 }} disabled={!filtroFechaHabilitado} />
|
|
|
|
|
<TextField label="Hasta" type="date" size="small" value={filtroFechaHasta} onChange={(e) => setFiltroFechaHasta(e.target.value)} InputLabelProps={{ shrink: true }} sx={{ minWidth: 140 }} disabled={!filtroFechaHabilitado} />
|
|
|
|
|
</Box>
|
|
|
|
|
<Box
|
|
|
|
|
sx={{
|
|
|
|
|
display: 'flex',
|
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
|
gap: 2,
|
|
|
|
|
mt: 2
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
{/* Fechas Estado (Nuevo) */}
|
|
|
|
|
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', border: '1px dashed #ccc', p: 1, borderRadius: 1 }}>
|
|
|
|
|
<FormControlLabel
|
|
|
|
|
control={<Checkbox checked={filtroFechaEstadoHabilitado} onChange={(e) => setFiltroFechaEstadoHabilitado(e.target.checked)} />}
|
|
|
|
|
label="Filtrar por Fecha Estado"
|
|
|
|
|
/>
|
|
|
|
|
<TextField label="Desde" type="date" size="small" value={filtroFechaEstadoDesde} onChange={(e) => setFiltroFechaEstadoDesde(e.target.value)} InputLabelProps={{ shrink: true }} sx={{ minWidth: 140 }} disabled={!filtroFechaEstadoHabilitado} />
|
|
|
|
|
<TextField label="Hasta" type="date" size="small" value={filtroFechaEstadoHasta} onChange={(e) => setFiltroFechaEstadoHasta(e.target.value)} InputLabelProps={{ shrink: true }} sx={{ minWidth: 140 }} disabled={!filtroFechaEstadoHabilitado} />
|
|
|
|
|
</Box>
|
|
|
|
|
</Box>
|
|
|
|
|
|
|
|
|
|
{/* Botones de acción del filtro */}
|
|
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 2, mt: 2 }}>
|
|
|
|
|
<Box sx={{ display: 'flex', gap: 2 }}>
|
|
|
|
|
<Button variant="contained" startIcon={<SearchIcon />} onClick={handleBuscarClick} disabled={loading}>
|
|
|
|
|
Buscar
|
|
|
|
|
@@ -340,11 +405,6 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
</Box>
|
|
|
|
|
{puedeIngresar && (
|
|
|
|
|
<Box sx={{ display: 'flex', gap: 2 }}>
|
|
|
|
|
{/*
|
|
|
|
|
<Button variant="contained" startIcon={<AddIcon />} onClick={handleOpenIngresoModal}>
|
|
|
|
|
Ingreso Individual
|
|
|
|
|
</Button>
|
|
|
|
|
*/}
|
|
|
|
|
<Button variant="contained" color="secondary" startIcon={<AddIcon />} onClick={() => setLoteModalOpen(true)}>
|
|
|
|
|
Ingreso por Remito (Lote)
|
|
|
|
|
</Button>
|
|
|
|
|
@@ -353,59 +413,28 @@ const GestionarStockBobinasPage: React.FC = () => {
|
|
|
|
|
</Box>
|
|
|
|
|
</Paper>
|
|
|
|
|
|
|
|
|
|
{loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>}
|
|
|
|
|
{error && !loading && <Alert severity="warning" sx={{ my: 2 }}>{error}</Alert>}
|
|
|
|
|
{apiErrorMessage && <Alert severity="error" sx={{ my: 2 }}>{apiErrorMessage}</Alert>}
|
|
|
|
|
|
|
|
|
|
{!loading && !error && (
|
|
|
|
|
<TableContainer component={Paper}>
|
|
|
|
|
<Table size="small">
|
|
|
|
|
<TableHead><TableRow>
|
|
|
|
|
<TableCell>Nro. Bobina</TableCell><TableCell>Tipo</TableCell><TableCell>Peso (Kg)</TableCell>
|
|
|
|
|
<TableCell>Planta</TableCell><TableCell>Estado</TableCell><TableCell>Remito</TableCell>
|
|
|
|
|
<TableCell>F. Remito</TableCell><TableCell>F. Estado</TableCell>
|
|
|
|
|
<TableCell>Publicación</TableCell><TableCell>Sección</TableCell>
|
|
|
|
|
<TableCell>Obs.</TableCell>
|
|
|
|
|
{(puedeModificarDatos || puedeCambiarEstado || puedeEliminar) && <TableCell align="right">Acciones</TableCell>}
|
|
|
|
|
</TableRow></TableHead>
|
|
|
|
|
<TableBody>
|
|
|
|
|
{displayData.length === 0 ? (
|
|
|
|
|
<TableRow><TableCell colSpan={(puedeModificarDatos || puedeCambiarEstado || puedeEliminar) ? 12 : 11} align="center">No se encontraron bobinas con los filtros aplicados. Haga clic en "Buscar" para iniciar una consulta.</TableCell></TableRow>
|
|
|
|
|
) : (
|
|
|
|
|
displayData.map((b) => (
|
|
|
|
|
<TableRow key={b.idBobina} hover>
|
|
|
|
|
<TableCell>{b.nroBobina}</TableCell><TableCell>{b.nombreTipoBobina}</TableCell>
|
|
|
|
|
<TableCell align="right">{b.peso}</TableCell><TableCell>{b.nombrePlanta}</TableCell>
|
|
|
|
|
<TableCell><Chip label={b.nombreEstadoBobina} size="small" color={
|
|
|
|
|
b.idEstadoBobina === ID_ESTADO_DISPONIBLE ? "success" : b.idEstadoBobina === ID_ESTADO_UTILIZADA ? "primary" : b.idEstadoBobina === ID_ESTADO_DANADA ? "error" : "default"
|
|
|
|
|
} /></TableCell>
|
|
|
|
|
<TableCell>{b.remito}</TableCell><TableCell>{formatDate(b.fechaRemito)}</TableCell>
|
|
|
|
|
<TableCell>{formatDate(b.fechaEstado)}</TableCell>
|
|
|
|
|
<TableCell>{b.nombrePublicacion || '-'}</TableCell><TableCell>{b.nombreSeccion || '-'}</TableCell>
|
|
|
|
|
<TableCell>{b.obs || '-'}</TableCell>
|
|
|
|
|
{(puedeModificarDatos || puedeCambiarEstado || puedeEliminar) && (
|
|
|
|
|
<TableCell align="right">
|
|
|
|
|
<IconButton onClick={(e) => handleMenuOpen(e, b)}
|
|
|
|
|
disabled={
|
|
|
|
|
!(puedeModificarDatos) && // Simplificado, ya que todas las opciones requieren este permiso
|
|
|
|
|
!(puedeCambiarEstado) &&
|
|
|
|
|
!((b.idEstadoBobina === ID_ESTADO_DISPONIBLE || b.idEstadoBobina === ID_ESTADO_DANADA) && puedeEliminar)
|
|
|
|
|
}
|
|
|
|
|
><MoreVertIcon /></IconButton>
|
|
|
|
|
</TableCell>
|
|
|
|
|
)}
|
|
|
|
|
</TableRow>
|
|
|
|
|
)))}
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
<TablePagination
|
|
|
|
|
rowsPerPageOptions={[25, 50, 100]} component="div" count={stock.length}
|
|
|
|
|
rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage}
|
|
|
|
|
onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:"
|
|
|
|
|
{/* Tabla DataGrid */}
|
|
|
|
|
<Paper sx={{ width: '100%', height: 600 }}>
|
|
|
|
|
<DataGrid
|
|
|
|
|
rows={stock}
|
|
|
|
|
columns={columns}
|
|
|
|
|
getRowId={(row) => row.idBobina} // Importante: especificar el ID único
|
|
|
|
|
loading={loading}
|
|
|
|
|
localeText={esES.components.MuiDataGrid.defaultProps.localeText}
|
|
|
|
|
density="compact"
|
|
|
|
|
disableRowSelectionOnClick
|
|
|
|
|
initialState={{
|
|
|
|
|
pagination: { paginationModel: { pageSize: 25 } },
|
|
|
|
|
}}
|
|
|
|
|
pageSizeOptions={[25, 50, 100]}
|
|
|
|
|
sx={{ border: 0 }}
|
|
|
|
|
/>
|
|
|
|
|
</TableContainer>
|
|
|
|
|
)}
|
|
|
|
|
</Paper>
|
|
|
|
|
|
|
|
|
|
{/* Menú Contextual de Fila */}
|
|
|
|
|
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}>
|
|
|
|
|
{selectedBobinaForRowMenu && puedeModificarDatos && (
|
|
|
|
|
<MenuItem onClick={handleOpenFechaRemitoModal}>
|
|
|
|
|
|