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
}
}