feat: Implementación módulos Empresas, Plantas, Tipos y Estados Bobina
Backend API:
- Implementado CRUD completo para Empresas (DE001-DE004):
  - EmpresaRepository, EmpresaService, EmpresasController.
  - Lógica de creación/eliminación de saldos iniciales en EmpresaService.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos específicos.
- Implementado CRUD completo para Plantas de Impresión (IP001-IP004):
  - PlantaRepository, PlantaService, PlantasController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
- Implementado CRUD completo para Tipos de Bobina (IB006-IB009):
  - TipoBobinaRepository, TipoBobinaService, TiposBobinaController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
- Implementado CRUD completo para Estados de Bobina (IB010-IB013):
  - EstadoBobinaRepository, EstadoBobinaService, EstadosBobinaController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
Frontend React:
- Módulo Empresas:
  - empresaService.ts para interactuar con la API.
  - EmpresaFormModal.tsx para crear/editar empresas.
  - GestionarEmpresasPage.tsx con tabla, filtro, paginación y menú de acciones.
  - Integración con el hook usePermissions para control de acceso.
- Módulo Plantas de Impresión:
  - plantaService.ts.
  - PlantaFormModal.tsx.
  - GestionarPlantasPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Módulo Tipos de Bobina:
  - tipoBobinaService.ts.
  - TipoBobinaFormModal.tsx.
  - GestionarTiposBobinaPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Módulo Estados de Bobina:
  - estadoBobinaService.ts.
  - EstadoBobinaFormModal.tsx.
  - GestionarEstadosBobinaPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Navegación:
  - Añadidas sub-pestañas y rutas para los nuevos módulos dentro de "Distribución" (Empresas) e "Impresión" (Plantas, Tipos Bobina, Estados Bobina).
  - Creado ImpresionIndexPage.tsx para la navegación interna del módulo de Impresión.
Correcciones:
- Corregido el uso de CommitAsync/RollbackAsync a Commit/Rollback síncronos en PlantaService.cs debido a que IDbTransaction no los soporta asíncronamente.
											 
										 
										
											2025-05-09 10:08:53 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								using  Dapper ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  GestionIntegral.Api.Models.Impresion ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  Microsoft.Extensions.Logging ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  System.Collections.Generic ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  System.Data ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  System.Text ;  // Para StringBuilder  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  System.Threading.Tasks ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								namespace  GestionIntegral.Api.Data.Repositories.Impresion  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								{  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  class  PlantaRepository  :  IPlantaRepository 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  readonly  DbConnectionFactory  _connectionFactory ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  readonly  ILogger < PlantaRepository >  _logger ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  PlantaRepository ( DbConnectionFactory  connectionFactory ,  ILogger < PlantaRepository >  logger ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            _connectionFactory  =  connectionFactory ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            _logger  =  logger ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  async  Task < IEnumerable < Planta > >  GetAllAsync ( string?  nombreFilter ,  string?  detalleFilter ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  sqlBuilder  =  new  StringBuilder ( "SELECT Id_Planta AS IdPlanta, Nombre, Detalle FROM dbo.bob_dtPlantas WHERE 1=1" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  parameters  =  new  DynamicParameters ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( ! string . IsNullOrWhiteSpace ( nombreFilter ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                sqlBuilder . Append ( " AND Nombre LIKE @NombreFilter" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                parameters . Add ( "NombreFilter" ,  $"%{nombreFilter}%" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( ! string . IsNullOrWhiteSpace ( detalleFilter ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                sqlBuilder . Append ( " AND Detalle LIKE @DetalleFilter" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                parameters . Add ( "DetalleFilter" ,  $"%{detalleFilter}%" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            sqlBuilder . Append ( " ORDER BY Nombre;" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                using  var  connection  =  _connectionFactory . CreateConnection ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  await  connection . QueryAsync < Planta > ( sqlBuilder . ToString ( ) ,  parameters ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            catch  ( Exception  ex ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                _logger . LogError ( ex ,  "Error al obtener todas las Plantas. Filtros: Nombre={Nombre}, Detalle={Detalle}" ,  nombreFilter ,  detalleFilter ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  Enumerable . Empty < Planta > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  async  Task < Planta ? >  GetByIdAsync ( int  id ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sql  =  "SELECT Id_Planta AS IdPlanta, Nombre, Detalle FROM dbo.bob_dtPlantas WHERE Id_Planta = @Id" ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                using  var  connection  =  _connectionFactory . CreateConnection ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  await  connection . QuerySingleOrDefaultAsync < Planta > ( sql ,  new  {  Id  =  id  } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            catch  ( Exception  ex ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                _logger . LogError ( ex ,  "Error al obtener Planta por ID: {IdPlanta}" ,  id ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  null ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  async  Task < bool >  ExistsByNameAsync ( string  nombre ,  int?  excludeId  =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  sqlBuilder  =  new  StringBuilder ( "SELECT COUNT(1) FROM dbo.bob_dtPlantas WHERE Nombre = @Nombre" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  parameters  =  new  DynamicParameters ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            parameters . Add ( "Nombre" ,  nombre ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( excludeId . HasValue ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                sqlBuilder . Append ( " AND Id_Planta != @ExcludeId" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                parameters . Add ( "ExcludeId" ,  excludeId . Value ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                using  var  connection  =  _connectionFactory . CreateConnection ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                var  count  =  await  connection . ExecuteScalarAsync < int > ( sqlBuilder . ToString ( ) ,  parameters ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  count  >  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            catch  ( Exception  ex ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                _logger . LogError ( ex ,  "Error en ExistsByNameAsync para Planta con nombre: {Nombre}" ,  nombre ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // Asumir que existe en caso de error para prevenir duplicados accidentales 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  async  Task < bool >  IsInUseAsync ( int  id ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Verificar si la planta está referenciada en bob_StockBobinas O bob_RegPublicaciones 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sqlCheckStock  =  "SELECT TOP 1 1 FROM dbo.bob_StockBobinas WHERE Id_Planta = @IdPlanta" ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sqlCheckRegPubli  =  "SELECT TOP 1 1 FROM dbo.bob_RegPublicaciones WHERE Id_Planta = @IdPlanta" ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                using  var  connection  =  _connectionFactory . CreateConnection ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                var  inStock  =  await  connection . ExecuteScalarAsync < int? > ( sqlCheckStock ,  new  {  IdPlanta  =  id  } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( inStock . HasValue  & &  inStock . Value  = =  1 )  return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                var  inRegPubli  =  await  connection . ExecuteScalarAsync < int? > ( sqlCheckRegPubli ,  new  {  IdPlanta  =  id  } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  inRegPubli . HasValue  & &  inRegPubli . Value  = =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            catch  ( Exception  ex ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                _logger . LogError ( ex ,  "Error en IsInUseAsync para Planta ID: {IdPlanta}" ,  id ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // Asumir que está en uso si hay error para prevenir borrado incorrecto 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // --- Métodos de Escritura (USAN TRANSACCIÓN PASADA DESDE EL SERVICIO) --- 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  async  Task < Planta ? >  CreateAsync ( Planta  nuevaPlanta ,  int  idUsuario ,  IDbTransaction  transaction ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sqlInsert  =  @ "
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                INSERT  INTO  dbo . bob_dtPlantas  ( Nombre ,  Detalle ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                OUTPUT  INSERTED . Id_Planta  AS  IdPlanta ,  INSERTED . Nombre ,  INSERTED . Detalle 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                VALUES  ( @Nombre ,  @Detalle ) ; ";
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sqlInsertHistorico  =  @ "
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                INSERT  INTO  dbo . bob_dtPlantas_H  ( Id_Planta ,  Nombre ,  Detalle ,  Id_Usuario ,  FechaMod ,  TipoMod ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                VALUES  ( @IdPlanta ,  @Nombre ,  @Detalle ,  @IdUsuario ,  @FechaMod ,  @TipoMod ) ; ";
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Dapper requiere que la conexión esté asociada a la transacción 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  connection  =  transaction . Connection  ? ?  throw  new  InvalidOperationException ( "Transaction has no associated connection." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  insertedPlanta  =  await  connection . QuerySingleAsync < Planta > ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                sqlInsert , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                new  {  nuevaPlanta . Nombre ,  nuevaPlanta . Detalle  } , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                transaction :  transaction  // Pasar la transacción 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( insertedPlanta  = =  null  | |  insertedPlanta . IdPlanta  < =  0 ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                throw  new  DataException ( "No se pudo obtener el ID de la planta insertada." ) ;  // Usar DataException 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Insertar en historial 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            await  connection . ExecuteAsync ( sqlInsertHistorico ,  new 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                IdPlanta  =  insertedPlanta . IdPlanta , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                insertedPlanta . Nombre , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                insertedPlanta . Detalle , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                IdUsuario  =  idUsuario , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                FechaMod  =  DateTime . Now , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                TipoMod  =  "Insertada" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } ,  transaction :  transaction ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  insertedPlanta ;  // Devolver la entidad con el ID 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  async  Task < bool >  UpdateAsync ( Planta  plantaAActualizar ,  int  idUsuario ,  IDbTransaction  transaction ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
									
										
										
										
											2025-06-12 19:36:21 -03:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            // El servicio ya verificó que existe. Obtener estado actual para historial dentro de la transacción. 
							 
						 
					
						
							
								
									
										
											 
										
											
												feat: Implementación módulos Empresas, Plantas, Tipos y Estados Bobina
Backend API:
- Implementado CRUD completo para Empresas (DE001-DE004):
  - EmpresaRepository, EmpresaService, EmpresasController.
  - Lógica de creación/eliminación de saldos iniciales en EmpresaService.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos específicos.
- Implementado CRUD completo para Plantas de Impresión (IP001-IP004):
  - PlantaRepository, PlantaService, PlantasController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
- Implementado CRUD completo para Tipos de Bobina (IB006-IB009):
  - TipoBobinaRepository, TipoBobinaService, TiposBobinaController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
- Implementado CRUD completo para Estados de Bobina (IB010-IB013):
  - EstadoBobinaRepository, EstadoBobinaService, EstadosBobinaController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
Frontend React:
- Módulo Empresas:
  - empresaService.ts para interactuar con la API.
  - EmpresaFormModal.tsx para crear/editar empresas.
  - GestionarEmpresasPage.tsx con tabla, filtro, paginación y menú de acciones.
  - Integración con el hook usePermissions para control de acceso.
- Módulo Plantas de Impresión:
  - plantaService.ts.
  - PlantaFormModal.tsx.
  - GestionarPlantasPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Módulo Tipos de Bobina:
  - tipoBobinaService.ts.
  - TipoBobinaFormModal.tsx.
  - GestionarTiposBobinaPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Módulo Estados de Bobina:
  - estadoBobinaService.ts.
  - EstadoBobinaFormModal.tsx.
  - GestionarEstadosBobinaPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Navegación:
  - Añadidas sub-pestañas y rutas para los nuevos módulos dentro de "Distribución" (Empresas) e "Impresión" (Plantas, Tipos Bobina, Estados Bobina).
  - Creado ImpresionIndexPage.tsx para la navegación interna del módulo de Impresión.
Correcciones:
- Corregido el uso de CommitAsync/RollbackAsync a Commit/Rollback síncronos en PlantaService.cs debido a que IDbTransaction no los soporta asíncronamente.
											 
										 
										
											2025-05-09 10:08:53 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            var  connection  =  transaction . Connection ! ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  plantaActual  =  await  connection . QuerySingleOrDefaultAsync < Planta > ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                "SELECT Id_Planta AS IdPlanta, Nombre, Detalle FROM dbo.bob_dtPlantas WHERE Id_Planta = @Id" , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                 new  {  Id  =  plantaAActualizar . IdPlanta  } , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                 transaction ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( plantaActual  = =  null )  throw  new  KeyNotFoundException ( $"No se encontró la planta con ID {plantaAActualizar.IdPlanta} para actualizar." ) ;  // Más específico 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sqlUpdate  =  @ "
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                UPDATE  dbo . bob_dtPlantas 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                SET  Nombre  =  @Nombre ,  Detalle  =  @Detalle 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                WHERE  Id_Planta  =  @IdPlanta ; ";
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sqlInsertHistorico  =  @ "
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                INSERT  INTO  dbo . bob_dtPlantas_H  ( Id_Planta ,  Nombre ,  Detalle ,  Id_Usuario ,  FechaMod ,  TipoMod ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                VALUES  ( @IdPlanta ,  @NombreActual ,  @DetalleActual ,  @IdUsuario ,  @FechaMod ,  @TipoMod ) ; ";
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Insertar en historial (estado anterior) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            await  connection . ExecuteAsync ( sqlInsertHistorico ,  new 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                IdPlanta  =  plantaActual . IdPlanta , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                NombreActual  =  plantaActual . Nombre , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                DetalleActual  =  plantaActual . Detalle , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                IdUsuario  =  idUsuario , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                FechaMod  =  DateTime . Now , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                TipoMod  =  "Modificada" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } ,  transaction :  transaction ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Actualizar principal 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  rowsAffected  =  await  connection . ExecuteAsync ( sqlUpdate ,  new 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                plantaAActualizar . Nombre , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                plantaAActualizar . Detalle , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                plantaAActualizar . IdPlanta 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } ,  transaction :  transaction ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  rowsAffected  = =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  async  Task < bool >  DeleteAsync ( int  id ,  int  idUsuario ,  IDbTransaction  transaction ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  connection  =  transaction . Connection ! ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Obtener datos para historial ANTES de borrar 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  plantaActual  =  await  connection . QuerySingleOrDefaultAsync < Planta > ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                 "SELECT Id_Planta AS IdPlanta, Nombre, Detalle FROM dbo.bob_dtPlantas WHERE Id_Planta = @Id" , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                 new  {  Id  =  id  } , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                 transaction ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( plantaActual  = =  null )  throw  new  KeyNotFoundException ( $"No se encontró la planta con ID {id} para eliminar." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sqlDelete  =  "DELETE FROM dbo.bob_dtPlantas WHERE Id_Planta = @Id" ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            const  string  sqlInsertHistorico  =  @ "
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                INSERT  INTO  dbo . bob_dtPlantas_H  ( Id_Planta ,  Nombre ,  Detalle ,  Id_Usuario ,  FechaMod ,  TipoMod ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                VALUES  ( @IdPlanta ,  @Nombre ,  @Detalle ,  @IdUsuario ,  @FechaMod ,  @TipoMod ) ; ";
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Insertar en historial (estado antes de borrar) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            await  connection . ExecuteAsync ( sqlInsertHistorico ,  new 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                IdPlanta  =  plantaActual . IdPlanta , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                plantaActual . Nombre , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                plantaActual . Detalle , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                IdUsuario  =  idUsuario , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                FechaMod  =  DateTime . Now , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                TipoMod  =  "Eliminada" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } ,  transaction :  transaction ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Eliminar de la tabla principal 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  rowsAffected  =  await  connection . ExecuteAsync ( sqlDelete ,  new  {  Id  =  id  } ,  transaction :  transaction ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  rowsAffected  = =  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
									
										
										
										
											2025-06-12 19:36:21 -03:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        public  async  Task < IEnumerable < ( PlantaHistorico  Historial ,  string  NombreUsuarioModifico ) > >  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 < PlantaHistorico ,  string ,  ( PlantaHistorico ,  string ) > ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    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 ) > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
									
										
											 
										
											
												feat: Implementación módulos Empresas, Plantas, Tipos y Estados Bobina
Backend API:
- Implementado CRUD completo para Empresas (DE001-DE004):
  - EmpresaRepository, EmpresaService, EmpresasController.
  - Lógica de creación/eliminación de saldos iniciales en EmpresaService.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos específicos.
- Implementado CRUD completo para Plantas de Impresión (IP001-IP004):
  - PlantaRepository, PlantaService, PlantasController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
- Implementado CRUD completo para Tipos de Bobina (IB006-IB009):
  - TipoBobinaRepository, TipoBobinaService, TiposBobinaController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
- Implementado CRUD completo para Estados de Bobina (IB010-IB013):
  - EstadoBobinaRepository, EstadoBobinaService, EstadosBobinaController.
  - Transacciones y registro en tablas _H.
  - Verificación de permisos.
Frontend React:
- Módulo Empresas:
  - empresaService.ts para interactuar con la API.
  - EmpresaFormModal.tsx para crear/editar empresas.
  - GestionarEmpresasPage.tsx con tabla, filtro, paginación y menú de acciones.
  - Integración con el hook usePermissions para control de acceso.
- Módulo Plantas de Impresión:
  - plantaService.ts.
  - PlantaFormModal.tsx.
  - GestionarPlantasPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Módulo Tipos de Bobina:
  - tipoBobinaService.ts.
  - TipoBobinaFormModal.tsx.
  - GestionarTiposBobinaPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Módulo Estados de Bobina:
  - estadoBobinaService.ts.
  - EstadoBobinaFormModal.tsx.
  - GestionarEstadosBobinaPage.tsx con tabla, filtro, paginación y acciones.
  - Integración con usePermissions.
- Navegación:
  - Añadidas sub-pestañas y rutas para los nuevos módulos dentro de "Distribución" (Empresas) e "Impresión" (Plantas, Tipos Bobina, Estados Bobina).
  - Creado ImpresionIndexPage.tsx para la navegación interna del módulo de Impresión.
Correcciones:
- Corregido el uso de CommitAsync/RollbackAsync a Commit/Rollback síncronos en PlantaService.cs debido a que IDbTransaction no los soporta asíncronamente.
											 
										 
										
											2025-05-09 10:08:53 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}