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;
							 | 
						
					
						
							
								
									
										
										
										
											2025-05-23 15:47:39 -03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								using GestionIntegral.Api.Dtos.Usuarios.Auditoria;
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												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 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 UsuarioRepository : IUsuarioRepository
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        private readonly DbConnectionFactory _connectionFactory;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        private readonly ILogger<UsuarioRepository> _logger;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public UsuarioRepository(DbConnectionFactory connectionFactory, ILogger<UsuarioRepository> logger)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            _connectionFactory = connectionFactory;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            _logger = logger;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<IEnumerable<Usuario>> GetAllAsync(string? userFilter, string? nombreFilter)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var sqlBuilder = new StringBuilder("SELECT * FROM dbo.gral_Usuarios WHERE 1=1");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var parameters = new DynamicParameters();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (!string.IsNullOrWhiteSpace(userFilter))
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                sqlBuilder.Append(" AND [User] LIKE @UserParam");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                parameters.Add("UserParam", $"%{userFilter}%");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (!string.IsNullOrWhiteSpace(nombreFilter))
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                sqlBuilder.Append(" AND (Nombre LIKE @NombreParam OR Apellido LIKE @NombreParam)");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                parameters.Add("NombreParam", $"%{nombreFilter}%");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            sqlBuilder.Append(" ORDER BY [User];");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            try
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                using var connection = _connectionFactory.CreateConnection();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return await connection.QueryAsync<Usuario>(sqlBuilder.ToString(), parameters);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            catch (Exception ex)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                _logger.LogError(ex, "Error al obtener todos los Usuarios.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return Enumerable.Empty<Usuario>();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<IEnumerable<(Usuario Usuario, string NombrePerfil)>> GetAllWithProfileNameAsync(string? userFilter, string? nombreFilter)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var sqlBuilder = new StringBuilder(@"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                SELECT u.*, p.perfil AS NombrePerfil
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                FROM dbo.gral_Usuarios u
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                INNER JOIN dbo.gral_Perfiles p ON u.IdPerfil = p.id
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                WHERE 1=1");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var parameters = new DynamicParameters();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (!string.IsNullOrWhiteSpace(userFilter))
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                sqlBuilder.Append(" AND u.[User] LIKE @UserParam");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                parameters.Add("UserParam", $"%{userFilter}%");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (!string.IsNullOrWhiteSpace(nombreFilter))
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                sqlBuilder.Append(" AND (u.Nombre LIKE @NombreParam OR u.Apellido LIKE @NombreParam)");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                parameters.Add("NombreParam", $"%{nombreFilter}%");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            sqlBuilder.Append(" ORDER BY u.[User];");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            try
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                using var connection = _connectionFactory.CreateConnection();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return await connection.QueryAsync<Usuario, string, (Usuario, string)>(
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    sqlBuilder.ToString(),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    (usuario, nombrePerfil) => (usuario, nombrePerfil),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    parameters,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    splitOn: "NombrePerfil"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                );
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            catch (Exception ex)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                _logger.LogError(ex, "Error al obtener todos los Usuarios con nombre de perfil.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return Enumerable.Empty<(Usuario, string)>();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<Usuario?> GetByIdAsync(int id)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sql = "SELECT * FROM dbo.gral_Usuarios WHERE Id = @Id";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            try
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                using var connection = _connectionFactory.CreateConnection();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return await connection.QuerySingleOrDefaultAsync<Usuario>(sql, new { Id = id });
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            catch (Exception ex)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                _logger.LogError(ex, "Error al obtener Usuario por ID: {UsuarioId}", id);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return null;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2025-05-23 15:47:39 -03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        public async Task<(Usuario? Usuario, string? NombrePerfil)> GetByIdWithProfileNameAsync(int id)
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												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
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sql = @"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                SELECT u.*, p.perfil AS NombrePerfil
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                FROM dbo.gral_Usuarios u
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                INNER JOIN dbo.gral_Perfiles p ON u.IdPerfil = p.id
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                WHERE u.Id = @Id";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            try
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                using var connection = _connectionFactory.CreateConnection();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                var result = await connection.QueryAsync<Usuario, string, (Usuario Usuario, string NombrePerfil)>(
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    sql,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    (usuario, nombrePerfil) => (usuario, nombrePerfil),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    new { Id = id },
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    splitOn: "NombrePerfil"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                );
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return result.SingleOrDefault();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            catch (Exception ex)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                _logger.LogError(ex, "Error al obtener Usuario por ID con nombre de perfil: {UsuarioId}", id);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return (null, null);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<Usuario?> GetByUsernameAsync(string username)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            // Esta es la misma que en AuthRepository, si se unifican, se puede eliminar una.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sql = "SELECT * FROM dbo.gral_Usuarios WHERE [User] = @Username";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            try
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                using var connection = _connectionFactory.CreateConnection();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return await connection.QuerySingleOrDefaultAsync<Usuario>(sql, new { Username = username });
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            catch (Exception ex)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                _logger.LogError(ex, "Error al obtener Usuario por Username: {Username}", username);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return null;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<bool> UserExistsAsync(string username, int? excludeId = null)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.gral_Usuarios WHERE [User] = @Username");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var parameters = new DynamicParameters();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            parameters.Add("Username", username);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (excludeId.HasValue)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                sqlBuilder.Append(" AND Id != @ExcludeId");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                parameters.Add("ExcludeId", excludeId.Value);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            try
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                using var connection = _connectionFactory.CreateConnection();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            catch (Exception ex)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							
								
									
										
										
										
											2025-05-23 15:47:39 -03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                _logger.LogError(ex, "Error en UserExistsAsync para username: {Username}", username);
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												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
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return true; // Asumir que existe para prevenir duplicados
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<Usuario?> CreateAsync(Usuario nuevoUsuario, int idUsuarioCreador, IDbTransaction transaction)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sqlInsert = @"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                INSERT INTO dbo.gral_Usuarios ([User], ClaveHash, ClaveSalt, Habilitada, SupAdmin, Nombre, Apellido, IdPerfil, VerLog, DebeCambiarClave)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                OUTPUT INSERTED.*
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                VALUES (@User, @ClaveHash, @ClaveSalt, @Habilitada, @SupAdmin, @Nombre, @Apellido, @IdPerfil, @VerLog, @DebeCambiarClave);";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sqlInsertHistorico = @"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                INSERT INTO dbo.gral_Usuarios_H
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    (IdUsuario, UserNvo, HabilitadaNva, SupAdminNvo, NombreNvo, ApellidoNvo, IdPerfilNvo, DebeCambiarClaveNva, Id_UsuarioMod, FechaMod, TipoMod)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                VALUES (@IdUsuarioHist, @UserNvoHist, @HabilitadaNvaHist, @SupAdminNvoHist, @NombreNvoHist, @ApellidoNvoHist, @IdPerfilNvoHist, @DebeCambiarClaveNvaHist, @IdUsuarioModHist, @FechaModHist, @TipoModHist);";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var connection = transaction.Connection!;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var insertedUsuario = await connection.QuerySingleAsync<Usuario>(sqlInsert, nuevoUsuario, transaction);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (insertedUsuario == null) throw new DataException("No se pudo crear el usuario.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            await connection.ExecuteAsync(sqlInsertHistorico, new
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdUsuarioHist = insertedUsuario.Id,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                UserNvoHist = insertedUsuario.User,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                HabilitadaNvaHist = insertedUsuario.Habilitada,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                SupAdminNvoHist = insertedUsuario.SupAdmin,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                NombreNvoHist = insertedUsuario.Nombre,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                ApellidoNvoHist = insertedUsuario.Apellido,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdPerfilNvoHist = insertedUsuario.IdPerfil,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                DebeCambiarClaveNvaHist = insertedUsuario.DebeCambiarClave,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdUsuarioModHist = idUsuarioCreador,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                FechaModHist = DateTime.Now,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                TipoModHist = "Creado"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }, transaction);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            return insertedUsuario;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<bool> UpdateAsync(Usuario usuarioAActualizar, int idUsuarioModificador, IDbTransaction transaction)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var connection = transaction.Connection!;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var usuarioActual = await connection.QuerySingleOrDefaultAsync<Usuario>(
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                "SELECT * FROM dbo.gral_Usuarios WHERE Id = @Id", new { usuarioAActualizar.Id }, transaction);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (usuarioActual == null) throw new KeyNotFoundException("Usuario no encontrado para actualizar.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            // User (nombre de usuario) no se actualiza aquí
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sqlUpdate = @"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                UPDATE dbo.gral_Usuarios SET
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    Habilitada = @Habilitada, SupAdmin = @SupAdmin, Nombre = @Nombre, Apellido = @Apellido,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    IdPerfil = @IdPerfil, VerLog = @VerLog, DebeCambiarClave = @DebeCambiarClave
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                WHERE Id = @Id;";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sqlInsertHistorico = @"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                INSERT INTO dbo.gral_Usuarios_H
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    (IdUsuario, UserAnt, UserNvo, HabilitadaAnt, HabilitadaNva, SupAdminAnt, SupAdminNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                     NombreAnt, NombreNvo, ApellidoAnt, ApellidoNvo, IdPerfilAnt, IdPerfilNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                     DebeCambiarClaveAnt, DebeCambiarClaveNva, Id_UsuarioMod, FechaMod, TipoMod)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                VALUES (@IdUsuarioHist, @UserAntHist, @UserNvoHist, @HabilitadaAntHist, @HabilitadaNvaHist, @SupAdminAntHist, @SupAdminNvoHist,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        @NombreAntHist, @NombreNvoHist, @ApellidoAntHist, @ApellidoNvoHist, @IdPerfilAntHist, @IdPerfilNvoHist,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        @DebeCambiarClaveAntHist, @DebeCambiarClaveNvaHist, @IdUsuarioModHist, @FechaModHist, @TipoModHist);";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            await connection.ExecuteAsync(sqlInsertHistorico, new
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdUsuarioHist = usuarioActual.Id,
							 | 
						
					
						
							
								
									
										
										
										
											2025-05-23 15:47:39 -03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								                UserAntHist = usuarioActual.User,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                UserNvoHist = usuarioAActualizar.User, // Aunque no cambiemos User, lo registramos
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                HabilitadaAntHist = usuarioActual.Habilitada,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                HabilitadaNvaHist = usuarioAActualizar.Habilitada,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                SupAdminAntHist = usuarioActual.SupAdmin,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                SupAdminNvoHist = usuarioAActualizar.SupAdmin,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                NombreAntHist = usuarioActual.Nombre,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                NombreNvoHist = usuarioAActualizar.Nombre,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                ApellidoAntHist = usuarioActual.Apellido,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                ApellidoNvoHist = usuarioAActualizar.Apellido,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdPerfilAntHist = usuarioActual.IdPerfil,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdPerfilNvoHist = usuarioAActualizar.IdPerfil,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                DebeCambiarClaveAntHist = usuarioActual.DebeCambiarClave,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                DebeCambiarClaveNvaHist = usuarioAActualizar.DebeCambiarClave,
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												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
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdUsuarioModHist = idUsuarioModificador,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                FechaModHist = DateTime.Now,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                TipoModHist = "Actualizado"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }, transaction);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var rowsAffected = await connection.ExecuteAsync(sqlUpdate, usuarioAActualizar, transaction);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            return rowsAffected == 1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<bool> SetPasswordAsync(int userId, string newHash, string newSalt, bool debeCambiarClave, int idUsuarioModificador, IDbTransaction transaction)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							
								
									
										
										
										
											2025-05-23 15:47:39 -03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            var connection = transaction.Connection!;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var usuarioActual = await connection.QuerySingleOrDefaultAsync<Usuario>(
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								               "SELECT * FROM dbo.gral_Usuarios WHERE Id = @Id", new { Id = userId }, transaction);
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												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
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (usuarioActual == null) throw new KeyNotFoundException("Usuario no encontrado para cambiar contraseña.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sqlUpdate = @"UPDATE dbo.gral_Usuarios
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                                       SET ClaveHash = @ClaveHash, ClaveSalt = @ClaveSalt, DebeCambiarClave = @DebeCambiarClave
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                                       WHERE Id = @UserId";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            const string sqlInsertHistorico = @"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                INSERT INTO dbo.gral_Usuarios_H
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    (IdUsuario, UserNvo, HabilitadaNva, SupAdminNvo, NombreNvo, ApellidoNvo, IdPerfilNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                     DebeCambiarClaveAnt, DebeCambiarClaveNva, Id_UsuarioMod, FechaMod, TipoMod)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                VALUES (@IdUsuarioHist, @UserNvoHist, @HabilitadaNvaHist, @SupAdminNvoHist, @NombreNvoHist, @ApellidoNvoHist, @IdPerfilNvoHist,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                        @DebeCambiarClaveAntHist, @DebeCambiarClaveNvaHist, @IdUsuarioModHist, @FechaModHist, @TipoModHist);";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            await connection.ExecuteAsync(sqlInsertHistorico, new
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdUsuarioHist = usuarioActual.Id,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                UserNvoHist = usuarioActual.User, // No cambia el user
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                HabilitadaNvaHist = usuarioActual.Habilitada,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                SupAdminNvoHist = usuarioActual.SupAdmin,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                NombreNvoHist = usuarioActual.Nombre,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                ApellidoNvoHist = usuarioActual.Apellido,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdPerfilNvoHist = usuarioActual.IdPerfil,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                DebeCambiarClaveAntHist = usuarioActual.DebeCambiarClave, // Estado anterior de este flag
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                DebeCambiarClaveNvaHist = debeCambiarClave,             // Nuevo estado de este flag
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                IdUsuarioModHist = idUsuarioModificador,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                FechaModHist = DateTime.Now,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                TipoModHist = "Clave Cambiada" // O "Clave Reseteada"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }, transaction);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var rowsAffected = await connection.ExecuteAsync(sqlUpdate, new
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                ClaveHash = newHash,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                ClaveSalt = newSalt,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                DebeCambiarClave = debeCambiarClave,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                UserId = userId
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }, transaction);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            return rowsAffected == 1;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2025-05-23 15:47:39 -03:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<IEnumerable<UsuarioHistorialDto>> GetHistorialByUsuarioIdAsync(int idUsuarioAfectado, DateTime? fechaDesde, DateTime? fechaHasta)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var sqlBuilder = new StringBuilder(@"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        SELECT
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.IdHist,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.IdUsuario AS IdUsuarioAfectado,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            uPrincipal.[User] AS UserAfectado, -- DELIMITADO CON []
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.UserAnt, h.UserNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.HabilitadaAnt, h.HabilitadaNva,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.SupAdminAnt, h.SupAdminNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.NombreAnt, h.NombreNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.ApellidoAnt, h.ApellidoNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.IdPerfilAnt, h.IdPerfilNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pAnt.perfil AS NombrePerfilAnt,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pNvo.perfil AS NombrePerfilNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.DebeCambiarClaveAnt, h.DebeCambiarClaveNva,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.Id_UsuarioMod AS IdUsuarioModifico,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ISNULL(uMod.Nombre + ' ' + uMod.Apellido, 'Sistema') AS NombreUsuarioModifico,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.FechaMod AS FechaModificacion,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.TipoMod AS TipoModificacion
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        FROM dbo.gral_Usuarios_H h
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        INNER JOIN dbo.gral_Usuarios uPrincipal ON h.IdUsuario = uPrincipal.Id -- No necesita delimitador aquí si 'Id' es el nombre de la columna PK en gral_Usuarios
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        LEFT JOIN dbo.gral_Usuarios uMod ON h.Id_UsuarioMod = uMod.Id
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        LEFT JOIN dbo.gral_Perfiles pAnt ON h.IdPerfilAnt = pAnt.id
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        LEFT JOIN dbo.gral_Perfiles pNvo ON h.IdPerfilNvo = pNvo.id
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        WHERE h.IdUsuario = @IdUsuarioAfectadoParam");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var parameters = new DynamicParameters();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            parameters.Add("IdUsuarioAfectadoParam", idUsuarioAfectado);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            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)); // Hasta el final del día
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            sqlBuilder.Append(" ORDER BY h.FechaMod DESC, h.IdHist DESC;");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            try
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                using var connection = _connectionFactory.CreateConnection();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return await connection.QueryAsync<UsuarioHistorialDto>(sqlBuilder.ToString(), parameters);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            catch (Exception ex)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                _logger.LogError(ex, "Error al obtener historial para Usuario ID: {IdUsuarioAfectado}", idUsuarioAfectado);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return Enumerable.Empty<UsuarioHistorialDto>();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        public async Task<IEnumerable<UsuarioHistorialDto>> GetAllHistorialAsync(DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModificoFilter, string? tipoModFilter)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            var sqlBuilder = new StringBuilder(@"
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        SELECT
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.IdHist,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.IdUsuario AS IdUsuarioAfectado,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            uPrincipal.[User] AS UserAfectado, -- DELIMITADO CON []
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.UserAnt, h.UserNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.HabilitadaAnt, h.HabilitadaNva,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.SupAdminAnt, h.SupAdminNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.NombreAnt, h.NombreNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.ApellidoAnt, h.ApellidoNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.IdPerfilAnt, h.IdPerfilNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pAnt.perfil AS NombrePerfilAnt,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            pNvo.perfil AS NombrePerfilNvo,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.DebeCambiarClaveAnt, h.DebeCambiarClaveNva,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.Id_UsuarioMod AS IdUsuarioModifico,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ISNULL(uMod.Nombre + ' ' + uMod.Apellido, 'Sistema') AS NombreUsuarioModifico,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.FechaMod AS FechaModificacion,
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            h.TipoMod AS TipoModificacion
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        FROM dbo.gral_Usuarios_H h
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        INNER JOIN dbo.gral_Usuarios uPrincipal ON h.IdUsuario = uPrincipal.Id
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        LEFT JOIN dbo.gral_Usuarios uMod ON h.Id_UsuarioMod = uMod.Id
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        LEFT JOIN dbo.gral_Perfiles pAnt ON h.IdPerfilAnt = pAnt.id
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        LEFT JOIN dbo.gral_Perfiles pNvo ON h.IdPerfilNvo = pNvo.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 (idUsuarioModificoFilter.HasValue)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                sqlBuilder.Append(" AND h.Id_UsuarioMod = @IdUsuarioModFilterParam");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                parameters.Add("IdUsuarioModFilterParam", idUsuarioModificoFilter.Value);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (!string.IsNullOrWhiteSpace(tipoModFilter))
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                sqlBuilder.Append(" AND h.TipoMod LIKE @TipoModFilterParam");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                parameters.Add("TipoModFilterParam", $"%{tipoModFilter}%");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            sqlBuilder.Append(" ORDER BY h.FechaMod DESC, h.IdHist DESC;");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            try
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                using var connection = _connectionFactory.CreateConnection();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return await connection.QueryAsync<UsuarioHistorialDto>(sqlBuilder.ToString(), parameters);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            catch (Exception ex)
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                _logger.LogError(ex, "Error al obtener todo el historial de Usuarios.");
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                return Enumerable.Empty<UsuarioHistorialDto>();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
											 
										 
										
											
												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
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								}
							 |