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.Distribucion ;
using System.Collections.Generic ;
using System.Data ;
using System.Text ; // Para StringBuilder
using System.Threading.Tasks ;
namespace GestionIntegral.Api.Data.Repositories.Distribucion
{
public class ZonaRepository : IZonaRepository
{
private readonly DbConnectionFactory _connectionFactory ;
private readonly ILogger < ZonaRepository > _logger ;
public ZonaRepository ( DbConnectionFactory connectionFactory , ILogger < ZonaRepository > logger )
{
_connectionFactory = connectionFactory ;
_logger = logger ;
}
public async Task < IEnumerable < Zona > > GetAllAsync ( string? nombreFilter , string? descripcionFilter , bool soloActivas = true )
{
var sqlBuilder = new StringBuilder ( "SELECT Id_Zona AS IdZona, Nombre, Descripcion, Estado FROM dbo.dist_dtZonas WHERE 1=1" ) ;
var parameters = new DynamicParameters ( ) ;
if ( soloActivas )
{
sqlBuilder . Append ( " AND Estado = 1" ) ;
}
if ( ! string . IsNullOrWhiteSpace ( nombreFilter ) )
{
sqlBuilder . Append ( " AND Nombre LIKE @NombreFilter" ) ;
parameters . Add ( "NombreFilter" , $"%{nombreFilter}%" ) ;
}
if ( ! string . IsNullOrWhiteSpace ( descripcionFilter ) )
{
sqlBuilder . Append ( " AND Descripcion LIKE @DescripcionFilter" ) ;
parameters . Add ( "DescripcionFilter" , $"%{descripcionFilter}%" ) ;
}
sqlBuilder . Append ( " ORDER BY Nombre;" ) ;
try
{
using ( var connection = _connectionFactory . CreateConnection ( ) )
{
return await connection . QueryAsync < Zona > ( sqlBuilder . ToString ( ) , parameters ) ;
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error al obtener todas las Zonas. Filtros: Nombre={NombreFilter}, Descripcion={DescFilter}" , nombreFilter , descripcionFilter ) ;
return Enumerable . Empty < Zona > ( ) ;
}
}
public async Task < Zona ? > GetByIdAsync ( int id , bool soloActivas = true )
{
var sqlBuilder = new StringBuilder ( "SELECT Id_Zona AS IdZona, Nombre, Descripcion, Estado FROM dbo.dist_dtZonas WHERE Id_Zona = @Id" ) ;
if ( soloActivas )
{
sqlBuilder . Append ( " AND Estado = 1" ) ;
}
try
{
using ( var connection = _connectionFactory . CreateConnection ( ) )
{
return await connection . QuerySingleOrDefaultAsync < Zona > ( sqlBuilder . ToString ( ) , new { Id = id } ) ;
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error al obtener Zona por ID: {IdZona}" , id ) ;
return null ;
}
}
public async Task < bool > ExistsByNameAsync ( string nombre , int? excludeId = null , bool soloActivas = true )
{
var sqlBuilder = new StringBuilder ( "SELECT COUNT(1) FROM dbo.dist_dtZonas WHERE Nombre = @Nombre" ) ;
var parameters = new DynamicParameters ( ) ;
parameters . Add ( "Nombre" , nombre ) ;
if ( soloActivas )
{
sqlBuilder . Append ( " AND Estado = 1" ) ;
}
if ( excludeId . HasValue )
{
sqlBuilder . Append ( " AND Id_Zona != @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 Zona con nombre: {Nombre}" , nombre ) ;
return true ;
}
}
public async Task < Zona ? > CreateAsync ( Zona nuevaZona , int idUsuario )
{
// Las nuevas zonas siempre se crean con Estado = 1
var sqlInsert = @ "
INSERT INTO dbo . dist_dtZonas ( Nombre , Descripcion , Estado )
OUTPUT INSERTED . Id_Zona AS IdZona , INSERTED . Nombre , INSERTED . Descripcion , INSERTED . Estado
VALUES ( @Nombre , @Descripcion , 1 ) ; ";
var sqlInsertHistorico = @ "
INSERT INTO dbo . dist_dtZonas_H ( Id_Zona , Nombre , Descripcion , Estado , Id_Usuario , FechaMod , TipoMod )
VALUES ( @IdZona , @Nombre , @Descripcion , @Estado , @IdUsuario , @FechaMod , @TipoMod ) ; ";
try
{
using ( var connection = _connectionFactory . CreateConnection ( ) )
{
connection . Open ( ) ;
using ( var transaction = connection . BeginTransaction ( ) )
{
try
{
var insertedZona = await connection . QuerySingleAsync < Zona > (
sqlInsert ,
new { nuevaZona . Nombre , nuevaZona . Descripcion } ,
transaction : transaction
) ;
await connection . ExecuteAsync ( sqlInsertHistorico , new
{
IdZona = insertedZona . IdZona ,
insertedZona . Nombre ,
insertedZona . Descripcion ,
Estado = insertedZona . Estado , // Será true (1)
IdUsuario = idUsuario ,
FechaMod = DateTime . Now ,
TipoMod = "Insertada"
} , transaction : transaction ) ;
transaction . Commit ( ) ;
return insertedZona ;
}
catch ( Exception exTrans )
{
transaction . Rollback ( ) ;
_logger . LogError ( exTrans , "Error en transacción CreateAsync para Zona. Nombre: {Nombre}" , nuevaZona . Nombre ) ;
return null ;
}
}
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error general en CreateAsync para Zona. Nombre: {Nombre}" , nuevaZona . Nombre ) ;
return null ;
}
}
public async Task < bool > UpdateAsync ( Zona zonaAActualizar , int idUsuario )
{
var zonaActual = await GetByIdAsync ( zonaAActualizar . IdZona , soloActivas : false ) ; // Obtener incluso si está inactiva para el historial
if ( zonaActual = = null ) return false ;
// Solo actualizamos si está activa, pero el historial registra el estado que tenía.
// El servicio decidirá si se puede actualizar una zona inactiva.
var sqlUpdate = @ "
UPDATE dbo . dist_dtZonas
SET Nombre = @Nombre , Descripcion = @Descripcion
WHERE Id_Zona = @IdZona ; "; // Podríamos añadir AND Estado = 1 si el servicio lo requiere así estrictamente.
var sqlInsertHistorico = @ "
INSERT INTO dbo . dist_dtZonas_H ( Id_Zona , Nombre , Descripcion , Estado , Id_Usuario , FechaMod , TipoMod )
VALUES ( @IdZona , @NombreActual , @DescripcionActual , @EstadoActual , @IdUsuario , @FechaMod , @TipoMod ) ; ";
try
{
using ( var connection = _connectionFactory . CreateConnection ( ) )
{
connection . Open ( ) ;
using ( var transaction = connection . BeginTransaction ( ) )
{
try
{
// Registrar el estado *antes* de la modificación
await connection . ExecuteAsync ( sqlInsertHistorico , new
{
IdZona = zonaActual . IdZona ,
NombreActual = zonaActual . Nombre ,
DescripcionActual = zonaActual . Descripcion ,
EstadoActual = zonaActual . Estado ,
IdUsuario = idUsuario ,
FechaMod = DateTime . Now ,
TipoMod = "Modificada"
} , transaction : transaction ) ;
var rowsAffected = await connection . ExecuteAsync ( sqlUpdate , new
{
zonaAActualizar . Nombre ,
zonaAActualizar . Descripcion ,
zonaAActualizar . IdZona
} , transaction : transaction ) ;
transaction . Commit ( ) ;
return rowsAffected = = 1 ;
}
catch ( Exception exTrans )
{
transaction . Rollback ( ) ;
_logger . LogError ( exTrans , "Error en transacción UpdateAsync para Zona ID: {IdZona}" , zonaAActualizar . IdZona ) ;
return false ;
}
}
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error general en UpdateAsync para Zona ID: {IdZona}" , zonaAActualizar . IdZona ) ;
return false ;
}
}
public async Task < bool > SoftDeleteAsync ( int id , int idUsuario )
{
var zonaActual = await GetByIdAsync ( id , soloActivas : false ) ; // Obtenerla sin importar su estado para el historial
if ( zonaActual = = null ) return false ;
if ( ! zonaActual . Estado ) return true ; // Ya está "eliminada" (inactiva), considerar éxito
var sqlSoftDelete = @"UPDATE dbo.dist_dtZonas SET Estado = 0 WHERE Id_Zona = @IdZona;" ;
var sqlInsertHistorico = @ "
INSERT INTO dbo . dist_dtZonas_H ( Id_Zona , Nombre , Descripcion , Estado , Id_Usuario , FechaMod , TipoMod )
VALUES ( @IdZona , @Nombre , @Descripcion , @EstadoNuevo , @IdUsuario , @FechaMod , @TipoMod ) ; ";
try
{
using ( var connection = _connectionFactory . CreateConnection ( ) )
{
connection . Open ( ) ;
using ( var transaction = connection . BeginTransaction ( ) )
{
try
{
// Actualizar la tabla principal
var rowsAffected = await connection . ExecuteAsync ( sqlSoftDelete , new { IdZona = id } , transaction : transaction ) ;
if ( rowsAffected = = 1 )
{
// Registrar en el historial con el nuevo estado (0)
await connection . ExecuteAsync ( sqlInsertHistorico , new
{
IdZona = zonaActual . IdZona ,
zonaActual . Nombre ,
zonaActual . Descripcion ,
EstadoNuevo = false , // El estado después del soft delete
IdUsuario = idUsuario ,
FechaMod = DateTime . Now ,
TipoMod = "Eliminada" // O "Deshabilitada"
} , transaction : transaction ) ;
}
2025-06-12 19:36:21 -03:00
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
transaction . Commit ( ) ;
return rowsAffected = = 1 ;
}
catch ( Exception exTrans )
{
transaction . Rollback ( ) ;
_logger . LogError ( exTrans , "Error en transacción SoftDeleteAsync para Zona ID: {IdZona}" , id ) ;
return false ;
}
}
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error general en SoftDeleteAsync para Zona ID: {IdZona}" , id ) ;
return false ;
}
}
public async Task < bool > IsInUseAsync ( int id )
{
// Verifica en dist_dtDistribuidores y dist_dtCanillas
var sqlCheckDist = "SELECT COUNT(1) FROM dbo.dist_dtDistribuidores WHERE Id_Zona = @IdZona" ;
var sqlCheckCanillas = "SELECT COUNT(1) FROM dbo.dist_dtCanillas WHERE Id_Zona = @IdZona AND Baja = 0" ; // Solo canillas activos
try
{
using ( var connection = _connectionFactory . CreateConnection ( ) )
{
var countDist = await connection . ExecuteScalarAsync < int > ( sqlCheckDist , new { IdZona = id } ) ;
if ( countDist > 0 ) return true ;
var countCanillas = await connection . ExecuteScalarAsync < int > ( sqlCheckCanillas , new { IdZona = id } ) ;
return countCanillas > 0 ;
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en IsInUseAsync para Zona ID: {IdZona}" , id ) ;
return true ; // Asumir que está en uso si hay error
}
}
2025-06-12 19:36:21 -03:00
public async Task < IEnumerable < ( ZonaHistorico Historial , string NombreUsuarioModifico ) > > 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 < ZonaHistorico , string , ( ZonaHistorico , string ) > (
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 ) > ( ) ;
}
}
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
}
}