From b04a3b99bf2a45834787bea2220e2af89715eff2 Mon Sep 17 00:00:00 2001 From: eldiadmolinari Date: Thu, 12 Jun 2025 19:36:21 -0300 Subject: [PATCH] =?UTF-8?q?1.=20Funcionalidad=20Principal:=20Auditor=C3=AD?= =?UTF-8?q?a=20General=20Se=20cre=C3=B3=20una=20nueva=20secci=C3=B3n=20de?= =?UTF-8?q?=20"Auditor=C3=ADa"=20en=20la=20aplicaci=C3=B3n,=20dise=C3=B1ad?= =?UTF-8?q?a=20para=20ser=20accedida=20por=20SuperAdmins.=20Se=20implement?= =?UTF-8?q?=C3=B3=20una=20p=C3=A1gina=20AuditoriaGeneralPage.tsx=20que=20a?= =?UTF-8?q?ct=C3=BAa=20como=20un=20visor=20centralizado=20para=20el=20hist?= =?UTF-8?q?orial=20de=20cambios=20de=20m=C3=BAltiples=20entidades=20del=20?= =?UTF-8?q?sistema.=202.=20Backend:=20Nuevo=20Controlador=20(AuditoriaCont?= =?UTF-8?q?roller.cs):=20Centraliza=20los=20endpoints=20para=20obtener=20d?= =?UTF-8?q?atos=20de=20las=20tablas=20de=20historial=20(=5FH).=20Servicios?= =?UTF-8?q?=20y=20Repositorios=20Extendidos:=20Se=20a=C3=B1adieron=20m?= =?UTF-8?q?=C3=A9todos=20GetHistorialAsync=20y=20ObtenerHistorialAsync=20a?= =?UTF-8?q?=20las=20capas=20de=20repositorio=20y=20servicio=20para=20cada?= =?UTF-8?q?=20una=20de=20las=20siguientes=20entidades,=20permitiendo=20con?= =?UTF-8?q?sultar=20sus=20tablas=20=5FH=20con=20filtros:=20Usuarios=20(gra?= =?UTF-8?q?l=5FUsuarios=5FH)=20Pagos=20de=20Distribuidores=20(cue=5FPagosD?= =?UTF-8?q?istribuidor=5FH)=20Notas=20de=20Cr=C3=A9dito/D=C3=A9bito=20(cue?= =?UTF-8?q?=5FCreditosDebitos=5FH)=20Entradas/Salidas=20de=20Distribuidore?= =?UTF-8?q?s=20(dist=5FEntradasSalidas=5FH)=20Entradas/Salidas=20de=20Cani?= =?UTF-8?q?llitas=20(dist=5FEntradasSalidasCanillas=5FH)=20Novedades=20de?= =?UTF-8?q?=20Canillitas=20(dist=5FdtNovedadesCanillas=5FH)=20Tipos=20de?= =?UTF-8?q?=20Pago=20(cue=5FdtTipopago=5FH)=20Canillitas=20(Maestro)=20(di?= =?UTF-8?q?st=5FdtCanillas=5FH)=20Distribuidores=20(Maestro)=20(dist=5FdtD?= =?UTF-8?q?istribuidores=5FH)=20Empresas=20(Maestro)=20(dist=5FdtEmpresas?= =?UTF-8?q?=5FH)=20Zonas=20(Maestro)=20(dist=5FdtZonas=5FH)=20Otros=20Dest?= =?UTF-8?q?inos=20(Maestro)=20(dist=5FdtOtrosDestinos=5FH)=20Publicaciones?= =?UTF-8?q?=20(Maestro)=20(dist=5FdtPublicaciones=5FH)=20Secciones=20de=20?= =?UTF-8?q?Publicaci=C3=B3n=20(dist=5FdtPubliSecciones=5FH)=20Precios=20de?= =?UTF-8?q?=20Publicaci=C3=B3n=20(dist=5FPrecios=5FH)=20Recargos=20por=20Z?= =?UTF-8?q?ona=20(dist=5FRecargoZona=5FH)=20Porcentajes=20Pago=20Distribui?= =?UTF-8?q?dores=20(dist=5FPorcPago=5FH)=20Porcentajes/Montos=20Canillita?= =?UTF-8?q?=20(dist=5FPorcMonPagoCanilla=5FH)=20Control=20de=20Devolucione?= =?UTF-8?q?s=20(dist=5FdtCtrlDevoluciones=5FH)=20Tipos=20de=20Bobina=20(bo?= =?UTF-8?q?b=5FdtBobinas=5FH)=20Estados=20de=20Bobina=20(bob=5FdtEstadosBo?= =?UTF-8?q?binas=5FH)=20Plantas=20de=20Impresi=C3=B3n=20(bob=5FdtPlantas?= =?UTF-8?q?=5FH)=20Stock=20de=20Bobinas=20(bob=5FStockBobinas=5FH)=20Tirad?= =?UTF-8?q?as=20(Registro=20Principal)=20(bob=5FRegTiradas=5FH)=20Seccione?= =?UTF-8?q?s=20de=20Tirada=20(bob=5FRegPublicaciones=5FH)=20Cambios=20de?= =?UTF-8?q?=20Parada=20de=20Canillitas=20(dist=5FCambiosParadasCanillas=5F?= =?UTF-8?q?H)=20Ajustes=20Manuales=20de=20Saldo=20(cue=5FSaldoAjustesHisto?= =?UTF-8?q?rial)=20DTOs=20de=20Historial:=20Se=20crearon=20DTOs=20espec?= =?UTF-8?q?=C3=ADficos=20para=20cada=20tabla=20de=20historial=20(ej.=20Usu?= =?UTF-8?q?arioHistorialDto,=20PagoDistribuidorHistorialDto,=20etc.)=20par?= =?UTF-8?q?a=20transferir=20los=20datos=20al=20frontend,=20incluyendo=20el?= =?UTF-8?q?=20nombre=20del=20usuario=20que=20realiz=C3=B3=20la=20modificac?= =?UTF-8?q?i=C3=B3n.=20Correcci=C3=B3n=20de=20L=C3=B3gica=20de=20Saldos:?= =?UTF-8?q?=20Se=20revis=C3=B3=20y=20corrigi=C3=B3=20la=20l=C3=B3gica=20de?= =?UTF-8?q?=20afectaci=C3=B3n=20de=20saldos=20en=20los=20servicios=20PagoD?= =?UTF-8?q?istribuidorService=20y=20NotaCreditoDebitoService=20para=20aseg?= =?UTF-8?q?urar=20que=20los=20d=C3=A9bitos=20y=20cr=C3=A9ditos=20se=20apli?= =?UTF-8?q?quen=20correctamente.=203.=20Frontend:=20Nuevo=20Servicio=20(au?= =?UTF-8?q?ditoriaService.ts):=20Contiene=20m=C3=A9todos=20para=20llamar?= =?UTF-8?q?=20a=20cada=20uno=20de=20los=20nuevos=20endpoints=20de=20audito?= =?UTF-8?q?r=C3=ADa=20del=20backend.=20Nueva=20P=C3=A1gina=20(AuditoriaGen?= =?UTF-8?q?eralPage.tsx):=20Permite=20al=20SuperAdmin=20seleccionar=20el?= =?UTF-8?q?=20"Tipo=20de=20Entidad"=20a=20auditar=20desde=20un=20dropdown.?= =?UTF-8?q?=20Ofrece=20filtros=20comunes=20(rango=20de=20fechas,=20usuario?= =?UTF-8?q?=20modificador,=20tipo=20de=20acci=C3=B3n)=20y=20filtros=20espe?= =?UTF-8?q?c=C3=ADficos=20que=20aparecen=20din=C3=A1micamente=20seg=C3=BAn?= =?UTF-8?q?=20la=20entidad=20seleccionada.=20Utiliza=20un=20DataGrid=20de?= =?UTF-8?q?=20Material-UI=20para=20mostrar=20el=20historial,=20con=20colum?= =?UTF-8?q?nas=20que=20se=20adaptan=20din=C3=A1micamente=20al=20tipo=20de?= =?UTF-8?q?=20entidad=20consultada.=20Nuevos=20DTOs=20en=20TypeScript:=20S?= =?UTF-8?q?e=20crearon=20las=20interfaces=20correspondientes=20a=20los=20D?= =?UTF-8?q?TOs=20de=20historial=20del=20backend.=20Gesti=C3=B3n=20de=20Per?= =?UTF-8?q?misos:=20La=20secci=C3=B3n=20de=20Auditor=C3=ADa=20en=20MainLay?= =?UTF-8?q?out.tsx=20y=20su=20ruta=20en=20AppRoutes.tsx=20est=C3=A1n=20pro?= =?UTF-8?q?tegidas=20para=20ser=20visibles=20y=20accesibles=20solo=20por?= =?UTF-8?q?=20SuperAdmins.=20Se=20a=C3=B1adi=C3=B3=20un=20permiso=20de=20e?= =?UTF-8?q?jemplo=20AU=5FGENERAL=5FVIEW=20para=20ser=20usado=20si=20se=20d?= =?UTF-8?q?ecide=20extender=20el=20acceso=20en=20el=20futuro.=20Correcci?= =?UTF-8?q?=C3=B3n=20de=20Errores=20Menores:=20Se=20solucion=C3=B3=20el=20?= =?UTF-8?q?problema=20del=20"parpadeo"=20del=20selector=20de=20fecha=20en?= =?UTF-8?q?=20GestionarNovedadesCanillaPage=20al=20adoptar=20un=20patr?= =?UTF-8?q?=C3=B3n=20de=20carga=20de=20datos=20m=C3=A1s=20controlado,=20si?= =?UTF-8?q?milar=20a=20otras=20p=C3=A1ginas=20funcionales.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Auditoria/AuditoriaController.cs | 443 +++++++++- .../Impresion/StockBobinasController.cs | 11 +- .../Distribucion/CambioParadaRepository.cs | 70 +- .../ControlDevolucionesRepository.cs | 98 ++- .../Distribucion/ICambioParadaRepository.cs | 4 + .../Distribucion/ICanillaRepository.cs | 2 +- .../IControlDevolucionesRepository.cs | 4 + .../Distribucion/IOtroDestinoRepository.cs | 4 + .../Distribucion/IPorcMonCanillaRepository.cs | 4 + .../Distribucion/IPorcPagoRepository.cs | 4 + .../Distribucion/IPrecioRepository.cs | 5 + .../Distribucion/IPubliSeccionRepository.cs | 4 + .../Distribucion/IPublicacionRepository.cs | 4 + .../Distribucion/IRecargoZonaRepository.cs | 5 + .../Distribucion/IZonaRepository.cs | 4 + .../Distribucion/OtroDestinoRepository.cs | 42 + .../Distribucion/PorcMonCanillaRepository.cs | 62 +- .../Distribucion/PorcPagoRepository.cs | 58 +- .../Distribucion/PrecioRepository.cs | 182 ++++- .../Distribucion/PubliSeccionRepository.cs | 55 +- .../Distribucion/PublicacionRepository.cs | 64 +- .../Distribucion/RecargoZonaRepository.cs | 112 ++- .../Distribucion/ZonaRepository.cs | 43 +- .../Impresion/EstadoBobinaRepository.cs | 42 + .../Impresion/IEstadoBobinaRepository.cs | 4 + .../Impresion/IPlantaRepository.cs | 4 + .../Impresion/IRegTiradaRepository.cs | 10 +- .../Impresion/IStockBobinaRepository.cs | 4 + .../Impresion/ITipoBobinaRepository.cs | 4 + .../Impresion/PlantaRepository.cs | 44 +- .../Impresion/RegTiradaRepository.cs | 191 ++++- .../Impresion/StockBobinaRepository.cs | 152 +++- .../Impresion/TipoBobinaRepository.cs | 46 +- .../Usuarios/IPerfilRepository.cs | 9 + .../Usuarios/IPermisoRepository.cs | 4 + .../Repositories/Usuarios/PerfilRepository.cs | 111 ++- .../Usuarios/PermisoRepository.cs | 44 +- .../ControlDevolucionesHistorico.cs | 4 +- .../Distribucion/OtroDestinoHistorico.cs | 17 +- .../Distribucion/PorcMonCanillaHistorico.cs | 4 +- .../Models/Distribucion/PorcPagoHistorico.cs | 13 +- .../Models/Distribucion/PrecioHistorico.cs | 10 +- .../Distribucion/PubliSeccionHistorico.cs | 6 +- .../Distribucion/PublicacionHistorico.cs | 14 +- .../Distribucion/RecargoZonaHistorico.cs | 12 +- .../Models/Distribucion/ZonaHistorico.cs | 8 +- .../Auditoria/CambioParadaHistorialDto.cs | 19 + .../Dtos/Auditoria/CanillaHistorialDto.cs | 2 +- .../ControlDevolucionesHistorialDto.cs | 21 + .../Dtos/Auditoria/EmpresaHistorialDto.cs | 2 +- .../Auditoria/EstadoBobinaHistorialDto.cs | 16 + .../Dtos/Auditoria/OtroDestinoHistorialDto.cs | 16 + .../Dtos/Auditoria/PerfilHistorialDto.cs | 19 + .../Dtos/Auditoria/PermisoHistorialDto.cs | 19 + .../Auditoria/PermisosPerfilesHistorialDto.cs | 19 + .../Dtos/Auditoria/PlantaHistorialDto.cs | 16 + .../Auditoria/PorcMonCanillaHistorialDto.cs | 22 + .../Dtos/Auditoria/PorcPagoHistorialDto.cs | 21 + .../Dtos/Auditoria/PrecioHistorialDto.cs | 25 + .../Auditoria/PubliSeccionHistorialDto.cs | 18 + .../Dtos/Auditoria/PublicacionHistorialDto.cs | 20 + .../Dtos/Auditoria/RecargoZonaHistorialDto.cs | 21 + .../Auditoria/RegSeccionTiradaHistorialDto.cs | 22 + .../Dtos/Auditoria/RegTiradaHistorialDto.cs | 20 + .../Dtos/Auditoria/StockBobinaHistorialDto.cs | 30 + .../Dtos/Auditoria/TipoBobinaHistorialDto.cs | 15 + .../Models/Dtos/Auditoria/ZonaHistorialDto.cs | 17 + .../Dtos/Distribucion/UpdatePrecioDto.cs | 2 - .../Models/Impresion/EstadoBobinaHistorico.cs | 17 +- .../Models/Impresion/PlantaHistorico.cs | 12 +- .../Models/Impresion/RegTiradaHistorico.cs | 8 +- .../Models/Impresion/StockBobinaHistorico.cs | 8 +- .../Models/Impresion/TipoBobinaHistorico.cs | 15 +- .../Models/Usuarios/PerfilHistorico.cs | 25 +- .../Models/Usuarios/PermisoHistorico.cs | 10 +- .../Usuarios/PermisosPerfilesHistorico.cs | 16 + .../Distribucion/CambioParadaService.cs | 22 + .../Services/Distribucion/CanillaService.cs | 2 +- .../ControlDevolucionesService.cs | 25 + .../Distribucion/ICambioParadaService.cs | 4 + .../IControlDevolucionesService.cs | 5 + .../Distribucion/IOtroDestinoService.cs | 5 + .../Distribucion/IPorcMonCanillaService.cs | 5 + .../Services/Distribucion/IPorcPagoService.cs | 6 + .../Services/Distribucion/IPrecioService.cs | 5 + .../Distribucion/IPubliSeccionService.cs | 5 + .../Distribucion/IPublicacionService.cs | 5 + .../Distribucion/IRecargoZonaService.cs | 5 + .../Services/Distribucion/IZonaService.cs | 5 + .../Distribucion/OtroDestinoService.cs | 20 + .../Distribucion/PorcMonCanillaService.cs | 58 +- .../Services/Distribucion/PorcPagoService.cs | 40 +- .../Services/Distribucion/PrecioService.cs | 195 +++-- .../Distribucion/PubliSeccionService.cs | 24 +- .../Distribucion/PublicacionService.cs | 24 + .../Distribucion/RecargoZonaService.cs | 84 +- .../Services/Distribucion/ZonaService.cs | 26 +- .../Services/Impresion/EstadoBobinaService.cs | 40 +- .../Impresion/IEstadoBobinaService.cs | 5 + .../Services/Impresion/IPlantaService.cs | 5 + .../Services/Impresion/IStockBobinaService.cs | 5 + .../Services/Impresion/ITipoBobinaService.cs | 5 + .../Services/Impresion/ITiradaService.cs | 11 +- .../Services/Impresion/PlantaService.cs | 20 + .../Services/Impresion/StockBobinaService.cs | 165 +++- .../Services/Impresion/TipoBobinaService.cs | 39 +- .../Services/Impresion/TiradaService.cs | 63 +- .../Services/Usuarios/IPerfilService.cs | 9 + .../Services/Usuarios/IPermisoService.cs | 5 + .../Services/Usuarios/PerfilService.cs | 89 +- .../Services/Usuarios/PermisoService.cs | 24 +- .../GestionIntegral.Api.AssemblyInfo.cs | 2 +- .../Debug/net9.0/rjsmcshtml.dswa.cache.json | 2 +- .../Debug/net9.0/rjsmrazor.dswa.cache.json | 2 +- .../Contables/NotaCreditoDebitoFormModal.tsx | 6 +- .../Contables/PagoDistribuidorFormModal.tsx | 2 +- .../Modals/Distribucion/PrecioFormModal.tsx | 201 ++--- .../Distribucion/RecargoZonaFormModal.tsx | 46 +- .../StockBobinaCambioEstadoModal.tsx | 149 ++-- .../Impresion/StockBobinaEditFormModal.tsx | 232 +++--- .../Auditoria/CambioParadaHistorialDto.ts | 15 + .../ControlDevolucionesHistorialDto.ts | 17 + .../Auditoria/EstadoBobinaHistorialDto.ts | 12 + .../dtos/Auditoria/OtroDestinoHistorialDto.ts | 12 + .../dtos/Auditoria/PerfilHistorialDto.ts | 12 + .../dtos/Auditoria/PermisoHistorialDto.ts | 14 + .../Auditoria/PermisosPerfilesHistorialDto.ts | 15 + .../dtos/Auditoria/PlantaHistorialDto.ts | 12 + .../Auditoria/PorcMonCanillaHistorialDto.ts | 18 + .../dtos/Auditoria/PorcPagoHistorialDto.ts | 17 + .../dtos/Auditoria/PrecioHistorialDto.ts | 21 + .../Auditoria/PubliSeccionHistorialDto.ts | 14 + .../dtos/Auditoria/PublicacionHistorialDto.ts | 16 + .../dtos/Auditoria/RecargoZonaHistorialDto.ts | 17 + .../Auditoria/RegSeccionTiradaHistorialDto.ts | 18 + .../dtos/Auditoria/RegTiradaHistorialDto.ts | 16 + .../dtos/Auditoria/StockBobinaHistorialDto.ts | 26 + .../dtos/Auditoria/TipoBobinaHistorialDto.ts | 11 + .../models/dtos/Auditoria/ZonaHistorialDto.ts | 13 + .../pages/Auditoria/AuditoriaGeneralPage.tsx | 772 ++++++++++++++---- .../pages/Contables/GestionarNotasCDPage.tsx | 2 +- .../GestionarPreciosPublicacionPage.tsx | 93 ++- .../GestionarRecargosPublicacionPage.tsx | 86 +- .../Impresion/GestionarStockBobinasPage.tsx | 310 ++++--- .../services/Auditoria/auditoriaService.ts | 313 +++++++ 145 files changed, 5033 insertions(+), 1070 deletions(-) create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/CambioParadaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/ControlDevolucionesHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/EstadoBobinaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/OtroDestinoHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PerfilHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PermisoHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PermisosPerfilesHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PlantaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PorcMonCanillaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PorcPagoHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PrecioHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PubliSeccionHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PublicacionHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RecargoZonaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RegSeccionTiradaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RegTiradaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/StockBobinaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/TipoBobinaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Dtos/Auditoria/ZonaHistorialDto.cs create mode 100644 Backend/GestionIntegral.Api/Models/Usuarios/PermisosPerfilesHistorico.cs create mode 100644 Frontend/src/models/dtos/Auditoria/CambioParadaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/ControlDevolucionesHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/EstadoBobinaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/OtroDestinoHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PerfilHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PermisoHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PermisosPerfilesHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PlantaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PorcMonCanillaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PorcPagoHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PrecioHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PubliSeccionHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/PublicacionHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/RecargoZonaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/RegSeccionTiradaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/RegTiradaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/StockBobinaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/TipoBobinaHistorialDto.ts create mode 100644 Frontend/src/models/dtos/Auditoria/ZonaHistorialDto.ts diff --git a/Backend/GestionIntegral.Api/Controllers/Auditoria/AuditoriaController.cs b/Backend/GestionIntegral.Api/Controllers/Auditoria/AuditoriaController.cs index 6c9bb22..befd4ce 100644 --- a/Backend/GestionIntegral.Api/Controllers/Auditoria/AuditoriaController.cs +++ b/Backend/GestionIntegral.Api/Controllers/Auditoria/AuditoriaController.cs @@ -7,11 +7,13 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Security.Claims; // Para ClaimTypes using GestionIntegral.Api.Services.Contables; // Para IPagoDistribuidorService, etc. -using GestionIntegral.Api.Dtos.Contables; // Para PagoDistribuidorHistorialDto, etc. using GestionIntegral.Api.Services.Distribucion; -using GestionIntegral.Api.Dtos.Distribucion; -using GestionIntegral.Api.Dtos.Usuarios.Auditoria; using GestionIntegral.Api.Dtos.Auditoria; +using GestionIntegral.Api.Dtos.Zonas; +using GestionIntegral.Api.Dtos.Distribucion; +using GestionIntegral.Api.Services.Impresion; +using GestionIntegral.Api.Dtos.Impresion; +using GestionIntegral.Api.Dtos.Usuarios; namespace GestionIntegral.Api.Controllers @@ -32,6 +34,23 @@ namespace GestionIntegral.Api.Controllers private readonly ISaldoService _saldoService; private readonly ITipoPagoService _tipoPagoService; private readonly IEmpresaService _empresaService; + private readonly IZonaService _zonaService; + private readonly IOtroDestinoService _otroDestinoService; + private readonly IPublicacionService _publicacionService; + private readonly IPubliSeccionService _publiSeccionService; + private readonly IPrecioService _precioService; + private readonly IRecargoZonaService _recargoZonaService; + private readonly IPorcPagoService _porcPagoService; + private readonly IPorcMonCanillaService _porcMonCanillaService; + private readonly IControlDevolucionesService _controlDevolucionesService; + private readonly ITipoBobinaService _tipoBobinaService; + private readonly IEstadoBobinaService _estadoBobinaService; + private readonly IPlantaService _plantaService; + private readonly IStockBobinaService _stockBobinaService; + private readonly ITiradaService _tiradaService; + private readonly IPerfilService _perfilService; + private readonly IPermisoService _permisoService; + private readonly ICambioParadaService _cambioParadaService; private readonly ILogger _logger; // Permiso general para ver cualquier auditoría. @@ -50,6 +69,23 @@ namespace GestionIntegral.Api.Controllers ISaldoService saldoService, ITipoPagoService tipoPagoService, IEmpresaService empresaService, + IZonaService zonaService, + IOtroDestinoService otroDestinoService, + IPublicacionService publicacionService, + IPubliSeccionService publiSeccionService, + IPrecioService precioService, + IRecargoZonaService recargoZonaService, + IPorcPagoService porcPagoService, + IPorcMonCanillaService porcMonCanillaService, + IControlDevolucionesService controlDevolucionesService, + ITipoBobinaService tipoBobinaService, + IEstadoBobinaService estadoBobinaService, + IPlantaService plantaService, + IStockBobinaService stockBobinaService, + ITiradaService tiradaService, + IPerfilService perfilService, + IPermisoService permisoService, + ICambioParadaService cambioParadaService, ILogger logger) { _usuarioService = usuarioService; @@ -63,6 +99,23 @@ namespace GestionIntegral.Api.Controllers _saldoService = saldoService; _tipoPagoService = tipoPagoService; _empresaService = empresaService; + _zonaService = zonaService; + _otroDestinoService = otroDestinoService; + _publicacionService = publicacionService; + _publiSeccionService = publiSeccionService; + _precioService = precioService; + _recargoZonaService = recargoZonaService; + _porcPagoService = porcPagoService; + _porcMonCanillaService = porcMonCanillaService; + _controlDevolucionesService = controlDevolucionesService; + _tipoBobinaService = tipoBobinaService; + _estadoBobinaService = estadoBobinaService; + _plantaService = plantaService; + _stockBobinaService = stockBobinaService; + _tiradaService = tiradaService; + _perfilService = perfilService; + _cambioParadaService = cambioParadaService; + _permisoService = permisoService; _logger = logger; } @@ -274,5 +327,389 @@ namespace GestionIntegral.Api.Controllers return StatusCode(500, "Error interno al obtener historial de Empresas (Maestro)."); } } + + [HttpGet("zonas-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialZonasMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idZonaAfectada) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + // Asumiendo que _zonaService está inyectado + var historial = await _zonaService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idZonaAfectada); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Zonas (Maestro)."); + return StatusCode(500, "Error interno al obtener historial de Zonas (Maestro)."); + } + } + + [HttpGet("otros-destinos-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialOtrosDestinosMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idOtroDestinoAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _otroDestinoService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idOtroDestinoAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Otros Destinos (Maestro)."); + return StatusCode(500, "Error interno al obtener historial de Otros Destinos (Maestro)."); + } + } + + [HttpGet("publicaciones-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPublicacionesMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idPublicacionAfectada) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _publicacionService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPublicacionAfectada); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Publicaciones (Maestro)."); + return StatusCode(500, "Error interno al obtener historial de Publicaciones (Maestro)."); + } + } + + [HttpGet("publi-secciones-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPubliSeccionesMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idSeccionAfectada, [FromQuery] int? idPublicacionAfectada) // Nuevos filtros + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _publiSeccionService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idSeccionAfectada, idPublicacionAfectada); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Secciones de Publicación."); + return StatusCode(500, "Error interno al obtener historial de Secciones de Publicación."); + } + } + + [HttpGet("precios-publicacion-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPreciosMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idPrecioAfectado, [FromQuery] int? idPublicacionAfectada) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _precioService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPrecioAfectado, idPublicacionAfectada); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Precios de Publicación."); + return StatusCode(500, "Error interno al obtener historial de Precios de Publicación."); + } + } + + [HttpGet("recargos-zona-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialRecargosZonaMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idRecargoAfectado, [FromQuery] int? idPublicacionAfectada, [FromQuery] int? idZonaAfectada) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _recargoZonaService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idRecargoAfectado, idPublicacionAfectada, idZonaAfectada); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Recargos por Zona."); + return StatusCode(500, "Error interno al obtener historial de Recargos por Zona."); + } + } + + [HttpGet("porc-pago-dist-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPorcPagoDistMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idPorcentajeAfectado, [FromQuery] int? idPublicacionAfectada, [FromQuery] int? idDistribuidorAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _porcPagoService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPorcentajeAfectado, idPublicacionAfectada, idDistribuidorAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Porcentajes de Pago (Dist)."); + return StatusCode(500, "Error interno al obtener historial de Porcentajes de Pago (Dist)."); + } + } + + [HttpGet("porc-mon-canilla-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPorcMonCanillaMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idPorcMonAfectado, [FromQuery] int? idPublicacionAfectada, [FromQuery] int? idCanillaAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _porcMonCanillaService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPorcMonAfectado, idPublicacionAfectada, idCanillaAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Porc/Mon Canillita."); + return StatusCode(500, "Error interno al obtener historial de Porc/Mon Canillita."); + } + } + + [HttpGet("control-devoluciones-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialControlDevolucionesMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idControlAfectado, [FromQuery] int? idEmpresaAfectada, [FromQuery] DateTime? fechaAfectada) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _controlDevolucionesService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idControlAfectado, idEmpresaAfectada, fechaAfectada); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Control de Devoluciones."); + return StatusCode(500, "Error interno al obtener historial de Control de Devoluciones."); + } + } + + [HttpGet("tipos-bobina-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialTiposBobinaMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idTipoBobinaAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _tipoBobinaService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idTipoBobinaAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Tipos de Bobina."); + return StatusCode(500, "Error interno al obtener historial de Tipos de Bobina."); + } + } + + [HttpGet("estados-bobina-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialEstadosBobinaMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idEstadoBobinaAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _estadoBobinaService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idEstadoBobinaAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Estados de Bobina."); + return StatusCode(500, "Error interno al obtener historial de Estados de Bobina."); + } + } + + [HttpGet("plantas-impresion-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPlantasMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idPlantaAfectada) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _plantaService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPlantaAfectada); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Plantas de Impresión."); + return StatusCode(500, "Error interno al obtener historial de Plantas de Impresión."); + } + } + + [HttpGet("stock-bobinas-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialStockBobinasMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idBobinaAfectada, [FromQuery] int? idTipoBobinaFiltro, + [FromQuery] int? idPlantaFiltro, [FromQuery] int? idEstadoBobinaFiltro) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _stockBobinaService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idBobinaAfectada, idTipoBobinaFiltro, idPlantaFiltro, idEstadoBobinaFiltro); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Stock de Bobinas."); + return StatusCode(500, "Error interno al obtener historial de Stock de Bobinas."); + } + } + + [HttpGet("reg-tiradas-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialRegTiradasMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idRegistroAfectado, [FromQuery] int? idPublicacionFiltro, + [FromQuery] int? idPlantaFiltro, [FromQuery] DateTime? fechaTiradaFiltro) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _tiradaService.ObtenerRegTiradasHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idRegistroAfectado, idPublicacionFiltro, idPlantaFiltro, fechaTiradaFiltro); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Registro de Tiradas."); + return StatusCode(500, "Error interno al obtener historial de Registro de Tiradas."); + } + } + + [HttpGet("reg-secciones-tirada-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialRegSeccionesTiradaMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idTiradaAfectada, [FromQuery] int? idPublicacionFiltro, + [FromQuery] int? idSeccionFiltro, [FromQuery] int? idPlantaFiltro, [FromQuery] DateTime? fechaTiradaFiltro) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _tiradaService.ObtenerRegSeccionesTiradaHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idTiradaAfectada, idPublicacionFiltro, idSeccionFiltro, idPlantaFiltro, fechaTiradaFiltro); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Secciones de Tirada."); + return StatusCode(500, "Error interno al obtener historial de Secciones de Tirada."); + } + } + + [HttpGet("perfiles-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPerfilesMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idPerfilAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _perfilService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPerfilAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Perfiles (Maestro)."); + return StatusCode(500, "Error interno al obtener historial de Perfiles (Maestro)."); + } + } + + [HttpGet("permisos-maestro")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPermisosMaestro( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idPermisoAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _permisoService.ObtenerHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPermisoAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Permisos (Maestro)."); + return StatusCode(500, "Error interno al obtener historial de Permisos (Maestro)."); + } + } + + [HttpGet("permisos-perfiles-historial")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialPermisosPerfiles( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idPerfilAfectado, [FromQuery] int? idPermisoAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _perfilService.ObtenerPermisosAsignadosHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPerfilAfectado, idPermisoAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Asignación de Permisos."); + return StatusCode(500, "Error interno al obtener historial de Asignación de Permisos."); + } + } + + [HttpGet("cambios-parada-canilla")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetHistorialCambiosParada( + [FromQuery] DateTime? fechaDesde, [FromQuery] DateTime? fechaHasta, + [FromQuery] int? idUsuarioModifico, [FromQuery] string? tipoModificacion, + [FromQuery] int? idCanillaAfectado) + { + if (!TienePermiso(PermisoVerAuditoria)) return Forbid(); + try + { + var historial = await _cambioParadaService.ObtenerCambiosParadaHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idCanillaAfectado); + return Ok(historial ?? Enumerable.Empty()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obteniendo historial de Cambios de Parada."); + return StatusCode(500, "Error interno al obtener historial de Cambios de Parada."); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Controllers/Impresion/StockBobinasController.cs b/Backend/GestionIntegral.Api/Controllers/Impresion/StockBobinasController.cs index f93f806..02212c4 100644 --- a/Backend/GestionIntegral.Api/Controllers/Impresion/StockBobinasController.cs +++ b/Backend/GestionIntegral.Api/Controllers/Impresion/StockBobinasController.cs @@ -124,11 +124,15 @@ namespace GestionIntegral.Api.Controllers.Impresion public async Task CambiarEstadoBobina(int idBobina, [FromBody] CambiarEstadoBobinaDto cambiarEstadoDto) { if (!TienePermiso(PermisoCambiarEstado)) return Forbid(); - if (!ModelState.IsValid) return BadRequest(ModelState); + if (!ModelState.IsValid) return BadRequest(ModelState); // Validaciones de DTO (Required, Range, etc.) var userId = GetCurrentUserId(); if (userId == null) return Unauthorized(); - // Validación adicional en el controlador para el caso "En Uso" + // La validación de que IdPublicacion/IdSeccion son requeridos para estado "En Uso" + // ahora está más robusta en el servicio. Se puede quitar del controlador + // si se prefiere que el servicio sea la única fuente de verdad para esa lógica. + // Si la mantenés acá, es una validación temprana. + /* if (cambiarEstadoDto.NuevoEstadoId == 2) // Asumiendo 2 = En Uso { if (!cambiarEstadoDto.IdPublicacion.HasValue || cambiarEstadoDto.IdPublicacion.Value <= 0) @@ -136,12 +140,13 @@ namespace GestionIntegral.Api.Controllers.Impresion if (!cambiarEstadoDto.IdSeccion.HasValue || cambiarEstadoDto.IdSeccion.Value <=0) return BadRequest(new { message = "Se requiere IdSeccion para el estado 'En Uso'."}); } - + */ var (exito, error) = await _stockBobinaService.CambiarEstadoBobinaAsync(idBobina, cambiarEstadoDto, userId.Value); if (!exito) { if (error == "Bobina no encontrada.") return NotFound(new { message = error }); + // Otros errores específicos del servicio (ej. flujo de estado no permitido) vienen como BadRequest return BadRequest(new { message = error }); } return NoContent(); diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/CambioParadaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/CambioParadaRepository.cs index c22c296..6aee6c7 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/CambioParadaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/CambioParadaRepository.cs @@ -105,23 +105,33 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion } public async Task UpdateVigenciaHAsync(int idRegistro, DateTime vigenciaH, int idUsuario, IDbTransaction transaction) +{ + var paradaOriginal = await GetByIdAsync(idRegistro); + if (paradaOriginal == null) throw new KeyNotFoundException("Registro de parada no encontrado para actualizar VigenciaH."); + + var paradaParaHistorial = new CambioParadaCanilla { - var paradaOriginal = await GetByIdAsync(idRegistro); // Obtener para el log - if (paradaOriginal == null) throw new KeyNotFoundException("Registro de parada no encontrado para actualizar VigenciaH."); + IdRegistro = paradaOriginal.IdRegistro, + IdCanilla = paradaOriginal.IdCanilla, + Parada = paradaOriginal.Parada, + VigenciaD = paradaOriginal.VigenciaD, + VigenciaH = vigenciaH.Date + }; - // Loggear ANTES de actualizar - await LogHistorialAsync(paradaOriginal, idUsuario, "Cerrada", transaction.Connection!, transaction); + // Loggear el estado que QUEDARÁ en la tabla principal (con la VigenciaH actualizada) + // El TipoMod debería reflejar la acción. "Cerrada". + await LogHistorialAsync(paradaParaHistorial, idUsuario, "Cerrada", transaction.Connection!, transaction); - const string sqlUpdate = @" + const string sqlUpdate = @" UPDATE dbo.dist_CambiosParadasCanillas SET VigenciaH = @VigenciaH WHERE Id_Registro = @IdRegistro;"; - var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, + var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, new { VigenciaH = vigenciaH.Date, IdRegistro = idRegistro }, transaction); - return rowsAffected == 1; - } + return rowsAffected == 1; +} public async Task DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction) { @@ -135,5 +145,49 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdRegistro = idRegistro }, transaction); return rowsAffected == 1; } + + public async Task> GetCambiosParadaHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idCanillaOriginal) + { + using var connection = _cf.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Registro, h.Id_Canilla, h.Parada, h.VigenciaD, h.VigenciaH, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico, + c.NomApe AS NombreCanilla + FROM dbo.dist_CambiosParadasCanillas_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + JOIN dbo.dist_dtCanillas c ON h.Id_Canilla = c.Id_Canilla + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idCanillaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Canilla = @IdCanillaOriginalParam"); parameters.Add("IdCanillaOriginalParam", idCanillaOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userMod, canillaNombre) => (hist, userMod, canillaNombre), + parameters, + splitOn: "NombreUsuarioModifico,NombreCanilla" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Cambios de Parada."); + return Enumerable.Empty<(CambioParadaCanillaHistorial, string, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ControlDevolucionesRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ControlDevolucionesRepository.cs index 66258c1..5e1cde1 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ControlDevolucionesRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ControlDevolucionesRepository.cs @@ -36,7 +36,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion if (fechaDesde.HasValue) { sqlBuilder.Append(" AND Fecha >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } if (fechaHasta.HasValue) { sqlBuilder.Append(" AND Fecha <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date); } if (idEmpresa.HasValue) { sqlBuilder.Append(" AND Id_Empresa = @IdEmpresaParam"); parameters.Add("IdEmpresaParam", idEmpresa.Value); } - + sqlBuilder.Append(" ORDER BY Fecha DESC, Id_Empresa;"); try @@ -101,10 +101,18 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevoControl, transaction); if (inserted == null || inserted.IdControl == 0) throw new DataException("Error al crear control de devoluciones o ID no generado."); - await transaction.Connection!.ExecuteAsync(sqlHistorico, new { - IdControlParam = inserted.IdControl, IdEmpresaParam = inserted.IdEmpresa, FechaParam = inserted.Fecha, - EntradaParam = inserted.Entrada, SobrantesParam = inserted.Sobrantes, DetalleParam = inserted.Detalle, SinCargoParam = inserted.SinCargo, - IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Creado" + await transaction.Connection!.ExecuteAsync(sqlHistorico, new + { + IdControlParam = inserted.IdControl, + IdEmpresaParam = inserted.IdEmpresa, + FechaParam = inserted.Fecha, + EntradaParam = inserted.Entrada, + SobrantesParam = inserted.Sobrantes, + DetalleParam = inserted.Detalle, + SinCargoParam = inserted.SinCargo, + IdUsuarioParam = idUsuario, + FechaModParam = DateTime.Now, + TipoModParam = "Creado" }, transaction); return inserted; } @@ -121,15 +129,23 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion UPDATE dbo.dist_dtCtrlDevoluciones SET Entrada = @Entrada, Sobrantes = @Sobrantes, Detalle = @Detalle, SinCargo = @SinCargo WHERE Id_Control = @IdControl;"; - const string sqlHistorico = @" + const string sqlHistorico = @" INSERT INTO dbo.dist_dtCtrlDevoluciones_H (Id_Control, Id_Empresa, Fecha, Entrada, Sobrantes, Detalle, SinCargo, Id_Usuario, FechaMod, TipoMod) VALUES (@IdControlParam, @IdEmpresaParam, @FechaParam, @EntradaParam, @SobrantesParam, @DetalleParam, @SinCargoParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; - - await transaction.Connection!.ExecuteAsync(sqlHistorico, new { - IdControlParam = actual.IdControl, IdEmpresaParam = actual.IdEmpresa, FechaParam = actual.Fecha, // Datos originales - EntradaParam = actual.Entrada, SobrantesParam = actual.Sobrantes, DetalleParam = actual.Detalle, SinCargoParam = actual.SinCargo, // Valores ANTERIORES - IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Actualizado" + + await transaction.Connection!.ExecuteAsync(sqlHistorico, new + { + IdControlParam = actual.IdControl, + IdEmpresaParam = actual.IdEmpresa, + FechaParam = actual.Fecha, // Datos originales + EntradaParam = actual.Entrada, + SobrantesParam = actual.Sobrantes, + DetalleParam = actual.Detalle, + SinCargoParam = actual.SinCargo, // Valores ANTERIORES + IdUsuarioParam = idUsuario, + FechaModParam = DateTime.Now, + TipoModParam = "Actualizado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, controlAActualizar, transaction); @@ -149,14 +165,66 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion (Id_Control, Id_Empresa, Fecha, Entrada, Sobrantes, Detalle, SinCargo, Id_Usuario, FechaMod, TipoMod) VALUES (@IdControlParam, @IdEmpresaParam, @FechaParam, @EntradaParam, @SobrantesParam, @DetalleParam, @SinCargoParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; - await transaction.Connection!.ExecuteAsync(sqlHistorico, new { - IdControlParam = actual.IdControl, IdEmpresaParam = actual.IdEmpresa, FechaParam = actual.Fecha, - EntradaParam = actual.Entrada, SobrantesParam = actual.Sobrantes, DetalleParam = actual.Detalle, SinCargoParam = actual.SinCargo, - IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Eliminado" + await transaction.Connection!.ExecuteAsync(sqlHistorico, new + { + IdControlParam = actual.IdControl, + IdEmpresaParam = actual.IdEmpresa, + FechaParam = actual.Fecha, + EntradaParam = actual.Entrada, + SobrantesParam = actual.Sobrantes, + DetalleParam = actual.Detalle, + SinCargoParam = actual.SinCargo, + IdUsuarioParam = idUsuario, + FechaModParam = DateTime.Now, + TipoModParam = "Eliminado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdControlParam = idControl }, transaction); return rowsAffected == 1; } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idControlOriginal, int? idEmpresaOriginal, DateTime? fechaOriginal) + { + using var connection = _cf.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Control, h.Id_Empresa, h.Fecha, h.Entrada, h.Sobrantes, h.Detalle, h.SinCargo, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_dtCtrlDevoluciones_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idControlOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Control = @IdControlOriginalParam"); parameters.Add("IdControlOriginalParam", idControlOriginal.Value); } + if (idEmpresaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Empresa = @IdEmpresaOriginalParam"); parameters.Add("IdEmpresaOriginalParam", idEmpresaOriginal.Value); } + if (fechaOriginal.HasValue) { sqlBuilder.Append(" AND h.Fecha = @FechaOriginalParam"); parameters.Add("FechaOriginalParam", fechaOriginal.Value.Date); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _log.LogError(ex, "Error al obtener historial de Control de Devoluciones."); + return Enumerable.Empty<(ControlDevolucionesHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ICambioParadaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ICambioParadaRepository.cs index bdda3c3..8c79bed 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ICambioParadaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ICambioParadaRepository.cs @@ -14,5 +14,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task CreateAsync(CambioParadaCanilla nuevaParada, int idUsuario, IDbTransaction transaction); Task UpdateVigenciaHAsync(int idRegistro, DateTime vigenciaH, int idUsuario, IDbTransaction transaction); Task DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction); + Task> GetCambiosParadaHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idCanillaOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ICanillaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ICanillaRepository.cs index b6f592d..ef68eb5 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ICanillaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ICanillaRepository.cs @@ -17,6 +17,6 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task> GetHistorialAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, - int? idCanillaOriginal); // Para filtrar por un canillita específico + int? idCanillaOriginal); // Para filtrar por un canillita específico } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IControlDevolucionesRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IControlDevolucionesRepository.cs index a659196..930caa3 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IControlDevolucionesRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IControlDevolucionesRepository.cs @@ -14,5 +14,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task CreateAsync(ControlDevoluciones nuevoControl, int idUsuario, IDbTransaction transaction); Task UpdateAsync(ControlDevoluciones controlAActualizar, int idUsuario, IDbTransaction transaction); Task DeleteAsync(int idControl, int idUsuario, IDbTransaction transaction); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idControlOriginal, int? idEmpresaOriginal, DateTime? fechaOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IOtroDestinoRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IOtroDestinoRepository.cs index d4cadc1..f3fe5ed 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IOtroDestinoRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IOtroDestinoRepository.cs @@ -14,5 +14,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task DeleteAsync(int id, int idUsuario, IDbTransaction transaction); Task ExistsByNameAsync(string nombre, int? excludeId = null); Task IsInUseAsync(int id); // Verificar si se usa en dist_SalidasOtrosDestinos + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idOtroDestinoOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPorcMonCanillaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPorcMonCanillaRepository.cs index 5fa7c91..31c5240 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPorcMonCanillaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPorcMonCanillaRepository.cs @@ -16,5 +16,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task DeleteAsync(int idPorcMon, int idUsuario, IDbTransaction transaction); Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction); Task GetPreviousActiveAsync(int idPublicacion, int idCanilla, DateTime vigenciaDNuevo, IDbTransaction transaction); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPorcMonOriginal, int? idPublicacionOriginal, int? idCanillaOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPorcPagoRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPorcPagoRepository.cs index 723de17..5041766 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPorcPagoRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPorcPagoRepository.cs @@ -16,5 +16,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task DeleteAsync(int idPorcentaje, int idUsuario, IDbTransaction transaction); Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction); Task GetPreviousActivePorcPagoAsync(int idPublicacion, int idDistribuidor, DateTime vigenciaDNuevo, IDbTransaction transaction); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPorcentajeOriginal, int? idPublicacionOriginal, int? idDistribuidorOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPrecioRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPrecioRepository.cs index fa83182..df29d10 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPrecioRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPrecioRepository.cs @@ -9,6 +9,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion { Task> GetByPublicacionIdAsync(int idPublicacion); Task GetByIdAsync(int idPrecio); + Task IsInUseAsync(int idPrecio, DateTime vigenciaD, DateTime? vigenciaH); Task GetActiveByPublicacionAndDateAsync(int idPublicacion, DateTime fecha, IDbTransaction? transaction = null); Task CreateAsync(Precio nuevoPrecio, int idUsuario, IDbTransaction transaction); Task UpdateAsync(Precio precioAActualizar, int idUsuario, IDbTransaction transaction); @@ -16,5 +17,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion // MODIFICADO: Añadir idUsuarioAuditoria Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction); Task GetPreviousActivePriceAsync(int idPublicacion, DateTime vigenciaDNuevo, IDbTransaction transaction); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPrecioOriginal, int? idPublicacionOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPubliSeccionRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPubliSeccionRepository.cs index 3eabb47..749e309 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPubliSeccionRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPubliSeccionRepository.cs @@ -15,5 +15,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction); // Ya existe Task ExistsByNameInPublicacionAsync(string nombre, int idPublicacion, int? excludeIdSeccion = null); Task IsInUseAsync(int idSeccion); // Verificar en bob_RegPublicaciones, bob_StockBobinas + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idSeccionOriginal, int? idPublicacionOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPublicacionRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPublicacionRepository.cs index d62f15d..2f93c3e 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPublicacionRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IPublicacionRepository.cs @@ -18,5 +18,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task> GetConfiguracionDiasAsync(int idPublicacion); Task> GetPublicacionesIdsPorDiaSemanaAsync(byte diaSemana); // Devuelve solo IDs Task UpdateConfiguracionDiasAsync(int idPublicacion, IEnumerable diasActivos, IDbTransaction transaction); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPublicacionOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IRecargoZonaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IRecargoZonaRepository.cs index 5085ee1..e9208e7 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IRecargoZonaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IRecargoZonaRepository.cs @@ -10,11 +10,16 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion { Task> GetByPublicacionIdAsync(int idPublicacion); Task GetByIdAsync(int idRecargo); // Para obtener un recargo específico + Task IsInUseAsync(int idRecargo, DateTime vigenciaD, DateTime? vigenciaH); Task GetActiveByPublicacionZonaAndDateAsync(int idPublicacion, int idZona, DateTime fecha, IDbTransaction? transaction = null); Task CreateAsync(RecargoZona nuevoRecargo, int idUsuario, IDbTransaction transaction); Task UpdateAsync(RecargoZona recargoAActualizar, int idUsuario, IDbTransaction transaction); Task DeleteAsync(int idRecargo, int idUsuario, IDbTransaction transaction); Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction); // Ya existe Task GetPreviousActiveRecargoAsync(int idPublicacion, int idZona, DateTime vigenciaDNuevo, IDbTransaction transaction); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idRecargoOriginal, int? idPublicacionOriginal, int? idZonaOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IZonaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IZonaRepository.cs index 3b46136..fb631e6 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IZonaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/IZonaRepository.cs @@ -13,5 +13,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task SoftDeleteAsync(int id, int idUsuario); // Cambiado de DeleteAsync a SoftDeleteAsync Task ExistsByNameAsync(string nombre, int? excludeId = null, bool soloActivas = true); Task IsInUseAsync(int id); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idZonaOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/OtroDestinoRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/OtroDestinoRepository.cs index cf316b0..6b107ad 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/OtroDestinoRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/OtroDestinoRepository.cs @@ -185,5 +185,47 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { Id = id }, transaction); return rowsAffected == 1; } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idOtroDestinoOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Destino, h.Nombre, h.Obs, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_dtOtrosDestinos_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idOtroDestinoOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Destino = @IdOtroDestinoOriginalParam"); parameters.Add("IdOtroDestinoOriginalParam", idOtroDestinoOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Otros Destinos (Maestro)."); + return Enumerable.Empty<(OtroDestinoHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PorcMonCanillaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PorcMonCanillaRepository.cs index cfcf9d9..f753f7e 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PorcMonCanillaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PorcMonCanillaRepository.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Text; using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion @@ -63,7 +64,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion } catch (Exception ex) { - _log.LogError(ex, "Error al obtener PorcMonCanilla por ID: {IdPorcMon}", idPorcMon); + _log.LogError(ex, "Error al obtener PorcMonCanilla por ID: {IdPorcMon}", idPorcMon); return null; } } @@ -79,15 +80,15 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion AND VigenciaD <= @FechaParam AND (VigenciaH IS NULL OR VigenciaH >= @FechaParam) ORDER BY VigenciaD DESC;"; - + var cn = transaction?.Connection ?? _cf.CreateConnection(); bool ownConnection = transaction == null; PorcMonCanilla? result = null; try { if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); - result = await cn.QuerySingleOrDefaultAsync(sql, - new { IdPublicacionParam = idPublicacion, IdCanillaParam = idCanilla, FechaParam = fecha.Date }, + result = await cn.QuerySingleOrDefaultAsync(sql, + new { IdPublicacionParam = idPublicacion, IdCanillaParam = idCanilla, FechaParam = fecha.Date }, transaction); } finally @@ -109,8 +110,8 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion AND VigenciaD < @VigenciaDNuevoParam AND VigenciaH IS NULL ORDER BY VigenciaD DESC;"; - return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, - new { IdPublicacionParam = idPublicacion, IdCanillaParam = idCanilla, VigenciaDNuevoParam = vigenciaDNuevo.Date }, + return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, + new { IdPublicacionParam = idPublicacion, IdCanillaParam = idCanilla, VigenciaDNuevoParam = vigenciaDNuevo.Date }, transaction); } @@ -184,12 +185,12 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_PorcMon AS IdPorcMon, Id_Publicacion AS IdPublicacion, Id_Canilla AS IdCanilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje - FROM dbo.dist_PorcMonPagoCanilla WHERE Id_PorcMon = @IdPorcMonParam", + FROM dbo.dist_PorcMonPagoCanilla WHERE Id_PorcMon = @IdPorcMonParam", new { IdPorcMonParam = idPorcMon }, transaction); if (actual == null) throw new KeyNotFoundException("Registro PorcMonCanilla no encontrado para eliminar."); const string sqlDelete = "DELETE FROM dbo.dist_PorcMonPagoCanilla WHERE Id_PorcMon = @IdPorcMonParam"; - const string sqlInsertHistorico = @" + const string sqlInsertHistorico = @" INSERT INTO dbo.dist_PorcMonPagoCanilla_H (Id_PorcMon, Id_Publicacion, Id_Canilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcMonParam, @IdPublicacionParam, @IdCanillaParam, @VigenciaDParam, @VigenciaHParam, @PorcMonParam, @EsPorcentajeParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; @@ -253,5 +254,50 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion throw; } } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPorcMonOriginal, int? idPublicacionOriginal, int? idCanillaOriginal) + { + using var connection = _cf.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_PorcMon, h.Id_Publicacion, h.Id_Canilla, + h.VigenciaD, h.VigenciaH, h.PorcMon, h.EsPorcentaje, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_PorcMonPagoCanilla_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idPorcMonOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_PorcMon = @IdPorcMonOriginalParam"); parameters.Add("IdPorcMonOriginalParam", idPorcMonOriginal.Value); } + if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } + if (idCanillaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Canilla = @IdCanillaOriginalParam"); parameters.Add("IdCanillaOriginalParam", idCanillaOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _log.LogError(ex, "Error al obtener historial de Porcentajes/Montos Canillita."); + return Enumerable.Empty<(PorcMonCanillaHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PorcPagoRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PorcPagoRepository.cs index 6617b9e..972e8a7 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PorcPagoRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PorcPagoRepository.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Text; using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion @@ -67,7 +68,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion return null; } } - + public async Task GetActiveByPublicacionDistribuidorAndDateAsync(int idPublicacion, int idDistribuidor, DateTime fecha, IDbTransaction? transaction = null) { const string sql = @" @@ -79,7 +80,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion AND VigenciaD <= @FechaParam AND (VigenciaH IS NULL OR VigenciaH >= @FechaParam) ORDER BY VigenciaD DESC;"; - + var cn = transaction?.Connection ?? _cf.CreateConnection(); bool ownConnection = transaction == null; PorcPago? result = null; @@ -107,8 +108,8 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion AND VigenciaD < @VigenciaDNuevoParam AND VigenciaH IS NULL ORDER BY VigenciaD DESC;"; - return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, - new { IdPublicacionParam = idPublicacion, IdDistribuidorParam = idDistribuidor, VigenciaDNuevoParam = vigenciaDNuevo.Date }, + return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, + new { IdPublicacionParam = idPublicacion, IdDistribuidorParam = idDistribuidor, VigenciaDNuevoParam = vigenciaDNuevo.Date }, transaction); } @@ -154,7 +155,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion UPDATE dbo.dist_PorcPago SET Porcentaje = @Porcentaje, VigenciaH = @VigenciaH WHERE Id_Porcentaje = @IdPorcentaje;"; - const string sqlInsertHistorico = @" + const string sqlInsertHistorico = @" INSERT INTO dbo.dist_PorcPago_H (Id_Porcentaje, Id_Publicacion, Id_Distribuidor, VigenciaD, VigenciaH, Porcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcentajeHist, @IdPublicacionHist, @IdDistribuidorHist, @VigenciaDHist, @VigenciaHHist, @PorcentajeHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; @@ -180,7 +181,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_Porcentaje AS IdPorcentaje, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, VigenciaD, VigenciaH, Porcentaje - FROM dbo.dist_PorcPago WHERE Id_Porcentaje = @IdPorcentajeParam", + FROM dbo.dist_PorcPago WHERE Id_Porcentaje = @IdPorcentajeParam", new { IdPorcentajeParam = idPorcentaje }, transaction); if (actual == null) throw new KeyNotFoundException("Porcentaje de pago no encontrado para eliminar."); @@ -247,5 +248,50 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion throw; } } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPorcentajeOriginal, int? idPublicacionOriginal, int? idDistribuidorOriginal) + { + using var connection = _cf.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Porcentaje, h.Id_Publicacion, h.Id_Distribuidor, + h.VigenciaD, h.VigenciaH, h.Porcentaje, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_PorcPago_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idPorcentajeOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Porcentaje = @IdPorcentajeOriginalParam"); parameters.Add("IdPorcentajeOriginalParam", idPorcentajeOriginal.Value); } + if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } + if (idDistribuidorOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Distribuidor = @IdDistribuidorOriginalParam"); parameters.Add("IdDistribuidorOriginalParam", idDistribuidorOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _log.LogError(ex, "Error al obtener historial de Porcentajes de Pago (Distribuidores)."); + return Enumerable.Empty<(PorcPagoHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PrecioRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PrecioRepository.cs index c1aa02f..99e0bb8 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PrecioRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PrecioRepository.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Text; using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion @@ -71,18 +72,18 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion WHERE Id_Publicacion = @IdPublicacionParam AND VigenciaD <= @FechaParam AND (VigenciaH IS NULL OR VigenciaH >= @FechaParam) ORDER BY VigenciaD DESC;"; - + var cn = transaction?.Connection ?? _connectionFactory.CreateConnection(); bool ownConnection = transaction == null; Precio? result = null; try { - if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) + if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) { await dbConn.OpenAsync(); } - result = await cn.QuerySingleOrDefaultAsync(sql, - new { IdPublicacionParam = idPublicacion, FechaParam = fecha.Date }, + result = await cn.QuerySingleOrDefaultAsync(sql, + new { IdPublicacionParam = idPublicacion, FechaParam = fecha.Date }, transaction); } catch (Exception ex) @@ -92,7 +93,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion } finally { - if (ownConnection && cn.State == ConnectionState.Open && cn is System.Data.Common.DbConnection dbConnClose) + if (ownConnection && cn.State == ConnectionState.Open && cn is System.Data.Common.DbConnection dbConnClose) { await dbConnClose.CloseAsync(); } @@ -100,7 +101,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion } return result; } - + public async Task GetPreviousActivePriceAsync(int idPublicacion, DateTime vigenciaDNuevo, IDbTransaction transaction) { const string sql = @" @@ -112,8 +113,8 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion AND VigenciaD < @VigenciaDNuevoParam AND VigenciaH IS NULL ORDER BY VigenciaD DESC;"; - return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, - new { IdPublicacionParam = idPublicacion, VigenciaDNuevoParam = vigenciaDNuevo.Date }, + return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, + new { IdPublicacionParam = idPublicacion, VigenciaDNuevoParam = vigenciaDNuevo.Date }, transaction); } @@ -124,7 +125,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion OUTPUT INSERTED.Id_Precio AS IdPrecio, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.VigenciaD, INSERTED.VigenciaH, INSERTED.Lunes, INSERTED.Martes, INSERTED.Miercoles, INSERTED.Jueves, INSERTED.Viernes, INSERTED.Sabado, INSERTED.Domingo VALUES (@IdPublicacion, @VigenciaD, @VigenciaH, @Lunes, @Martes, @Miercoles, @Jueves, @Viernes, @Sabado, @Domingo);"; - var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevoPrecio, transaction); + var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevoPrecio, transaction); if (inserted == null || inserted.IdPrecio == 0) throw new DataException("Error al crear precio o al obtener el ID generado."); const string sqlInsertHistorico = @" @@ -134,14 +135,19 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { - IdPrecioHist = inserted.IdPrecio, + IdPrecioHist = inserted.IdPrecio, IdPublicacionHist = inserted.IdPublicacion, VigenciaDHist = inserted.VigenciaD, VigenciaHHist = inserted.VigenciaH, - LunesHist = inserted.Lunes, MartesHist = inserted.Martes, MiercolesHist = inserted.Miercoles, JuevesHist = inserted.Jueves, - ViernesHist = inserted.Viernes, SabadoHist = inserted.Sabado, DomingoHist = inserted.Domingo, - IdUsuarioHist = idUsuario, - FechaModHist = DateTime.Now, + LunesHist = inserted.Lunes, + MartesHist = inserted.Martes, + MiercolesHist = inserted.Miercoles, + JuevesHist = inserted.Jueves, + ViernesHist = inserted.Viernes, + SabadoHist = inserted.Sabado, + DomingoHist = inserted.Domingo, + IdUsuarioHist = idUsuario, + FechaModHist = DateTime.Now, TipoModHist = "Creado" }, transaction); return inserted; @@ -160,22 +166,27 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion VigenciaH = @VigenciaH, Lunes = @Lunes, Martes = @Martes, Miercoles = @Miercoles, Jueves = @Jueves, Viernes = @Viernes, Sabado = @Sabado, Domingo = @Domingo WHERE Id_Precio = @IdPrecio;"; - const string sqlInsertHistorico = @" + const string sqlInsertHistorico = @" INSERT INTO dbo.dist_Precios_H (Id_Precio, Id_Publicacion, VigenciaD, VigenciaH, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPrecioHist, @IdPublicacionHist, @VigenciaDHist, @VigenciaHHist, @LunesHist, @MartesHist, @MiercolesHist, @JuevesHist, @ViernesHist, @SabadoHist, @DomingoHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { - IdPrecioHist = actual.IdPrecio, - IdPublicacionHist = actual.IdPublicacion, - VigenciaDHist = actual.VigenciaD, - VigenciaHHist = actual.VigenciaH, - LunesHist = actual.Lunes, MartesHist = actual.Martes, MiercolesHist = actual.Miercoles, JuevesHist = actual.Jueves, - ViernesHist = actual.Viernes, SabadoHist = actual.Sabado, DomingoHist = actual.Domingo, - IdUsuarioHist = idUsuario, - FechaModHist = DateTime.Now, - TipoModHist = "Actualizado" + IdPrecioHist = actual.IdPrecio, + IdPublicacionHist = actual.IdPublicacion, + VigenciaDHist = actual.VigenciaD, + VigenciaHHist = actual.VigenciaH, + LunesHist = actual.Lunes, + MartesHist = actual.Martes, + MiercolesHist = actual.Miercoles, + JuevesHist = actual.Jueves, + ViernesHist = actual.Viernes, + SabadoHist = actual.Sabado, + DomingoHist = actual.Domingo, + IdUsuarioHist = idUsuario, + FechaModHist = DateTime.Now, + TipoModHist = "Actualizado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, precioAActualizar, transaction); @@ -184,8 +195,8 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion public async Task DeleteAsync(int idPrecio, int idUsuario, IDbTransaction transaction) { - var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( - @"SELECT Id_Precio AS IdPrecio, Id_Publicacion AS IdPublicacion, VigenciaD, VigenciaH, + var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( + @"SELECT Id_Precio AS IdPrecio, Id_Publicacion AS IdPublicacion, VigenciaD, VigenciaH, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo FROM dbo.dist_Precios WHERE Id_Precio = @IdPrecioParam", new { IdPrecioParam = idPrecio }, transaction); if (actual == null) throw new KeyNotFoundException("Precio no encontrado para eliminar."); @@ -198,14 +209,19 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { - IdPrecioHist = actual.IdPrecio, - IdPublicacionHist = actual.IdPublicacion, - VigenciaDHist = actual.VigenciaD, + IdPrecioHist = actual.IdPrecio, + IdPublicacionHist = actual.IdPublicacion, + VigenciaDHist = actual.VigenciaD, VigenciaHHist = actual.VigenciaH, - LunesHist = actual.Lunes, MartesHist = actual.Martes, MiercolesHist = actual.Miercoles, JuevesHist = actual.Jueves, - ViernesHist = actual.Viernes, SabadoHist = actual.Sabado, DomingoHist = actual.Domingo, - IdUsuarioHist = idUsuario, - FechaModHist = DateTime.Now, + LunesHist = actual.Lunes, + MartesHist = actual.Martes, + MiercolesHist = actual.Miercoles, + JuevesHist = actual.Jueves, + ViernesHist = actual.Viernes, + SabadoHist = actual.Sabado, + DomingoHist = actual.Domingo, + IdUsuarioHist = idUsuario, + FechaModHist = DateTime.Now, TipoModHist = "Eliminado" }, transaction); @@ -214,7 +230,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion } public async Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction) - { + { const string selectSql = @" SELECT Id_Precio AS IdPrecio, Id_Publicacion AS IdPublicacion, VigenciaD, VigenciaH, @@ -228,19 +244,24 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion INSERT INTO dbo.dist_Precios_H (Id_Precio, Id_Publicacion, VigenciaD, VigenciaH, Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPrecioHist, @IdPublicacionHist, @VigenciaDHist, @VigenciaHHist, @LunesHist, @MartesHist, @MiercolesHist, @JuevesHist, @ViernesHist, @SabadoHist, @DomingoHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; - + foreach (var item in itemsToDelete) { await transaction.Connection!.ExecuteAsync(insertHistoricoSql, new { - IdPrecioHist = item.IdPrecio, - IdPublicacionHist = item.IdPublicacion, - VigenciaDHist = item.VigenciaD, + IdPrecioHist = item.IdPrecio, + IdPublicacionHist = item.IdPublicacion, + VigenciaDHist = item.VigenciaD, VigenciaHHist = item.VigenciaH, - LunesHist = item.Lunes, MartesHist = item.Martes, MiercolesHist = item.Miercoles, JuevesHist = item.Jueves, - ViernesHist = item.Viernes, SabadoHist = item.Sabado, DomingoHist = item.Domingo, - IdUsuarioHist = idUsuarioAuditoria, - FechaModHist = DateTime.Now, + LunesHist = item.Lunes, + MartesHist = item.Martes, + MiercolesHist = item.Miercoles, + JuevesHist = item.Jueves, + ViernesHist = item.Viernes, + SabadoHist = item.Sabado, + DomingoHist = item.Domingo, + IdUsuarioHist = idUsuarioAuditoria, + FechaModHist = DateTime.Now, TipoModHist = "Eliminado (Cascada)" }, transaction); } @@ -257,5 +278,82 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion throw; } } + + public async Task IsInUseAsync(int idPrecio, DateTime vigenciaD, DateTime? vigenciaH) + { + // Verificar si el Id_Precio se usa en movimientos DENTRO del rango de vigencia del precio + // Si VigenciaH es NULL, significa que el precio está activo indefinidamente hacia el futuro. + var sql = @" + SELECT TOP 1 1 + FROM ( + SELECT Id_Precio, Fecha FROM dbo.dist_EntradasSalidas + UNION ALL + SELECT Id_Precio, Fecha FROM dbo.dist_EntradasSalidasCanillas + ) AS Movimientos + WHERE Movimientos.Id_Precio = @IdPrecioParam + AND Movimientos.Fecha >= @VigenciaDParam + AND (@VigenciaHParam IS NULL OR Movimientos.Fecha <= @VigenciaHParam);"; + try + { + using var connection = _connectionFactory.CreateConnection(); + var parameters = new + { + IdPrecioParam = idPrecio, + VigenciaDParam = vigenciaD.Date, + VigenciaHParam = vigenciaH?.Date + }; + var inUse = await connection.ExecuteScalarAsync(sql, parameters); + return inUse.HasValue && inUse.Value == 1; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error en IsInUseAsync para Precio ID: {IdPrecio}", idPrecio); + return true; // Asumir en uso si hay error, para ser cauteloso + } + } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPrecioOriginal, int? idPublicacionOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Precio, h.Id_Publicacion, h.VigenciaD, h.VigenciaH, + h.Lunes, h.Martes, h.Miercoles, h.Jueves, h.Viernes, h.Sabado, h.Domingo, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_Precios_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idPrecioOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Precio = @IdPrecioOriginalParam"); parameters.Add("IdPrecioOriginalParam", idPrecioOriginal.Value); } + if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Precios de Publicación."); + return Enumerable.Empty<(PrecioHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PubliSeccionRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PubliSeccionRepository.cs index 1e7a27d..99b38b3 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PubliSeccionRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PubliSeccionRepository.cs @@ -28,7 +28,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado FROM dbo.dist_dtPubliSecciones WHERE Id_Publicacion = @IdPublicacionParam"); - + if (soloActivas.HasValue) { sqlBuilder.Append(soloActivas.Value ? " AND Estado = 1" : " AND Estado = 0"); @@ -97,7 +97,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion "SELECT TOP 1 1 FROM dbo.bob_RegPublicaciones WHERE Id_Seccion = @IdSeccionParam", "SELECT TOP 1 1 FROM dbo.bob_StockBobinas WHERE Id_Seccion = @IdSeccionParam" }; - try + try { foreach (var query in checkQueries) { @@ -171,10 +171,10 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion public async Task DeleteAsync(int idSeccion, int idUsuario, IDbTransaction transaction) { - var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( - @"SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado - FROM dbo.dist_dtPubliSecciones WHERE Id_Seccion = @IdSeccionParam", - new { IdSeccionParam = idSeccion }, transaction); + var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( + @"SELECT Id_Seccion AS IdSeccion, Id_Publicacion AS IdPublicacion, Nombre, Estado + FROM dbo.dist_dtPubliSecciones WHERE Id_Seccion = @IdSeccionParam", + new { IdSeccionParam = idSeccion }, transaction); if (actual == null) throw new KeyNotFoundException("Sección no encontrada para eliminar."); const string sqlDelete = "DELETE FROM dbo.dist_dtPubliSecciones WHERE Id_Seccion = @IdSeccionParam"; @@ -235,5 +235,48 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion throw; } } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idSeccionOriginal, int? idPublicacionOriginal) + { + using var connection = _cf.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Seccion, h.Id_Publicacion, h.Nombre, h.Estado, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_dtPubliSecciones_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idSeccionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Seccion = @IdSeccionOriginalParam"); parameters.Add("IdSeccionOriginalParam", idSeccionOriginal.Value); } + if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _log.LogError(ex, "Error al obtener historial de Secciones de Publicación."); + return Enumerable.Empty<(PubliSeccionHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PublicacionRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PublicacionRepository.cs index 8bbde5e..6fcf378 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PublicacionRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/PublicacionRepository.cs @@ -139,32 +139,33 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion } } - public async Task IsInUseAsync(int id) + public async Task IsInUseAsync(int idPublicacion) { using var connection = _connectionFactory.CreateConnection(); string[] checkQueries = { "SELECT TOP 1 1 FROM dbo.dist_EntradasSalidas WHERE Id_Publicacion = @IdParam", "SELECT TOP 1 1 FROM dbo.dist_EntradasSalidasCanillas WHERE Id_Publicacion = @IdParam", - "SELECT TOP 1 1 FROM dbo.dist_Precios WHERE Id_Publicacion = @IdParam", - "SELECT TOP 1 1 FROM dbo.dist_RecargoZona WHERE Id_Publicacion = @IdParam", - "SELECT TOP 1 1 FROM dbo.dist_PorcPago WHERE Id_Publicacion = @IdParam", - "SELECT TOP 1 1 FROM dbo.dist_PorcMonPagoCanilla WHERE Id_Publicacion = @IdParam", - "SELECT TOP 1 1 FROM dbo.dist_dtPubliSecciones WHERE Id_Publicacion = @IdParam", + "SELECT TOP 1 1 FROM dbo.dist_SalidasOtrosDestinos WHERE Id_Publicacion = @IdParam", "SELECT TOP 1 1 FROM dbo.bob_RegPublicaciones WHERE Id_Publicacion = @IdParam", - "SELECT TOP 1 1 FROM dbo.bob_StockBobinas WHERE Id_Publicacion = @IdParam" + "SELECT TOP 1 1 FROM dbo.bob_RegTiradas WHERE Id_Publicacion = @IdParam", + "SELECT TOP 1 1 FROM dbo.bob_StockBobinas WHERE Id_Publicacion = @IdParam AND Id_EstadoBobina != 1" // Ejemplo: si una bobina EN USO o DAÑADA está asociada }; try { foreach (var query in checkQueries) { - if (await connection.ExecuteScalarAsync(query, new { IdParam = id }) == 1) return true; + if (await connection.ExecuteScalarAsync(query, new { IdParam = idPublicacion }) == 1) + { + _logger.LogInformation("Publicacion ID {IdPublicacion} está en uso (Tabla: {Tabla})", idPublicacion, query.Split("FROM")[1].Split("WHERE")[0].Trim()); + return true; + } } return false; } catch (Exception ex) { - _logger.LogError(ex, "Error en IsInUseAsync para Publicacion ID: {IdPublicacion}", id); - return true; // Asumir en uso si hay error de BD + _logger.LogError(ex, "Error en IsInUseAsync para Publicacion ID: {IdPublicacion}", idPublicacion); + return true; // Asumir en uso si hay error } } @@ -325,7 +326,48 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion ); await Task.WhenAll(insertTasks); } - // No se necesita historial para esta tabla de configuración por ahora. + } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPublicacionOriginal) + { + using var connection = _connectionFactory.CreateConnection(); // Asumiendo _connectionFactory + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Publicacion, h.Nombre, h.Observacion, h.Id_Empresa, h.Habilitada, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_dtPublicaciones_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Publicaciones (Maestro)."); // Asumiendo _logger + return Enumerable.Empty<(PublicacionHistorico, string)>(); + } } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/RecargoZonaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/RecargoZonaRepository.cs index a604675..84290f2 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/RecargoZonaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/RecargoZonaRepository.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; // Necesario para .Any() +using System.Text; using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion @@ -48,6 +49,39 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion } } + public async Task IsInUseAsync(int idRecargo, DateTime vigenciaD, DateTime? vigenciaH) + { + // Similar a Precios, verificar en dist_EntradasSalidas y dist_EntradasSalidasCanillas + // si Id_Recargo se usa en movimientos DENTRO del rango de vigencia del recargo. + var sql = @" + SELECT TOP 1 1 + FROM ( + SELECT Id_Recargo, Fecha FROM dbo.dist_EntradasSalidas + UNION ALL + SELECT Id_Recargo, Fecha FROM dbo.dist_EntradasSalidasCanillas + ) AS Movimientos + WHERE Movimientos.Id_Recargo = @IdRecargoParam + AND Movimientos.Fecha >= @VigenciaDParam + AND (@VigenciaHParam IS NULL OR Movimientos.Fecha <= @VigenciaHParam);"; + try + { + using var connection = _connectionFactory.CreateConnection(); + var parameters = new + { + IdRecargoParam = idRecargo, + VigenciaDParam = vigenciaD.Date, + VigenciaHParam = vigenciaH?.Date + }; + var inUse = await connection.ExecuteScalarAsync(sql, parameters); + return inUse.HasValue && inUse.Value == 1; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error en IsInUseAsync para Recargo ID: {IdRecargo}", idRecargo); + return true; + } + } + public async Task GetByIdAsync(int idRecargo) { const string sql = @" @@ -67,7 +101,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion return null; } } - + public async Task GetActiveByPublicacionZonaAndDateAsync(int idPublicacion, int idZona, DateTime fecha, IDbTransaction? transaction = null) { const string sql = @" @@ -79,28 +113,28 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion AND VigenciaD <= @FechaParam AND (VigenciaH IS NULL OR VigenciaH >= @FechaParam) ORDER BY VigenciaD DESC;"; - + var cn = transaction?.Connection ?? _connectionFactory.CreateConnection(); bool ownConnection = transaction == null; RecargoZona? result = null; try { - if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) + if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) { await dbConn.OpenAsync(); } - result = await cn.QuerySingleOrDefaultAsync(sql, - new { IdPublicacionParam = idPublicacion, IdZonaParam = idZona, FechaParam = fecha.Date }, + result = await cn.QuerySingleOrDefaultAsync(sql, + new { IdPublicacionParam = idPublicacion, IdZonaParam = idZona, FechaParam = fecha.Date }, transaction); } catch (Exception ex) { - _logger.LogError(ex, "Error en GetActiveByPublicacionZonaAndDateAsync. IdPublicacion: {IdPublicacion}, IdZona: {IdZona}, Fecha: {Fecha}", idPublicacion, idZona, fecha); + _logger.LogError(ex, "Error en GetActiveByPublicacionZonaAndDateAsync. IdPublicacion: {IdPublicacion}, IdZona: {IdZona}, Fecha: {Fecha}", idPublicacion, idZona, fecha); throw; } finally { - if (ownConnection && cn.State == ConnectionState.Open && cn is System.Data.Common.DbConnection dbConnClose) + if (ownConnection && cn.State == ConnectionState.Open && cn is System.Data.Common.DbConnection dbConnClose) { await dbConnClose.CloseAsync(); } @@ -120,8 +154,8 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion AND VigenciaD < @VigenciaDNuevoParam AND VigenciaH IS NULL ORDER BY VigenciaD DESC;"; - return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, - new { IdPublicacionParam = idPublicacion, IdZonaParam = idZona, VigenciaDNuevoParam = vigenciaDNuevo.Date }, + return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, + new { IdPublicacionParam = idPublicacion, IdZonaParam = idZona, VigenciaDNuevoParam = vigenciaDNuevo.Date }, transaction); } @@ -141,13 +175,13 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { - IdRecargoParam = inserted.IdRecargo, + IdRecargoParam = inserted.IdRecargo, IdPublicacionParam = inserted.IdPublicacion, IdZonaParam = inserted.IdZona, VigenciaDParam = inserted.VigenciaD, VigenciaHParam = inserted.VigenciaH, ValorParam = inserted.Valor, - Id_UsuarioParam = idUsuario, + Id_UsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Creado" }, transaction); @@ -176,9 +210,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion IdRecargoParam = actual.IdRecargo, IdPublicacionParam = actual.IdPublicacion, IdZonaParam = actual.IdZona, - VigenciaDParam = actual.VigenciaD, - VigenciaHParam = actual.VigenciaH, - ValorParam = actual.Valor, + VigenciaDParam = actual.VigenciaD, + VigenciaHParam = actual.VigenciaH, + ValorParam = actual.Valor, Id_UsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Actualizado" @@ -236,14 +270,14 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion { await transaction.Connection!.ExecuteAsync(insertHistoricoSql, new { - IdRecargoParam = item.IdRecargo, + IdRecargoParam = item.IdRecargo, IdPublicacionParam = item.IdPublicacion, IdZonaParam = item.IdZona, VigenciaDParam = item.VigenciaD, VigenciaHParam = item.VigenciaH, ValorParam = item.Valor, - Id_UsuarioParam = idUsuarioAuditoria, - FechaModParam = DateTime.Now, + Id_UsuarioParam = idUsuarioAuditoria, + FechaModParam = DateTime.Now, TipoModParam = "Eliminado (Cascada)" }, transaction); } @@ -260,5 +294,49 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion throw; } } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idRecargoOriginal, int? idPublicacionOriginal, int? idZonaOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Recargo, h.Id_Publicacion, h.Id_Zona, h.VigenciaD, h.VigenciaH, h.Valor, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_RecargoZona_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idRecargoOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Recargo = @IdRecargoOriginalParam"); parameters.Add("IdRecargoOriginalParam", idRecargoOriginal.Value); } + if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } + if (idZonaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Zona = @IdZonaOriginalParam"); parameters.Add("IdZonaOriginalParam", idZonaOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Recargos por Zona."); + return Enumerable.Empty<(RecargoZonaHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ZonaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ZonaRepository.cs index 96079d7..e3c2cd3 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ZonaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/ZonaRepository.cs @@ -259,7 +259,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion TipoMod = "Eliminada" // O "Deshabilitada" }, transaction: transaction); } - + transaction.Commit(); return rowsAffected == 1; } @@ -301,5 +301,46 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion return true; // Asumir que está en uso si hay error } } + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idZonaOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Zona, h.Nombre, h.Descripcion, h.Estado, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.dist_dtZonas_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idZonaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Zona = @IdZonaOriginalParam"); parameters.Add("IdZonaOriginalParam", idZonaOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Zonas (Maestro)."); + return Enumerable.Empty<(ZonaHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/EstadoBobinaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/EstadoBobinaRepository.cs index 3275eff..bc8bfd7 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/EstadoBobinaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/EstadoBobinaRepository.cs @@ -204,5 +204,47 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion return rowsAffected == 1; } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idEstadoBobinaOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_EstadoBobina, h.Denominacion, h.Obs, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.bob_dtEstadosBobinas_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idEstadoBobinaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_EstadoBobina = @IdEstadoBobinaOriginalParam"); parameters.Add("IdEstadoBobinaOriginalParam", idEstadoBobinaOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Estados de Bobina."); + return Enumerable.Empty<(EstadoBobinaHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IEstadoBobinaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IEstadoBobinaRepository.cs index 69fcca7..0d61be6 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IEstadoBobinaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IEstadoBobinaRepository.cs @@ -14,5 +14,9 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion Task DeleteAsync(int id, int idUsuario, IDbTransaction transaction); Task ExistsByDenominacionAsync(string denominacion, int? excludeId = null); Task IsInUseAsync(int id); // Verificar si se usa en bob_StockBobinas + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idEstadoBobinaOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IPlantaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IPlantaRepository.cs index 505a4e7..db87e00 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IPlantaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IPlantaRepository.cs @@ -14,5 +14,9 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion Task DeleteAsync(int id, int idUsuario, IDbTransaction transaction); // Borrado físico con historial Task ExistsByNameAsync(string nombre, int? excludeId = null); Task IsInUseAsync(int id); // Verificar si se usa en bob_StockBobinas o bob_RegPublicaciones + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPlantaOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IRegTiradaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IRegTiradaRepository.cs index c8bc0a7..2f7b4e5 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IRegTiradaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IRegTiradaRepository.cs @@ -14,7 +14,15 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion Task DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction); // Si se borra el registro principal Task DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction); Task GetByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, IDbTransaction? transaction = null); - + Task> GetRegTiradasHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idRegistroOriginal, int? idPublicacionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro); + Task> GetRegSeccionesTiradaHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idTiradaOriginal, // ID del registro en bob_RegPublicaciones + int? idPublicacionFiltro, int? idSeccionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro); } public interface IRegPublicacionSeccionRepository // Para bob_RegPublicaciones diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IStockBobinaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IStockBobinaRepository.cs index 03fd767..e4490b2 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IStockBobinaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/IStockBobinaRepository.cs @@ -22,5 +22,9 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Task CreateAsync(StockBobina nuevaBobina, int idUsuario, IDbTransaction transaction); Task UpdateAsync(StockBobina bobinaAActualizar, int idUsuario, IDbTransaction transaction, string tipoMod = "Actualizada"); // tipoMod para historial Task DeleteAsync(int idBobina, int idUsuario, IDbTransaction transaction); // Solo si está en estado "Disponible" + Task> GetHistorialDetalladoAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idBobinaOriginal, int? idTipoBobinaFiltro, int? idPlantaFiltro, int? idEstadoBobinaFiltro); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/ITipoBobinaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/ITipoBobinaRepository.cs index fa3f4c0..60a39bc 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/ITipoBobinaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/ITipoBobinaRepository.cs @@ -14,5 +14,9 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion Task DeleteAsync(int id, int idUsuario, IDbTransaction transaction); Task ExistsByDenominacionAsync(string denominacion, int? excludeId = null); Task IsInUseAsync(int id); // Verificar si se usa en bob_StockBobinas + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idTipoBobinaOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/PlantaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/PlantaRepository.cs index b21ea66..51782fd 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/PlantaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/PlantaRepository.cs @@ -154,7 +154,7 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion public async Task UpdateAsync(Planta plantaAActualizar, int idUsuario, IDbTransaction transaction) { - // El servicio ya verificó que existe. Obtener estado actual para historial dentro de la transacción. + // El servicio ya verificó que existe. Obtener estado actual para historial dentro de la transacción. var connection = transaction.Connection!; var plantaActual = await connection.QuerySingleOrDefaultAsync( "SELECT Id_Planta AS IdPlanta, Nombre, Detalle FROM dbo.bob_dtPlantas WHERE Id_Planta = @Id", @@ -225,5 +225,47 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion return rowsAffected == 1; } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPlantaOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Planta, h.Nombre, h.Detalle, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.bob_dtPlantas_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idPlantaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Planta = @IdPlantaOriginalParam"); parameters.Add("IdPlantaOriginalParam", idPlantaOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Plantas de Impresión."); + return Enumerable.Empty<(PlantaHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/RegTiradaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/RegTiradaRepository.cs index c8e5166..eb7dbe3 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/RegTiradaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/RegTiradaRepository.cs @@ -35,7 +35,7 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion try { if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); - result = await cn.QuerySingleOrDefaultAsync(sql, new { FechaParam = fecha.Date, IdPublicacionParam = idPublicacion, IdPlantaParam = idPlanta }, transaction); + result = await cn.QuerySingleOrDefaultAsync(sql, new { FechaParam = fecha.Date, IdPublicacionParam = idPublicacion, IdPlantaParam = idPlanta }, transaction); } finally { @@ -69,55 +69,178 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod) VALUES (@IdRegistroParam, @EjemplaresParam, @IdPublicacionParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; - await transaction.Connection!.ExecuteAsync(sqlHistorico, new { - IdRegistroParam = inserted.IdRegistro, EjemplaresParam = inserted.Ejemplares, IdPublicacionParam = inserted.IdPublicacion, FechaParam = inserted.Fecha, IdPlantaParam = inserted.IdPlanta, - IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Creado" + await transaction.Connection!.ExecuteAsync(sqlHistorico, new + { + IdRegistroParam = inserted.IdRegistro, + EjemplaresParam = inserted.Ejemplares, + IdPublicacionParam = inserted.IdPublicacion, + FechaParam = inserted.Fecha, + IdPlantaParam = inserted.IdPlanta, + IdUsuarioParam = idUsuario, + FechaModParam = DateTime.Now, + TipoModParam = "Creado" }, transaction); return inserted; } public async Task DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction) { - var actual = await GetByIdAsync(idRegistro); // No necesita TX aquí ya que es solo para historial - if (actual == null) throw new KeyNotFoundException("Registro de tirada no encontrado."); + var actual = await GetByIdAsync(idRegistro); // No necesita TX aquí ya que es solo para historial + if (actual == null) throw new KeyNotFoundException("Registro de tirada no encontrado."); const string sqlDelete = "DELETE FROM dbo.bob_RegTiradas WHERE Id_Registro = @IdRegistroParam"; const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod) VALUES (@IdRegistroParam, @EjemplaresParam, @IdPublicacionParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; - await transaction.Connection!.ExecuteAsync(sqlHistorico, new { - IdRegistroParam = actual.IdRegistro, EjemplaresParam = actual.Ejemplares, IdPublicacionParam = actual.IdPublicacion, FechaParam = actual.Fecha, IdPlantaParam = actual.IdPlanta, - IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Eliminado" + await transaction.Connection!.ExecuteAsync(sqlHistorico, new + { + IdRegistroParam = actual.IdRegistro, + EjemplaresParam = actual.Ejemplares, + IdPublicacionParam = actual.IdPublicacion, + FechaParam = actual.Fecha, + IdPlantaParam = actual.IdPlanta, + IdUsuarioParam = idUsuario, + FechaModParam = DateTime.Now, + TipoModParam = "Eliminado" }, transaction); - + var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdRegistroParam = idRegistro }, transaction); return rowsAffected == 1; } - public async Task DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction) + public async Task DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction) { var tiradasAEliminar = await GetByCriteriaAsync(fecha.Date, idPublicacion, idPlanta); // Obtener antes de borrar para historial - foreach(var actual in tiradasAEliminar) + foreach (var actual in tiradasAEliminar) { - const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod) + const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod) VALUES (@IdRegistroParam, @EjemplaresParam, @IdPublicacionParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; - await transaction.Connection!.ExecuteAsync(sqlHistorico, new { - IdRegistroParam = actual.IdRegistro, EjemplaresParam = actual.Ejemplares, IdPublicacionParam = actual.IdPublicacion, FechaParam = actual.Fecha, IdPlantaParam = actual.IdPlanta, - IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Eliminado (Por Fecha/Pub/Planta)" + await transaction.Connection!.ExecuteAsync(sqlHistorico, new + { + IdRegistroParam = actual.IdRegistro, + EjemplaresParam = actual.Ejemplares, + IdPublicacionParam = actual.IdPublicacion, + FechaParam = actual.Fecha, + IdPlantaParam = actual.IdPlanta, + IdUsuarioParam = idUsuario, + FechaModParam = DateTime.Now, + TipoModParam = "Eliminado (Por Fecha/Pub/Planta)" }, transaction); } const string sqlDelete = "DELETE FROM dbo.bob_RegTiradas WHERE Fecha = @FechaParam AND Id_Publicacion = @IdPublicacionParam AND Id_Planta = @IdPlantaParam"; - var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, + var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { FechaParam = fecha.Date, IdPublicacionParam = idPublicacion, IdPlantaParam = idPlanta }, transaction); return rowsAffected > 0; // Devuelve true si se borró al menos una fila } + + public async Task> GetRegTiradasHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idRegistroOriginal, int? idPublicacionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro) + { + using var connection = _cf.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Registro, h.Ejemplares, h.Id_Publicacion, h.Fecha, h.Id_Planta, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico, + pub.Nombre AS NombrePublicacion, + p.Nombre AS NombrePlanta + FROM dbo.bob_RegTiradas_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + JOIN dbo.dist_dtPublicaciones pub ON h.Id_Publicacion = pub.Id_Publicacion + JOIN dbo.bob_dtPlantas p ON h.Id_Planta = p.Id_Planta + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idRegistroOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Registro = @IdRegistroOriginalParam"); parameters.Add("IdRegistroOriginalParam", idRegistroOriginal.Value); } + if (idPublicacionFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionFiltroParam"); parameters.Add("IdPublicacionFiltroParam", idPublicacionFiltro.Value); } + if (idPlantaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Planta = @IdPlantaFiltroParam"); parameters.Add("IdPlantaFiltroParam", idPlantaFiltro.Value); } + if (fechaTiradaFiltro.HasValue) { sqlBuilder.Append(" AND h.Fecha = @FechaTiradaFiltroParam"); parameters.Add("FechaTiradaFiltroParam", fechaTiradaFiltro.Value.Date); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userMod, pubNombre, plantaNombre) => (hist, userMod, pubNombre, plantaNombre), + parameters, + splitOn: "NombreUsuarioModifico,NombrePublicacion,NombrePlanta" + ); + return result; + } + catch (Exception ex) + { + _log.LogError(ex, "Error al obtener historial de Registro de Tiradas."); + return Enumerable.Empty<(RegTiradaHistorico, string, string, string)>(); + } + } + + public async Task> GetRegSeccionesTiradaHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idTiradaOriginal, int? idPublicacionFiltro, int? idSeccionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro) + { + using var connection = _cf.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Tirada, h.Id_Publicacion, h.Id_Seccion, h.CantPag, h.Fecha, h.Id_Planta, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico, + pub.Nombre AS NombrePublicacion, + sec.Nombre AS NombreSeccion, + p.Nombre AS NombrePlanta + FROM dbo.bob_RegPublicaciones_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + JOIN dbo.dist_dtPublicaciones pub ON h.Id_Publicacion = pub.Id_Publicacion + JOIN dbo.dist_dtPubliSecciones sec ON h.Id_Seccion = sec.Id_Seccion + AND h.Id_Publicacion = sec.Id_Publicacion -- Crucial para el JOIN correcto de sección + JOIN dbo.bob_dtPlantas p ON h.Id_Planta = p.Id_Planta + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idTiradaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Tirada = @IdTiradaOriginalParam"); parameters.Add("IdTiradaOriginalParam", idTiradaOriginal.Value); } + if (idPublicacionFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionFiltroParam"); parameters.Add("IdPublicacionFiltroParam", idPublicacionFiltro.Value); } + if (idSeccionFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Seccion = @IdSeccionFiltroParam"); parameters.Add("IdSeccionFiltroParam", idSeccionFiltro.Value); } + if (idPlantaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Planta = @IdPlantaFiltroParam"); parameters.Add("IdPlantaFiltroParam", idPlantaFiltro.Value); } + if (fechaTiradaFiltro.HasValue) { sqlBuilder.Append(" AND h.Fecha = @FechaTiradaFiltroParam"); parameters.Add("FechaTiradaFiltroParam", fechaTiradaFiltro.Value.Date); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userMod, pubNombre, secNombre, plantaNombre) => (hist, userMod, pubNombre, secNombre, plantaNombre), + parameters, + splitOn: "NombreUsuarioModifico,NombrePublicacion,NombreSeccion,NombrePlanta" + ); + return result; + } + catch (Exception ex) + { + _log.LogError(ex, "Error al obtener historial de Secciones de Tirada."); + return Enumerable.Empty<(RegPublicacionSeccionHistorico, string, string, string, string)>(); + } + } } public class RegPublicacionSeccionRepository : IRegPublicacionSeccionRepository { private readonly DbConnectionFactory _cf; private readonly ILogger _log; - public RegPublicacionSeccionRepository(DbConnectionFactory cf, ILogger log){ _cf = cf; _log = log; } + public RegPublicacionSeccionRepository(DbConnectionFactory cf, ILogger log) { _cf = cf; _log = log; } public async Task> GetByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta) { @@ -138,9 +261,17 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion const string sqlHistorico = @"INSERT INTO dbo.bob_RegPublicaciones_H (Id_Tirada, Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod) VALUES (@IdTiradaParam, @IdPublicacionParam, @IdSeccionParam, @CantPagParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; - await transaction.Connection!.ExecuteAsync(sqlHistorico, new { - IdTiradaParam = inserted.IdTirada, IdPublicacionParam = inserted.IdPublicacion, IdSeccionParam = inserted.IdSeccion, CantPagParam = inserted.CantPag, FechaParam = inserted.Fecha, IdPlantaParam = inserted.IdPlanta, - IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Creado" + await transaction.Connection!.ExecuteAsync(sqlHistorico, new + { + IdTiradaParam = inserted.IdTirada, + IdPublicacionParam = inserted.IdPublicacion, + IdSeccionParam = inserted.IdSeccion, + CantPagParam = inserted.CantPag, + FechaParam = inserted.Fecha, + IdPlantaParam = inserted.IdPlanta, + IdUsuarioParam = idUsuario, + FechaModParam = DateTime.Now, + TipoModParam = "Creado" }, transaction); return inserted; } @@ -149,18 +280,26 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion { var seccionesAEliminar = await GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta); // No necesita TX, es SELECT - foreach(var actual in seccionesAEliminar) + foreach (var actual in seccionesAEliminar) { const string sqlHistorico = @"INSERT INTO dbo.bob_RegPublicaciones_H (Id_Tirada, Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod) VALUES (@IdTiradaParam, @IdPublicacionParam, @IdSeccionParam, @CantPagParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; - await transaction.Connection!.ExecuteAsync(sqlHistorico, new { - IdTiradaParam = actual.IdTirada, IdPublicacionParam = actual.IdPublicacion, IdSeccionParam = actual.IdSeccion, CantPagParam = actual.CantPag, FechaParam = actual.Fecha, IdPlantaParam = actual.IdPlanta, - IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Eliminado (Por Fecha/Pub/Planta)" + await transaction.Connection!.ExecuteAsync(sqlHistorico, new + { + IdTiradaParam = actual.IdTirada, + IdPublicacionParam = actual.IdPublicacion, + IdSeccionParam = actual.IdSeccion, + CantPagParam = actual.CantPag, + FechaParam = actual.Fecha, + IdPlantaParam = actual.IdPlanta, + IdUsuarioParam = idUsuario, + FechaModParam = DateTime.Now, + TipoModParam = "Eliminado (Por Fecha/Pub/Planta)" }, transaction); } const string sqlDelete = "DELETE FROM dbo.bob_RegPublicaciones WHERE Fecha = @FechaParam AND Id_Publicacion = @IdPublicacionParam AND Id_Planta = @IdPlantaParam"; - var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, + var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { FechaParam = fecha.Date, IdPublicacionParam = idPublicacion, IdPlantaParam = idPlanta }, transaction); return rowsAffected >= 0; // Devuelve true incluso si no había nada que borrar (para que no falle la TX si es parte de una cadena) } diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/StockBobinaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/StockBobinaRepository.cs index 51b3ab9..2787fd1 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/StockBobinaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/StockBobinaRepository.cs @@ -136,7 +136,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion INSERTED.Id_Planta AS IdPlanta, INSERTED.Id_EstadoBobina AS IdEstadoBobina, INSERTED.Remito, INSERTED.FechaRemito, INSERTED.FechaEstado, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Id_Seccion AS IdSeccion, INSERTED.Obs VALUES (@IdTipoBobina, @NroBobina, @Peso, @IdPlanta, @IdEstadoBobina, @Remito, @FechaRemito, @FechaEstado, @IdPublicacion, @IdSeccion, @Obs);"; - + var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevaBobina, transaction); if (inserted == null || inserted.IdBobina == 0) throw new DataException("Error al ingresar bobina o ID no generado."); @@ -147,10 +147,21 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { - IdBobinaHist = inserted.IdBobina, IdTipoBobinaHist = inserted.IdTipoBobina, NroBobinaHist = inserted.NroBobina, PesoHist = inserted.Peso, - IdPlantaHist = inserted.IdPlanta, IdEstadoBobinaHist = inserted.IdEstadoBobina, RemitoHist = inserted.Remito, FechaRemitoHist = inserted.FechaRemito, - FechaEstadoHist = inserted.FechaEstado, IdPublicacionHist = inserted.IdPublicacion, IdSeccionHist = inserted.IdSeccion, ObsHist = inserted.Obs, - IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Ingreso" + IdBobinaHist = inserted.IdBobina, + IdTipoBobinaHist = inserted.IdTipoBobina, + NroBobinaHist = inserted.NroBobina, + PesoHist = inserted.Peso, + IdPlantaHist = inserted.IdPlanta, + IdEstadoBobinaHist = inserted.IdEstadoBobina, + RemitoHist = inserted.Remito, + FechaRemitoHist = inserted.FechaRemito, + FechaEstadoHist = inserted.FechaEstado, + IdPublicacionHist = inserted.IdPublicacion, + IdSeccionHist = inserted.IdSeccion, + ObsHist = inserted.Obs, + IdUsuarioHist = idUsuario, + FechaModHist = DateTime.Now, + TipoModHist = "Ingreso" }, transaction); return inserted; } @@ -172,7 +183,7 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion Id_EstadoBobina = @IdEstadoBobina, Remito = @Remito, FechaRemito = @FechaRemito, FechaEstado = @FechaEstado, Id_Publicacion = @IdPublicacion, Id_Seccion = @IdSeccion, Obs = @Obs WHERE Id_Bobina = @IdBobina;"; - + const string sqlInsertHistorico = @" INSERT INTO dbo.bob_StockBobinas_H (Id_Bobina, Id_TipoBobina, NroBobina, Peso, Id_Planta, Id_EstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion, Id_Seccion, Obs, Id_Usuario, FechaMod, TipoMod) @@ -180,10 +191,21 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { - IdBobinaHist = actual.IdBobina, IdTipoBobinaHist = actual.IdTipoBobina, NroBobinaHist = actual.NroBobina, PesoHist = actual.Peso, - IdPlantaHist = actual.IdPlanta, IdEstadoBobinaHist = actual.IdEstadoBobina, RemitoHist = actual.Remito, FechaRemitoHist = actual.FechaRemito, - FechaEstadoHist = actual.FechaEstado, IdPublicacionHist = actual.IdPublicacion, IdSeccionHist = actual.IdSeccion, ObsHist = actual.Obs, - IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = tipoMod // "Actualizada" o "Estado Cambiado" + IdBobinaHist = actual.IdBobina, + IdTipoBobinaHist = actual.IdTipoBobina, + NroBobinaHist = actual.NroBobina, + PesoHist = actual.Peso, + IdPlantaHist = actual.IdPlanta, + IdEstadoBobinaHist = actual.IdEstadoBobina, + RemitoHist = actual.Remito, + FechaRemitoHist = actual.FechaRemito, + FechaEstadoHist = actual.FechaEstado, + IdPublicacionHist = actual.IdPublicacion, + IdSeccionHist = actual.IdSeccion, + ObsHist = actual.Obs, + IdUsuarioHist = idUsuario, + FechaModHist = DateTime.Now, + TipoModHist = tipoMod // "Actualizada" o "Estado Cambiado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, bobinaAActualizar, transaction); @@ -192,41 +214,109 @@ namespace GestionIntegral.Api.Data.Repositories.Distribucion public async Task DeleteAsync(int idBobina, int idUsuario, IDbTransaction transaction) { - // Normalmente, una bobina en stock no se "elimina" físicamente si ya tuvo movimientos. - // Este método sería para borrar un ingreso erróneo que aún esté "Disponible". - var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( + var connection = transaction.Connection!; // Asegurar que la conexión no es null + var actual = await connection.QuerySingleOrDefaultAsync( @"SELECT Id_Bobina AS IdBobina, Id_TipoBobina AS IdTipoBobina, NroBobina, Peso, - Id_Planta AS IdPlanta, Id_EstadoBobina AS IdEstadoBobina, Remito, FechaRemito, - FechaEstado, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, Obs - FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam", + Id_Planta AS IdPlanta, Id_EstadoBobina AS IdEstadoBobina, Remito, FechaRemito, + FechaEstado, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, Obs + FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam", new { IdBobinaParam = idBobina }, transaction); + if (actual == null) throw new KeyNotFoundException("Bobina no encontrada para eliminar."); - // VALIDACIÓN IMPORTANTE: Solo permitir borrar si está en estado "Disponible" (o el que se defina) - if (actual.IdEstadoBobina != 1) // Asumiendo 1 = Disponible + // --- INICIO DE CAMBIO EN VALIDACIÓN --- + // Permitir eliminar si está Disponible (1) o Dañada (3) + if (actual.IdEstadoBobina != 1 && actual.IdEstadoBobina != 3) { - _logger.LogWarning("Intento de eliminar bobina {IdBobina} que no está en estado 'Disponible'. Estado actual: {EstadoActual}", idBobina, actual.IdEstadoBobina); - // No lanzar excepción para que la transacción no falle si es una validación de negocio - return false; + _logger.LogWarning("Intento de eliminar bobina {IdBobina} que no está en estado 'Disponible' o 'Dañada'. Estado actual: {EstadoActual}", idBobina, actual.IdEstadoBobina); + return false; // Devolver false si no cumple la condición para ser eliminada } + // --- FIN DE CAMBIO EN VALIDACIÓN --- const string sqlDelete = "DELETE FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam"; const string sqlInsertHistorico = @" - INSERT INTO dbo.bob_StockBobinas_H - (Id_Bobina, Id_TipoBobina, NroBobina, Peso, Id_Planta, Id_EstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion, Id_Seccion, Obs, Id_Usuario, FechaMod, TipoMod) - VALUES (@IdBobinaHist, @IdTipoBobinaHist, @NroBobinaHist, @PesoHist, @IdPlantaHist, @IdEstadoBobinaHist, @RemitoHist, @FechaRemitoHist, @FechaEstadoHist, @IdPublicacionHist, @IdSeccionHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; + INSERT INTO dbo.bob_StockBobinas_H + (Id_Bobina, Id_TipoBobina, NroBobina, Peso, Id_Planta, Id_EstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion, Id_Seccion, Obs, Id_Usuario, FechaMod, TipoMod) + VALUES (@IdBobinaHist, @IdTipoBobinaHist, @NroBobinaHist, @PesoHist, @IdPlantaHist, @IdEstadoBobinaHist, @RemitoHist, @FechaRemitoHist, @FechaEstadoHist, @IdPublicacionHist, @IdSeccionHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; - - await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new + await connection.ExecuteAsync(sqlInsertHistorico, new { - IdBobinaHist = actual.IdBobina, IdTipoBobinaHist = actual.IdTipoBobina, NroBobinaHist = actual.NroBobina, PesoHist = actual.Peso, - IdPlantaHist = actual.IdPlanta, IdEstadoBobinaHist = actual.IdEstadoBobina, RemitoHist = actual.Remito, FechaRemitoHist = actual.FechaRemito, - FechaEstadoHist = actual.FechaEstado, IdPublicacionHist = actual.IdPublicacion, IdSeccionHist = actual.IdSeccion, ObsHist = actual.Obs, - IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Eliminada" + IdBobinaHist = actual.IdBobina, + IdTipoBobinaHist = actual.IdTipoBobina, + NroBobinaHist = actual.NroBobina, + PesoHist = actual.Peso, + IdPlantaHist = actual.IdPlanta, + IdEstadoBobinaHist = actual.IdEstadoBobina, + RemitoHist = actual.Remito, + FechaRemitoHist = actual.FechaRemito, + FechaEstadoHist = actual.FechaEstado, + IdPublicacionHist = actual.IdPublicacion, + IdSeccionHist = actual.IdSeccion, + ObsHist = actual.Obs, + IdUsuarioHist = idUsuario, + FechaModHist = DateTime.Now, + TipoModHist = "Eliminada" }, transaction); - var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdBobinaParam = idBobina }, transaction); + var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { IdBobinaParam = idBobina }, transaction); return rowsAffected == 1; } + + public async Task> GetHistorialDetalladoAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idBobinaOriginal, int? idTipoBobinaFiltro, int? idPlantaFiltro, int? idEstadoBobinaFiltro) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_Bobina, h.Id_TipoBobina, h.NroBobina, h.Peso, h.Id_Planta, h.Id_EstadoBobina, + h.Remito, h.FechaRemito, h.FechaEstado, h.Id_Publicacion, h.Id_Seccion, h.Obs, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico, + tb.Denominacion AS NombreTipoBobina, + p.Nombre AS NombrePlanta, + eb.Denominacion AS NombreEstadoBobina, + pub.Nombre AS NombrePublicacion, + sec.Nombre AS NombreSeccion + FROM dbo.bob_StockBobinas_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + LEFT JOIN dbo.bob_dtBobinas tb ON h.Id_TipoBobina = tb.Id_TipoBobina + LEFT JOIN dbo.bob_dtPlantas p ON h.Id_Planta = p.Id_Planta + LEFT JOIN dbo.bob_dtEstadosBobinas eb ON h.Id_EstadoBobina = eb.Id_EstadoBobina + LEFT JOIN dbo.dist_dtPublicaciones pub ON h.Id_Publicacion = pub.Id_Publicacion + LEFT JOIN dbo.dist_dtPubliSecciones sec ON h.Id_Seccion = sec.Id_Seccion + AND h.Id_Publicacion = sec.Id_Publicacion -- Asegurar que la sección pertenezca a la publicación + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idBobinaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Bobina = @IdBobinaOriginalParam"); parameters.Add("IdBobinaOriginalParam", idBobinaOriginal.Value); } + if (idTipoBobinaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_TipoBobina = @IdTipoBobinaFiltroParam"); parameters.Add("IdTipoBobinaFiltroParam", idTipoBobinaFiltro.Value); } + if (idPlantaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Planta = @IdPlantaFiltroParam"); parameters.Add("IdPlantaFiltroParam", idPlantaFiltro.Value); } + if (idEstadoBobinaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_EstadoBobina = @IdEstadoBobinaFiltroParam"); parameters.Add("IdEstadoBobinaFiltroParam", idEstadoBobinaFiltro.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userMod, tipoBob, planta, estadoBob, pub, secc) => (hist, userMod, tipoBob, planta, estadoBob, pub, secc), + parameters, + splitOn: "NombreUsuarioModifico,NombreTipoBobina,NombrePlanta,NombreEstadoBobina,NombrePublicacion,NombreSeccion" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial detallado de Stock de Bobinas."); + return Enumerable.Empty<(StockBobinaHistorico, string, string?, string?, string?, string?, string?)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/TipoBobinaRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/TipoBobinaRepository.cs index 05948f9..a04e458 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Impresion/TipoBobinaRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Impresion/TipoBobinaRepository.cs @@ -101,8 +101,6 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion } } - // --- Métodos de Escritura (USAN TRANSACCIÓN) --- - public async Task CreateAsync(TipoBobina nuevoTipoBobina, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" @@ -199,5 +197,47 @@ namespace GestionIntegral.Api.Data.Repositories.Impresion return rowsAffected == 1; } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idTipoBobinaOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.Id_TipoBobina, h.Denominacion, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.bob_dtBobinas_H h -- Nombre de tabla de historial correcto + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idTipoBobinaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_TipoBobina = @IdTipoBobinaOriginalParam"); parameters.Add("IdTipoBobinaOriginalParam", idTipoBobinaOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Tipos de Bobina."); + return Enumerable.Empty<(TipoBobinaHistorico, string)>(); + } + } } -} \ No newline at end of file +} diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/IPerfilRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/IPerfilRepository.cs index af15327..b75c4c3 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/IPerfilRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/IPerfilRepository.cs @@ -16,5 +16,14 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios Task IsInUseAsync(int id); Task> GetPermisoIdsByPerfilIdAsync(int idPerfil); Task UpdatePermisosByPerfilIdAsync(int idPerfil, IEnumerable nuevosPermisosIds, IDbTransaction transaction); + Task LogPermisoAsignacionHistorialAsync(int idPerfil, int idPermiso, int idUsuario, string tipoMod, IDbTransaction transaction); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPerfilOriginal); + Task> GetPermisosAsignadosHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPerfilAfectado, int? idPermisoAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/IPermisoRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/IPermisoRepository.cs index 1a1c93a..e0c8ae2 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/IPermisoRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/IPermisoRepository.cs @@ -15,5 +15,9 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios Task ExistsByCodAccAsync(string codAcc, int? excludeId = null); Task IsInUseAsync(int id); Task> GetPermisosByIdsAsync(IEnumerable ids); + Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPermisoOriginal); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/PerfilRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/PerfilRepository.cs index b0109d8..d7d9845 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/PerfilRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/PerfilRepository.cs @@ -240,11 +240,112 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios var permisosParaInsertar = nuevosPermisosIds.Select(idPermiso => new { IdPerfil = idPerfil, IdPermiso = idPermiso }); await connection.ExecuteAsync(sqlInsert, permisosParaInsertar, transaction: transaction); } - // No hay tabla _H para gral_PermisosPerfiles directamente en este diseño. - // La auditoría de qué usuario cambió los permisos de un perfil se podría registrar - // en una tabla de log de acciones más general si fuera necesario, o deducir - // indirectamente si la interfaz de usuario solo permite a SuperAdmin hacer esto. + } + + public async Task LogPermisoAsignacionHistorialAsync(int idPerfil, int idPermiso, int idUsuario, string tipoMod, IDbTransaction transaction) + { + const string sql = @" + INSERT INTO dbo.gral_PermisosPerfiles_H (idPerfil, idPermiso, Id_Usuario, FechaMod, TipoMod) + VALUES (@IdPerfil, @IdPermiso, @IdUsuario, @FechaMod, @TipoMod);"; + + await transaction.Connection!.ExecuteAsync(sql, new + { + IdPerfil = idPerfil, + IdPermiso = idPermiso, + IdUsuario = idUsuario, + FechaMod = DateTime.Now, + TipoMod = tipoMod + }, transaction: transaction); + } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPerfilOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.idPerfil, h.perfil, h.descPerfil, -- Campos de gral_Perfiles_H + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.gral_Perfiles_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idPerfilOriginal.HasValue) { sqlBuilder.Append(" AND h.idPerfil = @IdPerfilOriginalParam"); parameters.Add("IdPerfilOriginalParam", idPerfilOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Perfiles."); + return Enumerable.Empty<(PerfilHistorico, string)>(); + } + } + + public async Task> GetPermisosAsignadosHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPerfilAfectado, int? idPermisoAfectado) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.IdHist, h.idPerfil, h.idPermiso, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico, + pf.perfil AS NombrePerfil, + p.descPermiso AS DescPermiso, + p.codAcc AS CodAccPermiso + FROM dbo.gral_PermisosPerfiles_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + JOIN dbo.gral_Perfiles pf ON h.idPerfil = pf.id + JOIN dbo.gral_Permisos p ON h.idPermiso = p.id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idPerfilAfectado.HasValue) { sqlBuilder.Append(" AND h.idPerfil = @IdPerfilAfectadoParam"); parameters.Add("IdPerfilAfectadoParam", idPerfilAfectado.Value); } + if (idPermisoAfectado.HasValue) { sqlBuilder.Append(" AND h.idPermiso = @IdPermisoAfectadoParam"); parameters.Add("IdPermisoAfectadoParam", idPermisoAfectado.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userMod, perf, desc, cod) => (hist, userMod, perf, desc, cod), + parameters, + splitOn: "NombreUsuarioModifico,NombrePerfil,DescPermiso,CodAccPermiso" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Asignación de Permisos."); + return Enumerable.Empty<(PermisosPerfilesHistorico, string, string, string, string)>(); + } } } - } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/PermisoRepository.cs b/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/PermisoRepository.cs index 6f47ecc..2ae60a4 100644 --- a/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/PermisoRepository.cs +++ b/Backend/GestionIntegral.Api/Data/Repositories/Usuarios/PermisoRepository.cs @@ -104,7 +104,7 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios return true; } } - + public async Task> GetPermisosByIdsAsync(IEnumerable ids) { if (ids == null || !ids.Any()) @@ -227,5 +227,47 @@ namespace GestionIntegral.Api.Data.Repositories.Usuarios var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { Id = id }, transaction: transaction); return rowsAffected == 1; } + + public async Task> GetHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPermisoOriginal) + { + using var connection = _connectionFactory.CreateConnection(); + var sqlBuilder = new StringBuilder(@" + SELECT + h.IdHist, h.idPermiso, h.modulo, h.descPermiso, h.codAcc, + h.Id_Usuario, h.FechaMod, h.TipoMod, + u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico + FROM dbo.gral_Permisos_H h + JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id + WHERE 1=1"); + + var parameters = new DynamicParameters(); + + if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } + if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } + if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } + if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } + if (idPermisoOriginal.HasValue) { sqlBuilder.Append(" AND h.idPermiso = @IdPermisoOriginalParam"); parameters.Add("IdPermisoOriginalParam", idPermisoOriginal.Value); } + + sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); + + try + { + var result = await connection.QueryAsync( + sqlBuilder.ToString(), + (hist, userName) => (hist, userName), + parameters, + splitOn: "NombreUsuarioModifico" + ); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error al obtener historial de Permisos (Maestro)."); + return Enumerable.Empty<(PermisoHistorico, string)>(); + } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/ControlDevolucionesHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/ControlDevolucionesHistorico.cs index d793a64..4a94b5c 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/ControlDevolucionesHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/ControlDevolucionesHistorico.cs @@ -1,10 +1,10 @@ using System; + namespace GestionIntegral.Api.Models.Distribucion { public class ControlDevolucionesHistorico // Corresponde a dist_dtCtrlDevoluciones_H { - // No hay PK explícita en _H - public int Id_Control { get; set; } // Coincide con columna en _H + public int Id_Control { get; set; } // ID del ControlDevoluciones original public int Id_Empresa { get; set; } public DateTime Fecha { get; set; } public int Entrada { get; set; } diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/OtroDestinoHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/OtroDestinoHistorico.cs index 3d60676..c9a49d0 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/OtroDestinoHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/OtroDestinoHistorico.cs @@ -1,19 +1,14 @@ +using System; + namespace GestionIntegral.Api.Models.Distribucion { - public class OtroDestinoHistorico + public class OtroDestinoHistorico // Corresponde a dist_dtOtrosDestinos_H { - // Columna: Id_Destino (int, FK) - public int IdDestino { get; set; } - - // Columna: Nombre (varchar(100), NOT NULL) + public int Id_Destino { get; set; } // ID del OtroDestino original public string Nombre { get; set; } = string.Empty; - - // Columna: Obs (varchar(250), NULL) public string? Obs { get; set; } - - // Columnas de Auditoría - public int IdUsuario { get; set; } + public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } - public string TipoMod { get; set; } = string.Empty; // "Insertada", "Modificada", "Eliminada" + public string TipoMod { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/PorcMonCanillaHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/PorcMonCanillaHistorico.cs index 6a0389c..5fcf871 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/PorcMonCanillaHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/PorcMonCanillaHistorico.cs @@ -2,9 +2,9 @@ using System; namespace GestionIntegral.Api.Models.Distribucion { - public class PorcMonCanillaHistorico + public class PorcMonCanillaHistorico // Corresponde a dist_PorcMonPagoCanilla_H { - public int Id_PorcMon { get; set; } // Coincide con la columna + public int Id_PorcMon { get; set; } // ID del PorcMon original public int Id_Publicacion { get; set; } public int Id_Canilla { get; set; } public DateTime VigenciaD { get; set; } diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/PorcPagoHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/PorcPagoHistorico.cs index 8568b13..bf20de4 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/PorcPagoHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/PorcPagoHistorico.cs @@ -1,15 +1,16 @@ using System; + namespace GestionIntegral.Api.Models.Distribucion { - public class PorcPagoHistorico + public class PorcPagoHistorico // Corresponde a dist_PorcPago_H { - public int IdPorcentaje { get; set; } // Id_Porcentaje - public int IdPublicacion { get; set; } - public int IdDistribuidor { get; set; } - public DateTime VigenciaD { get; set; } + public int Id_Porcentaje { get; set; } // ID del PorcPago original + public int Id_Publicacion { get; set; } + public int Id_Distribuidor { get; set; } + public DateTime VigenciaD { get; set; } // smalldatetime en BD, DateTime en C# está bien public DateTime? VigenciaH { get; set; } public decimal Porcentaje { get; set; } - public int IdUsuario { get; set; } // Coincide con Id_Usuario en la tabla _H + public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } public string TipoMod { get; set; } = string.Empty; } diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/PrecioHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/PrecioHistorico.cs index df30dc0..996e7fc 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/PrecioHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/PrecioHistorico.cs @@ -1,9 +1,11 @@ +using System; + namespace GestionIntegral.Api.Models.Distribucion { - public class PrecioHistorico + public class PrecioHistorico // Corresponde a dist_Precios_H { - public int IdPrecio { get; set; } // FK a dist_Precios.Id_Precio - public int IdPublicacion { get; set; } + public int Id_Precio { get; set; } // ID del Precio original + public int Id_Publicacion { get; set; } public DateTime VigenciaD { get; set; } public DateTime? VigenciaH { get; set; } public decimal? Lunes { get; set; } @@ -13,8 +15,6 @@ namespace GestionIntegral.Api.Models.Distribucion public decimal? Viernes { get; set; } public decimal? Sabado { get; set; } public decimal? Domingo { get; set; } - - // Campos de Auditoría public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } public string TipoMod { get; set; } = string.Empty; diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/PubliSeccionHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/PubliSeccionHistorico.cs index 08d8932..cfde2ad 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/PubliSeccionHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/PubliSeccionHistorico.cs @@ -2,10 +2,10 @@ using System; namespace GestionIntegral.Api.Models.Distribucion { - public class PubliSeccionHistorico + public class PubliSeccionHistorico // Corresponde a dist_dtPubliSecciones_H { - public int Id_Seccion { get; set; } // Coincide con la columna - public int Id_Publicacion { get; set; } + public int Id_Seccion { get; set; } // ID de la Sección original + public int Id_Publicacion { get; set; } // A qué publicación pertenecía en ese momento public string Nombre { get; set; } = string.Empty; public bool Estado { get; set; } public int Id_Usuario { get; set; } diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/PublicacionHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/PublicacionHistorico.cs index e50a374..93e63f4 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/PublicacionHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/PublicacionHistorico.cs @@ -1,15 +1,15 @@ +using System; + namespace GestionIntegral.Api.Models.Distribucion { - public class PublicacionHistorico + public class PublicacionHistorico // Corresponde a dist_dtPublicaciones_H { - // No hay PK autoincremental explícita en el script de _H - public int IdPublicacion { get; set; } + public int Id_Publicacion { get; set; } // ID de la Publicacion original public string Nombre { get; set; } = string.Empty; public string? Observacion { get; set; } - public int IdEmpresa { get; set; } - public bool? Habilitada { get; set; } // Coincide con la tabla _H - - // Campos de Auditoría + public int Id_Empresa { get; set; } + // public bool CtrlDevoluciones { get; set; } // Parece que no está en _H + public bool? Habilitada { get; set; } // Es nullable en _H public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } public string TipoMod { get; set; } = string.Empty; diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/RecargoZonaHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/RecargoZonaHistorico.cs index 5435da1..bd2fe1d 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/RecargoZonaHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/RecargoZonaHistorico.cs @@ -1,14 +1,16 @@ +using System; + namespace GestionIntegral.Api.Models.Distribucion { - public class RecargoZonaHistorico + public class RecargoZonaHistorico // Corresponde a dist_RecargoZona_H { - public int IdRecargo { get; set; } - public int IdPublicacion { get; set; } - public int IdZona { get; set; } + public int Id_Recargo { get; set; } // ID del Recargo original + public int Id_Publicacion { get; set; } + public int Id_Zona { get; set; } public DateTime VigenciaD { get; set; } public DateTime? VigenciaH { get; set; } public decimal Valor { get; set; } - public int IdUsuario { get; set; } + public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } public string TipoMod { get; set; } = string.Empty; } diff --git a/Backend/GestionIntegral.Api/Models/Distribucion/ZonaHistorico.cs b/Backend/GestionIntegral.Api/Models/Distribucion/ZonaHistorico.cs index 20bb2f5..4236b90 100644 --- a/Backend/GestionIntegral.Api/Models/Distribucion/ZonaHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Distribucion/ZonaHistorico.cs @@ -1,12 +1,14 @@ +using System; + namespace GestionIntegral.Api.Models.Distribucion { public class ZonaHistorico { - public int IdZona { get; set; } // NO es IDENTITY + public int Id_Zona { get; set; } public string Nombre { get; set; } = string.Empty; public string? Descripcion { get; set; } - public bool Estado { get; set; } // Importante para registrar el estado al momento del cambio - public int IdUsuario { get; set; } + public bool Estado { get; set; } // El estado de la zona en ese momento del historial + public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } public string TipoMod { get; set; } = string.Empty; } diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/CambioParadaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/CambioParadaHistorialDto.cs new file mode 100644 index 0000000..a886442 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/CambioParadaHistorialDto.cs @@ -0,0 +1,19 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Distribucion // O Auditoria +{ + public class CambioParadaHistorialDto + { + public int Id_Registro { get; set; } + public int Id_Canilla { get; set; } + public string NombreCanilla { get; set; } = string.Empty; // Para mostrar + public string Parada { get; set; } = string.Empty; // Dirección de la parada en ese momento + public DateTime VigenciaD { get; set; } + public DateTime? VigenciaH { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/CanillaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/CanillaHistorialDto.cs index a98b01d..7f0b198 100644 --- a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/CanillaHistorialDto.cs +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/CanillaHistorialDto.cs @@ -1,6 +1,6 @@ using System; -namespace GestionIntegral.Api.Dtos.Auditoria // O Auditoria +namespace GestionIntegral.Api.Dtos.Auditoria { public class CanillaHistorialDto { diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/ControlDevolucionesHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/ControlDevolucionesHistorialDto.cs new file mode 100644 index 0000000..fb5783e --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/ControlDevolucionesHistorialDto.cs @@ -0,0 +1,21 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class ControlDevolucionesHistorialDto + { + public int Id_Control { get; set; } + public int Id_Empresa { get; set; } + // public string NombreEmpresa { get; set; } // Opcional + public DateTime Fecha { get; set; } // Fecha original del control + public int Entrada { get; set; } + public int Sobrantes { get; set; } + public string? Detalle { get; set; } + public int SinCargo { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/EmpresaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/EmpresaHistorialDto.cs index 472a02a..4b2bf8f 100644 --- a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/EmpresaHistorialDto.cs +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/EmpresaHistorialDto.cs @@ -1,6 +1,6 @@ using System; -namespace GestionIntegral.Api.Dtos.Auditoria // O Auditoria +namespace GestionIntegral.Api.Dtos.Auditoria { public class EmpresaHistorialDto { diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/EstadoBobinaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/EstadoBobinaHistorialDto.cs new file mode 100644 index 0000000..bd5fc3a --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/EstadoBobinaHistorialDto.cs @@ -0,0 +1,16 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class EstadoBobinaHistorialDto + { + public int Id_EstadoBobina { get; set; } + public string Denominacion { get; set; } = string.Empty; + public string? Obs { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/OtroDestinoHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/OtroDestinoHistorialDto.cs new file mode 100644 index 0000000..61d18c1 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/OtroDestinoHistorialDto.cs @@ -0,0 +1,16 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class OtroDestinoHistorialDto + { + public int Id_Destino { get; set; } + public string Nombre { get; set; } = string.Empty; + public string? Obs { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PerfilHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PerfilHistorialDto.cs new file mode 100644 index 0000000..84e43ae --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PerfilHistorialDto.cs @@ -0,0 +1,19 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PerfilHistorialDto + { + public int IdPerfil { get; set; } // ID del Perfil original + public string Perfil { get; set; } = string.Empty; // Nombre del perfil + public string? DescPerfil { get; set; } + + public int Id_Usuario { get; set; } // Quién hizo el cambio + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + // El IdHist (PK de _H) no es estrictamente necesario en el DTO para mostrar, + // pero se puede incluir un identificador único para cada entrada de historial. + // public int IdHistorial { get; set;} + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PermisoHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PermisoHistorialDto.cs new file mode 100644 index 0000000..c5d77c0 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PermisoHistorialDto.cs @@ -0,0 +1,19 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PermisoHistorialDto + { + public int IdPermiso { get; set; } // ID del Permiso original + public string Modulo { get; set; } = string.Empty; + public string DescPermiso { get; set; } = string.Empty; + public string CodAcc { get; set; } = string.Empty; + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + // El IdHist de la tabla _H puede ser útil para la key en el frontend + public int IdHistorial { get; set; } + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PermisosPerfilesHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PermisosPerfilesHistorialDto.cs new file mode 100644 index 0000000..80bd1db --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PermisosPerfilesHistorialDto.cs @@ -0,0 +1,19 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PermisosPerfilesHistorialDto + { + public int IdHistorial { get; set; } // El ID de la entrada de historial + public int IdPerfil { get; set; } + public string NombrePerfil { get; set; } = string.Empty; + public int IdPermiso { get; set; } + public string DescPermiso { get; set; } = string.Empty; + public string CodAccPermiso { get; set; } = string.Empty; + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; // "Asignado", "Removido" + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PlantaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PlantaHistorialDto.cs new file mode 100644 index 0000000..b793243 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PlantaHistorialDto.cs @@ -0,0 +1,16 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PlantaHistorialDto + { + public int Id_Planta { get; set; } + public string Nombre { get; set; } = string.Empty; + public string Detalle { get; set; } = string.Empty; + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PorcMonCanillaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PorcMonCanillaHistorialDto.cs new file mode 100644 index 0000000..b60aeb5 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PorcMonCanillaHistorialDto.cs @@ -0,0 +1,22 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PorcMonCanillaHistorialDto + { + public int Id_PorcMon { get; set; } + public int Id_Publicacion { get; set; } + // public string NombrePublicacion { get; set; } // Opcional + public int Id_Canilla { get; set; } + // public string NombreCanilla { get; set; } // Opcional + public DateTime VigenciaD { get; set; } + public DateTime? VigenciaH { get; set; } + public decimal PorcMon { get; set; } + public bool EsPorcentaje { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PorcPagoHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PorcPagoHistorialDto.cs new file mode 100644 index 0000000..b749639 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PorcPagoHistorialDto.cs @@ -0,0 +1,21 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PorcPagoHistorialDto + { + public int Id_Porcentaje { get; set; } + public int Id_Publicacion { get; set; } + // public string NombrePublicacion { get; set; } // Opcional + public int Id_Distribuidor { get; set; } + // public string NombreDistribuidor { get; set; } // Opcional + public DateTime VigenciaD { get; set; } + public DateTime? VigenciaH { get; set; } + public decimal Porcentaje { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PrecioHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PrecioHistorialDto.cs new file mode 100644 index 0000000..b53c786 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PrecioHistorialDto.cs @@ -0,0 +1,25 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PrecioHistorialDto + { + public int Id_Precio { get; set; } + public int Id_Publicacion { get; set; } + // public string NombrePublicacion { get; set; } // Opcional + public DateTime VigenciaD { get; set; } + public DateTime? VigenciaH { get; set; } + public decimal? Lunes { get; set; } + public decimal? Martes { get; set; } + public decimal? Miercoles { get; set; } + public decimal? Jueves { get; set; } + public decimal? Viernes { get; set; } + public decimal? Sabado { get; set; } + public decimal? Domingo { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PubliSeccionHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PubliSeccionHistorialDto.cs new file mode 100644 index 0000000..c51f62b --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PubliSeccionHistorialDto.cs @@ -0,0 +1,18 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PubliSeccionHistorialDto + { + public int Id_Seccion { get; set; } + public int Id_Publicacion { get; set; } + // public string NombrePublicacion { get; set; } // Opcional, si quieres traerlo con JOIN + public string Nombre { get; set; } = string.Empty; // Nombre de la sección + public bool Estado { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PublicacionHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PublicacionHistorialDto.cs new file mode 100644 index 0000000..3f15b16 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/PublicacionHistorialDto.cs @@ -0,0 +1,20 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class PublicacionHistorialDto + { + public int Id_Publicacion { get; set; } + public string Nombre { get; set; } = string.Empty; + public string? Observacion { get; set; } + public int Id_Empresa { get; set; } + // public string NombreEmpresa { get; set; } // Opcional + // public bool CtrlDevoluciones { get; set; } // Si la añades a _H y al modelo + public bool? Habilitada { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RecargoZonaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RecargoZonaHistorialDto.cs new file mode 100644 index 0000000..31c956b --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RecargoZonaHistorialDto.cs @@ -0,0 +1,21 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class RecargoZonaHistorialDto + { + public int Id_Recargo { get; set; } + public int Id_Publicacion { get; set; } + // public string NombrePublicacion { get; set; } // Opcional + public int Id_Zona { get; set; } + // public string NombreZona { get; set; } // Opcional + public DateTime VigenciaD { get; set; } + public DateTime? VigenciaH { get; set; } + public decimal Valor { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RegSeccionTiradaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RegSeccionTiradaHistorialDto.cs new file mode 100644 index 0000000..e5ad6c6 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RegSeccionTiradaHistorialDto.cs @@ -0,0 +1,22 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class RegSeccionTiradaHistorialDto + { + public int Id_Tirada { get; set; } // ID del registro original de la sección de la tirada + public int Id_Publicacion { get; set; } + public string NombrePublicacion { get; set; } = string.Empty; + public int Id_Seccion { get; set; } + public string NombreSeccion { get; set; } = string.Empty; + public int CantPag { get; set; } + public DateTime Fecha { get; set; } // Fecha original de la tirada + public int Id_Planta { get; set; } + public string NombrePlanta { get; set; } = string.Empty; + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RegTiradaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RegTiradaHistorialDto.cs new file mode 100644 index 0000000..e842763 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/RegTiradaHistorialDto.cs @@ -0,0 +1,20 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class RegTiradaHistorialDto + { + public int Id_Registro { get; set; } + public int Ejemplares { get; set; } + public int Id_Publicacion { get; set; } + public string NombrePublicacion { get; set; } = string.Empty; // Para mostrar + public DateTime Fecha { get; set; } + public int Id_Planta { get; set; } + public string NombrePlanta { get; set; } = string.Empty; // Para mostrar + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/StockBobinaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/StockBobinaHistorialDto.cs new file mode 100644 index 0000000..f10ab6a --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/StockBobinaHistorialDto.cs @@ -0,0 +1,30 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class StockBobinaHistorialDto + { + public int Id_Bobina { get; set; } + public int Id_TipoBobina { get; set; } + public string NombreTipoBobina { get; set; } = string.Empty; // Para mostrar + public string NroBobina { get; set; } = string.Empty; + public int Peso { get; set; } + public int Id_Planta { get; set; } + public string NombrePlanta { get; set; } = string.Empty; // Para mostrar + public int Id_EstadoBobina { get; set; } + public string NombreEstadoBobina { get; set; } = string.Empty; // Para mostrar + public string Remito { get; set; } = string.Empty; + public DateTime FechaRemito { get; set; } + public DateTime? FechaEstado { get; set; } + public int? Id_Publicacion { get; set; } + public string? NombrePublicacion { get; set; } // Para mostrar + public int? Id_Seccion { get; set; } + public string? NombreSeccion { get; set; } // Para mostrar + public string? Obs { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/TipoBobinaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/TipoBobinaHistorialDto.cs new file mode 100644 index 0000000..2233799 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/TipoBobinaHistorialDto.cs @@ -0,0 +1,15 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class TipoBobinaHistorialDto + { + public int Id_TipoBobina { get; set; } + public string Denominacion { get; set; } = string.Empty; + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/ZonaHistorialDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/ZonaHistorialDto.cs new file mode 100644 index 0000000..4750c73 --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Dtos/Auditoria/ZonaHistorialDto.cs @@ -0,0 +1,17 @@ +using System; + +namespace GestionIntegral.Api.Dtos.Auditoria +{ + public class ZonaHistorialDto + { + public int Id_Zona { get; set; } + public string Nombre { get; set; } = string.Empty; + public string? Descripcion { get; set; } + public bool Estado { get; set; } + + public int Id_Usuario { get; set; } + public string NombreUsuarioModifico { get; set; } = string.Empty; + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Distribucion/UpdatePrecioDto.cs b/Backend/GestionIntegral.Api/Models/Dtos/Distribucion/UpdatePrecioDto.cs index 24364a9..2679cd0 100644 --- a/Backend/GestionIntegral.Api/Models/Dtos/Distribucion/UpdatePrecioDto.cs +++ b/Backend/GestionIntegral.Api/Models/Dtos/Distribucion/UpdatePrecioDto.cs @@ -6,8 +6,6 @@ namespace GestionIntegral.Api.Dtos.Distribucion { // IdPublicacion no se puede cambiar una vez creado el precio (se borraría y crearía uno nuevo) // VigenciaD tampoco se debería cambiar directamente, se maneja creando nuevos periodos. - - [Required(ErrorMessage = "La fecha de Vigencia Hasta es obligatoria si se modifica un periodo existente para cerrarlo.")] public DateTime? VigenciaH { get; set; } // Para cerrar un periodo [Range(0, 999999.99, ErrorMessage = "El precio debe ser un valor positivo.")] diff --git a/Backend/GestionIntegral.Api/Models/Impresion/EstadoBobinaHistorico.cs b/Backend/GestionIntegral.Api/Models/Impresion/EstadoBobinaHistorico.cs index f0e3baf..e0cd771 100644 --- a/Backend/GestionIntegral.Api/Models/Impresion/EstadoBobinaHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Impresion/EstadoBobinaHistorico.cs @@ -1,19 +1,14 @@ +using System; + namespace GestionIntegral.Api.Models.Impresion { - public class EstadoBobinaHistorico + public class EstadoBobinaHistorico // Corresponde a bob_dtEstadosBobinas_H { - // Columna: Id_EstadoBobina (int, FK) - public int IdEstadoBobina { get; set; } - - // Columna: Denominacion (varchar(50), NOT NULL) + public int Id_EstadoBobina { get; set; } // ID del EstadoBobina original public string Denominacion { get; set; } = string.Empty; - - // Columna: Obs (varchar(150), NULL) public string? Obs { get; set; } - - // Columnas de Auditoría - public int IdUsuario { get; set; } + public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } - public string TipoMod { get; set; } = string.Empty; // "Insertada", "Modificada", "Eliminada" + public string TipoMod { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Impresion/PlantaHistorico.cs b/Backend/GestionIntegral.Api/Models/Impresion/PlantaHistorico.cs index 16ed802..8c28554 100644 --- a/Backend/GestionIntegral.Api/Models/Impresion/PlantaHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Impresion/PlantaHistorico.cs @@ -1,12 +1,14 @@ +using System; + namespace GestionIntegral.Api.Models.Impresion { - public class PlantaHistorico + public class PlantaHistorico // Corresponde a bob_dtPlantas_H { - public int IdPlanta { get; set; } // FK a bob_dtPlantas + public int Id_Planta { get; set; } // ID de la Planta original public string Nombre { get; set; } = string.Empty; - public string Detalle { get; set; } = string.Empty; - public int IdUsuario { get; set; } + public string Detalle { get; set; } = string.Empty; // No es nullable en _H + public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } - public string TipoMod { get; set; } = string.Empty; // "Insertada", "Modificada", "Eliminada" + public string TipoMod { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Impresion/RegTiradaHistorico.cs b/Backend/GestionIntegral.Api/Models/Impresion/RegTiradaHistorico.cs index a4d7bbb..3bd344e 100644 --- a/Backend/GestionIntegral.Api/Models/Impresion/RegTiradaHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Impresion/RegTiradaHistorico.cs @@ -1,11 +1,13 @@ +using System; + namespace GestionIntegral.Api.Models.Impresion { - public class RegTiradaHistorico + public class RegTiradaHistorico // Corresponde a bob_RegTiradas_H { - public int Id_Registro { get; set; } // Nombre de columna en _H + public int Id_Registro { get; set; } // ID del RegTirada original public int Ejemplares { get; set; } public int Id_Publicacion { get; set; } - public DateTime Fecha { get; set; } + public DateTime Fecha { get; set; } // Fecha original de la tirada public int Id_Planta { get; set; } public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } diff --git a/Backend/GestionIntegral.Api/Models/Impresion/StockBobinaHistorico.cs b/Backend/GestionIntegral.Api/Models/Impresion/StockBobinaHistorico.cs index 88d76b6..acd8322 100644 --- a/Backend/GestionIntegral.Api/Models/Impresion/StockBobinaHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Impresion/StockBobinaHistorico.cs @@ -2,9 +2,9 @@ using System; namespace GestionIntegral.Api.Models.Impresion { - public class StockBobinaHistorico + public class StockBobinaHistorico // Corresponde a bob_StockBobinas_H { - public int Id_Bobina { get; set; } // Columna en _H + public int Id_Bobina { get; set; } // ID de la Bobina en Stock original public int Id_TipoBobina { get; set; } public string NroBobina { get; set; } = string.Empty; public int Peso { get; set; } @@ -16,10 +16,8 @@ namespace GestionIntegral.Api.Models.Impresion public int? Id_Publicacion { get; set; } public int? Id_Seccion { get; set; } public string? Obs { get; set; } - - // Auditoría public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } - public string TipoMod { get; set; } = string.Empty; // "Ingreso", "Estado Cambiado", "Actualizacion", "Eliminada" + public string TipoMod { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Impresion/TipoBobinaHistorico.cs b/Backend/GestionIntegral.Api/Models/Impresion/TipoBobinaHistorico.cs index 5f04ed9..9677778 100644 --- a/Backend/GestionIntegral.Api/Models/Impresion/TipoBobinaHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Impresion/TipoBobinaHistorico.cs @@ -1,16 +1,13 @@ +using System; + namespace GestionIntegral.Api.Models.Impresion { - public class TipoBobinaHistorico + public class TipoBobinaHistorico // Corresponde a bob_dtBobinas_H { - // Columna: Id_TipoBobina (int, FK) - public int IdTipoBobina { get; set; } - - // Columna: Denominacion (varchar(150), NOT NULL) + public int Id_TipoBobina { get; set; } // ID del TipoBobina original public string Denominacion { get; set; } = string.Empty; - - // Columnas de Auditoría - public int IdUsuario { get; set; } + public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } - public string TipoMod { get; set; } = string.Empty; // "Insertada", "Modificada", "Eliminada" + public string TipoMod { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Usuarios/PerfilHistorico.cs b/Backend/GestionIntegral.Api/Models/Usuarios/PerfilHistorico.cs index 6d94347..5b90893 100644 --- a/Backend/GestionIntegral.Api/Models/Usuarios/PerfilHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Usuarios/PerfilHistorico.cs @@ -1,12 +1,19 @@ +using System; + namespace GestionIntegral.Api.Models.Usuarios { - public class PerfilHistorico - { - public int IdPerfil { get; set; } // FK a gral_Perfiles.id - public string NombrePerfil { get; set; } = string.Empty; - public string? Descripcion { get; set; } - public int IdUsuario { get; set; } - public DateTime FechaMod { get; set; } - public string TipoMod { get; set; } = string.Empty; // "Insertada", "Modificada", "Eliminada" - } + public class PerfilHistorico // Corresponde a gral_Perfiles_H + { + // La tabla gral_Perfiles_H tiene un Id IDENTITY(1,1) propio, + // pero también tiene idPerfil que es el FK al perfil original. + // Usamos idPerfil para identificar el perfil afectado. + // El Id de la tabla _H es más para la unicidad del registro de historial. + public int Id {get; set;} // PK de la tabla _H + public int IdPerfil { get; set; } // ID del Perfil original + public string Perfil { get; set; } = string.Empty; // Nombre del perfil en ese momento + public string? DescPerfil { get; set; } + public int Id_Usuario { get; set; } + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; + } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Models/Usuarios/PermisoHistorico.cs b/Backend/GestionIntegral.Api/Models/Usuarios/PermisoHistorico.cs index f170096..039d9ab 100644 --- a/Backend/GestionIntegral.Api/Models/Usuarios/PermisoHistorico.cs +++ b/Backend/GestionIntegral.Api/Models/Usuarios/PermisoHistorico.cs @@ -1,13 +1,15 @@ +using System; + namespace GestionIntegral.Api.Models.Usuarios { - public class PermisoHistorico + public class PermisoHistorico // Corresponde a gral_Permisos_H { - public int IdHist { get; set; } // PK de la tabla de historial - public int IdPermiso { get; set; } // FK a gral_Permisos.id + public int IdHist { get; set; } // PK de la tabla _H + public int IdPermiso { get; set; } // ID del Permiso original public string Modulo { get; set; } = string.Empty; public string DescPermiso { get; set; } = string.Empty; public string CodAcc { get; set; } = string.Empty; - public int IdUsuario { get; set; } + public int Id_Usuario { get; set; } public DateTime FechaMod { get; set; } public string TipoMod { get; set; } = string.Empty; } diff --git a/Backend/GestionIntegral.Api/Models/Usuarios/PermisosPerfilesHistorico.cs b/Backend/GestionIntegral.Api/Models/Usuarios/PermisosPerfilesHistorico.cs new file mode 100644 index 0000000..274615c --- /dev/null +++ b/Backend/GestionIntegral.Api/Models/Usuarios/PermisosPerfilesHistorico.cs @@ -0,0 +1,16 @@ +using System; + +namespace GestionIntegral.Api.Models.Usuarios +{ + public class PermisosPerfilesHistorico // Corresponde a gral_PermisosPerfiles_H + { + // Esta tabla registra cada asignación individualmente como un evento. + // El id de la tabla _H es el identificador único del evento de historial. + public int Id { get; set; } // PK de la tabla _H + public int idPerfil { get; set; } + public int idPermiso { get; set; } + public int Id_Usuario { get; set; } + public DateTime FechaMod { get; set; } + public string TipoMod { get; set; } = string.Empty; // "Asignado", "Removido" (o "Creado"/"Eliminado") + } +} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/CambioParadaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/CambioParadaService.cs index 4daa73a..c4031eb 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/CambioParadaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/CambioParadaService.cs @@ -181,5 +181,27 @@ namespace GestionIntegral.Api.Services.Distribucion } finally { if (connection.State == ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close(); } } } + + public async Task> ObtenerCambiosParadaHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idCanillaAfectado) + { + var historialData = await _paradaRepo.GetCambiosParadaHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idCanillaAfectado); + + return historialData.Select(h => new CambioParadaHistorialDto + { + Id_Registro = h.Historial.Id_Registro, + Id_Canilla = h.Historial.Id_Canilla, + NombreCanilla = h.NombreCanilla, + Parada = h.Historial.Parada, + VigenciaD = h.Historial.VigenciaD, + VigenciaH = h.Historial.VigenciaH ?? default(DateTime), + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/CanillaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/CanillaService.cs index 5ccd0f8..7631def 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/CanillaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/CanillaService.cs @@ -287,6 +287,6 @@ namespace GestionIntegral.Api.Services.Distribucion FechaMod = h.Historial.FechaMod, TipoMod = h.Historial.TipoMod }).ToList(); - } + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/ControlDevolucionesService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/ControlDevolucionesService.cs index ec24942..9970550 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/ControlDevolucionesService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/ControlDevolucionesService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; @@ -167,5 +168,29 @@ namespace GestionIntegral.Api.Services.Distribucion return (false, $"Error interno: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idControlAfectado, int? idEmpresaAfectada, DateTime? fechaAfectada) + { + var historialData = await _controlDevRepo.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idControlAfectado, idEmpresaAfectada, fechaAfectada); + + return historialData.Select(h => new ControlDevolucionesHistorialDto + { + Id_Control = h.Historial.Id_Control, + Id_Empresa = h.Historial.Id_Empresa, + Fecha = h.Historial.Fecha, + Entrada = h.Historial.Entrada, + Sobrantes = h.Historial.Sobrantes, + Detalle = h.Historial.Detalle, + SinCargo = h.Historial.SinCargo, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + // Mapear NombreEmpresa aquí si lo añades al DTO y lo obtienes + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/ICambioParadaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/ICambioParadaService.cs index 5f44df9..0a174be 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/ICambioParadaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/ICambioParadaService.cs @@ -13,5 +13,9 @@ namespace GestionIntegral.Api.Services.Distribucion // Update podría ser solo para cerrar la vigencia, no para cambiar la dirección de una parada histórica. Task<(bool Exito, string? Error)> CerrarParadaAsync(int idRegistro, UpdateCambioParadaDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarParadaAsync(int idRegistro, int idUsuario); // Si se permite + Task> ObtenerCambiosParadaHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idCanillaAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IControlDevolucionesService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IControlDevolucionesService.cs index f2bfa5f..57e6571 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IControlDevolucionesService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IControlDevolucionesService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using System; using System.Collections.Generic; @@ -12,5 +13,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task<(ControlDevolucionesDto? Control, string? Error)> CrearAsync(CreateControlDevolucionesDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int idControl, UpdateControlDevolucionesDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int idControl, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idControlAfectado, int? idEmpresaAfectada, DateTime? fechaAfectada); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IOtroDestinoService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IOtroDestinoService.cs index dc10066..19f9a14 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IOtroDestinoService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IOtroDestinoService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using System.Collections.Generic; using System.Threading.Tasks; @@ -11,5 +12,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task<(OtroDestinoDto? Destino, string? Error)> CrearAsync(CreateOtroDestinoDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateOtroDestinoDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idOtroDestinoAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IPorcMonCanillaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IPorcMonCanillaService.cs index c3def7c..785c425 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IPorcMonCanillaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IPorcMonCanillaService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using System; using System.Collections.Generic; @@ -12,5 +13,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task<(PorcMonCanillaDto? Item, string? Error)> CrearAsync(CreatePorcMonCanillaDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int idPorcMon, UpdatePorcMonCanillaDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int idPorcMon, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPorcMonAfectado, int? idPublicacionAfectada, int? idCanillaAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IPorcPagoService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IPorcPagoService.cs index 79b0ae3..fabc8cd 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IPorcPagoService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IPorcPagoService.cs @@ -1,4 +1,6 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; +using GestionIntegral.Api.Models.Distribucion; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -12,5 +14,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task<(PorcPagoDto? PorcPago, string? Error)> CrearAsync(CreatePorcPagoDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int idPorcentaje, UpdatePorcPagoDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int idPorcentaje, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPorcentajeAfectado, int? idPublicacionAfectada, int? idDistribuidorAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IPrecioService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IPrecioService.cs index e466c1b..3c5d607 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IPrecioService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IPrecioService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using System; using System.Collections.Generic; @@ -12,5 +13,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task<(PrecioDto? Precio, string? Error)> CrearAsync(CreatePrecioDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int idPrecio, UpdatePrecioDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int idPrecio, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPrecioAfectado, int? idPublicacionAfectada); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IPubliSeccionService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IPubliSeccionService.cs index 9e832d0..809bf06 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IPubliSeccionService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IPubliSeccionService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using System.Collections.Generic; using System.Threading.Tasks; @@ -11,5 +12,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task<(PubliSeccionDto? Seccion, string? Error)> CrearAsync(CreatePubliSeccionDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int idSeccion, UpdatePubliSeccionDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int idSeccion, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idSeccionAfectada, int? idPublicacionAfectada); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IPublicacionService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IPublicacionService.cs index 4fee753..2cf5aa4 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IPublicacionService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IPublicacionService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using System.Collections.Generic; using System.Threading.Tasks; @@ -15,5 +16,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task> ObtenerPublicacionesPorDiaSemanaAsync(byte diaSemana); // Devolvemos el DTO completo Task<(bool Exito, string? Error)> ActualizarConfiguracionDiasAsync(int idPublicacion, UpdatePublicacionDiasSemanaRequestDto requestDto, int idUsuario); Task> ObtenerParaDropdownAsync(bool soloHabilitadas = true); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPublicacionAfectada); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IRecargoZonaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IRecargoZonaService.cs index ef1294f..4ca8c92 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IRecargoZonaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IRecargoZonaService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using System; using System.Collections.Generic; @@ -12,5 +13,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task<(RecargoZonaDto? Recargo, string? Error)> CrearAsync(CreateRecargoZonaDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int idRecargo, UpdateRecargoZonaDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int idRecargo, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idRecargoAfectado, int? idPublicacionAfectada, int? idZonaAfectada); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/IZonaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/IZonaService.cs index 7e41ab3..4861eb8 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/IZonaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/IZonaService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Zonas; // Para los DTOs de Zonas using System.Collections.Generic; using System.Threading.Tasks; @@ -11,5 +12,9 @@ namespace GestionIntegral.Api.Services.Distribucion Task<(ZonaDto? Zona, string? Error)> CrearAsync(CreateZonaDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateZonaDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario); // Eliminar lógico (cambiar estado) + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idZonaAfectada); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/OtroDestinoService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/OtroDestinoService.cs index 9097e6c..f603363 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/OtroDestinoService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/OtroDestinoService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; @@ -139,5 +140,24 @@ namespace GestionIntegral.Api.Services.Distribucion return (false, $"Error interno al eliminar el destino: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idOtroDestinoAfectado) + { + var historialData = await _otroDestinoRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idOtroDestinoAfectado); + + return historialData.Select(h => new OtroDestinoHistorialDto + { + Id_Destino = h.Historial.Id_Destino, + Nombre = h.Historial.Nombre, + Obs = h.Historial.Obs, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/PorcMonCanillaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/PorcMonCanillaService.cs index 5d80d7c..29e93af 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/PorcMonCanillaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/PorcMonCanillaService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; @@ -44,7 +45,7 @@ namespace GestionIntegral.Api.Services.Distribucion PorcMon = data.Item.PorcMon, EsPorcentaje = data.Item.EsPorcentaje }; - + private async Task MapToDtoWithLookup(PorcMonCanilla? item) { if (item == null) return null; // Si el item es null, devuelve null @@ -81,7 +82,7 @@ namespace GestionIntegral.Api.Services.Distribucion { if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null) return (null, "La publicación especificada no existe."); - + var canillaData = await _canillaRepository.GetByIdAsync(createDto.IdCanilla); if (canillaData.Canilla == null) // GetByIdAsync devuelve una tupla return (null, "El canillita especificado no existe o no está activo."); @@ -116,7 +117,7 @@ namespace GestionIntegral.Api.Services.Distribucion var itemAnterior = await _porcMonCanillaRepository.GetPreviousActiveAsync(createDto.IdPublicacion, createDto.IdCanilla, nuevoItem.VigenciaD, transaction); if (itemAnterior != null) { - if (itemAnterior.VigenciaD.Date >= nuevoItem.VigenciaD.Date) + if (itemAnterior.VigenciaD.Date >= nuevoItem.VigenciaD.Date) { transaction.Rollback(); return (null, $"La fecha de inicio ({nuevoItem.VigenciaD:dd/MM/yyyy}) no puede ser anterior o igual a la del último período vigente ({itemAnterior.VigenciaD:dd/MM/yyyy})."); @@ -152,19 +153,19 @@ namespace GestionIntegral.Api.Services.Distribucion if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < itemExistente.VigenciaD.Date) return (false, "Vigencia Hasta no puede ser anterior a Vigencia Desde."); - + if (updateDto.VigenciaH.HasValue) { - var itemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemExistente.IdPublicacion); - var itemsPosteriores = itemsPubCanillaData - .Where(i => i.Item.IdCanilla == itemExistente.IdCanilla && - i.Item.IdPorcMon != idPorcMon && - i.Item.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && - i.Item.VigenciaD.Date > itemExistente.VigenciaD.Date); - if(itemsPosteriores.Any()) - { + var itemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemExistente.IdPublicacion); + var itemsPosteriores = itemsPubCanillaData + .Where(i => i.Item.IdCanilla == itemExistente.IdCanilla && + i.Item.IdPorcMon != idPorcMon && + i.Item.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && + i.Item.VigenciaD.Date > itemExistente.VigenciaD.Date); + if (itemsPosteriores.Any()) + { return (false, "No se puede cerrar este período porque existen configuraciones posteriores para este canillita que se solaparían."); - } + } } itemExistente.PorcMon = updateDto.PorcMon; @@ -200,19 +201,19 @@ namespace GestionIntegral.Api.Services.Distribucion var itemAEliminar = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon); if (itemAEliminar == null) return (false, "Registro no encontrado."); - if (itemAEliminar.VigenciaH == null) + if (itemAEliminar.VigenciaH == null) { var todosItemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemAEliminar.IdPublicacion); var todosItemsPubCanilla = todosItemsPubCanillaData .Where(i => i.Item.IdCanilla == itemAEliminar.IdCanilla) .Select(i => i.Item) .OrderByDescending(i => i.VigenciaD).ToList(); - + var indiceActual = todosItemsPubCanilla.FindIndex(i => i.IdPorcMon == idPorcMon); - if(indiceActual != -1 && (indiceActual + 1) < todosItemsPubCanilla.Count) + if (indiceActual != -1 && (indiceActual + 1) < todosItemsPubCanilla.Count) { var itemAnteriorDirecto = todosItemsPubCanilla[indiceActual + 1]; - if(itemAnteriorDirecto.VigenciaH.HasValue && + if (itemAnteriorDirecto.VigenciaH.HasValue && itemAnteriorDirecto.VigenciaH.Value.Date == itemAEliminar.VigenciaD.AddDays(-1).Date) { itemAnteriorDirecto.VigenciaH = null; @@ -236,5 +237,28 @@ namespace GestionIntegral.Api.Services.Distribucion return (false, $"Error interno: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPorcMonAfectado, int? idPublicacionAfectada, int? idCanillaAfectado) + { + var historialData = await _porcMonCanillaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPorcMonAfectado, idPublicacionAfectada, idCanillaAfectado); + + return historialData.Select(h => new PorcMonCanillaHistorialDto + { + Id_PorcMon = h.Historial.Id_PorcMon, + Id_Publicacion = h.Historial.Id_Publicacion, + Id_Canilla = h.Historial.Id_Canilla, + VigenciaD = h.Historial.VigenciaD, + VigenciaH = h.Historial.VigenciaH, + PorcMon = h.Historial.PorcMon, + EsPorcentaje = h.Historial.EsPorcentaje, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/PorcPagoService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/PorcPagoService.cs index 4a8ced5..c45e180 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/PorcPagoService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/PorcPagoService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; @@ -43,7 +44,7 @@ namespace GestionIntegral.Api.Services.Distribucion VigenciaH = data.PorcPago.VigenciaH?.ToString("yyyy-MM-dd"), Porcentaje = data.PorcPago.Porcentaje }; - + private async Task MapToDtoWithLookup(PorcPago porcPago) { var distribuidorData = await _distribuidorRepository.GetByIdAsync(porcPago.IdDistribuidor); @@ -117,7 +118,8 @@ namespace GestionIntegral.Api.Services.Distribucion transaction.Commit(); _logger.LogInformation("PorcPago ID {Id} creado por Usuario ID {UserId}.", porcPagoCreado.IdPorcentaje, idUsuario); - return (new PorcPagoDto { // Construir DTO manualmente ya que tenemos NombreDistribuidor + return (new PorcPagoDto + { // Construir DTO manualmente ya que tenemos NombreDistribuidor IdPorcentaje = porcPagoCreado.IdPorcentaje, IdPublicacion = porcPagoCreado.IdPublicacion, IdDistribuidor = porcPagoCreado.IdDistribuidor, @@ -156,9 +158,9 @@ namespace GestionIntegral.Api.Services.Distribucion p.PorcPago.IdPorcentaje != idPorcentaje && p.PorcPago.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && p.PorcPago.VigenciaD.Date > porcPagoExistente.VigenciaD.Date); - if(porcentajesPosteriores.Any()) + if (porcentajesPosteriores.Any()) { - return (false, "No se puede cerrar este período porque existen porcentajes posteriores para este distribuidor que se solaparían."); + return (false, "No se puede cerrar este período porque existen porcentajes posteriores para este distribuidor que se solaparían."); } } @@ -194,19 +196,19 @@ namespace GestionIntegral.Api.Services.Distribucion var porcPagoAEliminar = await _porcPagoRepository.GetByIdAsync(idPorcentaje); if (porcPagoAEliminar == null) return (false, "Porcentaje de pago no encontrado."); - if (porcPagoAEliminar.VigenciaH == null) + if (porcPagoAEliminar.VigenciaH == null) { var todosPorcPubDistData = await _porcPagoRepository.GetByPublicacionIdAsync(porcPagoAEliminar.IdPublicacion); var todosPorcPubDist = todosPorcPubDistData .Where(p => p.PorcPago.IdDistribuidor == porcPagoAEliminar.IdDistribuidor) .Select(p => p.PorcPago) .OrderByDescending(p => p.VigenciaD).ToList(); - + var indiceActual = todosPorcPubDist.FindIndex(p => p.IdPorcentaje == idPorcentaje); - if(indiceActual != -1 && (indiceActual + 1) < todosPorcPubDist.Count) + if (indiceActual != -1 && (indiceActual + 1) < todosPorcPubDist.Count) { var porcPagoAnteriorDirecto = todosPorcPubDist[indiceActual + 1]; - if(porcPagoAnteriorDirecto.VigenciaH.HasValue && + if (porcPagoAnteriorDirecto.VigenciaH.HasValue && porcPagoAnteriorDirecto.VigenciaH.Value.Date == porcPagoAEliminar.VigenciaD.AddDays(-1).Date) { porcPagoAnteriorDirecto.VigenciaH = null; @@ -230,5 +232,27 @@ namespace GestionIntegral.Api.Services.Distribucion return (false, $"Error interno: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPorcentajeAfectado, int? idPublicacionAfectada, int? idDistribuidorAfectado) + { + var historialData = await _porcPagoRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPorcentajeAfectado, idPublicacionAfectada, idDistribuidorAfectado); + + return historialData.Select(h => new PorcPagoHistorialDto + { + Id_Porcentaje = h.Historial.Id_Porcentaje, + Id_Publicacion = h.Historial.Id_Publicacion, + Id_Distribuidor = h.Historial.Id_Distribuidor, + VigenciaD = h.Historial.VigenciaD, + VigenciaH = h.Historial.VigenciaH, + Porcentaje = h.Historial.Porcentaje, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/PrecioService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/PrecioService.cs index d3a6608..ece02c2 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/PrecioService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/PrecioService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; @@ -37,8 +38,13 @@ namespace GestionIntegral.Api.Services.Distribucion IdPublicacion = precio.IdPublicacion, VigenciaD = precio.VigenciaD.ToString("yyyy-MM-dd"), VigenciaH = precio.VigenciaH?.ToString("yyyy-MM-dd"), - Lunes = precio.Lunes, Martes = precio.Martes, Miercoles = precio.Miercoles, - Jueves = precio.Jueves, Viernes = precio.Viernes, Sabado = precio.Sabado, Domingo = precio.Domingo + Lunes = precio.Lunes, + Martes = precio.Martes, + Miercoles = precio.Miercoles, + Jueves = precio.Jueves, + Viernes = precio.Viernes, + Sabado = precio.Sabado, + Domingo = precio.Domingo }; public async Task> ObtenerPorPublicacionIdAsync(int idPublicacion) @@ -73,8 +79,13 @@ namespace GestionIntegral.Api.Services.Distribucion IdPublicacion = createDto.IdPublicacion, VigenciaD = createDto.VigenciaD.Date, // Asegurar que solo sea fecha VigenciaH = null, // Se establece al crear el siguiente o al cerrar manualmente - Lunes = createDto.Lunes ?? 0, Martes = createDto.Martes ?? 0, Miercoles = createDto.Miercoles ?? 0, - Jueves = createDto.Jueves ?? 0, Viernes = createDto.Viernes ?? 0, Sabado = createDto.Sabado ?? 0, Domingo = createDto.Domingo ?? 0 + Lunes = createDto.Lunes ?? 0, + Martes = createDto.Martes ?? 0, + Miercoles = createDto.Miercoles ?? 0, + Jueves = createDto.Jueves ?? 0, + Viernes = createDto.Viernes ?? 0, + Sabado = createDto.Sabado ?? 0, + Domingo = createDto.Domingo ?? 0 }; using var connection = _connectionFactory.CreateConnection(); @@ -99,7 +110,7 @@ namespace GestionIntegral.Api.Services.Distribucion { throw new DataException("No se pudo cerrar el período de precio anterior."); } - _logger.LogInformation("Precio anterior ID {IdPrecioAnterior} cerrado con VigenciaH {VigenciaH}.", precioAnterior.IdPrecio, precioAnterior.VigenciaH); + _logger.LogInformation("Precio anterior ID {IdPrecioAnterior} cerrado con VigenciaH {VigenciaH}.", precioAnterior.IdPrecio, precioAnterior.VigenciaH); } // 3. Crear el nuevo registro de precio @@ -126,91 +137,150 @@ namespace GestionIntegral.Api.Services.Distribucion try { - var precioExistente = await _precioRepository.GetByIdAsync(idPrecio); // Obtener dentro de la TX por si acaso - if (precioExistente == null) return (false, "Período de precio no encontrado."); - - // En una actualización, principalmente actualizamos los montos de los días. - // VigenciaH se puede actualizar para cerrar un período explícitamente. - // No se permite cambiar IdPublicacion ni VigenciaD aquí. - // Si VigenciaH se establece, debe ser >= VigenciaD - if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < precioExistente.VigenciaD.Date) + var precioExistente = await _precioRepository.GetByIdAsync(idPrecio); // Obtener dentro de TX + if (precioExistente == null) { - return (false, "La Vigencia Hasta no puede ser anterior a la Vigencia Desde."); + transaction.Rollback(); + return (false, "Período de precio no encontrado."); } - // Adicional: si se establece VigenciaH, verificar que no haya precios posteriores que se solapen - if (updateDto.VigenciaH.HasValue) + + bool preciosDiasCambiaron = + (updateDto.Lunes.HasValue && updateDto.Lunes != precioExistente.Lunes) || + (updateDto.Martes.HasValue && updateDto.Martes != precioExistente.Martes) || + (updateDto.Miercoles.HasValue && updateDto.Miercoles != precioExistente.Miercoles) || + (updateDto.Jueves.HasValue && updateDto.Jueves != precioExistente.Jueves) || + (updateDto.Viernes.HasValue && updateDto.Viernes != precioExistente.Viernes) || + (updateDto.Sabado.HasValue && updateDto.Sabado != precioExistente.Sabado) || + (updateDto.Domingo.HasValue && updateDto.Domingo != precioExistente.Domingo); + + if (precioExistente.VigenciaH != null && preciosDiasCambiaron) { - var preciosPosteriores = await _precioRepository.GetByPublicacionIdAsync(precioExistente.IdPublicacion); - if (preciosPosteriores.Any(p => p.IdPrecio != idPrecio && p.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && p.VigenciaD.Date > precioExistente.VigenciaD.Date )) + transaction.Rollback(); + return (false, "No se pueden modificar los precios de los días de un período que ya ha sido cerrado."); + } + + // Validación 2: No modificar precios de días si el período está en uso + if (preciosDiasCambiaron && await _precioRepository.IsInUseAsync(idPrecio, precioExistente.VigenciaD, precioExistente.VigenciaH)) + { + transaction.Rollback(); + return (false, "No se pueden modificar los precios de los días de este período porque ya existen movimientos asociados."); + } + + // Solo realizar validaciones de VigenciaH si se está intentando SETEAR o CAMBIAR VigenciaH + if (updateDto.VigenciaH.HasValue) // <<--- CHEQUEO IMPORTANTE + { + // Validación 3: Nueva VigenciaH debe ser >= VigenciaD + if (updateDto.VigenciaH.Value.Date < precioExistente.VigenciaD.Date) { - return (false, "No se puede cerrar este período porque existen períodos de precios posteriores que se solaparían. Elimine o ajuste los períodos posteriores primero."); + transaction.Rollback(); + return (false, "La Vigencia Hasta no puede ser anterior a la Vigencia Desde."); } + + // Validación 4: Nueva VigenciaH no debe solaparse con períodos posteriores + var todosLosPreciosPub = await _precioRepository.GetByPublicacionIdAsync(precioExistente.IdPublicacion); + if (todosLosPreciosPub.Any(p => + p.IdPrecio != idPrecio && + p.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && + p.VigenciaD.Date > precioExistente.VigenciaD.Date && + (p.VigenciaH == null || p.VigenciaH.Value.Date >= p.VigenciaD.Date) + )) + { + transaction.Rollback(); + return (false, "No se puede cerrar este período con la Vigencia Hasta especificada porque se solaparía con un período posterior existente. Ajuste los períodos posteriores primero."); + } + // Si pasa las validaciones, se asigna la nueva VigenciaH más abajo } + // Si updateDto.VigenciaH es null, estas validaciones de fecha no aplican. + // Aplicar actualizaciones + bool seRealizoAlgunaActualizacion = false; - precioExistente.Lunes = updateDto.Lunes ?? precioExistente.Lunes; - precioExistente.Martes = updateDto.Martes ?? precioExistente.Martes; - precioExistente.Miercoles = updateDto.Miercoles ?? precioExistente.Miercoles; - precioExistente.Jueves = updateDto.Jueves ?? precioExistente.Jueves; - precioExistente.Viernes = updateDto.Viernes ?? precioExistente.Viernes; - precioExistente.Sabado = updateDto.Sabado ?? precioExistente.Sabado; - precioExistente.Domingo = updateDto.Domingo ?? precioExistente.Domingo; - if (updateDto.VigenciaH.HasValue) // Solo actualizar VigenciaH si se proporciona + // Aplicar actualizaciones + if (preciosDiasCambiaron) { - precioExistente.VigenciaH = updateDto.VigenciaH.Value.Date; + precioExistente.Lunes = updateDto.Lunes ?? precioExistente.Lunes; + precioExistente.Martes = updateDto.Martes ?? precioExistente.Martes; + precioExistente.Miercoles = updateDto.Miercoles ?? precioExistente.Miercoles; + precioExistente.Jueves = updateDto.Jueves ?? precioExistente.Jueves; + precioExistente.Viernes = updateDto.Viernes ?? precioExistente.Viernes; + precioExistente.Sabado = updateDto.Sabado ?? precioExistente.Sabado; + precioExistente.Domingo = updateDto.Domingo ?? precioExistente.Domingo; + seRealizoAlgunaActualizacion = true; } + // Actualizar VigenciaH solo si el valor recibido en el DTO es diferente al existente, + // o si se quiere pasar de un valor a null (reabrir) o de null a un valor (cerrar). + if ((updateDto.VigenciaH.HasValue && (!precioExistente.VigenciaH.HasValue || updateDto.VigenciaH.Value.Date != precioExistente.VigenciaH.Value.Date)) || + (!updateDto.VigenciaH.HasValue && precioExistente.VigenciaH.HasValue)) + { + precioExistente.VigenciaH = updateDto.VigenciaH.HasValue ? updateDto.VigenciaH.Value.Date : (DateTime?)null; + seRealizoAlgunaActualizacion = true; + } - var actualizado = await _precioRepository.UpdateAsync(precioExistente, idUsuario, transaction); - if (!actualizado) throw new DataException("Error al actualizar el período de precio."); + if (seRealizoAlgunaActualizacion) + { + var actualizado = await _precioRepository.UpdateAsync(precioExistente, idUsuario, transaction); + if (!actualizado) throw new DataException("Error al actualizar el período de precio."); + } + else + { + _logger.LogInformation("No se detectaron cambios para Precio ID {IdPrecio}. No se realizó actualización.", idPrecio); + } transaction.Commit(); - _logger.LogInformation("Precio ID {IdPrecio} actualizado por Usuario ID {IdUsuario}.", idPrecio, idUsuario); + _logger.LogInformation("Precio ID {IdPrecio} procesado (actualizado si hubo cambios) por Usuario ID {IdUsuario}.", idPrecio, idUsuario); return (true, null); } - catch (KeyNotFoundException) { try { transaction.Rollback(); } catch {} return (false, "Período de precio no encontrado.");} + catch (KeyNotFoundException) { try { transaction?.Rollback(); } catch { } return (false, "Período de precio no encontrado."); } catch (Exception ex) { - try { transaction.Rollback(); } catch {} + try { transaction?.Rollback(); } catch { } _logger.LogError(ex, "Error ActualizarAsync Precio ID: {IdPrecio}", idPrecio); return (false, $"Error interno al actualizar el período de precio: {ex.Message}"); } + finally + { + if (connection.State == ConnectionState.Open) + { + if (connection is System.Data.Common.DbConnection dbConnClose) await dbConnClose.CloseAsync(); else connection.Close(); + } + } } public async Task<(bool Exito, string? Error)> EliminarAsync(int idPrecio, int idUsuario) { - using var connection = _connectionFactory.CreateConnection(); + using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try { - var precioAEliminar = await _precioRepository.GetByIdAsync(idPrecio); + var precioAEliminar = await _precioRepository.GetByIdAsync(idPrecio); // Obtener dentro de TX if (precioAEliminar == null) return (false, "Período de precio no encontrado."); - // Lógica de ajuste de VigenciaH del período anterior si este que se elimina era el "último" abierto. - // Si el precio a eliminar tiene un VigenciaH == null (estaba activo indefinidamente) - // Y existe un precio anterior para la misma publicación. - // Entonces, el VigenciaH de ese precio anterior debe volver a ser NULL. + // --- VERIFICACIÓN DE USO --- + if (await _precioRepository.IsInUseAsync(idPrecio, precioAEliminar.VigenciaD, precioAEliminar.VigenciaH)) + { + transaction.Rollback(); // No olvidar rollback si la verificación falla + return (false, "No se puede eliminar. Este período de precio está referenciado en movimientos existentes."); + } if (precioAEliminar.VigenciaH == null) { var todosLosPreciosPub = (await _precioRepository.GetByPublicacionIdAsync(precioAEliminar.IdPublicacion)) .OrderByDescending(p => p.VigenciaD).ToList(); - - var indiceActual = todosLosPreciosPub.FindIndex(p=> p.IdPrecio == idPrecio); - if(indiceActual != -1 && (indiceActual + 1) < todosLosPreciosPub.Count) + var indiceActual = todosLosPreciosPub.FindIndex(p => p.IdPrecio == idPrecio); + if (indiceActual != -1 && (indiceActual + 1) < todosLosPreciosPub.Count) { var precioAnteriorDirecto = todosLosPreciosPub[indiceActual + 1]; - // Solo si el precioAnteriorDirecto fue cerrado por este que se elimina - if(precioAnteriorDirecto.VigenciaH.HasValue && precioAnteriorDirecto.VigenciaH.Value.Date == precioAEliminar.VigenciaD.AddDays(-1).Date) + if (precioAnteriorDirecto.VigenciaH.HasValue && precioAnteriorDirecto.VigenciaH.Value.Date == precioAEliminar.VigenciaD.AddDays(-1).Date) { precioAnteriorDirecto.VigenciaH = null; - await _precioRepository.UpdateAsync(precioAnteriorDirecto, idUsuario, transaction); // Usar un ID de auditoría adecuado - _logger.LogInformation("Precio anterior ID {IdPrecioAnterior} reabierto (VigenciaH a NULL) tras eliminación de Precio ID {IdPrecioEliminado}.", precioAnteriorDirecto.IdPrecio, idPrecio); + // Asegurar que UpdateAsync tome la transacción + await _precioRepository.UpdateAsync(precioAnteriorDirecto, idUsuario, transaction); + _logger.LogInformation("Precio anterior ID {IdPrecioAnterior} reabierto tras eliminación de Precio ID {IdPrecioEliminado}.", precioAnteriorDirecto.IdPrecio, idPrecio); } } } - var eliminado = await _precioRepository.DeleteAsync(idPrecio, idUsuario, transaction); if (!eliminado) throw new DataException("Error al eliminar el período de precio."); @@ -218,13 +288,40 @@ namespace GestionIntegral.Api.Services.Distribucion _logger.LogInformation("Precio ID {IdPrecio} eliminado por Usuario ID {IdUsuario}.", idPrecio, idUsuario); return (true, null); } - catch (KeyNotFoundException) { try { transaction.Rollback(); } catch {} return (false, "Período de precio no encontrado."); } + catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Período de precio no encontrado."); } catch (Exception ex) { - try { transaction.Rollback(); } catch {} + try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error EliminarAsync Precio ID: {IdPrecio}", idPrecio); return (false, $"Error interno al eliminar el período de precio: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPrecioAfectado, int? idPublicacionAfectada) + { + var historialData = await _precioRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPrecioAfectado, idPublicacionAfectada); + + return historialData.Select(h => new PrecioHistorialDto + { + Id_Precio = h.Historial.Id_Precio, + Id_Publicacion = h.Historial.Id_Publicacion, + VigenciaD = h.Historial.VigenciaD, + VigenciaH = h.Historial.VigenciaH, + Lunes = h.Historial.Lunes, + Martes = h.Historial.Martes, + Miercoles = h.Historial.Miercoles, + Jueves = h.Historial.Jueves, + Viernes = h.Historial.Viernes, + Sabado = h.Historial.Sabado, + Domingo = h.Historial.Domingo, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/PubliSeccionService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/PubliSeccionService.cs index 9506d8b..b0d07eb 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/PubliSeccionService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/PubliSeccionService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; @@ -121,7 +122,7 @@ namespace GestionIntegral.Api.Services.Distribucion public async Task<(bool Exito, string? Error)> EliminarAsync(int idSeccion, int idUsuario) { - using var connection = _connectionFactory.CreateConnection(); + using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try @@ -149,5 +150,26 @@ namespace GestionIntegral.Api.Services.Distribucion return (false, $"Error interno: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idSeccionAfectada, int? idPublicacionAfectada) + { + var historialData = await _publiSeccionRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idSeccionAfectada, idPublicacionAfectada); + + // Si necesitas NombrePublicacion, harías la llamada a _publicacionRepository aquí. + return historialData.Select(h => new PubliSeccionHistorialDto + { + Id_Seccion = h.Historial.Id_Seccion, + Id_Publicacion = h.Historial.Id_Publicacion, + Nombre = h.Historial.Nombre, + Estado = h.Historial.Estado, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/PublicacionService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/PublicacionService.cs index 6f088cd..9c16f7a 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/PublicacionService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/PublicacionService.cs @@ -1,6 +1,7 @@ // src/Services/Distribucion/PublicacionService.cs using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; @@ -284,5 +285,28 @@ namespace GestionIntegral.Api.Services.Distribucion return (false, $"Error interno al actualizar la configuración de días: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPublicacionAfectada) + { + var historialData = await _publicacionRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPublicacionAfectada); + + // Si necesitas NombreEmpresa, harías el JOIN en repo o llamada a _empresaRepository aquí. + return historialData.Select(h => new PublicacionHistorialDto + { + Id_Publicacion = h.Historial.Id_Publicacion, + Nombre = h.Historial.Nombre, + Observacion = h.Historial.Observacion, + Id_Empresa = h.Historial.Id_Empresa, + Habilitada = h.Historial.Habilitada, + // CtrlDevoluciones no está en _H, si se añade, mapearla aquí + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/RecargoZonaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/RecargoZonaService.cs index 5d8b792..8ab671b 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/RecargoZonaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/RecargoZonaService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Distribucion; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; @@ -43,7 +44,7 @@ namespace GestionIntegral.Api.Services.Distribucion VigenciaH = data.Recargo.VigenciaH?.ToString("yyyy-MM-dd"), Valor = data.Recargo.Valor }; - + // Helper para mapear cuando solo tenemos el RecargoZona y necesitamos buscar NombreZona private async Task MapToDtoWithZonaLookup(RecargoZona recargo) { @@ -74,10 +75,11 @@ namespace GestionIntegral.Api.Services.Distribucion { var recargo = await _recargoZonaRepository.GetByIdAsync(idRecargo); if (recargo == null) return null; - + var zona = await _zonaRepository.GetByIdAsync(recargo.IdZona); // Obtiene ZonaDto - return new RecargoZonaDto { - IdRecargo = recargo.IdRecargo, + return new RecargoZonaDto + { + IdRecargo = recargo.IdRecargo, IdPublicacion = recargo.IdPublicacion, IdZona = recargo.IdZona, NombreZona = zona?.Nombre ?? "Zona Desconocida/Inactiva", @@ -92,7 +94,7 @@ namespace GestionIntegral.Api.Services.Distribucion if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null) return (null, "La publicación especificada no existe."); var zona = await _zonaRepository.GetByIdAsync(createDto.IdZona); // Devuelve ZonaDto - if (zona == null) + if (zona == null) return (null, "La zona especificada no existe o no está activa."); // Usar createDto.VigenciaD directamente que ya es DateTime @@ -119,7 +121,7 @@ namespace GestionIntegral.Api.Services.Distribucion var recargoAnterior = await _recargoZonaRepository.GetPreviousActiveRecargoAsync(createDto.IdPublicacion, createDto.IdZona, nuevoRecargo.VigenciaD, transaction); if (recargoAnterior != null) { - if (recargoAnterior.VigenciaD.Date >= nuevoRecargo.VigenciaD.Date) // Comparar solo fechas + if (recargoAnterior.VigenciaD.Date >= nuevoRecargo.VigenciaD.Date) // Comparar solo fechas { transaction.Rollback(); return (null, $"La fecha de inicio del nuevo recargo ({nuevoRecargo.VigenciaD:dd/MM/yyyy}) no puede ser anterior o igual a la del último recargo vigente ({recargoAnterior.VigenciaD:dd/MM/yyyy}) para esta zona."); @@ -134,10 +136,15 @@ namespace GestionIntegral.Api.Services.Distribucion transaction.Commit(); _logger.LogInformation("Recargo ID {Id} creado por Usuario ID {UserId}.", recargoCreado.IdRecargo, idUsuario); // Pasar el nombre de la zona ya obtenido - return (new RecargoZonaDto { - IdRecargo = recargoCreado.IdRecargo, IdPublicacion = recargoCreado.IdPublicacion, IdZona = recargoCreado.IdZona, - NombreZona = zona.Nombre, VigenciaD = recargoCreado.VigenciaD.ToString("yyyy-MM-dd"), - VigenciaH = recargoCreado.VigenciaH?.ToString("yyyy-MM-dd"), Valor = recargoCreado.Valor + return (new RecargoZonaDto + { + IdRecargo = recargoCreado.IdRecargo, + IdPublicacion = recargoCreado.IdPublicacion, + IdZona = recargoCreado.IdZona, + NombreZona = zona.Nombre, + VigenciaD = recargoCreado.VigenciaD.ToString("yyyy-MM-dd"), + VigenciaH = recargoCreado.VigenciaH?.ToString("yyyy-MM-dd"), + Valor = recargoCreado.Valor }, null); } catch (Exception ex) @@ -155,21 +162,28 @@ namespace GestionIntegral.Api.Services.Distribucion using var transaction = connection.BeginTransaction(); try { - var recargoExistente = await _recargoZonaRepository.GetByIdAsync(idRecargo); + var recargoExistente = await _recargoZonaRepository.GetByIdAsync(idRecargo); if (recargoExistente == null) return (false, "Recargo por zona no encontrado."); + // --- VERIFICACIÓN PERÍODO CERRADO --- + if (recargoExistente.VigenciaH != null && updateDto.Valor != recargoExistente.Valor) + { + transaction.Rollback(); + return (false, "No se puede modificar el valor de un recargo que ya ha sido cerrado (tiene Vigencia Hasta)."); + } + if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < recargoExistente.VigenciaD.Date) return (false, "Vigencia Hasta no puede ser anterior a Vigencia Desde."); - + if (updateDto.VigenciaH.HasValue) { var recargosDeLaPublicacion = await _recargoZonaRepository.GetByPublicacionIdAsync(recargoExistente.IdPublicacion); var recargosPosteriores = recargosDeLaPublicacion - .Where(r => r.Recargo.IdZona == recargoExistente.IdZona && - r.Recargo.IdRecargo != idRecargo && - r.Recargo.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && + .Where(r => r.Recargo.IdZona == recargoExistente.IdZona && + r.Recargo.IdRecargo != idRecargo && + r.Recargo.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && r.Recargo.VigenciaD.Date > recargoExistente.VigenciaD.Date); - if(recargosPosteriores.Any()) + if (recargosPosteriores.Any()) { return (false, "No se puede cerrar este período porque existen recargos posteriores para esta zona que se solaparían."); } @@ -207,19 +221,26 @@ namespace GestionIntegral.Api.Services.Distribucion var recargoAEliminar = await _recargoZonaRepository.GetByIdAsync(idRecargo); if (recargoAEliminar == null) return (false, "Recargo no encontrado."); - if (recargoAEliminar.VigenciaH == null) + // --- VERIFICACIÓN DE USO --- + if (await _recargoZonaRepository.IsInUseAsync(idRecargo, recargoAEliminar.VigenciaD, recargoAEliminar.VigenciaH)) + { + transaction.Rollback(); + return (false, "No se puede eliminar. Este recargo está referenciado en movimientos existentes."); + } + + if (recargoAEliminar.VigenciaH == null) { var todosRecargosPubZonaData = await _recargoZonaRepository.GetByPublicacionIdAsync(recargoAEliminar.IdPublicacion); var todosRecargosPubZona = todosRecargosPubZonaData .Where(r => r.Recargo.IdZona == recargoAEliminar.IdZona) .Select(r => r.Recargo) .OrderByDescending(r => r.VigenciaD).ToList(); - + var indiceActual = todosRecargosPubZona.FindIndex(r => r.IdRecargo == idRecargo); - if(indiceActual != -1 && (indiceActual + 1) < todosRecargosPubZona.Count) + if (indiceActual != -1 && (indiceActual + 1) < todosRecargosPubZona.Count) { var recargoAnteriorDirecto = todosRecargosPubZona[indiceActual + 1]; - if(recargoAnteriorDirecto.VigenciaH.HasValue && recargoAnteriorDirecto.VigenciaH.Value.Date == recargoAEliminar.VigenciaD.AddDays(-1).Date) + if (recargoAnteriorDirecto.VigenciaH.HasValue && recargoAnteriorDirecto.VigenciaH.Value.Date == recargoAEliminar.VigenciaD.AddDays(-1).Date) { recargoAnteriorDirecto.VigenciaH = null; await _recargoZonaRepository.UpdateAsync(recargoAnteriorDirecto, idUsuario, transaction); @@ -243,5 +264,28 @@ namespace GestionIntegral.Api.Services.Distribucion return (false, $"Error interno: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idRecargoAfectado, int? idPublicacionAfectada, int? idZonaAfectada) + { + var historialData = await _recargoZonaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idRecargoAfectado, idPublicacionAfectada, idZonaAfectada); + + return historialData.Select(h => new RecargoZonaHistorialDto + { + Id_Recargo = h.Historial.Id_Recargo, + Id_Publicacion = h.Historial.Id_Publicacion, + Id_Zona = h.Historial.Id_Zona, + VigenciaD = h.Historial.VigenciaD, + VigenciaH = h.Historial.VigenciaH, + Valor = h.Historial.Valor, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + // Mapear NombrePublicacion y NombreZona si se añaden al DTO + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Distribucion/ZonaService.cs b/Backend/GestionIntegral.Api/Services/Distribucion/ZonaService.cs index 6be14a3..e0a96b6 100644 --- a/Backend/GestionIntegral.Api/Services/Distribucion/ZonaService.cs +++ b/Backend/GestionIntegral.Api/Services/Distribucion/ZonaService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data.Repositories; // Para IZonaRepository using GestionIntegral.Api.Data.Repositories.Distribucion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Zonas; using GestionIntegral.Api.Models.Distribucion; // Para Zona using System.Collections.Generic; @@ -123,7 +124,8 @@ namespace GestionIntegral.Api.Services.Distribucion return (false, "Zona no encontrada."); } // Si ya está inactiva, consideramos la operación exitosa (idempotencia) - if (!zonaExistente.Estado) { + if (!zonaExistente.Estado) + { return (true, null); } @@ -137,10 +139,30 @@ namespace GestionIntegral.Api.Services.Distribucion var eliminado = await _zonaRepository.SoftDeleteAsync(id, idUsuario); if (!eliminado) { - _logger.LogError("Falló la eliminación lógica de la Zona en el repositorio para el ID: {Id}", id); + _logger.LogError("Falló la eliminación lógica de la Zona en el repositorio para el ID: {Id}", id); return (false, "Error al eliminar la zona de la base de datos."); } return (true, null); // Éxito } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idZonaAfectada) + { + var historialData = await _zonaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idZonaAfectada); + + return historialData.Select(h => new ZonaHistorialDto + { + Id_Zona = h.Historial.Id_Zona, + Nombre = h.Historial.Nombre, + Descripcion = h.Historial.Descripcion, + Estado = h.Historial.Estado, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/EstadoBobinaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/EstadoBobinaService.cs index a08aa34..5b4fe60 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/EstadoBobinaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/EstadoBobinaService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Impresion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; using GestionIntegral.Api.Models.Impresion; using Microsoft.Extensions.Logging; @@ -112,10 +113,10 @@ namespace GestionIntegral.Api.Services.Impresion { // Estados "fijos" como Disponible (1), En Uso (2), Dañada (3) probablemente no deberían eliminarse. // Podrías añadir una validación aquí o en el repositorio si es necesario. - if (id <= 3) // Asumiendo IDs fijos para los estados base - { - return (false, "Los estados base (Disponible, En Uso, Dañada) no se pueden eliminar."); - } + if (id <= 3) // Asumiendo IDs fijos para los estados base + { + return (false, "Los estados base (Disponible, En Uso, Dañada) no se pueden eliminar."); + } if (await _estadoBobinaRepository.IsInUseAsync(id)) { @@ -129,7 +130,7 @@ namespace GestionIntegral.Api.Services.Impresion try { var eliminado = await _estadoBobinaRepository.DeleteAsync(id, idUsuario, transaction); - if (!eliminado) throw new DataException("La operación de eliminación no afectó ninguna fila."); + if (!eliminado) throw new DataException("La operación de eliminación no afectó ninguna fila."); transaction.Commit(); // Síncrono _logger.LogInformation("EstadoBobina ID {IdEstadoBobina} eliminado por Usuario ID {IdUsuario}.", id, idUsuario); @@ -137,16 +138,35 @@ namespace GestionIntegral.Api.Services.Impresion } catch (KeyNotFoundException knfex) { - try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback EliminarAsync EstadoBobina."); } - _logger.LogWarning(knfex, "Intento de eliminar EstadoBobina ID: {Id} no encontrado.", id); - return (false, "Estado de bobina no encontrado."); + try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback EliminarAsync EstadoBobina."); } + _logger.LogWarning(knfex, "Intento de eliminar EstadoBobina ID: {Id} no encontrado.", id); + return (false, "Estado de bobina no encontrado."); } catch (Exception ex) { - try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback EliminarAsync EstadoBobina."); } - _logger.LogError(ex, "Error EliminarAsync EstadoBobina ID: {Id}", id); + try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error rollback EliminarAsync EstadoBobina."); } + _logger.LogError(ex, "Error EliminarAsync EstadoBobina ID: {Id}", id); return (false, $"Error interno al eliminar el estado de bobina: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idEstadoBobinaAfectado) + { + var historialData = await _estadoBobinaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idEstadoBobinaAfectado); + + return historialData.Select(h => new EstadoBobinaHistorialDto + { + Id_EstadoBobina = h.Historial.Id_EstadoBobina, + Denominacion = h.Historial.Denominacion, + Obs = h.Historial.Obs, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/IEstadoBobinaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/IEstadoBobinaService.cs index d1a4a16..b6cbfa8 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/IEstadoBobinaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/IEstadoBobinaService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; using System.Collections.Generic; using System.Threading.Tasks; @@ -11,5 +12,9 @@ namespace GestionIntegral.Api.Services.Impresion Task<(EstadoBobinaDto? EstadoBobina, string? Error)> CrearAsync(CreateEstadoBobinaDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateEstadoBobinaDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idEstadoBobinaAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/IPlantaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/IPlantaService.cs index fe2d4e6..0786bc1 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/IPlantaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/IPlantaService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; // Para los DTOs using System.Collections.Generic; using System.Threading.Tasks; @@ -12,5 +13,9 @@ namespace GestionIntegral.Api.Services.Impresion Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdatePlantaDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario); Task> ObtenerParaDropdownAsync(); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPlantaAfectada); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/IStockBobinaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/IStockBobinaService.cs index 1f2917e..9a5fc4a 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/IStockBobinaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/IStockBobinaService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; using System; using System.Collections.Generic; @@ -16,5 +17,9 @@ namespace GestionIntegral.Api.Services.Impresion Task<(bool Exito, string? Error)> ActualizarDatosBobinaDisponibleAsync(int idBobina, UpdateStockBobinaDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> CambiarEstadoBobinaAsync(int idBobina, CambiarEstadoBobinaDto cambiarEstadoDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarIngresoErroneoAsync(int idBobina, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idBobinaAfectada, int? idTipoBobinaFiltro, int? idPlantaFiltro, int? idEstadoBobinaFiltro); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/ITipoBobinaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/ITipoBobinaService.cs index 7291773..3ba901d 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/ITipoBobinaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/ITipoBobinaService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; using System.Collections.Generic; using System.Threading.Tasks; @@ -11,5 +12,9 @@ namespace GestionIntegral.Api.Services.Impresion Task<(TipoBobinaDto? TipoBobina, string? Error)> CrearAsync(CreateTipoBobinaDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateTipoBobinaDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idTipoBobinaAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/ITiradaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/ITiradaService.cs index b8fdf2d..00a91c2 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/ITiradaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/ITiradaService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; using System; using System.Collections.Generic; @@ -8,9 +9,15 @@ namespace GestionIntegral.Api.Services.Impresion public interface ITiradaService { Task> ObtenerTiradasAsync(DateTime? fecha, int? idPublicacion, int? idPlanta); - // GetById podría ser útil si se editan tiradas individuales, pero la creación es el foco principal. - // Task ObtenerTiradaPorIdRegistroAsync(int idRegistroTirada); // Para bob_RegTiradas Task<(TiradaDto? TiradaCreada, string? Error)> RegistrarTiradaCompletaAsync(CreateTiradaRequestDto createDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarTiradaCompletaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario); + Task> ObtenerRegTiradasHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idRegistroAfectado, int? idPublicacionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro); + Task> ObtenerRegSeccionesTiradaHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idTiradaAfectada, int? idPublicacionFiltro, int? idSeccionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/PlantaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/PlantaService.cs index e9359e8..983033b 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/PlantaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/PlantaService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; // Para DbConnectionFactory using GestionIntegral.Api.Data.Repositories.Impresion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; using GestionIntegral.Api.Models.Impresion; // Para Planta using Microsoft.Extensions.Logging; @@ -190,5 +191,24 @@ namespace GestionIntegral.Api.Services.Impresion .Select(p => new PlantaDropdownDto { IdPlanta = p.IdPlanta, Nombre = p.Nombre }) .ToList(); } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPlantaAfectada) + { + var historialData = await _plantaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPlantaAfectada); + + return historialData.Select(h => new PlantaHistorialDto + { + Id_Planta = h.Historial.Id_Planta, + Nombre = h.Historial.Nombre, + Detalle = h.Historial.Detalle, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/StockBobinaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/StockBobinaService.cs index 5bb808c..e899939 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/StockBobinaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/StockBobinaService.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; +using GestionIntegral.Api.Dtos.Auditoria; namespace GestionIntegral.Api.Services.Impresion { @@ -160,10 +161,10 @@ namespace GestionIntegral.Api.Services.Impresion if (bobinaExistente.NroBobina != updateDto.NroBobina && await _stockBobinaRepository.GetByNroBobinaAsync(updateDto.NroBobina) != null) // Validar fuera de TX o pasar TX al repo { - try { transaction.Rollback(); } catch {} // Rollback antes de retornar por validación + try { transaction.Rollback(); } catch { } // Rollback antes de retornar por validación return (false, $"El nuevo número de bobina '{updateDto.NroBobina}' ya existe."); } - if (await _tipoBobinaRepository.GetByIdAsync(updateDto.IdTipoBobina) == null) + if (await _tipoBobinaRepository.GetByIdAsync(updateDto.IdTipoBobina) == null) return (false, "Tipo de bobina inválido."); if (await _plantaRepository.GetByIdAsync(updateDto.IdPlanta) == null) return (false, "Planta inválida."); @@ -193,57 +194,122 @@ namespace GestionIntegral.Api.Services.Impresion public async Task<(bool Exito, string? Error)> CambiarEstadoBobinaAsync(int idBobina, CambiarEstadoBobinaDto cambiarEstadoDto, int idUsuario) { - using var connection = _connectionFactory.CreateConnection(); + using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try { - var bobina = await _stockBobinaRepository.GetByIdAsync(idBobina); - if (bobina == null) return (false, "Bobina no encontrada."); + var bobina = await _stockBobinaRepository.GetByIdAsync(idBobina); // Obtener dentro de la transacción + if (bobina == null) + { + try { transaction.Rollback(); } catch { } + return (false, "Bobina no encontrada."); + } var nuevoEstado = await _estadoBobinaRepository.GetByIdAsync(cambiarEstadoDto.NuevoEstadoId); - if (nuevoEstado == null) return (false, "El nuevo estado especificado no es válido."); + if (nuevoEstado == null) + { + try { transaction.Rollback(); } catch { } + return (false, "El nuevo estado especificado no es válido."); + } - // Validaciones de flujo de estados + // --- INICIO DE VALIDACIONES DE FLUJO DE ESTADOS --- if (bobina.IdEstadoBobina == cambiarEstadoDto.NuevoEstadoId) + { + try { transaction.Rollback(); } catch { } return (false, "La bobina ya se encuentra en ese estado."); - if (bobina.IdEstadoBobina == 3 && cambiarEstadoDto.NuevoEstadoId != 3) // 3 = Dañada - return (false, "Una bobina dañada no puede cambiar de estado."); - if (bobina.IdEstadoBobina == 2 && cambiarEstadoDto.NuevoEstadoId == 1) // 2 = En Uso, 1 = Disponible - return (false, "Una bobina 'En Uso' no puede volver a 'Disponible' directamente mediante esta acción."); + } + + // Reglas específicas para salir de "Dañada" (ID 3) + if (bobina.IdEstadoBobina == 3) // 3 = Dañada + { + if (cambiarEstadoDto.NuevoEstadoId != 1) // 1 = Disponible + { + try { transaction.Rollback(); } catch { } + return (false, "Una bobina dañada solo puede ser revertida a 'Disponible' (si fue un error)."); + } + // Si se cambia de Dañada a Disponible, se limpiarán Publicacion y Seccion más abajo. + } + // Regla para salir de "Utilizada" (ID 2) + else if (bobina.IdEstadoBobina == 2) // 2 = En Uso + { + if (cambiarEstadoDto.NuevoEstadoId == 1) // No se puede volver a Disponible directamente + { + try { transaction.Rollback(); } catch { } + return (false, "Una bobina 'En Uso' no puede volver a 'Disponible' directamente mediante esta acción. Primero debe marcarse como Dañada si es necesario."); + } + // Si se cambia de Utilizada a cualquier otro estado que no sea Dañada (aunque aquí solo se permite Dañada), + // y si el nuevo estado NO es En Uso, también se deberían limpiar Publicacion y Seccion. + // La lógica actual ya lo hace si nuevoEstadoId no es 2. + } + // Regla para salir de "Disponible" (ID 1) + else if (bobina.IdEstadoBobina == 1) // 1 = Disponible + { + // Desde Disponible puede pasar a En Uso (2) o Dañada (3). + // El frontend ya debería filtrar esto, pero una validación aquí es buena. + if (cambiarEstadoDto.NuevoEstadoId != 2 && cambiarEstadoDto.NuevoEstadoId != 3) + { + try { transaction.Rollback(); } catch { } + return (false, "Desde 'Disponible', solo se puede cambiar a 'En Uso' o 'Dañada'."); + } + } + // --- FIN VALIDACIONES DE FLUJO DE ESTADOS --- bobina.IdEstadoBobina = cambiarEstadoDto.NuevoEstadoId; bobina.FechaEstado = cambiarEstadoDto.FechaCambioEstado.Date; - bobina.Obs = cambiarEstadoDto.Obs ?? bobina.Obs; // Mantener obs si no se provee uno nuevo + bobina.Obs = cambiarEstadoDto.Obs ?? bobina.Obs; - if (cambiarEstadoDto.NuevoEstadoId == 2) // "En Uso" + if (cambiarEstadoDto.NuevoEstadoId == 2) // 2 = "En Uso" { - if (!cambiarEstadoDto.IdPublicacion.HasValue || !cambiarEstadoDto.IdSeccion.HasValue) - return (false, "Para el estado 'En Uso', se requiere Publicación y Sección."); - if (await _publicacionRepository.GetByIdSimpleAsync(cambiarEstadoDto.IdPublicacion.Value) == null) + if (!cambiarEstadoDto.IdPublicacion.HasValue || cambiarEstadoDto.IdPublicacion.Value <= 0) + { + try { transaction.Rollback(); } catch { } + return (false, "Para el estado 'En Uso', se requiere Publicación."); + } + if (!cambiarEstadoDto.IdSeccion.HasValue || cambiarEstadoDto.IdSeccion.Value <= 0) + { + try { transaction.Rollback(); } catch { } + return (false, "Para el estado 'En Uso', se requiere Sección."); + } + + // Validar existencia de Publicación y Sección + var publicacion = await _publicacionRepository.GetByIdSimpleAsync(cambiarEstadoDto.IdPublicacion.Value); + if (publicacion == null) + { + try { transaction.Rollback(); } catch { } return (false, "Publicación inválida."); - if (await _publiSeccionRepository.GetByIdAsync(cambiarEstadoDto.IdSeccion.Value) == null) // Asume GetByIdAsync en IPubliSeccionRepository - return (false, "Sección inválida."); - + } + + // Asumiendo que GetByIdAsync en IPubliSeccionRepository valida si la sección pertenece a la publicación también + var seccion = await _publiSeccionRepository.GetByIdAsync(cambiarEstadoDto.IdSeccion.Value); + if (seccion == null || seccion.IdPublicacion != cambiarEstadoDto.IdPublicacion.Value) + { + try { transaction.Rollback(); } catch { } + return (false, "Sección inválida o no pertenece a la publicación seleccionada."); + } + bobina.IdPublicacion = cambiarEstadoDto.IdPublicacion.Value; bobina.IdSeccion = cambiarEstadoDto.IdSeccion.Value; } - else - { // Si no es "En Uso", limpiar estos campos + else // Si el nuevo estado NO es "En Uso" (ej. Disponible, Dañada) + { bobina.IdPublicacion = null; bobina.IdSeccion = null; + // La observación se mantiene o se actualiza según venga en el DTO. + // Si se pasa de "Dañada" a "Disponible", el DTO de cambio de estado debe incluir la nueva obs. } - var actualizado = await _stockBobinaRepository.UpdateAsync(bobina, idUsuario, transaction, $"Estado Cambiado a: {nuevoEstado.Denominacion}"); + var actualizado = await _stockBobinaRepository.UpdateAsync(bobina, idUsuario, transaction, $"Estado: {nuevoEstado.Denominacion}"); if (!actualizado) throw new DataException("Error al cambiar estado de la bobina."); + transaction.Commit(); return (true, null); } catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Bobina no encontrada."); } catch (Exception ex) { - try { transaction.Rollback(); } catch { } + try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback CambiarEstadoBobinaAsync."); } _logger.LogError(ex, "Error CambiarEstadoBobinaAsync ID: {IdBobina}", idBobina); return (false, $"Error interno: {ex.Message}"); } @@ -256,23 +322,66 @@ namespace GestionIntegral.Api.Services.Impresion using var transaction = connection.BeginTransaction(); try { - var bobina = await _stockBobinaRepository.GetByIdAsync(idBobina); - if (bobina == null) return (false, "Bobina no encontrada."); - if (bobina.IdEstadoBobina != 1) // Solo se pueden eliminar las "Disponibles" (ingresos erróneos) - return (false, "Solo se pueden eliminar ingresos de bobinas que estén en estado 'Disponible'."); + var bobina = await _stockBobinaRepository.GetByIdAsync(idBobina); // Obtener dentro de la transacción + if (bobina == null) + { + try { transaction.Rollback(); } catch { } + return (false, "Bobina no encontrada."); + } + + // --- Permitir eliminar si está Disponible (1) o Dañada (3) --- + if (bobina.IdEstadoBobina != 1 && bobina.IdEstadoBobina != 3) + { + try { transaction.Rollback(); } catch { } + return (false, "Solo se pueden eliminar ingresos de bobinas que estén en estado 'Disponible' o 'Dañada'."); + } var eliminado = await _stockBobinaRepository.DeleteAsync(idBobina, idUsuario, transaction); if (!eliminado) throw new DataException("Error al eliminar la bobina."); + transaction.Commit(); return (true, null); } catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Bobina no encontrada."); } catch (Exception ex) { - try { transaction.Rollback(); } catch { } + try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback EliminarIngresoErroneoAsync."); } _logger.LogError(ex, "Error EliminarIngresoErroneoAsync ID: {IdBobina}", idBobina); return (false, $"Error interno: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idBobinaAfectada, int? idTipoBobinaFiltro, int? idPlantaFiltro, int? idEstadoBobinaFiltro) + { + var historialData = await _stockBobinaRepository.GetHistorialDetalladoAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idBobinaAfectada, idTipoBobinaFiltro, idPlantaFiltro, idEstadoBobinaFiltro); + + return historialData.Select(h => new StockBobinaHistorialDto + { + Id_Bobina = h.Historial.Id_Bobina, + Id_TipoBobina = h.Historial.Id_TipoBobina, + NombreTipoBobina = h.NombreTipoBobina ?? "N/A", + NroBobina = h.Historial.NroBobina, + Peso = h.Historial.Peso, + Id_Planta = h.Historial.Id_Planta, + NombrePlanta = h.NombrePlanta ?? "N/A", + Id_EstadoBobina = h.Historial.Id_EstadoBobina, + NombreEstadoBobina = h.NombreEstadoBobina ?? "N/A", + Remito = h.Historial.Remito, + FechaRemito = h.Historial.FechaRemito, + FechaEstado = h.Historial.FechaEstado, + Id_Publicacion = h.Historial.Id_Publicacion, + NombrePublicacion = h.NombrePublicacion, + Id_Seccion = h.Historial.Id_Seccion, + NombreSeccion = h.NombreSeccion, + Obs = h.Historial.Obs, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/TipoBobinaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/TipoBobinaService.cs index fa51e35..0cd9bb5 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/TipoBobinaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/TipoBobinaService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Impresion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; using GestionIntegral.Api.Models.Impresion; using Microsoft.Extensions.Logging; @@ -87,7 +88,7 @@ namespace GestionIntegral.Api.Services.Impresion try { var actualizado = await _tipoBobinaRepository.UpdateAsync(tipoBobinaAActualizar, idUsuario, transaction); - if (!actualizado) throw new DataException("La operación de actualización no afectó ninguna fila."); + if (!actualizado) throw new DataException("La operación de actualización no afectó ninguna fila."); transaction.Commit(); // Síncrono _logger.LogInformation("TipoBobina ID {IdTipoBobina} actualizado por Usuario ID {IdUsuario}.", id, idUsuario); @@ -95,9 +96,9 @@ namespace GestionIntegral.Api.Services.Impresion } catch (KeyNotFoundException knfex) { - try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en ActualizarAsync TipoBobina."); } - _logger.LogWarning(knfex, "Intento de actualizar TipoBobina ID: {Id} no encontrado.", id); - return (false, "Tipo de bobina no encontrado."); + try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en ActualizarAsync TipoBobina."); } + _logger.LogWarning(knfex, "Intento de actualizar TipoBobina ID: {Id} no encontrado.", id); + return (false, "Tipo de bobina no encontrado."); } catch (Exception ex) { @@ -115,7 +116,7 @@ namespace GestionIntegral.Api.Services.Impresion } using var connection = _connectionFactory.CreateConnection(); - if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); } + if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); } using var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); try @@ -129,16 +130,34 @@ namespace GestionIntegral.Api.Services.Impresion } catch (KeyNotFoundException knfex) { - try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync TipoBobina."); } - _logger.LogWarning(knfex, "Intento de eliminar TipoBobina ID: {Id} no encontrado.", id); - return (false, "Tipo de bobina no encontrado."); + try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync TipoBobina."); } + _logger.LogWarning(knfex, "Intento de eliminar TipoBobina ID: {Id} no encontrado.", id); + return (false, "Tipo de bobina no encontrado."); } catch (Exception ex) { - try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync TipoBobina."); } - _logger.LogError(ex, "Error en transacción EliminarAsync para TipoBobina ID: {Id}", id); + try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error adicional durante el rollback en EliminarAsync TipoBobina."); } + _logger.LogError(ex, "Error en transacción EliminarAsync para TipoBobina ID: {Id}", id); return (false, $"Error interno al eliminar el tipo de bobina: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idTipoBobinaAfectado) + { + var historialData = await _tipoBobinaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idTipoBobinaAfectado); + + return historialData.Select(h => new TipoBobinaHistorialDto + { + Id_TipoBobina = h.Historial.Id_TipoBobina, + Denominacion = h.Historial.Denominacion, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Impresion/TiradaService.cs b/Backend/GestionIntegral.Api/Services/Impresion/TiradaService.cs index 4ed5d47..5f58376 100644 --- a/Backend/GestionIntegral.Api/Services/Impresion/TiradaService.cs +++ b/Backend/GestionIntegral.Api/Services/Impresion/TiradaService.cs @@ -1,6 +1,7 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; using GestionIntegral.Api.Data.Repositories.Impresion; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Impresion; using GestionIntegral.Api.Models.Distribucion; // Para Publicacion, PubliSeccion using GestionIntegral.Api.Models.Impresion; // Para RegTirada, RegPublicacionSeccion @@ -144,9 +145,10 @@ namespace GestionIntegral.Api.Services.Impresion }; var seccionCreadaEnTirada = await _regPublicacionSeccionRepository.CreateAsync(nuevaRegPubSeccion, idUsuario, transaction); if (seccionCreadaEnTirada == null) throw new DataException($"Error al registrar la sección ID {seccionDto.IdSeccion} en la tirada."); - + var seccionInfo = await _publiSeccionRepository.GetByIdAsync(seccionDto.IdSeccion); // Para obtener nombre - seccionesImpresasDto.Add(new DetalleSeccionEnListadoDto{ + seccionesImpresasDto.Add(new DetalleSeccionEnListadoDto + { IdSeccion = seccionCreadaEnTirada.IdSeccion, NombreSeccion = seccionInfo?.Nombre ?? "N/A", CantPag = seccionCreadaEnTirada.CantPag, @@ -159,7 +161,8 @@ namespace GestionIntegral.Api.Services.Impresion _logger.LogInformation("Tirada completa registrada para Pub ID {IdPub}, Fecha {Fecha}, Planta ID {IdPlanta} por Usuario ID {UserId}.", createDto.IdPublicacion, createDto.Fecha.Date, createDto.IdPlanta, idUsuario); - return (new TiradaDto { + return (new TiradaDto + { IdRegistroTirada = regTiradaCreada.IdRegistro, IdPublicacion = regTiradaCreada.IdPublicacion, NombrePublicacion = publicacion.Nombre, @@ -204,10 +207,10 @@ namespace GestionIntegral.Api.Services.Impresion // bool principalEliminado = await _regTiradaRepository.DeleteAsync(tiradaPrincipal.IdRegistro, idUsuario, transaction); // Alternativa si ya tienes el IdRegistro if (!principalEliminado && tiradaPrincipal != null) // Si DeleteByFechaPublicacionPlantaAsync devuelve false porque no encontró nada (raro si pasó la validación) { - _logger.LogWarning("No se eliminó el registro principal de tirada (bob_RegTiradas) para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}. Pudo haber sido eliminado concurrentemente.", fecha.Date, idPublicacion, idPlanta); - // Decidir si esto es un error que debe hacer rollback + _logger.LogWarning("No se eliminó el registro principal de tirada (bob_RegTiradas) para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}. Pudo haber sido eliminado concurrentemente.", fecha.Date, idPublicacion, idPlanta); + // Decidir si esto es un error que debe hacer rollback } - _logger.LogInformation("Registro principal de tirada eliminado para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}", fecha.Date, idPublicacion, idPlanta); + _logger.LogInformation("Registro principal de tirada eliminado para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}", fecha.Date, idPublicacion, idPlanta); transaction.Commit(); @@ -220,5 +223,53 @@ namespace GestionIntegral.Api.Services.Impresion return (false, $"Error interno al eliminar la tirada: {ex.Message}"); } } + + public async Task> ObtenerRegTiradasHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idRegistroAfectado, int? idPublicacionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro) + { + var historialData = await _regTiradaRepository.GetRegTiradasHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idRegistroAfectado, idPublicacionFiltro, idPlantaFiltro, fechaTiradaFiltro); + + return historialData.Select(h => new RegTiradaHistorialDto + { + Id_Registro = h.Historial.Id_Registro, + Ejemplares = h.Historial.Ejemplares, + Id_Publicacion = h.Historial.Id_Publicacion, + NombrePublicacion = h.NombrePublicacion, + Fecha = h.Historial.Fecha, + Id_Planta = h.Historial.Id_Planta, + NombrePlanta = h.NombrePlanta, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } + + public async Task> ObtenerRegSeccionesTiradaHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idTiradaAfectada, int? idPublicacionFiltro, int? idSeccionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro) + { + var historialData = await _regTiradaRepository.GetRegSeccionesTiradaHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idTiradaAfectada, idPublicacionFiltro, idSeccionFiltro, idPlantaFiltro, fechaTiradaFiltro); + + return historialData.Select(h => new RegSeccionTiradaHistorialDto + { + Id_Tirada = h.Historial.Id_Tirada, + Id_Publicacion = h.Historial.Id_Publicacion, + NombrePublicacion = h.NombrePublicacion, + Id_Seccion = h.Historial.Id_Seccion, + NombreSeccion = h.NombreSeccion, + CantPag = h.Historial.CantPag, + Fecha = h.Historial.Fecha, + Id_Planta = h.Historial.Id_Planta, + NombrePlanta = h.NombrePlanta, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Usuarios/IPerfilService.cs b/Backend/GestionIntegral.Api/Services/Usuarios/IPerfilService.cs index d526246..56c7361 100644 --- a/Backend/GestionIntegral.Api/Services/Usuarios/IPerfilService.cs +++ b/Backend/GestionIntegral.Api/Services/Usuarios/IPerfilService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Usuarios; using System.Collections.Generic; using System.Threading.Tasks; @@ -13,5 +14,13 @@ namespace GestionIntegral.Api.Services.Usuarios Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario); Task> ObtenerPermisosAsignadosAsync(int idPerfil); Task<(bool Exito, string? Error)> ActualizarPermisosAsignadosAsync(int idPerfil, ActualizarPermisosPerfilRequestDto request, int idUsuarioModificador); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPerfilAfectado); + Task> ObtenerPermisosAsignadosHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPerfilAfectado, int? idPermisoAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Usuarios/IPermisoService.cs b/Backend/GestionIntegral.Api/Services/Usuarios/IPermisoService.cs index 0a82a2c..bcea9e3 100644 --- a/Backend/GestionIntegral.Api/Services/Usuarios/IPermisoService.cs +++ b/Backend/GestionIntegral.Api/Services/Usuarios/IPermisoService.cs @@ -1,3 +1,4 @@ +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Usuarios; using System.Collections.Generic; using System.Threading.Tasks; @@ -11,5 +12,9 @@ namespace GestionIntegral.Api.Services.Usuarios Task<(PermisoDto? Permiso, string? Error)> CrearAsync(CreatePermisoDto createDto, int idUsuario); Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdatePermisoDto updateDto, int idUsuario); Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario); + Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPermisoAfectado); } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Usuarios/PerfilService.cs b/Backend/GestionIntegral.Api/Services/Usuarios/PerfilService.cs index 8a3b652..4068f1b 100644 --- a/Backend/GestionIntegral.Api/Services/Usuarios/PerfilService.cs +++ b/Backend/GestionIntegral.Api/Services/Usuarios/PerfilService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; // Para DbConnectionFactory using GestionIntegral.Api.Data.Repositories.Usuarios; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Usuarios; using GestionIntegral.Api.Models.Usuarios; using Microsoft.Extensions.Logging; @@ -179,9 +180,9 @@ namespace GestionIntegral.Api.Services.Usuarios } public async Task<(bool Exito, string? Error)> ActualizarPermisosAsignadosAsync( - int idPerfil, - ActualizarPermisosPerfilRequestDto request, - int idUsuarioModificador) + int idPerfil, + ActualizarPermisosPerfilRequestDto request, + int idUsuarioModificador) { // Validación: Verificar que el perfil exista var perfil = await _perfilRepository.GetByIdAsync(idPerfil); @@ -190,15 +191,7 @@ namespace GestionIntegral.Api.Services.Usuarios return (false, "Perfil no encontrado."); } - // Validación opcional: Verificar que todos los IDs de permisos en la solicitud sean válidos - if (request.PermisosIds != null && request.PermisosIds.Any()) - { - var permisosValidos = await _permisoRepository.GetPermisosByIdsAsync(request.PermisosIds); - if (permisosValidos.Count() != request.PermisosIds.Distinct().Count()) // Compara counts para detectar IDs inválidos - { - return (false, "Uno o más IDs de permisos proporcionados son inválidos."); - } - } + // --- INICIO DE LA LÓGICA DE AUDITORÍA --- using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); @@ -206,12 +199,33 @@ namespace GestionIntegral.Api.Services.Usuarios try { - // El idUsuarioModificador no se usa directamente en UpdatePermisosByPerfilIdAsync - // porque no estamos grabando en una tabla _H para gral_PermisosPerfiles. - // Si se necesitara auditoría de esta acción específica, se debería añadir. - await _perfilRepository.UpdatePermisosByPerfilIdAsync(idPerfil, request.PermisosIds ?? new List(), transaction); + // 1. Obtener los IDs de permisos actuales DENTRO de la transacción para evitar race conditions + var idsActuales = (await _perfilRepository.GetPermisoIdsByPerfilIdAsync(idPerfil)).ToHashSet(); + var idsNuevos = (request.PermisosIds ?? new List()).ToHashSet(); + + // 2. Calcular las diferencias + var permisosAñadidos = idsNuevos.Where(id => !idsActuales.Contains(id)).ToList(); + var permisosQuitados = idsActuales.Where(id => !idsNuevos.Contains(id)).ToList(); + + // 3. Registrar en el historial + foreach (var idPermisoAñadido in permisosAñadidos) + { + await _perfilRepository.LogPermisoAsignacionHistorialAsync(idPerfil, idPermisoAñadido, idUsuarioModificador, "Asignado", transaction); + } + foreach (var idPermisoQuitado in permisosQuitados) + { + await _perfilRepository.LogPermisoAsignacionHistorialAsync(idPerfil, idPermisoQuitado, idUsuarioModificador, "Removido", transaction); + } + // --- FIN DE LA LÓGICA DE AUDITORÍA --- + + + // 4. Actualizar la tabla principal (tu lógica actual) + await _perfilRepository.UpdatePermisosByPerfilIdAsync(idPerfil, idsNuevos, transaction); + transaction.Commit(); - _logger.LogInformation("Permisos actualizados para Perfil ID {IdPerfil} por Usuario ID {IdUsuarioModificador}.", idPerfil, idUsuarioModificador); + _logger.LogInformation("Permisos actualizados para Perfil ID {IdPerfil} por Usuario ID {IdUsuarioModificador}. Añadidos: {CountAdded}, Quitados: {CountRemoved}", + idPerfil, idUsuarioModificador, permisosAñadidos.Count, permisosQuitados.Count); + return (true, null); } catch (Exception ex) @@ -221,5 +235,46 @@ namespace GestionIntegral.Api.Services.Usuarios return (false, $"Error interno al actualizar los permisos del perfil: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPerfilAfectado) + { + var historialData = await _perfilRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPerfilAfectado); + + return historialData.Select(h => new PerfilHistorialDto + { + IdPerfil = h.Historial.IdPerfil, + Perfil = h.Historial.Perfil, + DescPerfil = h.Historial.DescPerfil, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } + + public async Task> ObtenerPermisosAsignadosHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPerfilAfectado, int? idPermisoAfectado) + { + var historialData = await _perfilRepository.GetPermisosAsignadosHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPerfilAfectado, idPermisoAfectado); + + return historialData.Select(h => new PermisosPerfilesHistorialDto + { + IdHistorial = h.Historial.Id, + IdPerfil = h.Historial.idPerfil, + NombrePerfil = h.NombrePerfil, + IdPermiso = h.Historial.idPermiso, + DescPermiso = h.DescPermiso, + CodAccPermiso = h.CodAccPermiso, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Services/Usuarios/PermisoService.cs b/Backend/GestionIntegral.Api/Services/Usuarios/PermisoService.cs index 93625f7..eeb2ae2 100644 --- a/Backend/GestionIntegral.Api/Services/Usuarios/PermisoService.cs +++ b/Backend/GestionIntegral.Api/Services/Usuarios/PermisoService.cs @@ -1,5 +1,6 @@ using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Usuarios; +using GestionIntegral.Api.Dtos.Auditoria; using GestionIntegral.Api.Dtos.Usuarios; using GestionIntegral.Api.Models.Usuarios; using Microsoft.Extensions.Logging; @@ -103,7 +104,7 @@ namespace GestionIntegral.Api.Services.Usuarios try { var actualizado = await _permisoRepository.UpdateAsync(permisoAActualizar, idUsuario, transaction); - if (!actualizado) throw new DataException("La operación de actualización no afectó ninguna fila."); + if (!actualizado) throw new DataException("La operación de actualización no afectó ninguna fila."); transaction.Commit(); _logger.LogInformation("Permiso ID {IdPermiso} actualizado por Usuario ID {IdUsuario}.", id, idUsuario); @@ -159,5 +160,26 @@ namespace GestionIntegral.Api.Services.Usuarios return (false, $"Error interno al eliminar el permiso: {ex.Message}"); } } + + public async Task> ObtenerHistorialAsync( + DateTime? fechaDesde, DateTime? fechaHasta, + int? idUsuarioModifico, string? tipoModificacion, + int? idPermisoAfectado) + { + var historialData = await _permisoRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPermisoAfectado); + + return historialData.Select(h => new PermisoHistorialDto + { + IdHistorial = h.Historial.IdHist, + IdPermiso = h.Historial.IdPermiso, + Modulo = h.Historial.Modulo, + DescPermiso = h.Historial.DescPermiso, + CodAcc = h.Historial.CodAcc, + Id_Usuario = h.Historial.Id_Usuario, + NombreUsuarioModifico = h.NombreUsuarioModifico, + FechaMod = h.Historial.FechaMod, + TipoMod = h.Historial.TipoMod + }).ToList(); + } } } \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/obj/Debug/net9.0/GestionIntegral.Api.AssemblyInfo.cs b/Backend/GestionIntegral.Api/obj/Debug/net9.0/GestionIntegral.Api.AssemblyInfo.cs index aa0ca02..e3d5eea 100644 --- a/Backend/GestionIntegral.Api/obj/Debug/net9.0/GestionIntegral.Api.AssemblyInfo.cs +++ b/Backend/GestionIntegral.Api/obj/Debug/net9.0/GestionIntegral.Api.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("GestionIntegral.Api")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+35e24ab7d2df29216a350a70ab6322c209221a4f")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+437b1e88641aca176cc46d68cee7c28d48eb88db")] [assembly: System.Reflection.AssemblyProductAttribute("GestionIntegral.Api")] [assembly: System.Reflection.AssemblyTitleAttribute("GestionIntegral.Api")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/Backend/GestionIntegral.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json b/Backend/GestionIntegral.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json index 3da0c04..c399b6a 100644 --- a/Backend/GestionIntegral.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json +++ b/Backend/GestionIntegral.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json @@ -1 +1 @@ -{"GlobalPropertiesHash":"C9goqBDGh4B0L1HpPwpJHjfbRNoIuzqnU7zFMHk1LhM=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["ZZivTt9Zh03vN/jzywHdSIjldJk\u002BW/DgTu7TlHDqhsY=","bxlPVWHR7EivQofjz9PzA8dMpKpZqCfOZ\u002BHD\u002Bf1Ew9Y=","\u002BzMwu5DIAA49kPmSydn2WMzj\u002Bdcf0MC3YakKoR6HwYg=","FUb20tYUiusFv5/KhAPdh2OB4ArUWiGApXbQJdx8tX0=","pTWqrhLBwEeWg1GsRlTKzfOAnT1JEklZ8F1/EYlc1Nk=","Hu0oNH4YYNcbnR5Ts4qd5yzC5j5JbY2kEDXces8V1vs=","TKMARE0bLM2dm9NOqxxWztnuqao5IvCh24TEHCtht6I=","84UEEMEbmmNwHVXD5Iw3dtKHTZC0Zqbk3rIRO\u002BxOq4o=","qfTzsJ\u002B5ilLyrc6EhNm61KkSH37yRi85MtgW1\u002BUD2Vo=","4ayt/JAApEOfr0yjg9szkYMPzSs6x2k3QEwmrK5RZVY=","d0weYwKWe3mH5R2BURuNLkAyytO/viA6zivv9AcIBtQ=","Ssyx6SvSGgWMOzhc9pQpk6f6\u002BmVbKQNKeDJbvVA2tjs=","FSqDybxILZmKXw160ANhj76usnM83geRrbPvJxr89OA=","k3qzLxTWHeeJhAuWKMdta6j24bmJ9BMRMjuFEEVCRu0=","x/sHyso3gy4zVCu3ljpnTYCqu8IGZNRok1JoXiabIP8=","fdI2RZZ9M9QOVHCYU5cE\u002BgVVuT7ssRbMzdXvX8rHofc=","8ePFhqKT0OT9nEg3b5T7COC81U\u002BQBcf\u002BindBGyMy6z0=","/ghcduGmSd1I25YtYli\u002BqxF0xuscxc4cTDkbEC6XYVA=","/a3YEu0oBUeA5Qr2VMdppqLuz4CQPWJt2JfBl2dtUwA=","jEO/q4IO3UFTWxlyFwRr7kbGWcTIiS\u002BClxx3kahX/Fk=","4iYOCKYvhsROdGkA1hINVBejb6r8IkwFj9SNMKub3DM=","CeDswsZIn5a7t\u002BKeHJA222yhFvDVVEW1ky98Xxnxebc=","50j34YXOc950QSqaQBMtgezD3tV5mWWR9c5qZcYQoz4=","W/aX9jIKpjNEVoGrU6RXFOY8SDJVT6XB4Rg4QCaeQkQ=","16IbB\u002B3zYHZvsWbCQK6hBFmKJ6Z28SecBn2jm8R3w8I=","COJtHNQqycTJqXkFv2hhpLUT\u002B/AD4IWyQlmxkUVQPNk=","cp6a5bdvkLnUn3x47KQODzPycnx57RmWO\u002B9q8MuoGQo=","oKZRNhIQRaZrETEa3L6JiwIp0\u002BmjzJo193EWBoCuVUg=","sjwbCAEQX51sEWhYVGBihWUNBxniUKZALVJIGK\u002BYgsk=","A4m4kVcox60bvdkJ1CswoZADAT70WPcs4TAKdpMoUjM=","zSzyOuNcK0NQJLwK8Yg4sH4EflX7RPf65Fl2CZUWIGs=","LSHkdvHGc9l/RgeSMksGWU1acjMl4KnCD0Uh6IcMfRo="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"GlobalPropertiesHash":"C9goqBDGh4B0L1HpPwpJHjfbRNoIuzqnU7zFMHk1LhM=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["ZZivTt9Zh03vN/jzywHdSIjldJk\u002BW/DgTu7TlHDqhsY=","bxlPVWHR7EivQofjz9PzA8dMpKpZqCfOZ\u002BHD\u002Bf1Ew9Y=","\u002BzMwu5DIAA49kPmSydn2WMzj\u002Bdcf0MC3YakKoR6HwYg=","FUb20tYUiusFv5/KhAPdh2OB4ArUWiGApXbQJdx8tX0=","pTWqrhLBwEeWg1GsRlTKzfOAnT1JEklZ8F1/EYlc1Nk=","Hu0oNH4YYNcbnR5Ts4qd5yzC5j5JbY2kEDXces8V1vs=","TKMARE0bLM2dm9NOqxxWztnuqao5IvCh24TEHCtht6I=","84UEEMEbmmNwHVXD5Iw3dtKHTZC0Zqbk3rIRO\u002BxOq4o=","qfTzsJ\u002B5ilLyrc6EhNm61KkSH37yRi85MtgW1\u002BUD2Vo=","4ayt/JAApEOfr0yjg9szkYMPzSs6x2k3QEwmrK5RZVY=","d0weYwKWe3mH5R2BURuNLkAyytO/viA6zivv9AcIBtQ=","Ssyx6SvSGgWMOzhc9pQpk6f6\u002BmVbKQNKeDJbvVA2tjs=","FSqDybxILZmKXw160ANhj76usnM83geRrbPvJxr89OA=","k3qzLxTWHeeJhAuWKMdta6j24bmJ9BMRMjuFEEVCRu0=","x/sHyso3gy4zVCu3ljpnTYCqu8IGZNRok1JoXiabIP8=","fdI2RZZ9M9QOVHCYU5cE\u002BgVVuT7ssRbMzdXvX8rHofc=","8ePFhqKT0OT9nEg3b5T7COC81U\u002BQBcf\u002BindBGyMy6z0=","/ghcduGmSd1I25YtYli\u002BqxF0xuscxc4cTDkbEC6XYVA=","/a3YEu0oBUeA5Qr2VMdppqLuz4CQPWJt2JfBl2dtUwA=","jEO/q4IO3UFTWxlyFwRr7kbGWcTIiS\u002BClxx3kahX/Fk=","4iYOCKYvhsROdGkA1hINVBejb6r8IkwFj9SNMKub3DM=","CeDswsZIn5a7t\u002BKeHJA222yhFvDVVEW1ky98Xxnxebc=","50j34YXOc950QSqaQBMtgezD3tV5mWWR9c5qZcYQoz4=","W/aX9jIKpjNEVoGrU6RXFOY8SDJVT6XB4Rg4QCaeQkQ=","16IbB\u002B3zYHZvsWbCQK6hBFmKJ6Z28SecBn2jm8R3w8I=","COJtHNQqycTJqXkFv2hhpLUT\u002B/AD4IWyQlmxkUVQPNk=","cp6a5bdvkLnUn3x47KQODzPycnx57RmWO\u002B9q8MuoGQo=","oKZRNhIQRaZrETEa3L6JiwIp0\u002BmjzJo193EWBoCuVUg=","sjwbCAEQX51sEWhYVGBihWUNBxniUKZALVJIGK\u002BYgsk=","A4m4kVcox60bvdkJ1CswoZADAT70WPcs4TAKdpMoUjM=","zSzyOuNcK0NQJLwK8Yg4sH4EflX7RPf65Fl2CZUWIGs=","flM0K9XRNNYylZG0CGbM3aCgbKpYSYF47xY41qCjtsY="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json b/Backend/GestionIntegral.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json index 950942a..de8e31a 100644 --- a/Backend/GestionIntegral.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json +++ b/Backend/GestionIntegral.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json @@ -1 +1 @@ -{"GlobalPropertiesHash":"w3MBbMV9Msh0YEq9AW/8s16bzXJ93T9lMVXKPm/r6es=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["ZZivTt9Zh03vN/jzywHdSIjldJk\u002BW/DgTu7TlHDqhsY=","bxlPVWHR7EivQofjz9PzA8dMpKpZqCfOZ\u002BHD\u002Bf1Ew9Y=","\u002BzMwu5DIAA49kPmSydn2WMzj\u002Bdcf0MC3YakKoR6HwYg=","FUb20tYUiusFv5/KhAPdh2OB4ArUWiGApXbQJdx8tX0=","pTWqrhLBwEeWg1GsRlTKzfOAnT1JEklZ8F1/EYlc1Nk=","Hu0oNH4YYNcbnR5Ts4qd5yzC5j5JbY2kEDXces8V1vs=","TKMARE0bLM2dm9NOqxxWztnuqao5IvCh24TEHCtht6I=","84UEEMEbmmNwHVXD5Iw3dtKHTZC0Zqbk3rIRO\u002BxOq4o=","qfTzsJ\u002B5ilLyrc6EhNm61KkSH37yRi85MtgW1\u002BUD2Vo=","4ayt/JAApEOfr0yjg9szkYMPzSs6x2k3QEwmrK5RZVY=","d0weYwKWe3mH5R2BURuNLkAyytO/viA6zivv9AcIBtQ=","Ssyx6SvSGgWMOzhc9pQpk6f6\u002BmVbKQNKeDJbvVA2tjs=","FSqDybxILZmKXw160ANhj76usnM83geRrbPvJxr89OA=","k3qzLxTWHeeJhAuWKMdta6j24bmJ9BMRMjuFEEVCRu0=","x/sHyso3gy4zVCu3ljpnTYCqu8IGZNRok1JoXiabIP8=","fdI2RZZ9M9QOVHCYU5cE\u002BgVVuT7ssRbMzdXvX8rHofc=","8ePFhqKT0OT9nEg3b5T7COC81U\u002BQBcf\u002BindBGyMy6z0=","/ghcduGmSd1I25YtYli\u002BqxF0xuscxc4cTDkbEC6XYVA=","/a3YEu0oBUeA5Qr2VMdppqLuz4CQPWJt2JfBl2dtUwA=","jEO/q4IO3UFTWxlyFwRr7kbGWcTIiS\u002BClxx3kahX/Fk=","4iYOCKYvhsROdGkA1hINVBejb6r8IkwFj9SNMKub3DM=","CeDswsZIn5a7t\u002BKeHJA222yhFvDVVEW1ky98Xxnxebc=","50j34YXOc950QSqaQBMtgezD3tV5mWWR9c5qZcYQoz4=","W/aX9jIKpjNEVoGrU6RXFOY8SDJVT6XB4Rg4QCaeQkQ=","16IbB\u002B3zYHZvsWbCQK6hBFmKJ6Z28SecBn2jm8R3w8I=","COJtHNQqycTJqXkFv2hhpLUT\u002B/AD4IWyQlmxkUVQPNk=","cp6a5bdvkLnUn3x47KQODzPycnx57RmWO\u002B9q8MuoGQo=","oKZRNhIQRaZrETEa3L6JiwIp0\u002BmjzJo193EWBoCuVUg=","sjwbCAEQX51sEWhYVGBihWUNBxniUKZALVJIGK\u002BYgsk=","A4m4kVcox60bvdkJ1CswoZADAT70WPcs4TAKdpMoUjM=","zSzyOuNcK0NQJLwK8Yg4sH4EflX7RPf65Fl2CZUWIGs=","LSHkdvHGc9l/RgeSMksGWU1acjMl4KnCD0Uh6IcMfRo="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"GlobalPropertiesHash":"w3MBbMV9Msh0YEq9AW/8s16bzXJ93T9lMVXKPm/r6es=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["ZZivTt9Zh03vN/jzywHdSIjldJk\u002BW/DgTu7TlHDqhsY=","bxlPVWHR7EivQofjz9PzA8dMpKpZqCfOZ\u002BHD\u002Bf1Ew9Y=","\u002BzMwu5DIAA49kPmSydn2WMzj\u002Bdcf0MC3YakKoR6HwYg=","FUb20tYUiusFv5/KhAPdh2OB4ArUWiGApXbQJdx8tX0=","pTWqrhLBwEeWg1GsRlTKzfOAnT1JEklZ8F1/EYlc1Nk=","Hu0oNH4YYNcbnR5Ts4qd5yzC5j5JbY2kEDXces8V1vs=","TKMARE0bLM2dm9NOqxxWztnuqao5IvCh24TEHCtht6I=","84UEEMEbmmNwHVXD5Iw3dtKHTZC0Zqbk3rIRO\u002BxOq4o=","qfTzsJ\u002B5ilLyrc6EhNm61KkSH37yRi85MtgW1\u002BUD2Vo=","4ayt/JAApEOfr0yjg9szkYMPzSs6x2k3QEwmrK5RZVY=","d0weYwKWe3mH5R2BURuNLkAyytO/viA6zivv9AcIBtQ=","Ssyx6SvSGgWMOzhc9pQpk6f6\u002BmVbKQNKeDJbvVA2tjs=","FSqDybxILZmKXw160ANhj76usnM83geRrbPvJxr89OA=","k3qzLxTWHeeJhAuWKMdta6j24bmJ9BMRMjuFEEVCRu0=","x/sHyso3gy4zVCu3ljpnTYCqu8IGZNRok1JoXiabIP8=","fdI2RZZ9M9QOVHCYU5cE\u002BgVVuT7ssRbMzdXvX8rHofc=","8ePFhqKT0OT9nEg3b5T7COC81U\u002BQBcf\u002BindBGyMy6z0=","/ghcduGmSd1I25YtYli\u002BqxF0xuscxc4cTDkbEC6XYVA=","/a3YEu0oBUeA5Qr2VMdppqLuz4CQPWJt2JfBl2dtUwA=","jEO/q4IO3UFTWxlyFwRr7kbGWcTIiS\u002BClxx3kahX/Fk=","4iYOCKYvhsROdGkA1hINVBejb6r8IkwFj9SNMKub3DM=","CeDswsZIn5a7t\u002BKeHJA222yhFvDVVEW1ky98Xxnxebc=","50j34YXOc950QSqaQBMtgezD3tV5mWWR9c5qZcYQoz4=","W/aX9jIKpjNEVoGrU6RXFOY8SDJVT6XB4Rg4QCaeQkQ=","16IbB\u002B3zYHZvsWbCQK6hBFmKJ6Z28SecBn2jm8R3w8I=","COJtHNQqycTJqXkFv2hhpLUT\u002B/AD4IWyQlmxkUVQPNk=","cp6a5bdvkLnUn3x47KQODzPycnx57RmWO\u002B9q8MuoGQo=","oKZRNhIQRaZrETEa3L6JiwIp0\u002BmjzJo193EWBoCuVUg=","sjwbCAEQX51sEWhYVGBihWUNBxniUKZALVJIGK\u002BYgsk=","A4m4kVcox60bvdkJ1CswoZADAT70WPcs4TAKdpMoUjM=","zSzyOuNcK0NQJLwK8Yg4sH4EflX7RPf65Fl2CZUWIGs=","flM0K9XRNNYylZG0CGbM3aCgbKpYSYF47xY41qCjtsY="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/Frontend/src/components/Modals/Contables/NotaCreditoDebitoFormModal.tsx b/Frontend/src/components/Modals/Contables/NotaCreditoDebitoFormModal.tsx index 5ffb898..13726a1 100644 --- a/Frontend/src/components/Modals/Contables/NotaCreditoDebitoFormModal.tsx +++ b/Frontend/src/components/Modals/Contables/NotaCreditoDebitoFormModal.tsx @@ -229,7 +229,7 @@ const NotaCreditoDebitoFormModal: React.FC = ({ margin="dense" fullWidth error={!!localErrors.fecha} helperText={localErrors.fecha || ''} disabled={loading || isEditing} InputLabelProps={{ shrink: true }} autoFocus={!isEditing} /> - setReferencia(e.target.value)} margin="dense" fullWidth disabled={loading || isEditing} /> @@ -244,10 +244,10 @@ const NotaCreditoDebitoFormModal: React.FC = ({ {setMonto(e.target.value); handleInputChange('monto');}} margin="dense" fullWidth error={!!localErrors.monto} helperText={localErrors.monto || ''} - disabled={loading} inputProps={{step: "0.01", min:0.01, lang:"es-AR" }} + disabled={loading} inputProps={{step: "0.01", min:0.05, lang:"es-AR" }} InputProps={{ startAdornment: $ }} /> - setObservaciones(e.target.value)} margin="dense" fullWidth multiline rows={2} disabled={loading} /> diff --git a/Frontend/src/components/Modals/Contables/PagoDistribuidorFormModal.tsx b/Frontend/src/components/Modals/Contables/PagoDistribuidorFormModal.tsx index 3c3da71..20ac026 100644 --- a/Frontend/src/components/Modals/Contables/PagoDistribuidorFormModal.tsx +++ b/Frontend/src/components/Modals/Contables/PagoDistribuidorFormModal.tsx @@ -210,7 +210,7 @@ const PagoDistribuidorFormModal: React.FC = ({ {setMonto(e.target.value); handleInputChange('monto');}} margin="dense" fullWidth error={!!localErrors.monto} helperText={localErrors.monto || ''} - disabled={loading} inputProps={{step: "0.01", min:0.01, lang:"es-AR" }} + disabled={loading} inputProps={{step: "0.01", min:0.05, lang:"es-AR" }} InputProps={{ startAdornment: $ }} /> diff --git a/Frontend/src/components/Modals/Distribucion/PrecioFormModal.tsx b/Frontend/src/components/Modals/Distribucion/PrecioFormModal.tsx index 2329d1e..9085ec6 100644 --- a/Frontend/src/components/Modals/Distribucion/PrecioFormModal.tsx +++ b/Frontend/src/components/Modals/Distribucion/PrecioFormModal.tsx @@ -1,25 +1,25 @@ import React, { useState, useEffect } from 'react'; import { - Modal, Box, Typography, TextField, Button, CircularProgress, Alert, - InputAdornment - // Quitar Grid si no se usa + Modal, Box, Typography, TextField, Button, CircularProgress, Alert, + InputAdornment + // Quitar Grid si no se usa } from '@mui/material'; import type { PrecioDto } from '../../../models/dtos/Distribucion/PrecioDto'; import type { CreatePrecioDto } from '../../../models/dtos/Distribucion/CreatePrecioDto'; import type { UpdatePrecioDto } from '../../../models/dtos/Distribucion/UpdatePrecioDto'; const modalStyle = { - position: 'absolute' as 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: { xs: '95%', sm: '80%', md: '700px' }, - bgcolor: 'background.paper', - border: '2px solid #000', - boxShadow: 24, - p: 4, - maxHeight: '90vh', - overflowY: 'auto' + position: 'absolute' as 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: { xs: '95%', sm: '80%', md: '700px' }, + bgcolor: 'background.paper', + border: '2px solid #000', + boxShadow: 24, + p: 4, + maxHeight: '90vh', + overflowY: 'auto' }; const diasSemana = ["Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo"] as const; @@ -55,48 +55,50 @@ const PrecioFormModal: React.FC = ({ const isEditing = Boolean(initialData); useEffect(() => { - if (open) { - setVigenciaD(initialData?.vigenciaD || ''); - setVigenciaH(initialData?.vigenciaH || ''); - const initialPrecios: Record = { Lunes: '', Martes: '', Miercoles: '', Jueves: '', Viernes: '', Sabado: '', Domingo: '' }; - if (initialData) { - diasSemana.forEach(dia => { - const key = dia.toLowerCase() as keyof Omit; - initialPrecios[dia] = initialData[key]?.toString() || ''; - }); - } - setPreciosDia(initialPrecios); - setLocalErrors({}); - clearErrorMessage(); + // Este efecto se ejecuta cuando el modal se abre o cuando los datos iniciales cambian + if (open) { + setVigenciaD(initialData?.vigenciaD || ''); + setVigenciaH(initialData?.vigenciaH || ''); + const initialPrecios: Record = { Lunes: '', Martes: '', Miercoles: '', Jueves: '', Viernes: '', Sabado: '', Domingo: '' }; + if (initialData) { + diasSemana.forEach(dia => { + const key = dia.toLowerCase() as keyof Omit; + initialPrecios[dia] = initialData[key]?.toString() || ''; + }); } - }, [open, initialData, clearErrorMessage]); + setPreciosDia(initialPrecios); + setLocalErrors({}); + // NO llamar a clearErrorMessage aquí automáticamente al abrir si ya hay un error de un intento previo. + // Solo limpiar si es una "nueva" apertura (ej, initialData cambió o se abrió desde cerrado) + } +}, [open, initialData]); const validate = (): boolean => { const errors: { [key: string]: string | null } = {}; if (!isEditing && !vigenciaD.trim()) { - errors.vigenciaD = 'La Vigencia Desde es obligatoria.'; + errors.vigenciaD = 'La Vigencia Desde es obligatoria.'; } else if (vigenciaD.trim() && !/^\d{4}-\d{2}-\d{2}$/.test(vigenciaD)) { - errors.vigenciaD = 'Formato de fecha inválido (YYYY-MM-DD).'; + errors.vigenciaD = 'Formato de fecha inválido (YYYY-MM-DD).'; } if (vigenciaH.trim() && !/^\d{4}-\d{2}-\d{2}$/.test(vigenciaH)) { - errors.vigenciaH = 'Formato de fecha inválido (YYYY-MM-DD).'; + errors.vigenciaH = 'Formato de fecha inválido (YYYY-MM-DD).'; } else if (vigenciaH.trim() && vigenciaD.trim() && new Date(vigenciaH) < new Date(vigenciaD)) { - errors.vigenciaH = 'Vigencia Hasta no puede ser anterior a Vigencia Desde.'; + errors.vigenciaH = 'Vigencia Hasta no puede ser anterior a Vigencia Desde.'; } let alMenosUnPrecio = false; diasSemana.forEach(dia => { - const valor = preciosDia[dia]; - if (valor.trim()) { - alMenosUnPrecio = true; - if (isNaN(parseFloat(valor)) || parseFloat(valor) < 0) { - errors[dia.toLowerCase()] = `Precio de ${dia} inválido.`; - } + const valor = preciosDia[dia]; + if (valor.trim()) { + alMenosUnPrecio = true; + if (isNaN(parseFloat(valor)) || parseFloat(valor) < 0) { + errors[dia.toLowerCase()] = `Precio de ${dia} inválido.`; } + } }); if (!isEditing && !alMenosUnPrecio) { - errors.dias = 'Debe ingresar al menos un precio para un día de la semana.'; + errors.dias = 'Debe ingresar al menos un precio para un día de la semana.'; } setLocalErrors(errors); @@ -106,53 +108,59 @@ const PrecioFormModal: React.FC = ({ const handlePrecioDiaChange = (dia: DiaSemana, value: string) => { setPreciosDia(prev => ({ ...prev, [dia]: value })); if (localErrors[dia.toLowerCase()] || localErrors.dias) { - setLocalErrors(prev => ({ ...prev, [dia.toLowerCase()]: null, dias: null })); + setLocalErrors(prev => ({ ...prev, [dia.toLowerCase()]: null, dias: null })); } if (errorMessage) clearErrorMessage(); }; const handleDateChange = (setter: React.Dispatch>, fieldName: string) => (e: React.ChangeEvent) => { setter(e.target.value); if (localErrors[fieldName]) { - setLocalErrors(prev => ({ ...prev, [fieldName]: null })); + setLocalErrors(prev => ({ ...prev, [fieldName]: null })); } if (errorMessage) clearErrorMessage(); }; const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); - clearErrorMessage(); + clearErrorMessage(); // Limpiar al inicio de un nuevo intento de submit if (!validate()) return; setLoading(true); + let success = false; // Bandera para controlar el cierre try { const preciosNumericos: Partial, number | null>> = {}; - diasSemana.forEach(dia => { - const valor = preciosDia[dia].trim(); - // Convertir el nombre del día a la clave correcta del DTO (ej. "Lunes" -> "lunes") - const key = dia.toLowerCase() as keyof typeof preciosNumericos; - preciosNumericos[key] = valor ? parseFloat(valor) : null; - }); - + diasSemana.forEach(dia => { + const valor = preciosDia[dia].trim(); + const key = dia.toLowerCase() as keyof typeof preciosNumericos; + preciosNumericos[key] = valor ? parseFloat(valor) : null; + }); if (isEditing && initialData) { const dataToSubmit: UpdatePrecioDto = { - vigenciaH: vigenciaH.trim() ? vigenciaH : null, - ...preciosNumericos // Spread de los precios de los días + vigenciaH: vigenciaH.trim() ? vigenciaH.split('T')[0] : null, + ...preciosNumericos }; await onSubmit(dataToSubmit, initialData.idPrecio); } else { const dataToSubmit: CreatePrecioDto = { - idPublicacion, - vigenciaD, - ...preciosNumericos + idPublicacion, + vigenciaD, + ...preciosNumericos }; await onSubmit(dataToSubmit); } - onClose(); + success = true; // Marcar como exitoso si no hubo excepciones } catch (error: any) { - console.error("Error en submit de PrecioFormModal:", error); + // El error ya fue seteado en el padre (GestionarPreciosPublicacionPage) + // y se pasa como prop 'errorMessage' a este modal. + // No necesitamos setearlo aquí de nuevo. + console.error("Error propagado al submit de PrecioFormModal:", error); + success = false; } finally { - setLoading(false); + setLoading(false); + if (success) { + onClose(); + } } }; @@ -163,51 +171,52 @@ const PrecioFormModal: React.FC = ({ {isEditing ? 'Editar Período de Precio' : 'Agregar Nuevo Período de Precio'} - {/* Sección de Vigencias */} - - - {isEditing && ( - - )} - + {/* Sección de Vigencias */} + + + {isEditing && ( + + )} + - {/* Sección de Precios por Día con Flexbox */} - Precios por Día: - - {diasSemana.map(dia => ( - - {/* El ajuste de -Xpx en flexBasis es aproximado para compensar el gap. + {/* Sección de Precios por Día con Flexbox */} + Precios por Día: + + {diasSemana.map(dia => ( + + {/* El ajuste de -Xpx en flexBasis es aproximado para compensar el gap. Para 3 columnas (33.33%): gap es 16px, se distribuye entre 2 espacios -> 16/2 * 2/3 = ~11px Para 4 columnas (25%): gap es 16px, se distribuye entre 3 espacios -> 16/3 * 3/4 = 12px */} - handlePrecioDiaChange(dia, e.target.value)} - margin="dense" fullWidth - error={!!localErrors[dia.toLowerCase()]} helperText={localErrors[dia.toLowerCase()] || ''} - disabled={loading} - InputProps={{ startAdornment: $ }} - inputProps={{ step: "0.01", lang:"es-AR" }} - /> - - ))} - - {localErrors.dias && {localErrors.dias}} + handlePrecioDiaChange(dia, e.target.value)} + margin="dense" fullWidth + error={!!localErrors[dia.toLowerCase()]} helperText={localErrors[dia.toLowerCase()] || ''} + disabled={loading} + InputProps={{ startAdornment: $ }} + inputProps={{ step: "0.01", lang: "es-AR" }} + /> + + ))} + + {localErrors.dias && {localErrors.dias}} + {/* Mostrar error de la API si existe (pasado desde el padre) */} {errorMessage && {errorMessage}} diff --git a/Frontend/src/components/Modals/Distribucion/RecargoZonaFormModal.tsx b/Frontend/src/components/Modals/Distribucion/RecargoZonaFormModal.tsx index 96b0eed..97cd323 100644 --- a/Frontend/src/components/Modals/Distribucion/RecargoZonaFormModal.tsx +++ b/Frontend/src/components/Modals/Distribucion/RecargoZonaFormModal.tsx @@ -1,3 +1,4 @@ +// src/components/Modals/Distribucion/RecargoZonaFormModal.tsx import React, { useState, useEffect } from 'react'; import { Modal, Box, Typography, TextField, Button, CircularProgress, Alert, @@ -6,10 +7,10 @@ import { import type { RecargoZonaDto } from '../../../models/dtos/Distribucion/RecargoZonaDto'; import type { CreateRecargoZonaDto } from '../../../models/dtos/Distribucion/CreateRecargoZonaDto'; import type { UpdateRecargoZonaDto } from '../../../models/dtos/Distribucion/UpdateRecargoZonaDto'; -import type { ZonaDto } from '../../../models/dtos/Zonas/ZonaDto'; // Para el dropdown de zonas -import zonaService from '../../../services/Distribucion/zonaService'; // Para cargar zonas +import type { ZonaDto } from '../../../models/dtos/Zonas/ZonaDto'; +import zonaService from '../../../services/Distribucion/zonaService'; -const modalStyle = { /* ... (mismo estilo) ... */ +const modalStyle = { position: 'absolute' as 'absolute', top: '50%', left: '50%', @@ -28,7 +29,7 @@ interface RecargoZonaFormModalProps { onClose: () => void; onSubmit: (data: CreateRecargoZonaDto | UpdateRecargoZonaDto, idRecargo?: number) => Promise; idPublicacion: number; - initialData?: RecargoZonaDto | null; // Para editar + initialData?: RecargoZonaDto | null; errorMessage?: string | null; clearErrorMessage: () => void; } @@ -39,12 +40,12 @@ const RecargoZonaFormModal: React.FC = ({ onSubmit, idPublicacion, initialData, - errorMessage, + errorMessage, // Esta prop viene del padre (GestionarRecargosPublicacionPage) clearErrorMessage }) => { const [idZona, setIdZona] = useState(''); - const [vigenciaD, setVigenciaD] = useState(''); // "yyyy-MM-dd" - const [vigenciaH, setVigenciaH] = useState(''); // "yyyy-MM-dd" + const [vigenciaD, setVigenciaD] = useState(''); + const [vigenciaH, setVigenciaH] = useState(''); const [valor, setValor] = useState(''); const [zonas, setZonas] = useState([]); @@ -58,7 +59,7 @@ const RecargoZonaFormModal: React.FC = ({ const fetchZonas = async () => { setLoadingZonas(true); try { - const data = await zonaService.getAllZonas(); // Asume que devuelve zonas activas + const data = await zonaService.getAllZonas(); setZonas(data); } catch (error) { console.error("Error al cargar zonas", error); @@ -75,11 +76,13 @@ const RecargoZonaFormModal: React.FC = ({ setVigenciaH(initialData?.vigenciaH || ''); setValor(initialData?.valor?.toString() || ''); setLocalErrors({}); - clearErrorMessage(); + // No limpiar errorMessage aquí si queremos que persista un error de submit anterior + // clearErrorMessage(); // Se limpia al abrir desde la página o al inicio de un nuevo submit } - }, [open, initialData, clearErrorMessage]); + }, [open, initialData]); // Quitar clearErrorMessage de aquí para que no limpie el error del padre en cada re-render por 'open' const validate = (): boolean => { + // ... (lógica de validación sin cambios) const errors: { [key: string]: string | null } = {}; if (!idZona) errors.idZona = 'Debe seleccionar una zona.'; if (!isEditing && !vigenciaD.trim()) { @@ -102,38 +105,46 @@ const RecargoZonaFormModal: React.FC = ({ const handleInputChange = (fieldName: string) => { if (localErrors[fieldName]) setLocalErrors(prev => ({ ...prev, [fieldName]: null })); - if (errorMessage) clearErrorMessage(); + if (errorMessage) clearErrorMessage(); // Limpiar error del padre si el usuario modifica un campo }; const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); - clearErrorMessage(); + clearErrorMessage(); // Limpiar errores del padre antes de un nuevo intento if (!validate()) return; setLoading(true); + let success = false; // Bandera para controlar el cierre del modal try { const valorNum = parseFloat(valor); if (isEditing && initialData) { const dataToSubmit: UpdateRecargoZonaDto = { valor: valorNum, - vigenciaH: vigenciaH.trim() ? vigenciaH : null, + vigenciaH: vigenciaH.trim() ? vigenciaH.split('T')[0] : null, // Enviar solo fecha o null }; await onSubmit(dataToSubmit, initialData.idRecargo); } else { const dataToSubmit: CreateRecargoZonaDto = { idPublicacion, idZona: Number(idZona), - vigenciaD, + vigenciaD: vigenciaD.split('T')[0], // Enviar solo fecha valor: valorNum, }; await onSubmit(dataToSubmit); } - onClose(); + success = true; // Marcar como exitoso si no hubo excepciones } catch (error: any) { - console.error("Error en submit de RecargoZonaFormModal:", error); + // El error de la API ya fue seteado en la página padre (GestionarRecargosPublicacionPage) + // y se pasa a este modal a través de la prop 'errorMessage'. + // No necesitamos hacer setApiErrorMessage aquí. + console.error("Error propagado al submit de RecargoZonaFormModal:", error); + success = false; // Marcar como no exitoso } finally { setLoading(false); + if (success) { // Solo cerrar si la operación fue exitosa + onClose(); + } } }; @@ -148,7 +159,7 @@ const RecargoZonaFormModal: React.FC = ({ Zona {setNuevoEstadoId(e.target.value as number); handleInputChange('nuevoEstadoId');}} + + Nuevo Estado + {setIdPublicacion(e.target.value as number); handleInputChange('idPublicacion');}} - disabled={loading || loadingDropdowns} + disabled={loading || loadingDropdowns || publicacionesDisponibles.length === 0} > Seleccione publicación {publicacionesDisponibles.map((p) => ({p.nombre}))} {localErrors.idPublicacion && {localErrors.idPublicacion}} - - Sección + + Sección {localErrors.idSeccion && {localErrors.idSeccion}} @@ -248,7 +290,8 @@ const StockBobinaCambioEstadoModal: React.FC - diff --git a/Frontend/src/components/Modals/Impresion/StockBobinaEditFormModal.tsx b/Frontend/src/components/Modals/Impresion/StockBobinaEditFormModal.tsx index 3fd3363..d7e7352 100644 --- a/Frontend/src/components/Modals/Impresion/StockBobinaEditFormModal.tsx +++ b/Frontend/src/components/Modals/Impresion/StockBobinaEditFormModal.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { - Modal, Box, Typography, TextField, Button, CircularProgress, Alert, - FormControl, InputLabel, Select, MenuItem + Modal, Box, Typography, TextField, Button, CircularProgress, Alert, + FormControl, InputLabel, Select, MenuItem } from '@mui/material'; import type { StockBobinaDto } from '../../../models/dtos/Impresion/StockBobinaDto'; import type { UpdateStockBobinaDto } from '../../../models/dtos/Impresion/UpdateStockBobinaDto'; @@ -11,17 +11,17 @@ import tipoBobinaService from '../../../services/Impresion/tipoBobinaService'; import plantaService from '../../../services/Impresion/plantaService'; const modalStyle = { /* ... (mismo estilo que StockBobinaIngresoFormModal) ... */ - position: 'absolute' as 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: { xs: '90%', sm: 550 }, - bgcolor: 'background.paper', - border: '2px solid #000', - boxShadow: 24, - p: 4, - maxHeight: '90vh', - overflowY: 'auto' + position: 'absolute' as 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: { xs: '90%', sm: 550 }, + bgcolor: 'background.paper', + border: '2px solid #000', + boxShadow: 24, + p: 4, + maxHeight: '90vh', + overflowY: 'auto' }; interface StockBobinaEditFormModalProps { @@ -56,34 +56,43 @@ const StockBobinaEditFormModal: React.FC = ({ useEffect(() => { const fetchDropdownData = async () => { - setLoadingDropdowns(true); - try { - const [tiposData, plantasData] = await Promise.all([ - tipoBobinaService.getAllTiposBobina(), - plantaService.getAllPlantas() - ]); - setTiposBobina(tiposData); - setPlantas(plantasData); - } catch (error) { - console.error("Error al cargar datos para dropdowns (StockBobina Edit)", error); - setLocalErrors(prev => ({...prev, dropdowns: 'Error al cargar tipos/plantas.'})); - } finally { - setLoadingDropdowns(false); - } + setLoadingDropdowns(true); // Ya está arriba, pero por claridad + try { + // --- CAMBIO: Usar los servicios que devuelven DTOs de dropdown si los tienes --- + const [tiposData, plantasData] = await Promise.all([ + tipoBobinaService.getAllTiposBobina(), // Asume que existe y devuelve TipoBobinaDropdownDto[] + plantaService.getAllPlantas() // Asume que existe y devuelve PlantaDropdownDto[] + ]); + setTiposBobina(tiposData); + setPlantas(plantasData); + } catch (error) { + console.error("Error al cargar datos para dropdowns (StockBobina Edit)", error); + setLocalErrors(prev => ({ ...prev, dropdowns: 'Error al cargar tipos/plantas.' })); + } finally { + setLoadingDropdowns(false); + } }; - - if (open && initialData) { - fetchDropdownData(); - setIdTipoBobina(initialData.idTipoBobina || ''); - setNroBobina(initialData.nroBobina || ''); - setPeso(initialData.peso?.toString() || ''); - setIdPlanta(initialData.idPlanta || ''); - setRemito(initialData.remito || ''); - setFechaRemito(initialData.fechaRemito || ''); // Asume yyyy-MM-dd del DTO - setLocalErrors({}); - clearErrorMessage(); + if (open) { // Solo fetchear si el modal está abierto + fetchDropdownData(); } - }, [open, initialData, clearErrorMessage]); + }, [open]); + + useEffect(() => { + if (open && initialData && !loadingDropdowns) { // <<--- ESPERAR A QUE loadingDropdowns SEA FALSE + setIdTipoBobina(initialData.idTipoBobina || ''); + setNroBobina(initialData.nroBobina || ''); + setPeso(initialData.peso?.toString() || ''); + setIdPlanta(initialData.idPlanta || ''); + setRemito(initialData.remito || ''); + setFechaRemito(initialData.fechaRemito ? initialData.fechaRemito.split('T')[0] : ''); // Formatear si es necesario + setLocalErrors({}); + clearErrorMessage(); + } else if (open && !initialData) { // Si se abre para crear (aunque este modal es de edición) + // Resetear o manejar como prefieras + setIdTipoBobina(''); setNroBobina(''); setPeso(''); setIdPlanta(''); + setRemito(''); setFechaRemito(new Date().toISOString().split('T')[0]); + } + }, [open, initialData, loadingDropdowns, clearErrorMessage]); // Añadir loadingDropdowns const validate = (): boolean => { const errors: { [key: string]: string | null } = {}; @@ -111,12 +120,12 @@ const StockBobinaEditFormModal: React.FC = ({ setLoading(true); try { const dataToSubmit: UpdateStockBobinaDto = { - idTipoBobina: Number(idTipoBobina), - nroBobina, - peso: parseInt(peso, 10), - idPlanta: Number(idPlanta), - remito, - fechaRemito, + idTipoBobina: Number(idTipoBobina), + nroBobina, + peso: parseInt(peso, 10), + idPlanta: Number(idPlanta), + remito, + fechaRemito, }; await onSubmit(initialData.idBobina, dataToSubmit); onClose(); @@ -124,80 +133,91 @@ const StockBobinaEditFormModal: React.FC = ({ console.error("Error en submit de StockBobinaEditFormModal:", error); // El error de API lo maneja la página que llama a este modal } finally { - setLoading(false); + setLoading(false); } }; - if (!initialData) return null; // No renderizar si no hay datos iniciales (aunque open lo controla) + if (!initialData && open) { // Si se abre sin initialData (no debería pasar para un modal de EDICIÓN) + return Error: No se proporcionaron datos iniciales para editar.; + } + if (!open) return null; // No renderizar nada si no está abierto return ( - Editar Datos de Bobina (ID: {initialData.idBobina}) + Editar Datos de Bobina (ID: {initialData?.idBobina || 'N/A'}) {/* Usar initialData?.idBobina */} - - - - - Tipo Bobina - - {localErrors.idTipoBobina && {localErrors.idTipoBobina}} - - {setNroBobina(e.target.value); handleInputChange('nroBobina');}} - margin="dense" fullWidth error={!!localErrors.nroBobina} helperText={localErrors.nroBobina || ''} - disabled={loading} sx={{flex:1, minWidth: '200px'}} autoFocus - /> - - - {setPeso(e.target.value); handleInputChange('peso');}} - margin="dense" fullWidth error={!!localErrors.peso} helperText={localErrors.peso || ''} - disabled={loading} sx={{flex:1, minWidth: '150px'}} - /> - - Planta Destino - - {localErrors.idPlanta && {localErrors.idPlanta}} - - - - {setRemito(e.target.value); handleInputChange('remito');}} - margin="dense" fullWidth error={!!localErrors.remito} helperText={localErrors.remito || ''} - disabled={loading} sx={{flex:1, minWidth: '200px'}} - /> - {setFechaRemito(e.target.value); handleInputChange('fechaRemito');}} - margin="dense" fullWidth error={!!localErrors.fechaRemito} helperText={localErrors.fechaRemito || ''} - disabled={loading} InputLabelProps={{ shrink: true }} sx={{flex:1, minWidth: '200px'}} - /> - + {loadingDropdowns ? : ( + + + + + Tipo Bobina + + {localErrors.idTipoBobina && {localErrors.idTipoBobina}} + + { setNroBobina(e.target.value); handleInputChange('nroBobina'); }} + margin="dense" fullWidth error={!!localErrors.nroBobina} helperText={localErrors.nroBobina || ''} + disabled={loading} sx={{ flex: 1, minWidth: '200px' }} autoFocus + /> + + + { setPeso(e.target.value); handleInputChange('peso'); }} + margin="dense" fullWidth error={!!localErrors.peso} helperText={localErrors.peso || ''} + disabled={loading} sx={{ flex: 1, minWidth: '150px' }} + /> + + Planta Destino + + {localErrors.idPlanta && {localErrors.idPlanta}} + + + + { setRemito(e.target.value); handleInputChange('remito'); }} + margin="dense" fullWidth error={!!localErrors.remito} helperText={localErrors.remito || ''} + disabled={loading} sx={{ flex: 1, minWidth: '200px' }} + /> + { setFechaRemito(e.target.value); handleInputChange('fechaRemito'); }} + margin="dense" fullWidth error={!!localErrors.fechaRemito} helperText={localErrors.fechaRemito || ''} + disabled={loading} InputLabelProps={{ shrink: true }} sx={{ flex: 1, minWidth: '200px' }} + /> + - {errorMessage && {errorMessage}} - {localErrors.dropdowns && {localErrors.dropdowns}} + {errorMessage && {errorMessage}} + {localErrors.dropdowns && {localErrors.dropdowns}} - - - + + + + - + )} ); diff --git a/Frontend/src/models/dtos/Auditoria/CambioParadaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/CambioParadaHistorialDto.ts new file mode 100644 index 0000000..a77c372 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/CambioParadaHistorialDto.ts @@ -0,0 +1,15 @@ +export interface CambioParadaHistorialDto { + id_Registro: number; + id_Canilla: number; + nombreCanilla: string; + parada: string; + vigenciaD: string; + vigenciaH: string; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/ControlDevolucionesHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/ControlDevolucionesHistorialDto.ts new file mode 100644 index 0000000..9a217ba --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/ControlDevolucionesHistorialDto.ts @@ -0,0 +1,17 @@ +export interface ControlDevolucionesHistorialDto { + id_Control: number; + id_Empresa: number; + // nombreEmpresa?: string; + fecha: string; // Fecha original del control + entrada: number; + sobrantes: number; + detalle?: string | null; + sinCargo: number; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/EstadoBobinaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/EstadoBobinaHistorialDto.ts new file mode 100644 index 0000000..76771e6 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/EstadoBobinaHistorialDto.ts @@ -0,0 +1,12 @@ +export interface EstadoBobinaHistorialDto { + id_EstadoBobina: number; + denominacion: string; + obs?: string | null; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/OtroDestinoHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/OtroDestinoHistorialDto.ts new file mode 100644 index 0000000..72d3ab3 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/OtroDestinoHistorialDto.ts @@ -0,0 +1,12 @@ +export interface OtroDestinoHistorialDto { + id_Destino: number; + nombre: string; + obs?: string | null; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PerfilHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PerfilHistorialDto.ts new file mode 100644 index 0000000..5b95771 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PerfilHistorialDto.ts @@ -0,0 +1,12 @@ +export interface PerfilHistorialDto { + idPerfil: number; + perfil: string; // Nombre del perfil + descPerfil?: string | null; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid (podría ser el IdHist de gral_Perfiles_H si se incluye) +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PermisoHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PermisoHistorialDto.ts new file mode 100644 index 0000000..d0af456 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PermisoHistorialDto.ts @@ -0,0 +1,14 @@ +export interface PermisoHistorialDto { + idHistorial: number; // PK de la tabla _H + idPermiso: number; + modulo: string; + descPermiso: string; + codAcc: string; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PermisosPerfilesHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PermisosPerfilesHistorialDto.ts new file mode 100644 index 0000000..ae850bc --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PermisosPerfilesHistorialDto.ts @@ -0,0 +1,15 @@ +export interface PermisosPerfilesHistorialDto { + idHistorial: number; + idPerfil: number; + nombrePerfil: string; + idPermiso: number; + descPermiso: string; + codAccPermiso: string; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PlantaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PlantaHistorialDto.ts new file mode 100644 index 0000000..7ba716c --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PlantaHistorialDto.ts @@ -0,0 +1,12 @@ +export interface PlantaHistorialDto { + id_Planta: number; + nombre: string; + detalle: string; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PorcMonCanillaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PorcMonCanillaHistorialDto.ts new file mode 100644 index 0000000..a284206 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PorcMonCanillaHistorialDto.ts @@ -0,0 +1,18 @@ +export interface PorcMonCanillaHistorialDto { + id_PorcMon: number; + id_Publicacion: number; + // nombrePublicacion?: string; + id_Canilla: number; + // nombreCanilla?: string; + vigenciaD: string; + vigenciaH?: string | null; + porcMon: number; + esPorcentaje: boolean; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PorcPagoHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PorcPagoHistorialDto.ts new file mode 100644 index 0000000..b4b3be8 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PorcPagoHistorialDto.ts @@ -0,0 +1,17 @@ +export interface PorcPagoHistorialDto { + id_Porcentaje: number; + id_Publicacion: number; + // nombrePublicacion?: string; + id_Distribuidor: number; + // nombreDistribuidor?: string; + vigenciaD: string; + vigenciaH?: string | null; + porcentaje: number; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PrecioHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PrecioHistorialDto.ts new file mode 100644 index 0000000..7c90de2 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PrecioHistorialDto.ts @@ -0,0 +1,21 @@ +export interface PrecioHistorialDto { + id_Precio: number; + id_Publicacion: number; + // nombrePublicacion?: string; + vigenciaD: string; // Fecha en formato string (YYYY-MM-DDTHH:mm:ss) + vigenciaH?: string | null; + lunes?: number | null; + martes?: number | null; + miercoles?: number | null; + jueves?: number | null; + viernes?: number | null; + sabado?: number | null; + domingo?: number | null; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PubliSeccionHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PubliSeccionHistorialDto.ts new file mode 100644 index 0000000..4c7c9f7 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PubliSeccionHistorialDto.ts @@ -0,0 +1,14 @@ +export interface PubliSeccionHistorialDto { + id_Seccion: number; + id_Publicacion: number; + // nombrePublicacion?: string; + nombre: string; // Nombre de la sección + estado: boolean; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/PublicacionHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/PublicacionHistorialDto.ts new file mode 100644 index 0000000..80375b2 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/PublicacionHistorialDto.ts @@ -0,0 +1,16 @@ +export interface PublicacionHistorialDto { + id_Publicacion: number; + nombre: string; + observacion?: string | null; + id_Empresa: number; + // nombreEmpresa?: string; + // ctrlDevoluciones: boolean; // Si la añades + habilitada: boolean | null; // Es nullable en la tabla _H + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/RecargoZonaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/RecargoZonaHistorialDto.ts new file mode 100644 index 0000000..e83e833 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/RecargoZonaHistorialDto.ts @@ -0,0 +1,17 @@ +export interface RecargoZonaHistorialDto { + id_Recargo: number; + id_Publicacion: number; + // nombrePublicacion?: string; + id_Zona: number; + // nombreZona?: string; + vigenciaD: string; // Fecha en formato string + vigenciaH?: string | null; + valor: number; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/RegSeccionTiradaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/RegSeccionTiradaHistorialDto.ts new file mode 100644 index 0000000..4475e97 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/RegSeccionTiradaHistorialDto.ts @@ -0,0 +1,18 @@ +export interface RegSeccionTiradaHistorialDto { + id_Tirada: number; // ID del registro original en bob_RegPublicaciones + id_Publicacion: number; + nombrePublicacion: string; + id_Seccion: number; + nombreSeccion: string; + cantPag: number; + fecha: string; // Fecha original de la tirada + id_Planta: number; + nombrePlanta: string; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/RegTiradaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/RegTiradaHistorialDto.ts new file mode 100644 index 0000000..ff49d94 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/RegTiradaHistorialDto.ts @@ -0,0 +1,16 @@ +export interface RegTiradaHistorialDto { + id_Registro: number; + ejemplares: number; + id_Publicacion: number; + nombrePublicacion: string; + fecha: string; // Fecha original de la tirada + id_Planta: number; + nombrePlanta: string; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/StockBobinaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/StockBobinaHistorialDto.ts new file mode 100644 index 0000000..b7a84d9 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/StockBobinaHistorialDto.ts @@ -0,0 +1,26 @@ +export interface StockBobinaHistorialDto { + id_Bobina: number; + id_TipoBobina: number; + nombreTipoBobina: string; + nroBobina: string; + peso: number; + id_Planta: number; + nombrePlanta: string; + id_EstadoBobina: number; + nombreEstadoBobina: string; + remito: string; + fechaRemito: string; // "YYYY-MM-DDTHH:mm:ss" + fechaEstado?: string | null; + id_Publicacion?: number | null; + nombrePublicacion?: string | null; + id_Seccion?: number | null; + nombreSeccion?: string | null; + obs?: string | null; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/TipoBobinaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/TipoBobinaHistorialDto.ts new file mode 100644 index 0000000..d60efcb --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/TipoBobinaHistorialDto.ts @@ -0,0 +1,11 @@ +export interface TipoBobinaHistorialDto { + id_TipoBobina: number; + denominacion: string; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/models/dtos/Auditoria/ZonaHistorialDto.ts b/Frontend/src/models/dtos/Auditoria/ZonaHistorialDto.ts new file mode 100644 index 0000000..0cac4b1 --- /dev/null +++ b/Frontend/src/models/dtos/Auditoria/ZonaHistorialDto.ts @@ -0,0 +1,13 @@ +export interface ZonaHistorialDto { + id_Zona: number; + nombre: string; + descripcion?: string | null; + estado: boolean; + + id_Usuario: number; + nombreUsuarioModifico: string; + fechaMod: string; + tipoMod: string; + + id?: string; // Para DataGrid +} \ No newline at end of file diff --git a/Frontend/src/pages/Auditoria/AuditoriaGeneralPage.tsx b/Frontend/src/pages/Auditoria/AuditoriaGeneralPage.tsx index 36c2a30..dfe5a8c 100644 --- a/Frontend/src/pages/Auditoria/AuditoriaGeneralPage.tsx +++ b/Frontend/src/pages/Auditoria/AuditoriaGeneralPage.tsx @@ -1,4 +1,3 @@ -// src/pages/Auditoria/AuditoriaGeneralPage.tsx import React, { useState, useEffect, useCallback } from 'react'; import { Box, Typography, Paper, TextField, Button, CircularProgress, Alert, @@ -7,23 +6,34 @@ import { import FilterListIcon from '@mui/icons-material/FilterList'; import { DataGrid, type GridColDef } from '@mui/x-data-grid'; import { esES } from '@mui/x-data-grid/locales'; - import auditoriaService from '../../services/Auditoria/auditoriaService'; - import type { UsuarioDto } from '../../models/dtos/Usuarios/UsuarioDto'; import usuarioService from '../../services/Usuarios/usuarioService'; import { usePermissions } from '../../hooks/usePermissions'; -//import axios from 'axios'; // Para el tipo de error de Axios +import type { PublicacionDropdownDto } from '../../models/dtos/Distribucion/PublicacionDropdownDto'; +import publicacionService from '../../services/Distribucion/publicacionService'; +import type { ZonaDto } from '../../models/dtos/Zonas/ZonaDto'; +import zonaService from '../../services/Distribucion/zonaService'; +import type { DistribuidorDropdownDto } from '../../models/dtos/Distribucion/DistribuidorDropdownDto'; +import distribuidorService from '../../services/Distribucion/distribuidorService'; +import type { CanillaDto } from '../../models/dtos/Distribucion/CanillaDto'; +import canillaService from '../../services/Distribucion/canillaService'; +import empresaService from '../../services/Distribucion/empresaService'; +import type { EmpresaDto } from '../../models/dtos/Distribucion/EmpresaDto'; +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 tipoBobinaService from '../../services/Impresion/tipoBobinaService'; +import plantaService from '../../services/Impresion/plantaService'; +import estadoBobinaService from '../../services/Impresion/estadoBobinaService'; +import type { PubliSeccionDto } from '../../models/dtos/Distribucion/PubliSeccionDto'; +import publiSeccionService from '../../services/Distribucion/publiSeccionService'; +import type { PerfilDto } from '../../models/dtos/Usuarios/PerfilDto'; +import type { PermisoDto } from '../../models/dtos/Usuarios/PermisoDto'; +import perfilService from '../../services/Usuarios/perfilService'; +import permisoService from '../../services/Usuarios/permisoService'; -// Lista de tipos de entidad para el filtro const TIPOS_ENTIDAD_AUDITABLES = [ - { value: "PagoDistribuidor", label: "Pagos de Distribuidores (cue_PagosDistribuidor_H)" }, - { value: "NotaCreditoDebito", label: "Notas C/D (cue_CreditosDebitos_H)" }, - { value: "EntradaSalidaDist", label: "E/S Distribuidores (dist_EntradasSalidas_H)" }, - { value: "EntradaSalidaCanilla", label: "E/S Canillitas (dist_EntradasSalidasCanillas_H)" }, - { value: "NovedadCanilla", label: "Novedades Canillitas (dist_dtNovedadesCanillas_H)" }, - { value: "SaldoAjuste", label: "Ajustes Manuales de Saldo (cue_SaldoAjustesHistorial)" }, - { value: "TipoPago", label: "Tipos de Pago (cue_dtTipopago_H)" }, { value: "Canillita", label: "Canillitas (Maestro) (dist_dtCanillas_H)" }, { value: "Distribuidor", label: "Distribuidores (Maestro) (dist_dtDistribuidores_H)" }, { value: "Empresa", label: "Empresas (Maestro) (dist_dtEmpresas_H)" }, @@ -35,27 +45,35 @@ const TIPOS_ENTIDAD_AUDITABLES = [ { value: "RecargoZona", label: "Recargos por Zona (dist_RecargoZona_H)" }, { value: "PorcPagoDistribuidor", label: "Porcentajes Pago Dist. (dist_PorcPago_H)" }, { value: "PorcMonCanilla", label: "Porcentajes/Montos Canillita (dist_PorcMonPagoCanilla_H)" }, + { value: "EntradaSalidaCanilla", label: "E/S Canillitas (dist_EntradasSalidasCanillas_H)" }, + { value: "EntradaSalidaDist", label: "E/S Distribuidores (dist_EntradasSalidas_H)" }, + { value: "NovedadCanilla", label: "Novedades Canillitas (dist_dtNovedadesCanillas_H)" }, { value: "ControlDevoluciones", label: "Control Devoluciones (dist_dtCtrlDevoluciones_H)" }, + { value: "PagoDistribuidor", label: "Pagos de Distribuidores (cue_PagosDistribuidor_H)" }, + { value: "NotaCreditoDebito", label: "Notas C/D (cue_CreditosDebitos_H)" }, + { value: "TipoPago", label: "Tipos de Pago (cue_dtTipopago_H)" }, + { value: "SaldoAjuste", label: "Ajustes Manuales de Saldo (cue_SaldoAjustesHistorial)" }, { value: "TipoBobina", label: "Tipos de Bobina (bob_dtBobinas_H)" }, { value: "EstadoBobina", label: "Estados de Bobina (bob_dtEstadosBobinas_H)" }, { value: "PlantaImpresion", label: "Plantas de Impresión (bob_dtPlantas_H)" }, { value: "StockBobina", label: "Stock de Bobinas (bob_StockBobinas_H)" }, - { value: "RegPublicacionesTirada", label: "Secciones de Tirada (bob_RegPublicaciones_H)" }, { value: "RegTirada", label: "Registro de Tirada (bob_RegTiradas_H)" }, + { value: "PerfilMaestro", label: "Perfiles (Maestro) (gral_Perfiles_H)" }, + { value: "PermisoMaestro", label: "Permisos (Definición) (gral_Permisos_H)" }, + { value: "PermisosPerfiles", label: "Asignación de Permisos a Perfiles (gral_PermisosPerfiles_H)" }, + { value: "CambioParada", label: "Cambios de Parada (dist_CambiosParadasCanillas_H)" }, ].sort((a, b) => a.label.localeCompare(b.label)); const TIPOS_MODIFICACION = [ "Creado", "Creada", "Actualizado", "Actualizada", "Modificado", "Modificada", "Eliminado", "Eliminada", "Insertada", "Baja", "Alta", "Liquidada", "AjusteManualSaldo" - // Añadir más si es necesario (ej. "AjusteManualSaldo") ].sort(); - const AuditoriaGeneralPage: React.FC = () => { - const [datosAuditoria, setDatosAuditoria] = useState([]); // Tipo genérico para los datos de la tabla + const [datosAuditoria, setDatosAuditoria] = useState([]); const [columnasActuales, setColumnasActuales] = useState([]); - const [loading, setLoading] = useState(false); // Un solo loading para la búsqueda - const [error, setError] = useState(null); // Error general de la página o de la búsqueda + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); const [filtroFechaDesde, setFiltroFechaDesde] = useState(new Date().toISOString().split('T')[0]); const [filtroFechaHasta, setFiltroFechaHasta] = useState(new Date().toISOString().split('T')[0]); @@ -64,39 +82,92 @@ const AuditoriaGeneralPage: React.FC = () => { const [filtroIdEntidad, setFiltroIdEntidad] = useState(''); const [filtroTipoMod, setFiltroTipoMod] = useState(''); + const [filtroIdPublicacionParaSeccion, setFiltroIdPublicacionParaSeccion] = useState(''); + const [filtroIdZonaParaRecargo, setFiltroIdZonaParaRecargo] = useState(''); + const [filtroIdDistribuidorParaPorc, setFiltroIdDistribuidorParaPorc] = useState(''); + const [filtroIdCanillitaParaPorcMon, setFiltroIdCanillitaParaPorcMon] = useState(''); + const [filtroIdEmpresa, setFiltroIdEmpresa] = useState(''); + const [filtroFechaAfectadaParaCtrlDev, setFiltroFechaAfectadaParaCtrlDev] = useState(''); + const [filtroIdTipoBobina, setFiltroIdTipoBobina] = useState(''); + const [filtroIdPlantaStock, setFiltroIdPlantaStock] = useState(''); + const [filtroIdEstadoBobina, setFiltroIdEstadoBobina] = useState(''); + const [filtroIdPlantaTirada, setFiltroIdPlantaTirada] = useState(''); + const [filtroFechaTirada, setFiltroFechaTirada] = useState(''); + const [filtroIdSeccion, setFiltroIdSeccion] = useState(''); + const [filtroIdPerfil, setFiltroIdPerfil] = useState(''); + const [filtroIdPermiso, setFiltroIdPermiso] = useState(''); + const [usuariosDropdown, setUsuariosDropdown] = useState([]); + const [publicacionesDropdown, setPublicacionesDropdown] = useState([]); + const [zonasDropdown, setZonasDropdown] = useState([]); + const [distribuidoresDropdown, setDistribuidoresDropdown] = useState([]); + const [canillitasDropdown, setCanillitasDropdown] = useState([]); + const [empresasDropdown, setEmpresasDropdown] = useState([]); + const [tiposBobinaDropdown, setTiposBobinaDropdown] = useState([]); + const [plantasStockDropdown, setPlantasStockDropdown] = useState([]); + const [estadosBobinaDropdown, setEstadosBobinaDropdown] = useState([]); const [loadingDropdowns, setLoadingDropdowns] = useState(false); + const [seccionesDropdown, setSeccionesDropdown] = useState([]); + const [perfilesDropdown, setPerfilesDropdown] = useState([]); + const [permisosDropdown, setPermisosDropdown] = useState([]); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(25); const { tienePermiso, isSuperAdmin } = usePermissions(); - const puedeVerAuditoria = isSuperAdmin || tienePermiso("AU_GENERAL_VIEW"); // Define este permiso + const puedeVerAuditoria = isSuperAdmin || tienePermiso("AU_GENERAL_VIEW"); const formatDate = (dateString?: string | null): string => { + // Si es nulo, undefined, o una cadena vacía, devuelve guion if (!dateString) return '-'; - const datePart = dateString.split('T')[0]; - const parts = datePart.split('-'); - if (parts.length === 3) { return `${parts[2]}/${parts[1]}/${parts[0]}`; } - return datePart; + + // Si es la fecha mínima de .NET, trátala como nula también + if (dateString.startsWith('0001-01-01')) { + return '-'; + } + try { + return new Date(dateString).toLocaleString('es-AR', { + day: '2-digit', month: '2-digit', year: 'numeric', + hour: '2-digit', minute: '2-digit', second: '2-digit', + // timeZone: 'UTC' // Mantener UTC si las fechas del backend son consistentemente UTC + }); + } catch (e) { return dateString; } }; const currencyFormatter = (value?: number | null) => value != null ? value.toLocaleString('es-AR', { style: 'currency', currency: 'ARS' }) : '-'; - const numberFormatter = (value?: number | null) => value != null ? value.toLocaleString('es-AR') : '-'; - + const numberFormatter = (value?: number | null) => value != null ? Number(value).toLocaleString('es-AR') : '-'; useEffect(() => { const fetchDropdowns = async () => { if (!puedeVerAuditoria) return; setLoadingDropdowns(true); try { - const users = await usuarioService.getAllUsuarios(); - setUsuariosDropdown(users); + setUsuariosDropdown(await usuarioService.getAllUsuarios()); + setPublicacionesDropdown(await publicacionService.getPublicacionesForDropdown(false)); + setZonasDropdown(await zonaService.getAllZonas()); + setDistribuidoresDropdown(await distribuidorService.getAllDistribuidoresDropdown()); + setCanillitasDropdown(await canillaService.getAllCanillas(undefined, undefined, true, undefined)); + setEmpresasDropdown(await empresaService.getAllEmpresas()); + setTiposBobinaDropdown(await tipoBobinaService.getAllTiposBobina()); + setPlantasStockDropdown(await plantaService.getAllPlantas()); // O getPlantasForDropdown() + setEstadosBobinaDropdown(await estadoBobinaService.getAllEstadosBobina()); + if (filtroIdPublicacionParaSeccion) { // Cargar secciones solo si hay una publicación seleccionada + const seccData = await publiSeccionService.getSeccionesPorPublicacion(Number(filtroIdPublicacionParaSeccion), false); // Traer todas + setSeccionesDropdown(seccData); + } else { + setSeccionesDropdown([]); + } + const [perfilesData, permisosData] = await Promise.all([ + // ... + perfilService.getAllPerfiles(), + permisoService.getAllPermisos() + ]); + setPerfilesDropdown(perfilesData); + setPermisosDropdown(permisosData); } catch (e) { - console.error("Error cargando usuarios para filtro auditoría", e); - setError("Error al cargar usuarios para filtro."); - } finally { - setLoadingDropdowns(false); + console.error("Error cargando datos para filtros de auditoría", e); + setError("Error al cargar datos para filtros."); } + finally { setLoadingDropdowns(false); } }; fetchDropdowns(); }, [puedeVerAuditoria]); @@ -105,7 +176,6 @@ const AuditoriaGeneralPage: React.FC = () => { { field: 'fechaMod', headerName: 'Fecha Mod.', width: 170, type: 'dateTime', valueFormatter: (value) => formatDate(value as string) }, { field: 'nombreUsuarioModifico', headerName: 'Modificado Por', width: 180, flex: 0.8 }, { field: 'tipoMod', headerName: 'Acción', width: 120, flex: 0.5 }, - // Se pueden añadir más columnas comunes aquí si aplican a TODOS los historiales ]; const cargarHistorial = useCallback(async () => { @@ -130,37 +200,54 @@ const AuditoriaGeneralPage: React.FC = () => { let cols: GridColDef[] = getCommonAuditColumns(); switch (filtroTipoEntidad) { + case "Usuario": + const userHist = await auditoriaService.getHistorialUsuarios({ ...commonParams, idUsuarioAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); + rawData = userHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'userAfectado', headerName: 'User Afectado', width: 150, flex: 0.6 }, + { field: 'idUsuarioAfectado', headerName: 'ID User Afect.', width: 110, align: 'center', headerAlign: 'center' }, + { field: 'userNvo', headerName: 'User (Nvo)', width: 130, valueGetter: (value, row) => row.userAnt !== value ? value : '' }, + { field: 'userAnt', headerName: 'User (Ant)', width: 130 }, + { field: 'nombreNvo', headerName: 'Nombre (Nvo)', width: 150, valueGetter: (value, row) => row.nombreAnt !== value ? value : '' }, + { field: 'nombreAnt', headerName: 'Nombre (Ant)', width: 150 }, + { field: 'apellidoNvo', headerName: 'Apellido (Nvo)', width: 150, valueGetter: (value, row) => row.apellidoAnt !== value ? value : '' }, + { field: 'apellidoAnt', headerName: 'Apellido (Ant)', width: 150 }, + { field: 'nombrePerfilNvo', headerName: 'Perfil (Nvo)', width: 150, valueGetter: (value, row) => row.nombrePerfilAnt !== value ? value : '' }, + { field: 'nombrePerfilAnt', headerName: 'Perfil (Ant)', width: 150 }, + { field: 'habilitadaNva', headerName: 'Hab. (Nvo)', width: 100, type: 'boolean', valueGetter: (value, row) => row.habilitadaAnt !== value ? value : null }, + { field: 'habilitadaAnt', headerName: 'Hab. (Ant)', width: 100, type: 'boolean' }, + { field: 'supAdminNvo', headerName: 'SupAdm (Nvo)', width: 100, type: 'boolean', valueGetter: (value, row) => row.supAdminAnt !== value ? value : null }, + { field: 'supAdminAnt', headerName: 'SupAdm (Ant)', width: 100, type: 'boolean' }, + { field: 'debeCambiarClaveNva', headerName: 'CambiaClave (Nvo)', width: 100, type: 'boolean', valueGetter: (value, row) => row.debeCambiarClaveAnt !== value ? value : null }, + { field: 'debeCambiarClaveAnt', headerName: 'CambiaClave (Ant)', width: 100, type: 'boolean' }, + ]; + break; case "PagoDistribuidor": - const pagoHist = await auditoriaService.getHistorialPagosDistribuidor({ - ...commonParams, - idPagoAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - }); + const pagoHist = await auditoriaService.getHistorialPagosDistribuidor({ ...commonParams, idPagoAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = pagoHist; cols = [ ...getCommonAuditColumns(), { field: 'id_Pago', headerName: 'ID Pago', width: 100, align: 'center', headerAlign: 'center' }, - { field: 'id_Distribuidor', headerName: 'ID Dist.', width: 100, align: 'center', headerAlign: 'center' }, // Podrías añadir Nombre si el DTO lo trae - { field: 'id_Empresa', headerName: 'ID Emp.', width: 100, align: 'center', headerAlign: 'center' }, // Podrías añadir Nombre + { field: 'id_Distribuidor', headerName: 'ID Dist.', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'id_Empresa', headerName: 'ID Emp.', width: 100, align: 'center', headerAlign: 'center' }, { field: 'fecha', headerName: 'Fecha Pago', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, { field: 'recibo', headerName: 'Recibo', width: 100 }, { field: 'tipoMovimiento', headerName: 'Tipo Mov.', width: 120 }, { field: 'monto', headerName: 'Monto Pago', width: 130, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, - { field: 'id_TipoPago', headerName: 'ID Tipo Pago', width: 110, align: 'center', headerAlign: 'center' }, // Podrías añadir Nombre + { field: 'id_TipoPago', headerName: 'ID Tipo Pago', width: 110, align: 'center', headerAlign: 'center' }, { field: 'detalle', headerName: 'Detalle Pago', flex: 1, minWidth: 150, renderCell: (params) => ({params.value || '-'}) }, ]; break; case "NotaCreditoDebito": - const notaCDHist = await auditoriaService.getHistorialNotasCD({ - ...commonParams, - idNotaAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - }); + const notaCDHist = await auditoriaService.getHistorialNotasCD({ ...commonParams, idNotaAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = notaCDHist; cols = [ ...getCommonAuditColumns(), - { field: 'id_Nota', headerName: 'ID Nota Orig.', width: 110, align: 'center', headerAlign: 'center' }, + { field: 'id_Nota', headerName: 'ID Nota', width: 110, align: 'center', headerAlign: 'center' }, { field: 'destino', headerName: 'Destino', width: 120 }, { field: 'id_Destino', headerName: 'ID Dest.', width: 100, align: 'center', headerAlign: 'center' }, - // NombreDestinatario y NombreEmpresa se podrían traer si el DTO de historial los incluyera + { field: 'id_Empresa', headerName: 'ID Emp.', width: 100, align: 'center', headerAlign: 'center' }, { field: 'tipo', headerName: 'Tipo Nota', width: 100 }, { field: 'fecha', headerName: 'Fecha Nota', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, { field: 'monto', headerName: 'Monto Nota', width: 130, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, @@ -169,11 +256,7 @@ const AuditoriaGeneralPage: React.FC = () => { ]; break; case "EntradaSalidaDist": - const esDistHist = await auditoriaService.getHistorialEntradasSalidasDist({ - ...commonParams, - idParteAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - // Si añades más filtros al servicio, pásalos aquí - }); + const esDistHist = await auditoriaService.getHistorialEntradasSalidasDist({ ...commonParams, idParteAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = esDistHist; cols = [ ...getCommonAuditColumns(), @@ -185,14 +268,10 @@ const AuditoriaGeneralPage: React.FC = () => { { field: 'cantidad', headerName: 'Cantidad', width: 100, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, { field: 'remito', headerName: 'Remito', width: 100, type: 'number' }, { field: 'observacion', headerName: 'Obs. Mov.', flex: 1, minWidth: 150, renderCell: (params) => ({params.value || '-'}) }, - // Podrías mostrar Id_Precio, Id_Recargo, Id_Porcentaje si es útil ]; break; case "EntradaSalidaCanilla": - const esCanillaHist = await auditoriaService.getHistorialEntradasSalidasCanilla({ - ...commonParams, - idParteAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - }); + const esCanillaHist = await auditoriaService.getHistorialEntradasSalidasCanilla({ ...commonParams, idParteAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = esCanillaHist; cols = [ ...getCommonAuditColumns(), @@ -203,94 +282,46 @@ const AuditoriaGeneralPage: React.FC = () => { { field: 'cantSalida', headerName: 'Salida', width: 90, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, { field: 'cantEntrada', headerName: 'Entrada', width: 90, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, { field: 'observacion', headerName: 'Obs. Mov.', flex: 1, minWidth: 150, renderCell: (params) => ({params.value || '-'}) }, - // Considera mostrar Id_Precio, Id_Recargo, Id_PorcMon si es relevante ]; break; case "NovedadCanilla": - const novedadHist = await auditoriaService.getHistorialNovedadesCanilla({ - ...commonParams, - idNovedadAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - // También podrías pasar un filtro de idCanilla si lo añades a HistorialNovedadesCanillaParams y al backend - }); + const novedadHist = await auditoriaService.getHistorialNovedadesCanilla({ ...commonParams, idNovedadAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = novedadHist; cols = [ ...getCommonAuditColumns(), { field: 'id_Novedad', headerName: 'ID Novedad', width: 110, align: 'center', headerAlign: 'center' }, { field: 'id_Canilla', headerName: 'ID Canillita', width: 110, align: 'center', headerAlign: 'center' }, - // Aquí podrías querer mostrar el NombreCanilla, necesitarías un JOIN o una llamada extra en el servicio { field: 'fecha', headerName: 'Fecha Novedad', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, - { - field: 'detalle', - headerName: 'Detalle Novedad', - flex: 1, - minWidth: 250, - renderCell: (params) => ( - - {params.value || '-'} - - ) - }, + { field: 'detalle', headerName: 'Detalle Novedad', flex: 1, minWidth: 250, renderCell: (params) => ({params.value || '-'}) }, ]; break; case "SaldoAjuste": - const ajusteHist = await auditoriaService.getHistorialAjustesSaldo({ - // Pasar los filtros comunes - fechaDesde: commonParams.fechaDesde, - fechaHasta: commonParams.fechaHasta, - idUsuarioModificador: commonParams.idUsuarioModificador, - // Filtros específicos para este historial (si el backend los usa para este endpoint en particular) - // Si no, estos se ignorarán o podrías omitirlos si el endpoint no los toma. - // El endpoint actual que definimos sí los toma. - destino: filtroIdEntidad ? (TIPOS_ENTIDAD_AUDITABLES.find(t => t.value === filtroTipoEntidad)?.label.includes("Dist") ? "Distribuidores" : "Canillas") : undefined, // Lógica para determinar Destino - idDestino: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, // ID Entidad Afectada aquí sería idDestino - // idEmpresa: si tienes un filtro de empresa general, pásalo - }); + const ajusteHist = await auditoriaService.getHistorialAjustesSaldo({ ...commonParams, idDestino: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, destino: undefined, idEmpresa: filtroIdEmpresa ? Number(filtroIdEmpresa) : undefined }); rawData = ajusteHist; cols = [ - // Reutilizar 'fechaMod' como 'Fecha Ajuste' y 'nombreUsuarioModifico' { field: 'fechaAjuste', headerName: 'Fecha Ajuste', width: 170, type: 'dateTime', valueFormatter: (value) => formatDate(value as string) }, { field: 'nombreUsuarioModifico', headerName: 'Ajustado Por', width: 180, flex: 0.8 }, { field: 'destino', headerName: 'Tipo Dest.', width: 120 }, { field: 'id_Destino', headerName: 'ID Dest.', width: 100, align: 'center', headerAlign: 'center' }, - // Aquí podrías querer mostrar NombreDestinatario y NombreEmpresa, - // requeriría que SaldoAjusteHistorialDto los traiga (JOINs en backend) { field: 'id_Empresa', headerName: 'ID Emp.', width: 100, align: 'center', headerAlign: 'center' }, { field: 'montoAjuste', headerName: 'Monto Ajuste', width: 130, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, { field: 'saldoAnterior', headerName: 'Saldo Ant.', width: 130, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, { field: 'saldoNuevo', headerName: 'Saldo Nvo.', width: 130, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, - { - field: 'justificacion', - headerName: 'Justificación', - flex: 1, - minWidth: 200, - renderCell: (params) => ({params.value || '-'}) - }, + { field: 'justificacion', headerName: 'Justificación', flex: 1, minWidth: 200, renderCell: (params) => ({params.value || '-'}) }, ]; break; case "TipoPago": - const tipoPagoHist = await auditoriaService.getHistorialTiposPago({ - ...commonParams, - idTipoPagoAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - }); + const tipoPagoHist = await auditoriaService.getHistorialTiposPago({ ...commonParams, idTipoPagoAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = tipoPagoHist; cols = [ ...getCommonAuditColumns(), { field: 'id_TipoPago', headerName: 'ID Tipo Pago', width: 110, align: 'center', headerAlign: 'center' }, { field: 'nombre', headerName: 'Nombre Tipo Pago', width: 200 }, - { - field: 'detalle', - headerName: 'Detalle', - flex: 1, - minWidth: 250, - renderCell: (params) => ({params.value || '-'}) - }, + { field: 'detalle', headerName: 'Detalle', flex: 1, minWidth: 250, renderCell: (params) => ({params.value || '-'}) }, ]; break; - case "Canillita": // Historial del maestro de Canillitas - const canMaestroHist = await auditoriaService.getHistorialCanillitasMaestro({ - ...commonParams, - idCanillaAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - }); + case "Canillita": + const canMaestroHist = await auditoriaService.getHistorialCanillitasMaestro({ ...commonParams, idCanillaAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = canMaestroHist; cols = [ ...getCommonAuditColumns(), @@ -300,17 +331,14 @@ const AuditoriaGeneralPage: React.FC = () => { { field: 'parada', headerName: 'Parada', width: 180, renderCell: (params) => ({params.value || '-'}) }, { field: 'id_Zona', headerName: 'ID Zona', width: 100, align: 'center', headerAlign: 'center' }, { field: 'accionista', headerName: 'Accionista', width: 100, type: 'boolean' }, - { field: 'empresa', headerName: 'ID Empresa', width: 100, align: 'center', headerAlign: 'center' }, // ID de la empresa + { field: 'empresa', headerName: 'ID Empresa', width: 100, align: 'center', headerAlign: 'center' }, { field: 'baja', headerName: 'Baja', width: 80, type: 'boolean' }, { field: 'fechaBaja', headerName: 'Fecha Baja', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, { field: 'obs', headerName: 'Obs.', flex: 1, minWidth: 150, renderCell: (params) => ({params.value || '-'}) }, ]; break; - case "Distribuidor": // Historial del maestro de Distribuidores - const distMaestroHist = await auditoriaService.getHistorialDistribuidoresMaestro({ - ...commonParams, - idDistribuidorAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - }); + case "Distribuidor": + const distMaestroHist = await auditoriaService.getHistorialDistribuidoresMaestro({ ...commonParams, idDistribuidorAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = distMaestroHist; cols = [ ...getCommonAuditColumns(), @@ -319,27 +347,310 @@ const AuditoriaGeneralPage: React.FC = () => { { field: 'nroDoc', headerName: 'Nro. Doc.', width: 120 }, { field: 'contacto', headerName: 'Contacto', width: 150, renderCell: (params) => ({params.value || '-'}) }, { field: 'id_Zona', headerName: 'ID Zona', width: 90, align: 'center', headerAlign: 'center' }, - // Podrías añadir más campos como Calle, Localidad, etc. si son importantes para la auditoría visual { field: 'email', headerName: 'Email', width: 180, renderCell: (params) => ({params.value || '-'}) }, { field: 'telefono', headerName: 'Teléfono', width: 130 }, ]; break; - case "Empresa": // Historial del maestro de Empresas - const empMaestroHist = await auditoriaService.getHistorialEmpresasMaestro({ - ...commonParams, - idEmpresaAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined - }); + case "Empresa": + const empMaestroHist = await auditoriaService.getHistorialEmpresasMaestro({ ...commonParams, idEmpresaAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); rawData = empMaestroHist; cols = [ ...getCommonAuditColumns(), - { field: 'id_Empresa', headerName: 'ID Empresa', width: 110, align:'center', headerAlign:'center' }, + { field: 'id_Empresa', headerName: 'ID Empresa', width: 110, align: 'center', headerAlign: 'center' }, { field: 'nombre', headerName: 'Nombre Empresa', width: 250 }, - { - field: 'detalle', - headerName: 'Detalle', - flex:1, - minWidth:250, - renderCell: (params) => ( {params.value || '-'})}, + { field: 'detalle', headerName: 'Detalle', flex: 1, minWidth: 250, renderCell: (params) => ({params.value || '-'}) }, + ]; + break; + case "Zona": + const zonaMaestroHist = await auditoriaService.getHistorialZonasMaestro({ ...commonParams, idZonaAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); + rawData = zonaMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Zona', headerName: 'ID Zona', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'nombre', headerName: 'Nombre Zona', width: 250 }, + { field: 'descripcion', headerName: 'Descripción', flex: 1, minWidth: 250, renderCell: (params) => ({params.value || '-'}) }, + { field: 'estado', headerName: 'Activa', width: 100, type: 'boolean' }, + ]; + break; + case "OtroDestino": + const otroDestMaestroHist = await auditoriaService.getHistorialOtrosDestinosMaestro({ ...commonParams, idOtroDestinoAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); + rawData = otroDestMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Destino', headerName: 'ID Destino', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'nombre', headerName: 'Nombre Destino', width: 250 }, + { field: 'obs', headerName: 'Observación', flex: 1, minWidth: 250, renderCell: (params) => ({params.value || '-'}) }, + ]; + break; + case "Publicacion": + const pubMaestroHist = await auditoriaService.getHistorialPublicacionesMaestro({ ...commonParams, idPublicacionAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined }); + rawData = pubMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Publicacion', headerName: 'ID Pub.', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'nombre', headerName: 'Nombre Publicación', width: 220 }, + { field: 'id_Empresa', headerName: 'ID Empresa', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'habilitada', headerName: 'Habilitada', width: 100, type: 'boolean' }, + { field: 'observacion', headerName: 'Observación', flex: 1, minWidth: 200, renderCell: (params) => ({params.value || '-'}) }, + ]; + break; + case "PubliSeccion": + const psMaestroHist = await auditoriaService.getHistorialPubliSeccionesMaestro({ ...commonParams, idSeccionAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, idPublicacionAfectada: filtroIdPublicacionParaSeccion ? Number(filtroIdPublicacionParaSeccion) : undefined }); + rawData = psMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Seccion', headerName: 'ID Sección', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'id_Publicacion', headerName: 'ID Pub. (Hist)', width: 120, align: 'center', headerAlign: 'center' }, + { field: 'nombre', headerName: 'Nombre Sección', width: 250 }, + { field: 'estado', headerName: 'Activa', width: 100, type: 'boolean' }, + ]; + break; + case "PrecioPublicacion": + const precioMaestroHist = await auditoriaService.getHistorialPreciosMaestro({ ...commonParams, idPrecioAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, idPublicacionAfectada: filtroIdPublicacionParaSeccion ? Number(filtroIdPublicacionParaSeccion) : undefined }); + rawData = precioMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Precio', headerName: 'ID Precio', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'id_Publicacion', headerName: 'ID Pub.', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'vigenciaD', headerName: 'Vig. Desde', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'vigenciaH', headerName: 'Vig. Hasta', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'lunes', headerName: 'Lun', width: 90, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, + { field: 'martes', headerName: 'Mar', width: 90, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, + { field: 'miercoles', headerName: 'Mié', width: 90, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, + { field: 'jueves', headerName: 'Jue', width: 90, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, + { field: 'viernes', headerName: 'Vie', width: 90, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, + { field: 'sabado', headerName: 'Sáb', width: 90, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, + { field: 'domingo', headerName: 'Dom', width: 90, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, + ]; + break; + case "RecargoZona": + const rzMaestroHist = await auditoriaService.getHistorialRecargosZonaMaestro({ ...commonParams, idRecargoAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, idPublicacionAfectada: filtroIdPublicacionParaSeccion ? Number(filtroIdPublicacionParaSeccion) : undefined, idZonaAfectada: filtroIdZonaParaRecargo ? Number(filtroIdZonaParaRecargo) : undefined }); + rawData = rzMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Recargo', headerName: 'ID Recargo', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'id_Publicacion', headerName: 'ID Pub.', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'id_Zona', headerName: 'ID Zona', width: 90, align: 'center', headerAlign: 'center' }, + { field: 'vigenciaD', headerName: 'Vig. Desde', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'vigenciaH', headerName: 'Vig. Hasta', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'valor', headerName: 'Valor Recargo', width: 130, type: 'number', valueFormatter: (v) => currencyFormatter(v as number) }, + ]; + break; + case "PorcPagoDistribuidor": + const ppdMaestroHist = await auditoriaService.getHistorialPorcPagoDistMaestro({ ...commonParams, idPorcentajeAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, idPublicacionAfectada: filtroIdPublicacionParaSeccion ? Number(filtroIdPublicacionParaSeccion) : undefined, idDistribuidorAfectado: filtroIdDistribuidorParaPorc ? Number(filtroIdDistribuidorParaPorc) : undefined }); + rawData = ppdMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Porcentaje', headerName: 'ID Porc.', width: 90, align: 'center', headerAlign: 'center' }, + { field: 'id_Publicacion', headerName: 'ID Pub.', width: 90, align: 'center', headerAlign: 'center' }, + { field: 'id_Distribuidor', headerName: 'ID Dist.', width: 90, align: 'center', headerAlign: 'center' }, + { field: 'vigenciaD', headerName: 'Vig. Desde', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'vigenciaH', headerName: 'Vig. Hasta', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'porcentaje', headerName: 'Porcentaje', width: 120, type: 'number', valueFormatter: (value) => value != null ? `${Number(value).toLocaleString('es-AR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%` : '-' }, + ]; + break; + case "PorcMonCanilla": + const pmcMaestroHist = await auditoriaService.getHistorialPorcMonCanillaMaestro({ ...commonParams, idPorcMonAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, idPublicacionAfectada: filtroIdPublicacionParaSeccion ? Number(filtroIdPublicacionParaSeccion) : undefined, idCanillaAfectada: filtroIdCanillitaParaPorcMon ? Number(filtroIdCanillitaParaPorcMon) : undefined }); + rawData = pmcMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_PorcMon', headerName: 'ID Conf.', width: 90, align: 'center', headerAlign: 'center' }, + { field: 'id_Publicacion', headerName: 'ID Pub.', width: 90, align: 'center', headerAlign: 'center' }, + { field: 'id_Canilla', headerName: 'ID Can.', width: 90, align: 'center', headerAlign: 'center' }, + { field: 'vigenciaD', headerName: 'Vig. Desde', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'vigenciaH', headerName: 'Vig. Hasta', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'porcMon', headerName: 'Valor', width: 100, type: 'number', valueFormatter: (value, row) => row.esPorcentaje ? `${Number(value).toLocaleString('es-AR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%` : currencyFormatter(value as number) }, + { field: 'esPorcentaje', headerName: 'Es %', width: 80, type: 'boolean' }, + ]; + break; + case "ControlDevoluciones": + const cdMaestroHist = await auditoriaService.getHistorialControlDevoluciones({ ...commonParams, idControlAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, idEmpresaAfectada: filtroIdEmpresa ? Number(filtroIdEmpresa) : undefined, fechaAfectada: filtroFechaAfectadaParaCtrlDev || undefined }); + rawData = cdMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Control', headerName: 'ID Control', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'id_Empresa', headerName: 'ID Empresa', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'fecha', headerName: 'Fecha Control', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'entrada', headerName: 'Entrada', width: 100, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, + { field: 'sobrantes', headerName: 'Sobrantes', width: 100, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, + { field: 'sinCargo', headerName: 'Sin Cargo', width: 100, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, + { field: 'detalle', headerName: 'Detalle', flex: 1, minWidth: 200, renderCell: (params) => ({params.value || '-'}) }, + ]; + break; + case "TipoBobina": + const tbMaestroHist = await auditoriaService.getHistorialTiposBobinaMaestro({ + ...commonParams, + idTipoBobinaAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined + }); + rawData = tbMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_TipoBobina', headerName: 'ID Tipo Bobina', width: 120, align: 'center', headerAlign: 'center' }, + { field: 'denominacion', headerName: 'Denominación', flex: 1, minWidth: 250 }, + ]; + break; + case "EstadoBobina": + const ebMaestroHist = await auditoriaService.getHistorialEstadosBobinaMaestro({ + ...commonParams, + idEstadoBobinaAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined + }); + rawData = ebMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_EstadoBobina', headerName: 'ID Estado Bob.', width: 120, align: 'center', headerAlign: 'center' }, + { field: 'denominacion', headerName: 'Denominación', width: 250 }, + { + field: 'obs', + headerName: 'Observación', + flex: 1, + minWidth: 250, + renderCell: (params) => ({params.value || '-'}) + }, + ]; + break; + case "PlantaImpresion": + const plantaMaestroHist = await auditoriaService.getHistorialPlantasMaestro({ + ...commonParams, + idPlantaAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined + }); + rawData = plantaMaestroHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Planta', headerName: 'ID Planta', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'nombre', headerName: 'Nombre Planta', width: 250 }, + { + field: 'detalle', + headerName: 'Detalle', + flex: 1, + minWidth: 250, + renderCell: (params) => ({params.value || '-'}) + }, + ]; + break; + case "StockBobina": + const stockHist = await auditoriaService.getHistorialStockBobinas({ + ...commonParams, + idBobinaAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, + idTipoBobinaFiltro: filtroIdTipoBobina ? Number(filtroIdTipoBobina) : undefined, + idPlantaFiltro: filtroIdPlantaStock ? Number(filtroIdPlantaStock) : undefined, + idEstadoBobinaFiltro: filtroIdEstadoBobina ? Number(filtroIdEstadoBobina) : undefined, + }); + rawData = stockHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Bobina', headerName: 'ID Bobina', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'nroBobina', headerName: 'Nro Bobina', width: 120 }, + { field: 'nombreTipoBobina', headerName: 'Tipo', width: 150 }, + { field: 'peso', headerName: 'Peso (Kg)', width: 100, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, + { field: 'nombrePlanta', headerName: 'Planta', width: 130 }, + { field: 'nombreEstadoBobina', headerName: 'Estado', width: 130 }, + { field: 'remito', headerName: 'Remito', width: 100 }, + { field: 'fechaRemito', headerName: 'F. Remito', width: 110, type: 'date', valueFormatter: (v) => formatDate(v as string) }, + { field: 'fechaEstado', headerName: 'F. Estado', width: 110, type: 'date', valueFormatter: (v) => formatDate(v as string) }, + { field: 'nombrePublicacion', headerName: 'Publicación', width: 150 }, + { field: 'nombreSeccion', headerName: 'Sección', width: 130 }, + { field: 'obs', headerName: 'Obs.', flex: 1, minWidth: 150, renderCell: (p) => {p.value || '-'} }, + ]; + break; + case "RegTirada": + const regTiradaHist = await auditoriaService.getHistorialRegTiradas({ + ...commonParams, + idRegistroAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, + idPublicacionFiltro: filtroIdPublicacionParaSeccion ? Number(filtroIdPublicacionParaSeccion) : undefined, + idPlantaFiltro: filtroIdPlantaTirada ? Number(filtroIdPlantaTirada) : undefined, + fechaTiradaFiltro: filtroFechaTirada || undefined, + }); + rawData = regTiradaHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Registro', headerName: 'ID Reg. Tirada', width: 120, align: 'center', headerAlign: 'center' }, + { field: 'nombrePublicacion', headerName: 'Publicación', width: 200 }, + { field: 'nombrePlanta', headerName: 'Planta', width: 150 }, + { field: 'fecha', headerName: 'Fecha Tirada', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'ejemplares', headerName: 'Ejemplares', width: 120, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, + ]; + break; + case "RegSeccionTirada": + const regSecHist = await auditoriaService.getHistorialRegSeccionesTirada({ + ...commonParams, + idTiradaAfectada: filtroIdEntidad ? Number(filtroIdEntidad) : undefined, + idPublicacionFiltro: filtroIdPublicacionParaSeccion ? Number(filtroIdPublicacionParaSeccion) : undefined, + idSeccionFiltro: filtroIdSeccion ? Number(filtroIdSeccion) : undefined, + idPlantaFiltro: filtroIdPlantaTirada ? Number(filtroIdPlantaTirada) : undefined, + fechaTiradaFiltro: filtroFechaTirada || undefined, + }); + rawData = regSecHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Tirada', headerName: 'ID Reg. Sec.', width: 110, align: 'center', headerAlign: 'center' }, + { field: 'nombrePublicacion', headerName: 'Publicación', width: 180 }, + { field: 'nombreSeccion', headerName: 'Sección', width: 180 }, + { field: 'nombrePlanta', headerName: 'Planta', width: 130 }, + { field: 'fecha', headerName: 'Fecha Tirada', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'cantPag', headerName: 'Págs.', width: 90, type: 'number', valueFormatter: (v) => numberFormatter(v as number) }, + ]; + break; + case "PerfilMaestro": + const perfilHist = await auditoriaService.getHistorialPerfilesMaestro({ + ...commonParams, + idPerfilAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined + }); + rawData = perfilHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'idPerfil', headerName: 'ID Perfil', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'perfil', headerName: 'Nombre Perfil', width: 200 }, + { + field: 'descPerfil', + headerName: 'Descripción', + flex: 1, + minWidth: 250, + renderCell: (params) => ({params.value || '-'}) + }, + ]; + break; + case "PermisoMaestro": + const permisoHist = await auditoriaService.getHistorialPermisosMaestro({ + ...commonParams, + idPermisoAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined + }); + rawData = permisoHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'idPermiso', headerName: 'ID Permiso', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'codAcc', headerName: 'Cod. Acceso', width: 120 }, + { field: 'descPermiso', headerName: 'Descripción', flex: 1, minWidth: 250 }, + { field: 'modulo', headerName: 'Módulo', width: 200 }, + ]; + break; + case "PermisosPerfiles": + const ppHist = await auditoriaService.getHistorialPermisosPerfiles({ + ...commonParams, + idPerfilAfectado: filtroIdPerfil ? Number(filtroIdPerfil) : undefined, + idPermisoAfectado: filtroIdPermiso ? Number(filtroIdPermiso) : undefined, + }); + rawData = ppHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'nombrePerfil', headerName: 'Perfil Afectado', width: 180 }, + { field: 'codAccPermiso', headerName: 'Cod. Permiso', width: 120 }, + { field: 'descPermiso', headerName: 'Descripción Permiso', flex: 1, minWidth: 250 }, + ]; + break; + case "CambioParada": + const paradaHist = await auditoriaService.getHistorialCambiosParada({ + ...commonParams, + // "ID Entidad Afectada" aquí se puede usar para filtrar por canillita + idCanillaAfectado: filtroIdEntidad ? Number(filtroIdEntidad) : undefined + }); + rawData = paradaHist; + cols = [ + ...getCommonAuditColumns(), + { field: 'id_Registro', headerName: 'ID Reg.', width: 100, align: 'center', headerAlign: 'center' }, + { field: 'nombreCanilla', headerName: 'Canillita', width: 200 }, + { field: 'parada', headerName: 'Parada', flex: 1, minWidth: 250 }, + { field: 'vigenciaD', headerName: 'Vig. Desde', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, + { field: 'vigenciaH', headerName: 'Vig. Hasta', width: 120, type: 'date', valueFormatter: (value) => formatDate(value as string) }, ]; break; default: @@ -349,12 +660,15 @@ const AuditoriaGeneralPage: React.FC = () => { setLoading(false); return; } - // Asignar un ID único a cada fila para el DataGrid si no viene del DTO - setDatosAuditoria(rawData.map((item, index) => ({ ...item, id: item.id_NotaOriginal || item.id_Nota || item.idHist || `hist-${filtroTipoEntidad}-${index}-${Math.random()}` }))); // Asegurar ID único + setDatosAuditoria(rawData.map((item, index) => { + const idKey = Object.keys(item).find(k => k.toLowerCase().startsWith('id_') || k.toLowerCase().startsWith('idhist') || k.toLowerCase() === 'id'); + const idValue = idKey ? item[idKey] : `fallback-${index}`; + return { ...item, id: `${filtroTipoEntidad}-${idValue}-${index}-${Math.random().toString(36).substring(2)}` }; + })); setColumnasActuales(cols); } catch (err: any) { - console.error(err); + console.error("Error en cargarHistorial:", err); setError(err.response?.data?.message || 'Error al cargar el historial.'); setDatosAuditoria([]); setColumnasActuales(getCommonAuditColumns()); @@ -363,7 +677,11 @@ const AuditoriaGeneralPage: React.FC = () => { } }, [ puedeVerAuditoria, filtroTipoEntidad, filtroFechaDesde, filtroFechaHasta, - filtroIdUsuarioMod, filtroIdEntidad, filtroTipoMod + filtroIdUsuarioMod, filtroIdEntidad, filtroTipoMod, + filtroIdPublicacionParaSeccion, filtroIdZonaParaRecargo, filtroIdDistribuidorParaPorc, + filtroIdCanillitaParaPorcMon, filtroIdEmpresa, filtroFechaAfectadaParaCtrlDev, + filtroIdTipoBobina, filtroIdPlantaStock, filtroIdEstadoBobina, filtroIdPlantaTirada, + filtroFechaTirada, filtroIdSeccion ]); const handleBuscar = () => { @@ -371,14 +689,13 @@ const AuditoriaGeneralPage: React.FC = () => { cargarHistorial(); } - if (!puedeVerAuditoria && !loadingDropdowns) { // Si ya terminaron de cargar los dropdowns y no tiene permiso + if (!puedeVerAuditoria && !loadingDropdowns) { return No tiene permiso para acceder a la Auditoría General.; } - if (loadingDropdowns && !usuariosDropdown.length) { // Spinner inicial para dropdowns + if (loadingDropdowns && !usuariosDropdown.length) { // Un chequeo más específico para el spinner inicial return ; } - return ( Auditoría General del Sistema @@ -400,6 +717,15 @@ const AuditoriaGeneralPage: React.FC = () => { onChange={(e) => { setFiltroTipoEntidad(e.target.value); setFiltroIdEntidad(''); + setFiltroIdPublicacionParaSeccion(''); + setFiltroIdZonaParaRecargo(''); + setFiltroIdDistribuidorParaPorc(''); + setFiltroIdCanillitaParaPorcMon(''); + setFiltroIdEmpresa(''); + setFiltroFechaAfectadaParaCtrlDev(''); + setFiltroIdTipoBobina(''); + setFiltroIdPlantaStock(''); + setFiltroIdEstadoBobina(''); setDatosAuditoria([]); setColumnasActuales(getCommonAuditColumns()); if (error && error.includes("Debe seleccionar un 'Tipo de Entidad")) setError(null); @@ -422,6 +748,170 @@ const AuditoriaGeneralPage: React.FC = () => { {TIPOS_MODIFICACION.map((t) => ({t}))} + + {/* Filtros condicionales */} + {(filtroTipoEntidad === "ControlDevoluciones" || filtroTipoEntidad === "SaldoAjuste") && ( + + Empresa + + + )} + {filtroTipoEntidad === "ControlDevoluciones" && ( + setFiltroFechaAfectadaParaCtrlDev(e.target.value)} + InputLabelProps={{ shrink: true }} + sx={{ minWidth: 170 }} + disabled={loading} + /> + )} + {(filtroTipoEntidad === "PubliSeccion" || filtroTipoEntidad === "PrecioPublicacion" || filtroTipoEntidad === "RecargoZona" || filtroTipoEntidad === "PorcPagoDistribuidor" || filtroTipoEntidad === "PorcMonCanilla") && ( + + Publicación (Filtro Opc.) + + + )} + {(filtroTipoEntidad === "RecargoZona") && ( + + Zona (Filtro Opc.) + + + )} + {(filtroTipoEntidad === "PorcPagoDistribuidor") && ( + + Distribuidor (Filtro Opc.) + + + )} + {(filtroTipoEntidad === "PorcMonCanilla") && ( + + Canillita/Accionista (Filtro Opc.) + + + )} + {filtroTipoEntidad === "StockBobina" && ( + <> + + Tipo Bobina + + + + Planta + + + + Estado Bobina + + + + )} + {filtroTipoEntidad === "RegTirada" && ( + <> + {/* Select de Publicación (reutiliza filtroIdPublicacionParaSeccion) */} + + Publicación (Filtro) + + + + Planta (Filtro) + + + setFiltroFechaTirada(e.target.value)} InputLabelProps={{ shrink: true }} + sx={{ minWidth: 170 }} + disabled={loading} + /> + + )} + {filtroTipoEntidad === "RegSeccionTirada" && ( + <> + + Publicación (Filtro) + + + + Planta (Filtro) + + + setFiltroFechaTirada(e.target.value)} InputLabelProps={{ shrink: true }} + sx={{ minWidth: 170 }} + disabled={loading} + /> + + Sección (Filtro) + + + + )} + {(filtroTipoEntidad === "PermisosPerfiles") && ( + <> + + Perfil (Filtro) + + + + Permiso (Filtro) + + + + )} @@ -432,25 +922,25 @@ const AuditoriaGeneralPage: React.FC = () => { {error && !error.includes("Debe seleccionar un 'Tipo de Entidad") && !loading && {error}} {!loading && !error && filtroTipoEntidad && ( - {/* Ajustar altura */} + {/* Ajustar altura */} { setPage(model.page); setRowsPerPage(model.pageSize); }} pageSizeOptions={[25, 50, 100]} - rowHeight={48} + rowHeight={40} // Reducir un poco la altura de fila para más datos sx={{ '& .MuiDataGrid-cell': { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }, '& .MuiDataGrid-columnHeaderTitleContainer': { overflow: 'hidden' }, }} - getRowId={(row) => row.id} // Asegurar que getRowId use el 'id' que generamos + getRowId={(row) => row.id} /> )} diff --git a/Frontend/src/pages/Contables/GestionarNotasCDPage.tsx b/Frontend/src/pages/Contables/GestionarNotasCDPage.tsx index 32f2a08..591b965 100644 --- a/Frontend/src/pages/Contables/GestionarNotasCDPage.tsx +++ b/Frontend/src/pages/Contables/GestionarNotasCDPage.tsx @@ -208,7 +208,7 @@ const GestionarNotasCDPage: React.FC = () => { FechaEmpresaDestino DestinatarioTipo - MontoReferenciaObs. + MontoReferencia InternaReferencia {(puedeModificar || puedeEliminar) && Acciones} diff --git a/Frontend/src/pages/Distribucion/GestionarPreciosPublicacionPage.tsx b/Frontend/src/pages/Distribucion/GestionarPreciosPublicacionPage.tsx index 0e44152..e89a57e 100644 --- a/Frontend/src/pages/Distribucion/GestionarPreciosPublicacionPage.tsx +++ b/Frontend/src/pages/Distribucion/GestionarPreciosPublicacionPage.tsx @@ -4,7 +4,9 @@ import { useParams, useNavigate } from 'react-router-dom'; import { Box, Typography, Button, Paper, IconButton, Menu, MenuItem, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, - CircularProgress, Alert, Chip + CircularProgress, Alert, Chip, // Alert se sigue usando para el error de carga general + ListItemIcon, + ListItemText } from '@mui/material'; import AddIcon from '@mui/icons-material/Add'; import MoreVertIcon from '@mui/icons-material/MoreVert'; @@ -30,11 +32,11 @@ const GestionarPreciosPublicacionPage: React.FC = () => { const [publicacion, setPublicacion] = useState(null); const [precios, setPrecios] = useState([]); const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); + const [error, setError] = useState(null); // Para errores de carga de datos const [modalOpen, setModalOpen] = useState(false); - const [editingPrecio, setEditingPrecio] = useState(null); // Este estado determina si el modal edita - const [apiErrorMessage, setApiErrorMessage] = useState(null); + const [editingPrecio, setEditingPrecio] = useState(null); + const [apiErrorMessage, setApiErrorMessage] = useState(null); // Este se pasa al modal const [anchorEl, setAnchorEl] = useState(null); const [selectedPrecioRow, setSelectedPrecioRow] = useState(null); @@ -54,7 +56,9 @@ const GestionarPreciosPublicacionPage: React.FC = () => { return; } - setLoading(true); setError(null); setApiErrorMessage(null); + setLoading(true); + setError(null); // Limpiar error de carga al reintentar + setApiErrorMessage(null); // Limpiar error de API de acciones previas try { const [pubData, preciosData] = await Promise.all([ publicacionService.getPublicacionById(idPublicacion), @@ -79,41 +83,48 @@ const GestionarPreciosPublicacionPage: React.FC = () => { }, [cargarDatos]); const handleOpenModal = (precio?: PrecioDto) => { - setEditingPrecio(precio || null); // Si hay 'precio', el modal estará en modo edición - setApiErrorMessage(null); + setEditingPrecio(precio || null); + setApiErrorMessage(null); // Limpiar error de API al abrir el modal setModalOpen(true); }; const handleCloseModal = () => { setModalOpen(false); setEditingPrecio(null); + // No limpiar apiErrorMessage aquí, el modal lo hace o se limpia al abrir de nuevo }; - // CORREGIDO: El segundo parámetro 'idPrecio' determina si es edición const handleSubmitModal = async (data: CreatePrecioDto | UpdatePrecioDto, idPrecio?: number) => { setApiErrorMessage(null); try { - // Si idPrecio tiene valor, Y editingPrecio (initialData del modal) también lo tenía, es una actualización if (idPrecio && editingPrecio) { await precioService.updatePrecio(idPublicacion, idPrecio, data as UpdatePrecioDto); } else { await precioService.createPrecio(idPublicacion, data as CreatePrecioDto); } - cargarDatos(); // Recargar lista + cargarDatos(); + // El modal se cerrará solo si el submit es exitoso (controlado en el modal) } catch (err: any) { - const message = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al guardar el período de precio.'; - setApiErrorMessage(message); throw err; // Re-lanzar para que el modal maneje el estado de error + const message = axios.isAxiosError(err) && err.response?.data?.message + ? err.response.data.message + : 'Error al guardar el período de precio.'; + setApiErrorMessage(message); // Pasar el error al estado que se pasa al modal + throw err; // Re-lanzar para que el modal NO se cierre } }; const handleDelete = async (idPrecio: number) => { if (window.confirm(`¿Está seguro de eliminar este período de precio (ID: ${idPrecio})? Esta acción puede afectar la vigencia de períodos anteriores.`)) { - setApiErrorMessage(null); + setApiErrorMessage(null); // Limpiar antes de la acción try { await precioService.deletePrecio(idPublicacion, idPrecio); cargarDatos(); } catch (err: any) { - const message = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al eliminar el período de precio.'; - setApiErrorMessage(message); + const message = axios.isAxiosError(err) && err.response?.data?.message + ? err.response.data.message + : 'Error al eliminar el período de precio.'; + setApiErrorMessage(message); // Establecer error para mostrar EN EL MODAL si fuera necesario, o en un Alert genérico de la PÁGINA SI EL MODAL NO ESTÁ ABIERTO + // Si el modal de confirmación no es parte de un modal de formulario, este apiErrorMessage se mostraría en la página. + // Como es un window.confirm, el error se mostrará en el Alert de la página principal. } } handleMenuClose(); @@ -128,25 +139,38 @@ const GestionarPreciosPublicacionPage: React.FC = () => { const formatDate = (dateString?: string | null) => { if (!dateString) return '-'; - const date = new Date(dateString); // Asegurar que se parsee correctamente si viene con hora + const date = new Date(dateString); const day = String(date.getUTCDate()).padStart(2, '0'); - const month = String(date.getUTCMonth() + 1).padStart(2, '0'); // Meses son 0-indexados + const month = String(date.getUTCMonth() + 1).padStart(2, '0'); const year = date.getUTCFullYear(); return `${day}/${month}/${year}`; }; - if (loading) { return ; } - if (error) { - return {error}; + // Mostrar error de carga de datos de la página + if (error) { + return ( + + + {error} + + ); } if (!puedeGestionarPrecios) { - return Acceso denegado.; + return ( + + + No tiene permiso para gestionar precios. + + ); } - return ( - */} - - {puedeIngresar && ()} + {puedeIngresar && ()} {loading && } - {error && !loading && {error}} - {apiErrorMessage && {apiErrorMessage}} + {error && !loading && {error}} + {apiErrorMessage && {apiErrorMessage}} {!loading && !error && puedeVer && ( - - - - Nro. BobinaTipoPeso (Kg) - PlantaEstadoRemito - F. RemitoF. Estado - PublicaciónSección - Obs.Acciones - - - {displayData.length === 0 ? ( - No se encontraron bobinas con los filtros aplicados. - ) : ( - displayData.map((b) => ( - - {b.nroBobina}{b.nombreTipoBobina} - {b.peso}{b.nombrePlanta} - - {b.remito}{formatDate(b.fechaRemito)} - {formatDate(b.fechaEstado)} - {b.nombrePublicacion || '-'}{b.nombreSeccion || '-'} - {b.obs || '-'} - + +
+ + Nro. BobinaTipoPeso (Kg) + PlantaEstadoRemito + F. RemitoF. Estado + PublicaciónSección + Obs. + {/* Mostrar columna de acciones si tiene algún permiso de acción */} + {(puedeModificarDatos || puedeCambiarEstado || puedeEliminar) && Acciones} + + + {displayData.length === 0 ? ( + No se encontraron bobinas con los filtros aplicados. + ) : ( + displayData.map((b) => ( + + {b.nroBobina}{b.nombreTipoBobina} + {b.peso}{b.nombrePlanta} + + {b.remito}{formatDate(b.fechaRemito)} + {formatDate(b.fechaEstado)} + {b.nombrePublicacion || '-'}{b.nombreSeccion || '-'} + {b.obs || '-'} + {(puedeModificarDatos || puedeCambiarEstado || puedeEliminar) && ( + handleMenuOpen(e, b)} - disabled={!puedeModificarDatos && !puedeCambiarEstado && !puedeEliminar} + // El botón de menú se deshabilita si no hay NINGUNA acción posible para esa fila + disabled={ + !(b.idEstadoBobina === ID_ESTADO_DISPONIBLE && puedeModificarDatos) && + !(puedeCambiarEstado) && // Siempre se puede intentar cambiar estado (el modal lo validará) + !((b.idEstadoBobina === ID_ESTADO_DISPONIBLE || b.idEstadoBobina === ID_ESTADO_DANADA) && puedeEliminar) + } > - - - )))} - -
- -
- )} + + )} + + )))} + + + + + )} - {selectedBobina?.idEstadoBobina === 1 && puedeModificarDatos && ( - handleOpenEditModal(selectedBobina!)}> Editar Datos)} - {selectedBobina?.idEstadoBobina !== 3 && puedeCambiarEstado && ( // No se puede cambiar estado si está dañada - handleOpenCambioEstadoModal(selectedBobina!)}> Cambiar Estado)} - {selectedBobina?.idEstadoBobina === 1 && puedeEliminar && ( - handleDeleteBobina(selectedBobina!.idBobina)}> Eliminar Ingreso)} - {selectedBobina && selectedBobina.idEstadoBobina === 3 && (!puedeModificarDatos && !puedeCambiarEstado && !puedeEliminar) && Sin acciones} - {selectedBobina && selectedBobina.idEstadoBobina !== 1 && selectedBobina.idEstadoBobina !== 3 && (!puedeCambiarEstado) && Sin acciones} + {selectedBobinaForRowMenu && puedeModificarDatos && selectedBobinaForRowMenu.idEstadoBobina === ID_ESTADO_DISPONIBLE && ( + { handleOpenEditModal(selectedBobinaForRowMenu); handleMenuClose(); }}> + Editar Datos + + )} + {/* --- CAMBIO: Permitir cambiar estado incluso si está Dañada --- */} + {selectedBobinaForRowMenu && puedeCambiarEstado && ( + { handleOpenCambioEstadoModal(selectedBobinaForRowMenu); handleMenuClose(); }}> + Cambiar Estado + + )} + {/* --- CAMBIO: Permitir eliminar si está Disponible o Dañada --- */} + {selectedBobinaForRowMenu && puedeEliminar && + (selectedBobinaForRowMenu.idEstadoBobina === ID_ESTADO_DISPONIBLE || selectedBobinaForRowMenu.idEstadoBobina === ID_ESTADO_DANADA) && ( + handleDeleteBobina(selectedBobinaForRowMenu)}> + Eliminar Ingreso + + )} + {/* Lógica para el MenuItem "Sin acciones" */} + {selectedBobinaForRowMenu && + !((selectedBobinaForRowMenu.idEstadoBobina === ID_ESTADO_DISPONIBLE && puedeModificarDatos)) && + !(puedeCambiarEstado) && + !(((selectedBobinaForRowMenu.idEstadoBobina === ID_ESTADO_DISPONIBLE || selectedBobinaForRowMenu.idEstadoBobina === ID_ESTADO_DANADA) && puedeEliminar)) && + Sin acciones disponibles + } setApiErrorMessage(null)} /> - {selectedBobina && editModalOpen && + {editModalOpen && selectedBobina && setApiErrorMessage(null)} + open={editModalOpen} onClose={handleCloseEditModal} onSubmit={handleSubmitEditModal} + initialData={selectedBobina} errorMessage={apiErrorMessage} + clearErrorMessage={() => setApiErrorMessage(null)} /> } - {selectedBobina && cambioEstadoModalOpen && + {cambioEstadoModalOpen && selectedBobina && setApiErrorMessage(null)} + open={cambioEstadoModalOpen} onClose={handleCloseCambioEstadoModal} onSubmit={handleSubmitCambioEstadoModal} + bobinaActual={selectedBobina} errorMessage={apiErrorMessage} + clearErrorMessage={() => setApiErrorMessage(null)} /> }
diff --git a/Frontend/src/services/Auditoria/auditoriaService.ts b/Frontend/src/services/Auditoria/auditoriaService.ts index b50d688..0000d22 100644 --- a/Frontend/src/services/Auditoria/auditoriaService.ts +++ b/Frontend/src/services/Auditoria/auditoriaService.ts @@ -10,6 +10,25 @@ import type { TipoPagoHistorialDto } from '../../models/dtos/Auditoria/TipoPagoH import type { CanillaHistorialDto } from '../../models/dtos/Auditoria/CanillaHistorialDto'; import type { DistribuidorHistorialDto } from '../../models/dtos/Auditoria/DistribuidorHistorialDto'; import type { EmpresaHistorialDto } from '../../models/dtos/Auditoria/EmpresaHistorialDto'; +import type { ZonaHistorialDto } from '../../models/dtos/Auditoria/ZonaHistorialDto'; +import type { OtroDestinoHistorialDto } from '../../models/dtos/Auditoria/OtroDestinoHistorialDto'; +import type { PublicacionHistorialDto } from '../../models/dtos/Auditoria/PublicacionHistorialDto'; +import type { PubliSeccionHistorialDto } from '../../models/dtos/Auditoria/PubliSeccionHistorialDto'; +import type { PrecioHistorialDto } from '../../models/dtos/Auditoria/PrecioHistorialDto'; +import type { RecargoZonaHistorialDto } from '../../models/dtos/Auditoria/RecargoZonaHistorialDto'; +import type { PorcPagoHistorialDto } from '../../models/dtos/Auditoria/PorcPagoHistorialDto'; +import type { PorcMonCanillaHistorialDto } from '../../models/dtos/Auditoria/PorcMonCanillaHistorialDto'; +import type { ControlDevolucionesHistorialDto } from '../../models/dtos/Auditoria/ControlDevolucionesHistorialDto'; +import type { TipoBobinaHistorialDto } from '../../models/dtos/Auditoria/TipoBobinaHistorialDto'; +import type { EstadoBobinaHistorialDto } from '../../models/dtos/Auditoria/EstadoBobinaHistorialDto'; +import type { PlantaHistorialDto } from '../../models/dtos/Auditoria/PlantaHistorialDto'; +import type { StockBobinaHistorialDto } from '../../models/dtos/Auditoria/StockBobinaHistorialDto'; +import type { RegTiradaHistorialDto } from '../../models/dtos/Auditoria/RegTiradaHistorialDto'; +import type { RegSeccionTiradaHistorialDto } from '../../models/dtos/Auditoria/RegSeccionTiradaHistorialDto'; +import type { PerfilHistorialDto } from '../../models/dtos/Auditoria/PerfilHistorialDto'; +import type { PermisoHistorialDto } from '../../models/dtos/Auditoria/PermisoHistorialDto'; +import type { PermisosPerfilesHistorialDto } from '../../models/dtos/Auditoria/PermisosPerfilesHistorialDto'; +import type { CambioParadaHistorialDto } from '../../models/dtos/Auditoria/CambioParadaHistorialDto'; interface HistorialParamsComunes { fechaDesde?: string; // "yyyy-MM-dd" @@ -18,6 +37,103 @@ interface HistorialParamsComunes { tipoModificacion?: string; // Cambiado de tipoMod } +interface HistorialCambiosParadaParams extends HistorialParamsComunes { + idCanillaAfectado?: number; +} + +interface HistorialPermisosPerfilesParams extends HistorialParamsComunes { + idPerfilAfectado?: number; + idPermisoAfectado?: number; +} + +interface HistorialPermisosMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idPermisoAfectado?: number; +} + +interface HistorialPerfilesMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idPerfilAfectado?: number; +} + +interface HistorialRegSeccionesTiradaParams extends HistorialParamsComunes { + idTiradaAfectada?: number; // ID de bob_RegPublicaciones + idPublicacionFiltro?: number; + idSeccionFiltro?: number; + idPlantaFiltro?: number; + fechaTiradaFiltro?: string; +} + +interface HistorialRegTiradasParams extends HistorialParamsComunes { + idRegistroAfectado?: number; + idPublicacionFiltro?: number; + idPlantaFiltro?: number; + fechaTiradaFiltro?: string; // YYYY-MM-DD +} + +interface HistorialStockBobinasParams extends HistorialParamsComunes { + idBobinaAfectada?: number; + idTipoBobinaFiltro?: number; + idPlantaFiltro?: number; + idEstadoBobinaFiltro?: number; +} + +interface HistorialPlantasMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idPlantaAfectada?: number; +} + +interface HistorialEstadosBobinaMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idEstadoBobinaAfectado?: number; +} + +interface HistorialTiposBobinaMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idTipoBobinaAfectado?: number; +} + +interface HistorialControlDevolucionesParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idControlAfectado?: number; + idEmpresaAfectada?: number; + fechaAfectada?: string; // yyyy-MM-dd +} + +interface HistorialPorcMonCanillaMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idPorcMonAfectado?: number; + idPublicacionAfectada?: number; + idCanillaAfectada?: number; +} + +interface HistorialPorcPagoDistMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idPorcentajeAfectado?: number; + idPublicacionAfectada?: number; + idDistribuidorAfectado?: number; +} + +interface HistorialRecargosZonaMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idRecargoAfectado?: number; + idPublicacionAfectada?: number; + idZonaAfectada?: number; +} + +interface HistorialPreciosMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idPrecioAfectado?: number; + idPublicacionAfectada?: number; +} + +interface HistorialPubliSeccionesMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idSeccionAfectada?: number; + idPublicacionAfectada?: number; // Para filtrar por publicación si es necesario +} + +interface HistorialPublicacionesMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idPublicacionAfectada?: number; +} + +interface HistorialOtrosDestinosMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idOtroDestinoAfectado?: number; +} + +interface HistorialZonasMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ + idZonaAfectada?: number; +} + interface HistorialEmpresasMaestroParams extends HistorialParamsComunes { // << NUEVA INTERFAZ idEmpresaAfectada?: number; } @@ -173,6 +289,184 @@ const getHistorialEmpresasMaestro = async (params: HistorialEmpresasMaestroParam return response.data; }; +const getHistorialZonasMaestro = async (params: HistorialZonasMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/zonas-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialOtrosDestinosMaestro = async (params: HistorialOtrosDestinosMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/otros-destinos-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPublicacionesMaestro = async (params: HistorialPublicacionesMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/publicaciones-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPubliSeccionesMaestro = async (params: HistorialPubliSeccionesMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + // Pasar los nuevos filtros si están presentes + if (params.idPublicacionAfectada) queryParams.idPublicacionAfectada = params.idPublicacionAfectada; + + + const response = await apiClient.get('/auditoria/publi-secciones-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPreciosMaestro = async (params: HistorialPreciosMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + if (params.idPublicacionAfectada) queryParams.idPublicacionAfectada = params.idPublicacionAfectada; + + + const response = await apiClient.get('/auditoria/precios-publicacion-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialRecargosZonaMaestro = async (params: HistorialRecargosZonaMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + if (params.idPublicacionAfectada) queryParams.idPublicacionAfectada = params.idPublicacionAfectada; + if (params.idZonaAfectada) queryParams.idZonaAfectada = params.idZonaAfectada; + + + const response = await apiClient.get('/auditoria/recargos-zona-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPorcPagoDistMaestro = async (params: HistorialPorcPagoDistMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + if (params.idPublicacionAfectada) queryParams.idPublicacionAfectada = params.idPublicacionAfectada; + if (params.idDistribuidorAfectado) queryParams.idDistribuidorAfectado = params.idDistribuidorAfectado; + + + const response = await apiClient.get('/auditoria/porc-pago-dist-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPorcMonCanillaMaestro = async (params: HistorialPorcMonCanillaMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + if (params.idPublicacionAfectada) queryParams.idPublicacionAfectada = params.idPublicacionAfectada; + if (params.idCanillaAfectada) queryParams.idCanillaAfectada = params.idCanillaAfectada; + + const response = await apiClient.get('/auditoria/porc-mon-canilla-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialControlDevoluciones = async (params: HistorialControlDevolucionesParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + if (params.idEmpresaAfectada) queryParams.idEmpresaAfectada = params.idEmpresaAfectada; + if (params.fechaAfectada) queryParams.fechaAfectada = params.fechaAfectada; + + + const response = await apiClient.get('/auditoria/control-devoluciones-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialTiposBobinaMaestro = async (params: HistorialTiposBobinaMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/tipos-bobina-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialEstadosBobinaMaestro = async (params: HistorialEstadosBobinaMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/estados-bobina-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPlantasMaestro = async (params: HistorialPlantasMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/plantas-impresion-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialStockBobinas = async (params: HistorialStockBobinasParams): Promise => { + const queryParams: any = { ...params }; + const response = await apiClient.get('/auditoria/stock-bobinas-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialRegTiradas = async (params: HistorialRegTiradasParams): Promise => { + const queryParams: any = { ...params }; + const response = await apiClient.get('/auditoria/reg-tiradas-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialRegSeccionesTirada = async (params: HistorialRegSeccionesTiradaParams): Promise => { + const queryParams: any = { ...params }; + // ... (mapeo de idUsuarioModificador) + const response = await apiClient.get('/auditoria/reg-secciones-tirada-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPerfilesMaestro = async (params: HistorialPerfilesMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/perfiles-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPermisosMaestro = async (params: HistorialPermisosMaestroParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/permisos-maestro', { params: queryParams }); + return response.data; +}; + +const getHistorialPermisosPerfiles = async (params: HistorialPermisosPerfilesParams): Promise => { + const queryParams: any = { ...params }; + // ... mapeo de idUsuarioModificador ... + const response = await apiClient.get('/auditoria/permisos-perfiles-historial', { params: queryParams }); + return response.data; +}; + +const getHistorialCambiosParada = async (params: HistorialCambiosParadaParams): Promise => { + const queryParams: any = { ...params }; + if (params.idUsuarioModificador) queryParams.idUsuarioModifico = params.idUsuarioModificador; + delete queryParams.idUsuarioModificador; + + const response = await apiClient.get('/auditoria/cambios-parada-canilla', { params: queryParams }); + return response.data; +}; + const auditoriaService = { getHistorialUsuarios, getHistorialPagosDistribuidor, @@ -185,6 +479,25 @@ const auditoriaService = { getHistorialCanillitasMaestro, getHistorialDistribuidoresMaestro, getHistorialEmpresasMaestro, + getHistorialZonasMaestro, + getHistorialOtrosDestinosMaestro, + getHistorialPublicacionesMaestro, + getHistorialPubliSeccionesMaestro, + getHistorialPreciosMaestro, + getHistorialRecargosZonaMaestro, + getHistorialPorcPagoDistMaestro, + getHistorialPorcMonCanillaMaestro, + getHistorialControlDevoluciones, + getHistorialTiposBobinaMaestro, + getHistorialEstadosBobinaMaestro, + getHistorialPlantasMaestro, + getHistorialStockBobinas, + getHistorialRegTiradas, + getHistorialRegSeccionesTirada, + getHistorialPerfilesMaestro, + getHistorialPermisosMaestro, + getHistorialPermisosPerfiles, + getHistorialCambiosParada, }; export default auditoriaService; \ No newline at end of file