2025-05-23 15:47:39 -03:00
import React , { useState , useEffect , useCallback } from 'react' ;
import {
Box , Typography , TextField , Button , Paper , IconButton , Menu , MenuItem , Chip ,
Table , TableBody , TableCell , TableContainer , TableHead , TableRow , TablePagination ,
CircularProgress , Alert , FormControl , InputLabel , Select , Checkbox , Tooltip ,
2025-06-06 18:33:09 -03:00
Dialog , DialogActions , DialogContent , DialogContentText , DialogTitle ,
ToggleButtonGroup , ToggleButton
2025-05-23 15:47:39 -03:00
} from '@mui/material' ;
import AddIcon from '@mui/icons-material/Add' ;
2025-06-03 13:45:20 -03:00
import PrintIcon from '@mui/icons-material/Print' ;
2025-05-23 15:47:39 -03:00
import MoreVertIcon from '@mui/icons-material/MoreVert' ;
import EditIcon from '@mui/icons-material/Edit' ;
import DeleteIcon from '@mui/icons-material/Delete' ;
import FilterListIcon from '@mui/icons-material/FilterList' ;
2025-06-03 13:45:20 -03:00
import PlaylistAddCheckIcon from '@mui/icons-material/PlaylistAddCheck' ;
2025-05-23 15:47:39 -03:00
import entradaSalidaCanillaService from '../../services/Distribucion/entradaSalidaCanillaService' ;
import publicacionService from '../../services/Distribucion/publicacionService' ;
import canillaService from '../../services/Distribucion/canillaService' ;
import type { EntradaSalidaCanillaDto } from '../../models/dtos/Distribucion/EntradaSalidaCanillaDto' ;
import type { UpdateEntradaSalidaCanillaDto } from '../../models/dtos/Distribucion/UpdateEntradaSalidaCanillaDto' ;
2025-06-06 18:33:09 -03:00
import type { PublicacionDropdownDto } from '../../models/dtos/Distribucion/PublicacionDropdownDto' ;
2025-05-23 15:47:39 -03:00
import type { CanillaDto } from '../../models/dtos/Distribucion/CanillaDto' ;
import type { LiquidarMovimientosCanillaRequestDto } from '../../models/dtos/Distribucion/LiquidarMovimientosCanillaDto' ;
import EntradaSalidaCanillaFormModal from '../../components/Modals/Distribucion/EntradaSalidaCanillaFormModal' ;
import { usePermissions } from '../../hooks/usePermissions' ;
import axios from 'axios' ;
2025-06-03 13:45:20 -03:00
import reportesService from '../../services/Reportes/reportesService' ;
2025-05-23 15:47:39 -03:00
2025-06-06 18:33:09 -03:00
type TipoDestinatarioFiltro = 'canillitas' | 'accionistas' ;
2025-05-23 15:47:39 -03:00
const GestionarEntradasSalidasCanillaPage : React.FC = ( ) = > {
const [ movimientos , setMovimientos ] = useState < EntradaSalidaCanillaDto [ ] > ( [ ] ) ;
2025-06-06 18:33:09 -03:00
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
2025-05-23 15:47:39 -03:00
2025-06-06 18:33:09 -03:00
const [ filtroFecha , setFiltroFecha ] = useState < string > ( new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ) ;
2025-05-23 15:47:39 -03:00
const [ filtroIdPublicacion , setFiltroIdPublicacion ] = useState < number | string > ( '' ) ;
2025-06-06 18:33:09 -03:00
const [ filtroIdCanillitaSeleccionado , setFiltroIdCanillitaSeleccionado ] = useState < number | string > ( '' ) ;
const [ filtroTipoDestinatario , setFiltroTipoDestinatario ] = useState < TipoDestinatarioFiltro > ( 'canillitas' ) ;
2025-05-23 15:47:39 -03:00
2025-06-06 18:33:09 -03:00
const [ loadingTicketPdf , setLoadingTicketPdf ] = useState ( false ) ;
const [ publicaciones , setPublicaciones ] = useState < PublicacionDropdownDto [ ] > ( [ ] ) ;
const [ destinatariosDropdown , setDestinatariosDropdown ] = useState < CanillaDto [ ] > ( [ ] ) ;
2025-05-23 15:47:39 -03:00
const [ loadingFiltersDropdown , setLoadingFiltersDropdown ] = useState ( false ) ;
const [ modalOpen , setModalOpen ] = useState ( false ) ;
const [ editingMovimiento , setEditingMovimiento ] = useState < EntradaSalidaCanillaDto | null > ( null ) ;
2025-06-06 18:33:09 -03:00
const [ prefillModalData , setPrefillModalData ] = useState < {
fecha? : string ;
idCanilla? : number | string ;
nombreCanilla? : string ; // << AÑADIDO PARA PASAR AL MODAL
idPublicacion? : number | string ;
} | null > ( null ) ;
2025-05-23 15:47:39 -03:00
const [ page , setPage ] = useState ( 0 ) ;
2025-06-03 18:42:56 -03:00
const [ rowsPerPage , setRowsPerPage ] = useState ( 25 ) ;
2025-05-23 15:47:39 -03:00
const [ anchorEl , setAnchorEl ] = useState < null | HTMLElement > ( null ) ;
const [ selectedRow , setSelectedRow ] = useState < EntradaSalidaCanillaDto | null > ( null ) ;
const [ selectedIdsParaLiquidar , setSelectedIdsParaLiquidar ] = useState < Set < number > > ( new Set ( ) ) ;
const [ fechaLiquidacionDialog , setFechaLiquidacionDialog ] = useState < string > ( new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ) ;
const [ openLiquidarDialog , setOpenLiquidarDialog ] = useState ( false ) ;
const { tienePermiso , isSuperAdmin } = usePermissions ( ) ;
const puedeVer = isSuperAdmin || tienePermiso ( "MC001" ) ;
const puedeCrear = isSuperAdmin || tienePermiso ( "MC002" ) ;
const puedeModificar = isSuperAdmin || tienePermiso ( "MC003" ) ;
const puedeEliminar = isSuperAdmin || tienePermiso ( "MC004" ) ;
const puedeLiquidar = isSuperAdmin || tienePermiso ( "MC005" ) ;
const puedeEliminarLiquidados = isSuperAdmin || tienePermiso ( "MC006" ) ;
2025-06-03 13:45:20 -03:00
const formatDate = ( dateString? : string | null ) : string = > {
if ( ! dateString ) return '-' ;
const datePart = dateString . split ( 'T' ) [ 0 ] ;
const parts = datePart . split ( '-' ) ;
2025-06-06 18:33:09 -03:00
if ( parts . length === 3 ) { return ` ${ parts [ 2 ] } / ${ parts [ 1 ] } / ${ parts [ 0 ] } ` ; }
2025-06-03 13:45:20 -03:00
return datePart ;
} ;
2025-06-06 18:33:09 -03:00
useEffect ( ( ) = > {
const fetchPublicaciones = async ( ) = > {
setLoadingFiltersDropdown ( true ) ; // Mover al inicio de la carga de pubs
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
} finally {
// No poner setLoadingFiltersDropdown(false) aquí, esperar a que ambas cargas terminen
}
} ;
fetchPublicaciones ( ) ;
} , [ ] ) ;
const fetchDestinatariosParaDropdown = useCallback ( async ( ) = > {
setLoadingFiltersDropdown ( true ) ; // Poner al inicio de esta carga también
setFiltroIdCanillitaSeleccionado ( '' ) ;
setDestinatariosDropdown ( [ ] ) ;
setError ( null ) ; // Limpiar errores de carga de dropdowns previos
2025-05-23 15:47:39 -03:00
try {
2025-06-06 18:33:09 -03:00
const esAccionistaFilter = filtroTipoDestinatario === 'accionistas' ;
const data = await canillaService . getAllCanillas ( undefined , undefined , true , esAccionistaFilter ) ;
setDestinatariosDropdown ( data ) ;
2025-05-23 15:47:39 -03:00
} catch ( err ) {
2025-06-06 18:33:09 -03:00
console . error ( "Error cargando destinatarios para filtro:" , err ) ;
setError ( "Error al cargar canillitas/accionistas." ) ; // Usar error general
} finally {
setLoadingFiltersDropdown ( false ) ; // Poner al final de AMBAS cargas de dropdown
}
} , [ filtroTipoDestinatario ] ) ;
useEffect ( ( ) = > {
fetchDestinatariosParaDropdown ( ) ;
} , [ fetchDestinatariosParaDropdown ] ) ;
2025-05-23 15:47:39 -03:00
const cargarMovimientos = useCallback ( async ( ) = > {
2025-06-06 18:33:09 -03:00
if ( ! puedeVer ) { setError ( "No tiene permiso para ver esta sección." ) ; setLoading ( false ) ; return ; }
if ( ! filtroFecha || ! filtroIdCanillitaSeleccionado ) {
if ( loading ) setLoading ( false ) ;
setMovimientos ( [ ] ) ;
return ;
}
2025-05-23 15:47:39 -03:00
setLoading ( true ) ; setError ( null ) ; setApiErrorMessage ( null ) ;
try {
const params = {
2025-06-06 18:33:09 -03:00
fechaDesde : filtroFecha ,
fechaHasta : filtroFecha ,
2025-05-23 15:47:39 -03:00
idPublicacion : filtroIdPublicacion ? Number ( filtroIdPublicacion ) : null ,
2025-06-06 18:33:09 -03:00
idCanilla : Number ( filtroIdCanillitaSeleccionado ) ,
liquidados : null ,
incluirNoLiquidados : null ,
2025-05-23 15:47:39 -03:00
} ;
const data = await entradaSalidaCanillaService . getAllEntradasSalidasCanilla ( params ) ;
setMovimientos ( data ) ;
2025-06-06 18:33:09 -03:00
setSelectedIdsParaLiquidar ( new Set ( ) ) ;
2025-05-23 15:47:39 -03:00
} catch ( err ) {
2025-06-06 18:33:09 -03:00
console . error ( "Error al cargar movimientos:" , err ) ;
setError ( 'Error al cargar movimientos.' ) ;
setMovimientos ( [ ] ) ;
} finally {
setLoading ( false ) ;
}
} , [ puedeVer , filtroFecha , filtroIdPublicacion , filtroIdCanillitaSeleccionado ] ) ;
useEffect ( ( ) = > {
if ( filtroFecha && filtroIdCanillitaSeleccionado ) {
cargarMovimientos ( ) ;
} else {
setMovimientos ( [ ] ) ;
if ( loading ) setLoading ( false ) ; // Asegurar que no se quede en loading si los filtros se limpian
}
} , [ cargarMovimientos , filtroFecha , filtroIdCanillitaSeleccionado ] ) ; // `cargarMovimientos` ya tiene sus dependencias
2025-05-23 15:47:39 -03:00
const handleOpenModal = ( item? : EntradaSalidaCanillaDto ) = > {
2025-06-06 18:33:09 -03:00
if ( ! puedeCrear && ! item ) {
setApiErrorMessage ( "No tiene permiso para registrar nuevos movimientos." ) ;
return ;
}
if ( item && ! puedeModificar ) {
setApiErrorMessage ( "No tiene permiso para modificar movimientos." ) ;
return ;
}
if ( item ) {
setEditingMovimiento ( item ) ;
setPrefillModalData ( null ) ;
} else {
// --- CAMBIO: Obtener nombre del canillita seleccionado para prefill ---
const canillitaSeleccionado = destinatariosDropdown . find (
c = > c . idCanilla === Number ( filtroIdCanillitaSeleccionado )
) ;
setEditingMovimiento ( null ) ;
setPrefillModalData ( {
fecha : filtroFecha ,
idCanilla : filtroIdCanillitaSeleccionado ,
nombreCanilla : canillitaSeleccionado?.nomApe , // << AÑADIR NOMBRE
idPublicacion : filtroIdPublicacion
} ) ;
}
setApiErrorMessage ( null ) ;
setModalOpen ( true ) ;
2025-05-23 15:47:39 -03:00
} ;
2025-06-06 18:33:09 -03:00
// ... handleDelete, handleMenuOpen, handleMenuClose, handleSelectRowForLiquidar, handleSelectAllForLiquidar, handleOpenLiquidarDialog, handleCloseLiquidarDialog sin cambios ...
2025-05-23 15:47:39 -03:00
const handleDelete = async ( idParte : number ) = > {
if ( window . confirm ( ` ¿Seguro de eliminar este movimiento (ID: ${ idParte } )? ` ) ) {
setApiErrorMessage ( null ) ;
try { await entradaSalidaCanillaService . deleteEntradaSalidaCanilla ( idParte ) ; cargarMovimientos ( ) ; }
catch ( err : any ) { const msg = axios . isAxiosError ( err ) && err . response ? . data ? . message ? err . response . data . message : 'Error al eliminar.' ; setApiErrorMessage ( msg ) ; }
}
handleMenuClose ( ) ;
} ;
const handleMenuOpen = ( event : React.MouseEvent < HTMLElement > , item : EntradaSalidaCanillaDto ) = > {
2025-06-03 13:45:20 -03:00
event . currentTarget . setAttribute ( 'data-rowid' , item . idParte . toString ( ) ) ;
setAnchorEl ( event . currentTarget ) ;
setSelectedRow ( item ) ;
2025-05-23 15:47:39 -03:00
} ;
const handleMenuClose = ( ) = > { setAnchorEl ( null ) ; setSelectedRow ( null ) ; } ;
const handleSelectRowForLiquidar = ( idParte : number ) = > {
setSelectedIdsParaLiquidar ( prev = > {
const newSet = new Set ( prev ) ;
if ( newSet . has ( idParte ) ) newSet . delete ( idParte ) ;
else newSet . add ( idParte ) ;
return newSet ;
} ) ;
} ;
const handleSelectAllForLiquidar = ( event : React.ChangeEvent < HTMLInputElement > ) = > {
2025-06-06 18:33:09 -03:00
if ( event . target . checked ) {
2025-05-23 15:47:39 -03:00
const newSelectedIds = new Set ( movimientos . filter ( m = > ! m . liquidado ) . map ( m = > m . idParte ) ) ;
setSelectedIdsParaLiquidar ( newSelectedIds ) ;
} else {
setSelectedIdsParaLiquidar ( new Set ( ) ) ;
}
} ;
const handleOpenLiquidarDialog = ( ) = > {
if ( selectedIdsParaLiquidar . size === 0 ) {
setApiErrorMessage ( "Seleccione al menos un movimiento para liquidar." ) ;
return ;
}
setOpenLiquidarDialog ( true ) ;
} ;
const handleCloseLiquidarDialog = ( ) = > setOpenLiquidarDialog ( false ) ;
2025-06-03 13:45:20 -03:00
2025-06-06 18:33:09 -03:00
const handleConfirmLiquidar = async ( ) = > {
if ( selectedIdsParaLiquidar . size === 0 ) { /* ... */ return ; }
if ( ! fechaLiquidacionDialog ) { /* ... */ return ; }
// ... (validación de fecha sin cambios)
const fechaLiquidacionDate = new Date ( fechaLiquidacionDialog + 'T00:00:00Z' ) ;
2025-06-03 13:45:20 -03:00
let fechaMovimientoMasReciente : Date | null = null ;
selectedIdsParaLiquidar . forEach ( idParte = > {
const movimiento = movimientos . find ( m = > m . idParte === idParte ) ;
2025-06-06 18:33:09 -03:00
if ( movimiento && movimiento . fecha ) {
const movFecha = new Date ( movimiento . fecha . split ( 'T' ) [ 0 ] + 'T00:00:00Z' ) ;
if ( fechaMovimientoMasReciente === null || movFecha . getTime ( ) > ( fechaMovimientoMasReciente as Date ) . getTime ( ) ) {
2025-06-03 13:45:20 -03:00
fechaMovimientoMasReciente = movFecha ;
}
}
} ) ;
2025-06-06 18:33:09 -03:00
if ( fechaMovimientoMasReciente !== null && fechaLiquidacionDate . getTime ( ) < ( fechaMovimientoMasReciente as Date ) . getTime ( ) ) {
2025-06-03 13:45:20 -03:00
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 ) ;
2025-06-06 18:33:09 -03:00
setLoading ( true ) ;
2025-05-23 15:47:39 -03:00
const liquidarDto : LiquidarMovimientosCanillaRequestDto = {
idsPartesALiquidar : Array.from ( selectedIdsParaLiquidar ) ,
2025-06-06 18:33:09 -03:00
fechaLiquidacion : fechaLiquidacionDialog
2025-05-23 15:47:39 -03:00
} ;
try {
await entradaSalidaCanillaService . liquidarMovimientos ( liquidarDto ) ;
2025-06-06 18:33:09 -03:00
setOpenLiquidarDialog ( false ) ;
2025-06-03 13:45:20 -03:00
const primerIdParteLiquidado = Array . from ( selectedIdsParaLiquidar ) [ 0 ] ;
const movimientoParaTicket = movimientos . find ( m = > m . idParte === primerIdParteLiquidado ) ;
2025-06-06 18:33:09 -03:00
await cargarMovimientos ( ) ;
2025-06-03 13:45:20 -03:00
2025-06-06 18:33:09 -03:00
// --- CAMBIO: NO IMPRIMIR TICKET SI ES ACCIONISTA ---
if ( movimientoParaTicket && ! movimientoParaTicket . canillaEsAccionista ) {
console . log ( "Liquidación exitosa, generando ticket para canillita NO accionista:" , movimientoParaTicket . idCanilla ) ;
2025-06-03 13:45:20 -03:00
await handleImprimirTicketLiquidacion (
movimientoParaTicket . idCanilla ,
2025-06-06 18:33:09 -03:00
fechaLiquidacionDialog ,
false // esAccionista = false
2025-06-03 13:45:20 -03:00
) ;
2025-06-06 18:33:09 -03:00
} else if ( movimientoParaTicket && movimientoParaTicket . canillaEsAccionista ) {
console . log ( "Liquidación exitosa para accionista. No se genera ticket automáticamente." ) ;
2025-06-03 13:45:20 -03:00
} else {
2025-06-06 18:33:09 -03:00
console . warn ( "No se pudo encontrar información del movimiento para ticket post-liquidación." ) ;
2025-06-03 13:45:20 -03:00
}
2025-05-23 15:47:39 -03:00
} catch ( err : any ) {
const msg = axios . isAxiosError ( err ) && err . response ? . data ? . message ? err . response . data . message : 'Error al liquidar.' ;
2025-06-06 18:33:09 -03:00
setApiErrorMessage ( msg ) ;
2025-05-23 15:47:39 -03:00
} finally {
setLoading ( false ) ;
}
} ;
2025-06-03 13:45:20 -03:00
const handleModalEditSubmit = async ( data : UpdateEntradaSalidaCanillaDto , idParte : number ) = > {
2025-06-06 18:33:09 -03:00
// ... (sin cambios)
2025-06-03 13:45:20 -03:00
setApiErrorMessage ( null ) ;
try {
await entradaSalidaCanillaService . updateEntradaSalidaCanilla ( idParte , data ) ;
} catch ( err : any ) {
const message = axios . isAxiosError ( err ) && err . response ? . data ? . message ? err . response . data . message : 'Error al guardar los cambios.' ;
setApiErrorMessage ( message ) ;
throw err ;
}
} ;
const handleCloseModal = ( ) = > {
setModalOpen ( false ) ;
setEditingMovimiento ( null ) ;
2025-06-06 18:33:09 -03:00
setPrefillModalData ( null ) ;
2025-06-03 13:45:20 -03:00
if ( ! apiErrorMessage ) {
cargarMovimientos ( ) ;
}
} ;
const handleImprimirTicketLiquidacion = useCallback ( async (
2025-06-06 18:33:09 -03:00
idCanilla : number , fecha : string , esAccionista : boolean
2025-06-03 13:45:20 -03:00
) = > {
2025-06-06 18:33:09 -03:00
// ... (sin cambios)
2025-06-03 13:45:20 -03:00
setLoadingTicketPdf ( true ) ;
setApiErrorMessage ( null ) ;
try {
2025-06-06 18:33:09 -03:00
const params = { fecha : fecha.split ( 'T' ) [ 0 ] , idCanilla , esAccionista } ;
2025-06-03 13:45:20 -03:00
const blob = await reportesService . getTicketLiquidacionCanillaPdf ( params ) ;
if ( blob . type === "application/json" ) {
const text = await blob . text ( ) ;
const msg = JSON . parse ( text ) . message ? ? "Error inesperado al generar el ticket PDF." ;
setApiErrorMessage ( msg ) ;
} else {
const url = URL . createObjectURL ( blob ) ;
const w = window . open ( url , '_blank' ) ;
if ( ! w ) alert ( "Permita popups para ver el PDF del ticket." ) ;
}
} catch ( error : any ) {
2025-06-06 18:33:09 -03:00
console . error ( "Error al generar ticket:" , error ) ;
const message = axios . isAxiosError ( error ) && error . response ? . data ? . message ? error . response . data . message : 'Error al generar ticket.' ;
2025-06-03 13:45:20 -03:00
setApiErrorMessage ( message ) ;
2025-06-06 18:33:09 -03:00
} finally { setLoadingTicketPdf ( false ) ; }
} , [ ] ) ;
2025-06-03 13:45:20 -03:00
2025-05-23 15:47:39 -03:00
const handleChangePage = ( _event : unknown , newPage : number ) = > setPage ( newPage ) ;
const handleChangeRowsPerPage = ( event : React.ChangeEvent < HTMLInputElement > ) = > {
setRowsPerPage ( parseInt ( event . target . value , 10 ) ) ; setPage ( 0 ) ;
} ;
const displayData = movimientos . slice ( page * rowsPerPage , page * rowsPerPage + rowsPerPage ) ;
2025-06-06 18:33:09 -03:00
if ( ! loading && ! puedeVer && ! loadingFiltersDropdown && movimientos . length === 0 && ! filtroFecha && ! filtroIdCanillitaSeleccionado ) { // Modificado para solo mostrar si no hay filtros y no puede ver
return < Box sx = { { p : 2 } } > < Alert severity = "error" > { error || "Acceso denegado." } < / Alert > < / Box > ;
}
2025-05-23 15:47:39 -03:00
const numSelectedToLiquidate = selectedIdsParaLiquidar . size ;
const numNotLiquidatedOnPage = displayData . filter ( m = > ! m . liquidado ) . length ;
return (
2025-06-03 13:45:20 -03:00
< Box sx = { { p : 1 } } >
2025-06-06 18:33:09 -03:00
< Typography variant = "h5" gutterBottom > Entradas / Salidas Canillitas & Accionistas < / Typography >
2025-05-23 15:47:39 -03:00
< 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 } } >
2025-06-06 18:33:09 -03:00
< 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
helperText = { ! filtroFecha ? "Fecha es obligatoria" : "" }
/ >
< ToggleButtonGroup
color = "primary"
value = { filtroTipoDestinatario }
exclusive
onChange = { ( _ , newValue : TipoDestinatarioFiltro | null ) = > {
if ( newValue !== null ) {
setFiltroTipoDestinatario ( newValue ) ;
}
} }
aria - label = "Tipo de Destinatario"
size = "small"
>
< ToggleButton value = "canillitas" > Canillitas < / ToggleButton >
< ToggleButton value = "accionistas" > Accionistas < / ToggleButton >
< / ToggleButtonGroup >
< FormControl size = "small" sx = { { minWidth : 220 , flexGrow : 1 } } disabled = { loadingFiltersDropdown } required error = { ! filtroIdCanillitaSeleccionado } >
< InputLabel > { filtroTipoDestinatario === 'canillitas' ? 'Canillita' : 'Accionista' } < / InputLabel >
< Select
value = { filtroIdCanillitaSeleccionado }
label = { filtroTipoDestinatario === 'canillitas' ? 'Canillita' : 'Accionista' }
onChange = { ( e ) = > setFiltroIdCanillitaSeleccionado ( e . target . value as number | string ) }
>
< 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 > ) }
< / Select >
{ ! filtroIdCanillitaSeleccionado && < Typography component = "p" color = "error" variant = "caption" sx = { { ml :1.5 , fontSize : '0.65rem' } } > Selección obligatoria < / Typography > }
< / FormControl >
2025-05-23 15:47:39 -03:00
< FormControl size = "small" sx = { { minWidth : 180 , flexGrow : 1 } } disabled = { loadingFiltersDropdown } >
2025-06-06 18:33:09 -03:00
< InputLabel > Publicación ( Opcional ) < / InputLabel >
< Select value = { filtroIdPublicacion } label = "Publicación (Opcional)" onChange = { ( e ) = > setFiltroIdPublicacion ( e . target . value as number | string ) } >
2025-05-23 15:47:39 -03:00
< MenuItem value = "" > < em > Todas < / em > < / MenuItem >
{ publicaciones . map ( p = > < MenuItem key = { p . idPublicacion } value = { p . idPublicacion } > { p . nombre } < / MenuItem > ) }
< / Select >
< / FormControl >
< / Box >
< Box sx = { { display : 'flex' , justifyContent : 'space-between' , alignItems : 'center' } } >
2025-06-06 18:33:09 -03:00
{ /* --- CAMBIO: DESHABILITAR BOTÓN SI FILTROS OBLIGATORIOS NO ESTÁN --- */ }
{ puedeCrear && (
< Button
variant = "contained"
startIcon = { < AddIcon / > }
onClick = { ( ) = > handleOpenModal ( ) }
disabled = { ! filtroFecha || ! filtroIdCanillitaSeleccionado } // <<-- AÑADIDO
>
Registrar Movimiento
< / Button >
) }
{ puedeLiquidar && numSelectedToLiquidate > 0 && movimientos . some ( m = > selectedIdsParaLiquidar . has ( m . idParte ) && ! m . liquidado ) && (
2025-05-23 15:47:39 -03:00
< Button variant = "contained" color = "success" startIcon = { < PlaylistAddCheckIcon / > } onClick = { handleOpenLiquidarDialog } >
Liquidar Seleccionados ( { numSelectedToLiquidate } )
< / Button >
) }
< / Box >
< / Paper >
2025-06-06 18:33:09 -03:00
{ ! 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 > }
2025-05-23 15:47:39 -03:00
{ loading && < Box sx = { { display : 'flex' , justifyContent : 'center' , my : 2 } } > < CircularProgress / > < / Box > }
2025-06-06 18:33:09 -03:00
{ /* 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 > }
2025-05-23 15:47:39 -03:00
{ apiErrorMessage && < Alert severity = "error" sx = { { my : 2 } } > { apiErrorMessage } < / Alert > }
2025-06-03 13:45:20 -03:00
{ loadingTicketPdf &&
< Box sx = { { display : 'flex' , justifyContent : 'center' , alignItems : 'center' , my : 2 } } >
< CircularProgress size = { 20 } sx = { { mr : 1 } } / >
< Typography variant = "body2" > Cargando ticket . . . < / Typography >
< / Box >
}
2025-05-23 15:47:39 -03:00
2025-06-06 18:33:09 -03:00
{ ! loading && ! error && puedeVer && filtroFecha && filtroIdCanillitaSeleccionado && (
// ... (Tabla y Paginación sin cambios)
< TableContainer component = { Paper } >
2025-05-23 15:47:39 -03:00
< Table size = "small" >
2025-06-03 13:45:20 -03:00
< TableHead >
< TableRow >
2025-06-06 18:33:09 -03:00
{ puedeLiquidar && (
2025-06-03 13:45:20 -03:00
< TableCell padding = "checkbox" >
< Checkbox
indeterminate = { numSelectedToLiquidate > 0 && numSelectedToLiquidate < numNotLiquidatedOnPage && numNotLiquidatedOnPage > 0 }
checked = { numNotLiquidatedOnPage > 0 && numSelectedToLiquidate === numNotLiquidatedOnPage }
onChange = { handleSelectAllForLiquidar }
disabled = { numNotLiquidatedOnPage === 0 }
/ >
< / TableCell >
) }
< TableCell > Fecha < / TableCell >
< TableCell > Publicación < / TableCell >
2025-06-06 18:33:09 -03:00
< TableCell > { filtroTipoDestinatario === 'canillitas' ? 'Canillita' : 'Accionista' } < / TableCell >
2025-06-03 13:45:20 -03:00
< TableCell align = "right" > Salida < / TableCell >
< TableCell align = "right" > Entrada < / TableCell >
< TableCell align = "right" > Vendidos < / TableCell >
< TableCell align = "right" > A Rendir < / TableCell >
< TableCell > Liquidado < / TableCell >
< TableCell > F . Liq . < / TableCell >
< TableCell > Obs . < / TableCell >
{ ( puedeModificar || puedeEliminar || puedeLiquidar ) && < TableCell align = "right" > Acciones < / TableCell > }
< / TableRow >
< / TableHead >
2025-05-23 15:47:39 -03:00
< TableBody >
{ displayData . length === 0 ? (
2025-06-03 13:45:20 -03:00
< TableRow >
< TableCell
colSpan = {
2025-06-06 18:33:09 -03:00
( puedeLiquidar ? 1 : 0 ) + 9 + ( ( puedeModificar || puedeEliminar || puedeLiquidar ) ? 1 : 0 )
2025-06-03 13:45:20 -03:00
}
align = "center"
>
2025-06-06 18:33:09 -03:00
No se encontraron movimientos con los filtros aplicados .
2025-06-03 13:45:20 -03:00
< / TableCell >
< / TableRow >
2025-05-23 15:47:39 -03:00
) : (
displayData . map ( ( m ) = > (
< TableRow key = { m . idParte } hover selected = { selectedIdsParaLiquidar . has ( m . idParte ) } >
2025-06-06 18:33:09 -03:00
{ puedeLiquidar && (
2025-05-23 15:47:39 -03:00
< TableCell padding = "checkbox" >
< Checkbox
checked = { selectedIdsParaLiquidar . has ( m . idParte ) }
onChange = { ( ) = > handleSelectRowForLiquidar ( m . idParte ) }
disabled = { m . liquidado }
/ >
< / TableCell >
2025-06-03 13:45:20 -03:00
) }
2025-05-23 15:47:39 -03:00
< TableCell > { formatDate ( m . fecha ) } < / TableCell >
< TableCell > { m . nombrePublicacion } < / TableCell >
< TableCell > { m . nomApeCanilla } < / TableCell >
< TableCell align = "right" > { m . cantSalida } < / TableCell >
< TableCell align = "right" > { m . cantEntrada } < / TableCell >
< TableCell align = "right" sx = { { fontWeight : 'bold' } } > { m . vendidos } < / TableCell >
2025-06-03 13:45:20 -03:00
< TableCell align = "right" sx = { { fontWeight : 'bold' } } >
{ m . montoARendir . toLocaleString ( 'es-AR' , { style : 'currency' , currency : 'ARS' } ) }
< / TableCell >
2025-05-23 15:47:39 -03:00
< TableCell align = "center" > { m . liquidado ? < Chip label = "Sí" color = "success" size = "small" / > : < Chip label = "No" size = "small" / > } < / TableCell >
< TableCell > { m . fechaLiquidado ? formatDate ( m . fechaLiquidado ) : '-' } < / TableCell >
2025-06-03 13:45:20 -03:00
< TableCell >
< Tooltip title = { m . observacion || '' } >
< Box sx = { { maxWidth : 100 , overflow : 'hidden' , textOverflow : 'ellipsis' , whiteSpace : 'nowrap' } } >
{ m . observacion || '-' }
< / Box >
< / Tooltip >
< / TableCell >
{ ( puedeModificar || puedeEliminar || puedeLiquidar ) && (
2025-05-23 15:47:39 -03:00
< TableCell align = "right" >
2025-06-03 13:45:20 -03:00
< IconButton
onClick = { ( e ) = > handleMenuOpen ( e , m ) }
2025-06-06 18:33:09 -03:00
data - rowid = { m . idParte . toString ( ) }
disabled = { m . liquidado && ! puedeEliminarLiquidados && ! puedeLiquidar }
2025-06-03 13:45:20 -03:00
>
< MoreVertIcon / >
< / IconButton >
2025-05-23 15:47:39 -03:00
< / TableCell >
) }
< / TableRow >
) ) ) }
< / TableBody >
< / Table >
< TablePagination
2025-06-03 18:42:56 -03:00
rowsPerPageOptions = { [ 25 , 50 , 100 ] } component = "div" count = { movimientos . length }
2025-05-23 15:47:39 -03:00
rowsPerPage = { rowsPerPage } page = { page } onPageChange = { handleChangePage }
onRowsPerPageChange = { handleChangeRowsPerPage } labelRowsPerPage = "Filas por página:"
/ >
< / TableContainer >
) }
< 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 > ) }
2025-06-06 18:33:09 -03:00
{ /* --- CAMBIO: MOSTRAR REIMPRIMIR TICKET SIEMPRE SI ESTÁ LIQUIDADO --- */ }
{ selectedRow && selectedRow . liquidado && puedeLiquidar && ( // Usar puedeLiquidar para consistencia
2025-06-03 13:45:20 -03:00
< MenuItem
onClick = { ( ) = > {
2025-06-06 18:33:09 -03:00
if ( selectedRow ) {
2025-06-03 13:45:20 -03:00
handleImprimirTicketLiquidacion (
selectedRow . idCanilla ,
2025-06-06 18:33:09 -03:00
selectedRow . fechaLiquidado || selectedRow . fecha ,
selectedRow . canillaEsAccionista // Pasar si es accionista
2025-06-03 13:45:20 -03:00
) ;
}
} }
disabled = { loadingTicketPdf }
>
< PrintIcon fontSize = "small" sx = { { mr : 1 } } / >
{ loadingTicketPdf && < CircularProgress size = { 16 } sx = { { mr : 1 } } / > }
Reimprimir Ticket Liq .
< / MenuItem >
) }
2025-06-06 18:33:09 -03:00
{ selectedRow && (
2025-06-03 13:45:20 -03:00
( ( ! selectedRow . liquidado && puedeEliminar ) || ( selectedRow . liquidado && puedeEliminarLiquidados ) )
2025-05-23 15:47:39 -03:00
) && (
2025-06-06 18:33:09 -03:00
< MenuItem onClick = { ( ) = > { if ( selectedRow ) handleDelete ( selectedRow . idParte ) ; } } >
2025-05-23 15:47:39 -03:00
< DeleteIcon fontSize = "small" sx = { { mr : 1 } } / > Eliminar
< / MenuItem >
) }
< / Menu >
< EntradaSalidaCanillaFormModal
2025-06-03 13:45:20 -03:00
open = { modalOpen }
onClose = { handleCloseModal }
2025-06-06 18:33:09 -03:00
onSubmit = { handleModalEditSubmit } // Este onSubmit es solo para edición
2025-06-03 13:45:20 -03:00
initialData = { editingMovimiento }
2025-06-06 18:33:09 -03:00
prefillData = { prefillModalData }
2025-06-03 13:45:20 -03:00
errorMessage = { apiErrorMessage }
2025-05-23 15:47:39 -03:00
clearErrorMessage = { ( ) = > setApiErrorMessage ( null ) }
/ >
2025-06-06 18:33:09 -03:00
{ /* ... (Dialog de Liquidación sin cambios) ... */ }
< Dialog open = { openLiquidarDialog } onClose = { handleCloseLiquidarDialog } >
2025-05-23 15:47:39 -03:00
< DialogTitle > Confirmar Liquidación < / DialogTitle >
< DialogContent >
< DialogContentText >
Se marcarán como liquidados { selectedIdsParaLiquidar . size } movimiento ( s ) .
< / DialogContentText >
< TextField autoFocus margin = "dense" id = "fechaLiquidacion" label = "Fecha de Liquidación" type = "date"
fullWidth variant = "standard" value = { fechaLiquidacionDialog }
onChange = { ( e ) = > setFechaLiquidacionDialog ( e . target . value ) }
InputLabelProps = { { shrink : true } }
/ >
< / DialogContent >
< DialogActions >
< Button onClick = { handleCloseLiquidarDialog } color = "secondary" disabled = { loading } > Cancelar < / Button >
< Button onClick = { handleConfirmLiquidar } variant = "contained" color = "primary" disabled = { loading || ! fechaLiquidacionDialog } >
{ loading ? < CircularProgress size = { 24 } / > : "Liquidar" }
< / Button >
< / DialogActions >
< / Dialog >
< / Box >
) ;
} ;
export default GestionarEntradasSalidasCanillaPage ;