feat: Implementación CRUD Canillitas, Distribuidores y Precios de Publicación
Backend API:
- Canillitas (`dist_dtCanillas`):
- Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Lógica para manejo de `Accionista`, `Baja`, `FechaBaja`.
- Auditoría en `dist_dtCanillas_H`.
- Validación de legajo único y lógica de empresa vs accionista.
- Distribuidores (`dist_dtDistribuidores`):
- Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Auditoría en `dist_dtDistribuidores_H`.
- Creación de saldos iniciales para el nuevo distribuidor en todas las empresas.
- Verificación de NroDoc único y Nombre opcionalmente único.
- Precios de Publicación (`dist_Precios`):
- Implementado CRUD básico (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/precios`.
- Lógica de negocio para cerrar período de precio anterior al crear uno nuevo.
- Lógica de negocio para reabrir período de precio anterior al eliminar el último.
- Auditoría en `dist_Precios_H`.
- Auditoría en Eliminación de Publicaciones:
- Extendido `PublicacionService.EliminarAsync` para eliminar en cascada registros de precios, recargos, porcentajes de pago (distribuidores y canillitas) y secciones de publicación.
- Repositorios correspondientes (`PrecioRepository`, `RecargoZonaRepository`, `PorcPagoRepository`, `PorcMonCanillaRepository`, `PubliSeccionRepository`) actualizados con métodos `DeleteByPublicacionIdAsync` que registran en sus respectivas tablas `_H` (si existen y se implementó la lógica).
- Asegurada la correcta propagación del `idUsuario` para la auditoría en cascada.
- Correcciones de Nulabilidad:
- Ajustados los métodos `MapToDto` y su uso en `CanillaService` y `PublicacionService` para manejar correctamente tipos anulables.
Frontend React:
- Canillitas:
- `canillaService.ts`.
- `CanillaFormModal.tsx` con selectores para Zona y Empresa, y lógica de Accionista.
- `GestionarCanillitasPage.tsx` con filtros, paginación, y acciones (editar, toggle baja).
- Distribuidores:
- `distribuidorService.ts`.
- `DistribuidorFormModal.tsx` con múltiples campos y selector de Zona.
- `GestionarDistribuidoresPage.tsx` con filtros, paginación, y acciones (editar, eliminar).
- Precios de Publicación:
- `precioService.ts`.
- `PrecioFormModal.tsx` para crear/editar períodos de precios (VigenciaD, VigenciaH opcional, precios por día).
- `GestionarPreciosPublicacionPage.tsx` accesible desde la gestión de publicaciones, para listar y gestionar los períodos de precios de una publicación específica.
- Layout:
- Reemplazado el uso de `Grid` por `Box` con Flexbox en `CanillaFormModal`, `GestionarCanillitasPage` (filtros), `DistribuidorFormModal` y `PrecioFormModal` para resolver problemas de tipos y mejorar la consistencia del layout de formularios.
- Navegación:
- Actualizadas las rutas y pestañas para los nuevos módulos y sub-módulos.
2025-05-20 12:38:55 -03:00
using Dapper ;
using GestionIntegral.Api.Models.Usuarios ;
using Microsoft.Extensions.Logging ;
using System.Collections.Generic ;
using System.Data ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
namespace GestionIntegral.Api.Data.Repositories.Usuarios
{
public class PermisoRepository : IPermisoRepository
{
private readonly DbConnectionFactory _connectionFactory ;
private readonly ILogger < PermisoRepository > _logger ;
public PermisoRepository ( DbConnectionFactory connectionFactory , ILogger < PermisoRepository > logger )
{
_connectionFactory = connectionFactory ;
_logger = logger ;
}
public async Task < IEnumerable < Permiso > > GetAllAsync ( string? moduloFilter , string? codAccFilter )
{
var sqlBuilder = new StringBuilder ( "SELECT id AS Id, modulo, descPermiso, codAcc FROM dbo.gral_Permisos WHERE 1=1" ) ;
var parameters = new DynamicParameters ( ) ;
if ( ! string . IsNullOrWhiteSpace ( moduloFilter ) )
{
sqlBuilder . Append ( " AND modulo LIKE @ModuloFilter" ) ;
parameters . Add ( "ModuloFilter" , $"%{moduloFilter}%" ) ;
}
if ( ! string . IsNullOrWhiteSpace ( codAccFilter ) )
{
sqlBuilder . Append ( " AND codAcc LIKE @CodAccFilter" ) ;
parameters . Add ( "CodAccFilter" , $"%{codAccFilter}%" ) ;
}
sqlBuilder . Append ( " ORDER BY modulo, codAcc;" ) ;
try
{
using var connection = _connectionFactory . CreateConnection ( ) ;
return await connection . QueryAsync < Permiso > ( sqlBuilder . ToString ( ) , parameters ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error al obtener todos los Permisos. Filtros: Modulo={Modulo}, CodAcc={CodAcc}" , moduloFilter , codAccFilter ) ;
return Enumerable . Empty < Permiso > ( ) ;
}
}
public async Task < Permiso ? > GetByIdAsync ( int id )
{
const string sql = "SELECT id AS Id, modulo, descPermiso, codAcc FROM dbo.gral_Permisos WHERE id = @Id" ;
try
{
using var connection = _connectionFactory . CreateConnection ( ) ;
return await connection . QuerySingleOrDefaultAsync < Permiso > ( sql , new { Id = id } ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error al obtener Permiso por ID: {IdPermiso}" , id ) ;
return null ;
}
}
public async Task < bool > ExistsByCodAccAsync ( string codAcc , int? excludeId = null )
{
var sqlBuilder = new StringBuilder ( "SELECT COUNT(1) FROM dbo.gral_Permisos WHERE codAcc = @CodAcc" ) ;
var parameters = new DynamicParameters ( ) ;
parameters . Add ( "CodAcc" , codAcc ) ;
if ( excludeId . HasValue )
{
sqlBuilder . Append ( " AND id != @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 ExistsByCodAccAsync para Permiso con codAcc: {CodAcc}" , codAcc ) ;
return true ;
}
}
public async Task < bool > IsInUseAsync ( int id )
{
const string sqlCheckPermisosPerfiles = "SELECT TOP 1 1 FROM dbo.gral_PermisosPerfiles WHERE idPermiso = @IdPermiso" ;
try
{
using var connection = _connectionFactory . CreateConnection ( ) ;
var inUse = await connection . ExecuteScalarAsync < int? > ( sqlCheckPermisosPerfiles , new { IdPermiso = id } ) ;
return inUse . HasValue & & inUse . Value = = 1 ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en IsInUseAsync para Permiso ID: {IdPermiso}" , id ) ;
return true ;
}
}
2025-06-12 19:36:21 -03:00
feat: Implementación CRUD Canillitas, Distribuidores y Precios de Publicación
Backend API:
- Canillitas (`dist_dtCanillas`):
- Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Lógica para manejo de `Accionista`, `Baja`, `FechaBaja`.
- Auditoría en `dist_dtCanillas_H`.
- Validación de legajo único y lógica de empresa vs accionista.
- Distribuidores (`dist_dtDistribuidores`):
- Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Auditoría en `dist_dtDistribuidores_H`.
- Creación de saldos iniciales para el nuevo distribuidor en todas las empresas.
- Verificación de NroDoc único y Nombre opcionalmente único.
- Precios de Publicación (`dist_Precios`):
- Implementado CRUD básico (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/precios`.
- Lógica de negocio para cerrar período de precio anterior al crear uno nuevo.
- Lógica de negocio para reabrir período de precio anterior al eliminar el último.
- Auditoría en `dist_Precios_H`.
- Auditoría en Eliminación de Publicaciones:
- Extendido `PublicacionService.EliminarAsync` para eliminar en cascada registros de precios, recargos, porcentajes de pago (distribuidores y canillitas) y secciones de publicación.
- Repositorios correspondientes (`PrecioRepository`, `RecargoZonaRepository`, `PorcPagoRepository`, `PorcMonCanillaRepository`, `PubliSeccionRepository`) actualizados con métodos `DeleteByPublicacionIdAsync` que registran en sus respectivas tablas `_H` (si existen y se implementó la lógica).
- Asegurada la correcta propagación del `idUsuario` para la auditoría en cascada.
- Correcciones de Nulabilidad:
- Ajustados los métodos `MapToDto` y su uso en `CanillaService` y `PublicacionService` para manejar correctamente tipos anulables.
Frontend React:
- Canillitas:
- `canillaService.ts`.
- `CanillaFormModal.tsx` con selectores para Zona y Empresa, y lógica de Accionista.
- `GestionarCanillitasPage.tsx` con filtros, paginación, y acciones (editar, toggle baja).
- Distribuidores:
- `distribuidorService.ts`.
- `DistribuidorFormModal.tsx` con múltiples campos y selector de Zona.
- `GestionarDistribuidoresPage.tsx` con filtros, paginación, y acciones (editar, eliminar).
- Precios de Publicación:
- `precioService.ts`.
- `PrecioFormModal.tsx` para crear/editar períodos de precios (VigenciaD, VigenciaH opcional, precios por día).
- `GestionarPreciosPublicacionPage.tsx` accesible desde la gestión de publicaciones, para listar y gestionar los períodos de precios de una publicación específica.
- Layout:
- Reemplazado el uso de `Grid` por `Box` con Flexbox en `CanillaFormModal`, `GestionarCanillitasPage` (filtros), `DistribuidorFormModal` y `PrecioFormModal` para resolver problemas de tipos y mejorar la consistencia del layout de formularios.
- Navegación:
- Actualizadas las rutas y pestañas para los nuevos módulos y sub-módulos.
2025-05-20 12:38:55 -03:00
public async Task < IEnumerable < Permiso > > GetPermisosByIdsAsync ( IEnumerable < int > ids )
{
if ( ids = = null | | ! ids . Any ( ) )
{
return Enumerable . Empty < Permiso > ( ) ;
}
const string sql = "SELECT id AS Id, modulo, descPermiso, codAcc FROM dbo.gral_Permisos WHERE id IN @Ids;" ;
try
{
using var connection = _connectionFactory . CreateConnection ( ) ;
return await connection . QueryAsync < Permiso > ( sql , new { Ids = ids } ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error al obtener permisos por IDs." ) ;
return Enumerable . Empty < Permiso > ( ) ;
}
}
public async Task < Permiso ? > CreateAsync ( Permiso nuevoPermiso , int idUsuario , IDbTransaction transaction )
{
const string sqlInsert = @ "
INSERT INTO dbo . gral_Permisos ( modulo , descPermiso , codAcc )
OUTPUT INSERTED . id AS Id , INSERTED . modulo , INSERTED . descPermiso , INSERTED . codAcc
VALUES ( @Modulo , @DescPermiso , @CodAcc ) ; ";
const string sqlInsertHistorico = @ "
INSERT INTO dbo . gral_Permisos_H ( idPermiso , modulo , descPermiso , codAcc , Id_Usuario , FechaMod , TipoMod )
VALUES ( @IdPermisoHist , @ModuloHist , @DescPermisoHist , @CodAccHist , @IdUsuarioHist , @FechaModHist , @TipoModHist ) ; ";
var connection = transaction . Connection ! ;
var insertedPermiso = await connection . QuerySingleAsync < Permiso > (
sqlInsert ,
nuevoPermiso ,
transaction : transaction ) ;
if ( insertedPermiso = = null | | insertedPermiso . Id < = 0 )
{
throw new DataException ( "No se pudo obtener el ID del permiso insertado." ) ;
}
await connection . ExecuteAsync ( sqlInsertHistorico , new
{
IdPermisoHist = insertedPermiso . Id ,
ModuloHist = insertedPermiso . Modulo ,
DescPermisoHist = insertedPermiso . DescPermiso ,
CodAccHist = insertedPermiso . CodAcc ,
IdUsuarioHist = idUsuario ,
FechaModHist = DateTime . Now ,
TipoModHist = "Insertada"
} , transaction : transaction ) ;
return insertedPermiso ;
}
public async Task < bool > UpdateAsync ( Permiso permisoAActualizar , int idUsuario , IDbTransaction transaction )
{
var connection = transaction . Connection ! ;
var permisoActual = await connection . QuerySingleOrDefaultAsync < Permiso > (
"SELECT id AS Id, modulo, descPermiso, codAcc FROM dbo.gral_Permisos WHERE id = @Id" ,
new { Id = permisoAActualizar . Id } ,
transaction ) ;
if ( permisoActual = = null )
{
throw new KeyNotFoundException ( $"Permiso con ID {permisoAActualizar.Id} no encontrado para actualizar." ) ;
}
const string sqlUpdate = @ "UPDATE dbo.gral_Permisos
SET modulo = @Modulo , descPermiso = @DescPermiso , codAcc = @CodAcc
WHERE id = @Id ; ";
const string sqlInsertHistorico = @ "
INSERT INTO dbo . gral_Permisos_H ( idPermiso , modulo , descPermiso , codAcc , Id_Usuario , FechaMod , TipoMod )
VALUES ( @IdPermisoHist , @ModuloHist , @DescPermisoHist , @CodAccHist , @IdUsuarioHist , @FechaModHist , @TipoModHist ) ; ";
await connection . ExecuteAsync ( sqlInsertHistorico , new
{
IdPermisoHist = permisoActual . Id ,
ModuloHist = permisoActual . Modulo ,
DescPermisoHist = permisoActual . DescPermiso ,
CodAccHist = permisoActual . CodAcc ,
IdUsuarioHist = idUsuario ,
FechaModHist = DateTime . Now ,
TipoModHist = "Modificada"
} , transaction : transaction ) ;
var rowsAffected = await connection . ExecuteAsync ( sqlUpdate , permisoAActualizar , transaction : transaction ) ;
return rowsAffected = = 1 ;
}
public async Task < bool > DeleteAsync ( int id , int idUsuario , IDbTransaction transaction )
{
var connection = transaction . Connection ! ;
var permisoActual = await connection . QuerySingleOrDefaultAsync < Permiso > (
"SELECT id AS Id, modulo, descPermiso, codAcc FROM dbo.gral_Permisos WHERE id = @Id" ,
new { Id = id } ,
transaction ) ;
if ( permisoActual = = null )
{
throw new KeyNotFoundException ( $"Permiso con ID {id} no encontrado para eliminar." ) ;
}
const string sqlDelete = "DELETE FROM dbo.gral_Permisos WHERE id = @Id" ;
const string sqlInsertHistorico = @ "
INSERT INTO dbo . gral_Permisos_H ( idPermiso , modulo , descPermiso , codAcc , Id_Usuario , FechaMod , TipoMod )
VALUES ( @IdPermisoHist , @ModuloHist , @DescPermisoHist , @CodAccHist , @IdUsuarioHist , @FechaModHist , @TipoModHist ) ; ";
await connection . ExecuteAsync ( sqlInsertHistorico , new
{
IdPermisoHist = permisoActual . Id ,
ModuloHist = permisoActual . Modulo ,
DescPermisoHist = permisoActual . DescPermiso ,
CodAccHist = permisoActual . CodAcc ,
IdUsuarioHist = idUsuario ,
FechaModHist = DateTime . Now ,
TipoModHist = "Eliminada"
} , transaction : transaction ) ;
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 < ( PermisoHistorico Historial , string NombreUsuarioModifico ) > > 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 < PermisoHistorico , string , ( PermisoHistorico , string ) > (
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 ) > ( ) ;
}
}
feat: Implementación CRUD Canillitas, Distribuidores y Precios de Publicación
Backend API:
- Canillitas (`dist_dtCanillas`):
- Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Lógica para manejo de `Accionista`, `Baja`, `FechaBaja`.
- Auditoría en `dist_dtCanillas_H`.
- Validación de legajo único y lógica de empresa vs accionista.
- Distribuidores (`dist_dtDistribuidores`):
- Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Auditoría en `dist_dtDistribuidores_H`.
- Creación de saldos iniciales para el nuevo distribuidor en todas las empresas.
- Verificación de NroDoc único y Nombre opcionalmente único.
- Precios de Publicación (`dist_Precios`):
- Implementado CRUD básico (Modelos, DTOs, Repositorio, Servicio, Controlador).
- Endpoints anidados bajo `/publicaciones/{idPublicacion}/precios`.
- Lógica de negocio para cerrar período de precio anterior al crear uno nuevo.
- Lógica de negocio para reabrir período de precio anterior al eliminar el último.
- Auditoría en `dist_Precios_H`.
- Auditoría en Eliminación de Publicaciones:
- Extendido `PublicacionService.EliminarAsync` para eliminar en cascada registros de precios, recargos, porcentajes de pago (distribuidores y canillitas) y secciones de publicación.
- Repositorios correspondientes (`PrecioRepository`, `RecargoZonaRepository`, `PorcPagoRepository`, `PorcMonCanillaRepository`, `PubliSeccionRepository`) actualizados con métodos `DeleteByPublicacionIdAsync` que registran en sus respectivas tablas `_H` (si existen y se implementó la lógica).
- Asegurada la correcta propagación del `idUsuario` para la auditoría en cascada.
- Correcciones de Nulabilidad:
- Ajustados los métodos `MapToDto` y su uso en `CanillaService` y `PublicacionService` para manejar correctamente tipos anulables.
Frontend React:
- Canillitas:
- `canillaService.ts`.
- `CanillaFormModal.tsx` con selectores para Zona y Empresa, y lógica de Accionista.
- `GestionarCanillitasPage.tsx` con filtros, paginación, y acciones (editar, toggle baja).
- Distribuidores:
- `distribuidorService.ts`.
- `DistribuidorFormModal.tsx` con múltiples campos y selector de Zona.
- `GestionarDistribuidoresPage.tsx` con filtros, paginación, y acciones (editar, eliminar).
- Precios de Publicación:
- `precioService.ts`.
- `PrecioFormModal.tsx` para crear/editar períodos de precios (VigenciaD, VigenciaH opcional, precios por día).
- `GestionarPreciosPublicacionPage.tsx` accesible desde la gestión de publicaciones, para listar y gestionar los períodos de precios de una publicación específica.
- Layout:
- Reemplazado el uso de `Grid` por `Box` con Flexbox en `CanillaFormModal`, `GestionarCanillitasPage` (filtros), `DistribuidorFormModal` y `PrecioFormModal` para resolver problemas de tipos y mejorar la consistencia del layout de formularios.
- Navegación:
- Actualizadas las rutas y pestañas para los nuevos módulos y sub-módulos.
2025-05-20 12:38:55 -03:00
}
}