Fix Selectores de Fechas Reporte Existencia de Papel.
Se agrega Total a Liquidar para la E/S de Canillitas.
This commit is contained in:
		| @@ -1,4 +1,4 @@ | ||||
| import React, { useState, useEffect, useCallback } from 'react'; | ||||
| import React, { useState, useEffect, useCallback, useMemo } from 'react'; // << Añadido useMemo | ||||
| import { | ||||
|   Box, Typography, TextField, Button, Paper, IconButton, Menu, MenuItem, Chip, | ||||
|   Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TablePagination, | ||||
| @@ -33,9 +33,9 @@ type TipoDestinatarioFiltro = 'canillitas' | 'accionistas'; | ||||
|  | ||||
| const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|   const [movimientos, setMovimientos] = useState<EntradaSalidaCanillaDto[]>([]); | ||||
|   const [loading, setLoading] = useState(true); // Para carga principal de movimientos | ||||
|   const [error, setError] = useState<string | null>(null); // Error general o de carga | ||||
|   const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null); // Para errores de modal/API | ||||
|   const [loading, setLoading] = useState(true); | ||||
|   const [error, setError] = useState<string | null>(null); | ||||
|   const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null); | ||||
|  | ||||
|   const [filtroFecha, setFiltroFecha] = useState<string>(new Date().toISOString().split('T')[0]); | ||||
|   const [filtroIdPublicacion, setFiltroIdPublicacion] = useState<number | string>(''); | ||||
| @@ -52,7 +52,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|   const [prefillModalData, setPrefillModalData] = useState<{ | ||||
|     fecha?: string; | ||||
|     idCanilla?: number | string; | ||||
|     nombreCanilla?: string; // << AÑADIDO PARA PASAR AL MODAL | ||||
|     nombreCanilla?: string; | ||||
|     idPublicacion?: number | string; | ||||
|   } | null>(null); | ||||
|  | ||||
| @@ -82,34 +82,34 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|  | ||||
|   useEffect(() => { | ||||
|     const fetchPublicaciones = async () => { | ||||
|       setLoadingFiltersDropdown(true); // Mover al inicio de la carga de pubs | ||||
|       setLoadingFiltersDropdown(true); | ||||
|       try { | ||||
|         const pubsData = await publicacionService.getPublicacionesForDropdown(true); | ||||
|         setPublicaciones(pubsData); | ||||
|       } catch (err) { | ||||
|         console.error("Error cargando publicaciones para filtro:",err); | ||||
|         setError("Error al cargar publicaciones."); // Usar error general | ||||
|         setError("Error al cargar publicaciones."); | ||||
|       } finally { | ||||
|         // No poner setLoadingFiltersDropdown(false) aquí, esperar a que ambas cargas terminen | ||||
|         // No setLoadingFiltersDropdown(false) acá, esperar a la otra carga | ||||
|       } | ||||
|     }; | ||||
|     fetchPublicaciones(); | ||||
|   }, []); | ||||
|  | ||||
|   const fetchDestinatariosParaDropdown = useCallback(async () => { | ||||
|     setLoadingFiltersDropdown(true); // Poner al inicio de esta carga también | ||||
|     setLoadingFiltersDropdown(true); | ||||
|     setFiltroIdCanillitaSeleccionado(''); | ||||
|     setDestinatariosDropdown([]); | ||||
|     setError(null); // Limpiar errores de carga de dropdowns previos | ||||
|     setError(null); | ||||
|     try { | ||||
|       const esAccionistaFilter = filtroTipoDestinatario === 'accionistas'; | ||||
|       const data = await canillaService.getAllCanillas(undefined, undefined, true, esAccionistaFilter); | ||||
|       setDestinatariosDropdown(data); | ||||
|     } catch (err) { | ||||
|       console.error("Error cargando destinatarios para filtro:", err); | ||||
|       setError("Error al cargar canillitas/accionistas."); // Usar error general | ||||
|       setError("Error al cargar canillitas/accionistas."); | ||||
|     } finally { | ||||
|       setLoadingFiltersDropdown(false); // Poner al final de AMBAS cargas de dropdown | ||||
|       setLoadingFiltersDropdown(false); | ||||
|     } | ||||
|   }, [filtroTipoDestinatario]); | ||||
|  | ||||
| @@ -153,9 +153,9 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|         cargarMovimientos(); | ||||
|     } else { | ||||
|         setMovimientos([]); | ||||
|         if (loading) setLoading(false); // Asegurar que no se quede en loading si los filtros se limpian | ||||
|         if (loading) setLoading(false); | ||||
|     } | ||||
|   }, [cargarMovimientos, filtroFecha, filtroIdCanillitaSeleccionado]); // `cargarMovimientos` ya tiene sus dependencias | ||||
|   }, [cargarMovimientos, filtroFecha, filtroIdCanillitaSeleccionado]); | ||||
|  | ||||
|  | ||||
|   const handleOpenModal = (item?: EntradaSalidaCanillaDto) => { | ||||
| @@ -172,7 +172,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|         setEditingMovimiento(item); | ||||
|         setPrefillModalData(null); | ||||
|     } else { | ||||
|         // --- CAMBIO: Obtener nombre del canillita seleccionado para prefill --- | ||||
|         const canillitaSeleccionado = destinatariosDropdown.find( | ||||
|             c => c.idCanilla === Number(filtroIdCanillitaSeleccionado) | ||||
|         ); | ||||
| @@ -180,7 +179,7 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|         setPrefillModalData({ | ||||
|             fecha: filtroFecha, | ||||
|             idCanilla: filtroIdCanillitaSeleccionado, | ||||
|             nombreCanilla: canillitaSeleccionado?.nomApe, // << AÑADIR NOMBRE | ||||
|             nombreCanilla: canillitaSeleccionado?.nomApe, | ||||
|             idPublicacion: filtroIdPublicacion | ||||
|         }); | ||||
|     } | ||||
| @@ -188,7 +187,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|     setModalOpen(true); | ||||
|   }; | ||||
|  | ||||
|   // ... handleDelete, handleMenuOpen, handleMenuClose, handleSelectRowForLiquidar, handleSelectAllForLiquidar, handleOpenLiquidarDialog, handleCloseLiquidarDialog sin cambios ... | ||||
|   const handleDelete = async (idParte: number) => { | ||||
|     if (window.confirm(`¿Seguro de eliminar este movimiento (ID: ${idParte})?`)) { | ||||
|       setApiErrorMessage(null); | ||||
| @@ -233,9 +231,8 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|  | ||||
|  | ||||
|   const handleConfirmLiquidar = async () => { | ||||
|     if (selectedIdsParaLiquidar.size === 0) { /* ... */ return; } | ||||
|     if (!fechaLiquidacionDialog) { /* ... */ return; } | ||||
|     // ... (validación de fecha sin cambios) | ||||
|     if (selectedIdsParaLiquidar.size === 0) { return; } | ||||
|     if (!fechaLiquidacionDialog) { return; } | ||||
|     const fechaLiquidacionDate = new Date(fechaLiquidacionDialog + 'T00:00:00Z'); | ||||
|     let fechaMovimientoMasReciente: Date | null = null; | ||||
|     selectedIdsParaLiquidar.forEach(idParte => { | ||||
| @@ -251,7 +248,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|     setApiErrorMessage(null); | ||||
|     setLoading(true); | ||||
|     const liquidarDto: LiquidarMovimientosCanillaRequestDto = { | ||||
| @@ -262,17 +258,18 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|       await entradaSalidaCanillaService.liquidarMovimientos(liquidarDto); | ||||
|       setOpenLiquidarDialog(false); | ||||
|       const primerIdParteLiquidado = Array.from(selectedIdsParaLiquidar)[0]; | ||||
|       // Necesitamos encontrar el movimiento en la lista ANTES de recargar | ||||
|       const movimientoParaTicket = movimientos.find(m => m.idParte === primerIdParteLiquidado); | ||||
|  | ||||
|       await cargarMovimientos(); | ||||
|       await cargarMovimientos(); // Recargar la lista para reflejar el estado liquidado | ||||
|  | ||||
|       // --- CAMBIO: NO IMPRIMIR TICKET SI ES ACCIONISTA --- | ||||
|       // Usar la fecha del movimiento original para el ticket | ||||
|       if (movimientoParaTicket && !movimientoParaTicket.canillaEsAccionista) { | ||||
|         console.log("Liquidación exitosa, generando ticket para canillita NO accionista:", movimientoParaTicket.idCanilla); | ||||
|         await handleImprimirTicketLiquidacion( | ||||
|             movimientoParaTicket.idCanilla, | ||||
|             fechaLiquidacionDialog, | ||||
|             false // esAccionista = false | ||||
|             movimientoParaTicket.fecha, // Usar la fecha del movimiento | ||||
|             false | ||||
|         ); | ||||
|       } else if (movimientoParaTicket && movimientoParaTicket.canillaEsAccionista) { | ||||
|         console.log("Liquidación exitosa para accionista. No se genera ticket automáticamente."); | ||||
| @@ -288,7 +285,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|   }; | ||||
|  | ||||
|   const handleModalEditSubmit = async (data: UpdateEntradaSalidaCanillaDto, idParte: number) => { | ||||
|     // ... (sin cambios) | ||||
|     setApiErrorMessage(null); | ||||
|     try { | ||||
|       await entradaSalidaCanillaService.updateEntradaSalidaCanilla(idParte, data); | ||||
| @@ -311,7 +307,6 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|   const handleImprimirTicketLiquidacion = useCallback(async ( | ||||
|     idCanilla: number, fecha: string, esAccionista: boolean | ||||
|   ) => { | ||||
|     // ... (sin cambios) | ||||
|     setLoadingTicketPdf(true); | ||||
|     setApiErrorMessage(null); | ||||
|     try { | ||||
| @@ -340,11 +335,14 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|   }; | ||||
|   const displayData = movimientos.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); | ||||
|  | ||||
|   if (!loading && !puedeVer && !loadingFiltersDropdown && movimientos.length === 0 && !filtroFecha && !filtroIdCanillitaSeleccionado ) { // Modificado para solo mostrar si no hay filtros y no puede ver | ||||
|   const totalARendirVisible = useMemo(() =>  | ||||
|     displayData.filter(m => !m.liquidado).reduce((sum, item) => sum + item.montoARendir, 0) | ||||
|   , [displayData]); | ||||
|  | ||||
|   if (!loading && !puedeVer && !loadingFiltersDropdown && movimientos.length === 0 && !filtroFecha && !filtroIdCanillitaSeleccionado ) { | ||||
|      return <Box sx={{ p: 2 }}><Alert severity="error">{error || "Acceso denegado."}</Alert></Box>; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   const numSelectedToLiquidate = selectedIdsParaLiquidar.size; | ||||
|   const numNotLiquidatedOnPage = displayData.filter(m => !m.liquidado).length; | ||||
|  | ||||
| @@ -354,11 +352,12 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|       <Paper sx={{ p: 2, mb: 2 }}> | ||||
|         <Typography variant="h6" gutterBottom>Filtros <FilterListIcon fontSize="small" /></Typography> | ||||
|         <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, alignItems: 'center', mb: 2 }}> | ||||
|           <TextField label="Fecha" type="date" size="small" value={filtroFecha} | ||||
|           {/* ... (Filtros sin cambios) ... */} | ||||
|            <TextField label="Fecha" type="date" size="small" value={filtroFecha} | ||||
|             onChange={(e) => setFiltroFecha(e.target.value)} | ||||
|             InputLabelProps={{ shrink: true }} sx={{ minWidth: 170 }} | ||||
|             required | ||||
|             error={!filtroFecha} // Se marca error si está vacío | ||||
|             error={!filtroFecha} | ||||
|             helperText={!filtroFecha ? "Fecha es obligatoria" : ""} | ||||
|           /> | ||||
|           <ToggleButtonGroup | ||||
| @@ -398,14 +397,13 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|             </Select> | ||||
|           </FormControl> | ||||
|         </Box> | ||||
|         <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> | ||||
|           {/* --- CAMBIO: DESHABILITAR BOTÓN SI FILTROS OBLIGATORIOS NO ESTÁN --- */} | ||||
|         <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap:2 }}> | ||||
|           {puedeCrear && ( | ||||
|             <Button  | ||||
|                 variant="contained"  | ||||
|                 startIcon={<AddIcon />}  | ||||
|                 onClick={() => handleOpenModal()} | ||||
|                 disabled={!filtroFecha || !filtroIdCanillitaSeleccionado} // <<-- AÑADIDO | ||||
|                 disabled={!filtroFecha || !filtroIdCanillitaSeleccionado} | ||||
|             > | ||||
|                 Registrar Movimiento | ||||
|             </Button> | ||||
| @@ -417,26 +415,32 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|           )} | ||||
|         </Box> | ||||
|       </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>} | ||||
|  | ||||
|       {loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>} | ||||
|       {/* Mostrar error general si no hay error de API específico y no está cargando filtros */} | ||||
|       {error && !loading && !apiErrorMessage && !loadingFiltersDropdown && <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>} | ||||
|       {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 && ( | ||||
|         <Paper sx={{ p: 1.5, mb: 2, mt:1, backgroundColor: 'grey.100' }}> | ||||
|             <Box sx={{display: 'flex', justifyContent: 'flex-end', alignItems: 'center'}}> | ||||
|                 <Typography variant="subtitle1" sx={{mr:2}}> | ||||
|                     Total a Liquidar: | ||||
|                 </Typography> | ||||
|                 <Typography variant="h6" sx={{fontWeight: 'bold', color: 'error.main'}}> | ||||
|                     {totalARendirVisible.toLocaleString('es-AR', { style: 'currency', currency: 'ARS' })} | ||||
|                 </Typography> | ||||
|             </Box> | ||||
|         </Paper> | ||||
|       )} | ||||
|  | ||||
|       {!loading && !error && puedeVer && filtroFecha && filtroIdCanillitaSeleccionado && ( | ||||
|         // ... (Tabla y Paginación sin cambios) | ||||
|          <TableContainer component={Paper}> | ||||
|           <Table size="small"> | ||||
|         <TableContainer component={Paper}> | ||||
|            <Table size="small"> | ||||
|             <TableHead> | ||||
|               <TableRow> | ||||
|                 {puedeLiquidar && ( | ||||
| @@ -527,20 +531,22 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|         </TableContainer> | ||||
|       )} | ||||
|  | ||||
|       <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}> | ||||
|         <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}> | ||||
|         {puedeModificar && selectedRow && !selectedRow.liquidado && ( | ||||
|           <MenuItem onClick={() => { handleOpenModal(selectedRow); handleMenuClose(); }}><EditIcon fontSize="small" sx={{ mr: 1 }} /> Modificar</MenuItem>)} | ||||
|         {/* --- CAMBIO: MOSTRAR REIMPRIMIR TICKET SIEMPRE SI ESTÁ LIQUIDADO --- */} | ||||
|         {selectedRow && selectedRow.liquidado && puedeLiquidar && ( // Usar puedeLiquidar para consistencia | ||||
|          | ||||
|         {selectedRow && selectedRow.liquidado && puedeLiquidar && ( | ||||
|           <MenuItem | ||||
|             onClick={() => { | ||||
|               if (selectedRow) { | ||||
|                 // Usar siempre selectedRow.fecha, que es la fecha original del movimiento | ||||
|                 handleImprimirTicketLiquidacion( | ||||
|                   selectedRow.idCanilla, | ||||
|                   selectedRow.fechaLiquidado || selectedRow.fecha, | ||||
|                   selectedRow.canillaEsAccionista // Pasar si es accionista | ||||
|                   selectedRow.fecha, // Usar siempre la fecha del movimiento | ||||
|                   selectedRow.canillaEsAccionista | ||||
|                 ); | ||||
|               } | ||||
|                handleMenuClose(); | ||||
|             }} | ||||
|             disabled={loadingTicketPdf} | ||||
|           > | ||||
| @@ -552,8 +558,9 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|         {selectedRow && ( | ||||
|           ((!selectedRow.liquidado && puedeEliminar) || (selectedRow.liquidado && puedeEliminarLiquidados)) | ||||
|         ) && ( | ||||
|             <MenuItem onClick={() => { if (selectedRow) handleDelete(selectedRow.idParte); }}> | ||||
|               <DeleteIcon fontSize="small" sx={{ mr: 1 }} /> Eliminar | ||||
|             <MenuItem onClick={() => {if (selectedRow) handleDelete(selectedRow.idParte);}}> | ||||
|                 <DeleteIcon fontSize="small" sx={{ mr: 1 }} /> | ||||
|                   Eliminar | ||||
|             </MenuItem> | ||||
|           )} | ||||
|       </Menu> | ||||
| @@ -561,14 +568,13 @@ const GestionarEntradasSalidasCanillaPage: React.FC = () => { | ||||
|       <EntradaSalidaCanillaFormModal | ||||
|         open={modalOpen} | ||||
|         onClose={handleCloseModal} | ||||
|         onSubmit={handleModalEditSubmit} // Este onSubmit es solo para edición | ||||
|         onSubmit={handleModalEditSubmit} | ||||
|         initialData={editingMovimiento} | ||||
|         prefillData={prefillModalData} | ||||
|         errorMessage={apiErrorMessage} | ||||
|         clearErrorMessage={() => setApiErrorMessage(null)} | ||||
|       /> | ||||
|  | ||||
|       {/* ... (Dialog de Liquidación sin cambios) ... */} | ||||
|        <Dialog open={openLiquidarDialog} onClose={handleCloseLiquidarDialog}> | ||||
|         <DialogTitle>Confirmar Liquidación</DialogTitle> | ||||
|         <DialogContent> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user