Fix: Fechas de Estado Bobinas
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 14m8s
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 14m8s
- Las fechas de estado de las bobinas no pueden ser anterior a la fecha de remito (ingreso).
This commit is contained in:
@@ -199,13 +199,21 @@ namespace GestionIntegral.Api.Services.Impresion
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
var bobina = await _stockBobinaRepository.GetByIdAsync(idBobina); // Obtener dentro de la transacción
|
||||
var bobina = await _stockBobinaRepository.GetByIdAsync(idBobina);
|
||||
if (bobina == null)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
return (false, "Bobina no encontrada.");
|
||||
}
|
||||
|
||||
// Comparamos solo las fechas (sin hora) para evitar problemas de precisión.
|
||||
if (cambiarEstadoDto.FechaCambioEstado.Date < bobina.FechaRemito.Date)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
return (false, $"Error de integridad: La fecha del nuevo estado ({cambiarEstadoDto.FechaCambioEstado:dd/MM/yyyy}) " +
|
||||
$"no puede ser anterior a la fecha de ingreso por remito ({bobina.FechaRemito:dd/MM/yyyy}).");
|
||||
}
|
||||
|
||||
var nuevoEstado = await _estadoBobinaRepository.GetByIdAsync(cambiarEstadoDto.NuevoEstadoId);
|
||||
if (nuevoEstado == null)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import type { StockBobinaDto } from '../../../models/dtos/Impresion/StockBobinaDto';
|
||||
import type { CambiarEstadoBobinaDto } from '../../../models/dtos/Impresion/CambiarEstadoBobinaDto';
|
||||
import type { EstadoBobinaDto } from '../../../models/dtos/Impresion/EstadoBobinaDto';
|
||||
// --- CAMBIO: Importar PublicacionDropdownDto ---
|
||||
import type { PublicacionDropdownDto } from '../../../models/dtos/Distribucion/PublicacionDropdownDto';
|
||||
import type { PubliSeccionDto } from '../../../models/dtos/Distribucion/PubliSeccionDto';
|
||||
import estadoBobinaService from '../../../services/Impresion/estadoBobinaService';
|
||||
@@ -56,7 +55,6 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
const [fechaCambioEstado, setFechaCambioEstado] = useState('');
|
||||
|
||||
const [estadosDisponibles, setEstadosDisponibles] = useState<EstadoBobinaDto[]>([]);
|
||||
// --- CAMBIO: Usar PublicacionDropdownDto para el estado ---
|
||||
const [publicacionesDisponibles, setPublicacionesDisponibles] = useState<PublicacionDropdownDto[]>([]);
|
||||
const [seccionesDisponibles, setSeccionesDisponibles] = useState<PubliSeccionDto[]>([]);
|
||||
|
||||
@@ -77,7 +75,6 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
} else if (bobinaActual.idEstadoBobina === ID_ESTADO_EN_USO) {
|
||||
estadosFiltrados = todosLosEstados.filter(e => e.idEstadoBobina === ID_ESTADO_DANADA);
|
||||
} else if (bobinaActual.idEstadoBobina === ID_ESTADO_DISPONIBLE) {
|
||||
// --- CAMBIO: Usar ID_ESTADO_EN_USO ---
|
||||
estadosFiltrados = todosLosEstados.filter(
|
||||
e => e.idEstadoBobina === ID_ESTADO_EN_USO || e.idEstadoBobina === ID_ESTADO_DANADA
|
||||
);
|
||||
@@ -90,7 +87,6 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
const sePuedePonerEnUso = estadosFiltrados.some(e => e.idEstadoBobina === ID_ESTADO_EN_USO);
|
||||
|
||||
if (sePuedePonerEnUso) {
|
||||
// --- CAMBIO: La data es PublicacionDropdownDto[] ---
|
||||
const publicacionesData: PublicacionDropdownDto[] = await publicacionService.getPublicacionesForDropdown(true);
|
||||
setPublicacionesDisponibles(publicacionesData);
|
||||
} else {
|
||||
@@ -101,7 +97,7 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error al cargar datos para dropdowns (Cambio Estado Bobina)", error);
|
||||
setLocalErrors(prev => ({...prev, dropdowns: 'Error al cargar datos necesarios.'}));
|
||||
setLocalErrors(prev => ({ ...prev, dropdowns: 'Error al cargar datos necesarios.' }));
|
||||
} finally {
|
||||
setLoadingDropdowns(false);
|
||||
}
|
||||
@@ -140,7 +136,7 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
setSeccionesDisponibles(data);
|
||||
} catch (error) {
|
||||
console.error("Error al cargar secciones:", error);
|
||||
setLocalErrors(prev => ({ ...prev, secciones: 'Error al cargar secciones.'}));
|
||||
setLocalErrors(prev => ({ ...prev, secciones: 'Error al cargar secciones.' }));
|
||||
setSeccionesDisponibles([]);
|
||||
} finally {
|
||||
setLoadingDropdowns(false);
|
||||
@@ -170,8 +166,16 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
const validate = (): boolean => {
|
||||
const errors: { [key: string]: string | null } = {};
|
||||
if (!nuevoEstadoId) errors.nuevoEstadoId = 'Seleccione un nuevo estado.';
|
||||
if (!fechaCambioEstado.trim()) errors.fechaCambioEstado = 'La fecha es obligatoria.';
|
||||
else if (!/^\d{4}-\d{2}-\d{2}$/.test(fechaCambioEstado)) errors.fechaCambioEstado = 'Formato de fecha inválido.';
|
||||
if (!fechaCambioEstado.trim()) {
|
||||
errors.fechaCambioEstado = 'La fecha es obligatoria.';
|
||||
} else if (!/^\d{4}-\d{2}-\d{2}$/.test(fechaCambioEstado)) {
|
||||
errors.fechaCambioEstado = 'Formato de fecha inválido.';
|
||||
} else if (bobinaActual) {
|
||||
const fechaRemitoSimple = bobinaActual.fechaRemito.split('T')[0];
|
||||
if (fechaCambioEstado < fechaRemitoSimple) {
|
||||
errors.fechaCambioEstado = `La fecha no puede ser anterior al ingreso (${fechaRemitoSimple}).`;
|
||||
}
|
||||
}
|
||||
|
||||
if (Number(nuevoEstadoId) === ID_ESTADO_EN_USO) {
|
||||
if (!idPublicacion) errors.idPublicacion = 'Seleccione una publicación.';
|
||||
@@ -251,7 +255,7 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
<FormControl fullWidth margin="dense" error={!!localErrors.idPublicacion} required>
|
||||
<InputLabel id="publicacion-estado-select-label">Publicación</InputLabel>
|
||||
<Select labelId="publicacion-estado-select-label" label="Publicación" value={idPublicacion}
|
||||
onChange={(e) => {setIdPublicacion(e.target.value as number); handleInputChange('idPublicacion');}}
|
||||
onChange={(e) => { setIdPublicacion(e.target.value as number); handleInputChange('idPublicacion'); }}
|
||||
disabled={loading || loadingDropdowns || publicacionesDisponibles.length === 0}
|
||||
>
|
||||
<MenuItem value="" disabled><em>Seleccione publicación</em></MenuItem>
|
||||
@@ -262,22 +266,33 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
<FormControl fullWidth margin="dense" error={!!localErrors.idSeccion} required>
|
||||
<InputLabel id="seccion-estado-select-label">Sección</InputLabel>
|
||||
<Select labelId="seccion-estado-select-label" label="Sección" value={idSeccion}
|
||||
onChange={(e) => {setIdSeccion(e.target.value as number); handleInputChange('idSeccion');}}
|
||||
onChange={(e) => { setIdSeccion(e.target.value as number); handleInputChange('idSeccion'); }}
|
||||
disabled={loading || loadingDropdowns || !idPublicacion || seccionesDisponibles.length === 0}
|
||||
>
|
||||
<MenuItem value="" disabled><em>{idPublicacion ? (seccionesDisponibles.length > 0 ? 'Seleccione sección' : 'No hay secciones para esta pub.') : 'Seleccione publicación primero'}</em></MenuItem>
|
||||
{seccionesDisponibles.map((s) => (<MenuItem key={s.idSeccion} value={s.idSeccion}>{s.nombre}</MenuItem>))}
|
||||
</Select>
|
||||
{localErrors.idSeccion && <Typography color="error" variant="caption">{localErrors.idSeccion}</Typography>}
|
||||
{localErrors.secciones && <Alert severity="warning" sx={{mt:0.5}}>{localErrors.secciones}</Alert>}
|
||||
{localErrors.secciones && <Alert severity="warning" sx={{ mt: 0.5 }}>{localErrors.secciones}</Alert>}
|
||||
</FormControl>
|
||||
</>
|
||||
)}
|
||||
|
||||
<TextField label="Fecha Cambio de Estado" type="date" value={fechaCambioEstado} required
|
||||
onChange={(e) => {setFechaCambioEstado(e.target.value); handleInputChange('fechaCambioEstado');}}
|
||||
margin="dense" fullWidth error={!!localErrors.fechaCambioEstado} helperText={localErrors.fechaCambioEstado || ''}
|
||||
disabled={loading} InputLabelProps={{ shrink: true }}
|
||||
<TextField
|
||||
label="Fecha Cambio de Estado"
|
||||
type="date"
|
||||
value={fechaCambioEstado}
|
||||
required
|
||||
onChange={(e) => { setFechaCambioEstado(e.target.value); handleInputChange('fechaCambioEstado'); }}
|
||||
margin="dense"
|
||||
fullWidth
|
||||
error={!!localErrors.fechaCambioEstado}
|
||||
helperText={localErrors.fechaCambioEstado || ''}
|
||||
disabled={loading}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
inputProps={{
|
||||
min: bobinaActual?.fechaRemito.split('T')[0]
|
||||
}}
|
||||
/>
|
||||
<TextField label="Observaciones (Opcional)" value={obs}
|
||||
onChange={(e) => setObs(e.target.value)}
|
||||
@@ -291,7 +306,7 @@ const StockBobinaCambioEstadoModal: React.FC<StockBobinaCambioEstadoModalProps>
|
||||
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'flex-end', gap: 1 }}>
|
||||
<Button onClick={onClose} color="secondary" disabled={loading}>Cancelar</Button>
|
||||
<Button type="submit" variant="contained"
|
||||
disabled={loading || loadingDropdowns || (estadosDisponibles.length === 0 && bobinaActual.idEstadoBobina === ID_ESTADO_EN_USO) }>
|
||||
disabled={loading || loadingDropdowns || (estadosDisponibles.length === 0 && bobinaActual.idEstadoBobina === ID_ESTADO_EN_USO)}>
|
||||
{loading ? <CircularProgress size={24} /> : 'Guardar Cambio de Estado'}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user