using Dapper; using GestionIntegral.Api.Models.Distribucion; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GestionIntegral.Api.Data.Repositories.Distribucion { public class PorcMonCanillaRepository : IPorcMonCanillaRepository { private readonly DbConnectionFactory _cf; private readonly ILogger _log; public PorcMonCanillaRepository(DbConnectionFactory connectionFactory, ILogger logger) { _cf = connectionFactory; _log = logger; } public async Task> GetByPublicacionIdAsync(int idPublicacion) { const string sql = @" SELECT pmc.Id_PorcMon AS IdPorcMon, pmc.Id_Publicacion AS IdPublicacion, pmc.Id_Canilla AS IdCanilla, pmc.VigenciaD, pmc.VigenciaH, pmc.PorcMon, pmc.EsPorcentaje, c.NomApe AS NomApeCanilla FROM dbo.dist_PorcMonPagoCanilla pmc INNER JOIN dbo.dist_dtCanillas c ON pmc.Id_Canilla = c.Id_Canilla WHERE pmc.Id_Publicacion = @IdPublicacionParam ORDER BY c.NomApe, pmc.VigenciaD DESC"; try { using var connection = _cf.CreateConnection(); return await connection.QueryAsync( sql, (item, nomApe) => (item, nomApe), new { IdPublicacionParam = idPublicacion }, splitOn: "NomApeCanilla" ); } catch (Exception ex) { _log.LogError(ex, "Error al obtener PorcMonCanilla por Publicacion ID: {IdPublicacion}", idPublicacion); return Enumerable.Empty<(PorcMonCanilla, string)>(); } } public async Task GetByIdAsync(int idPorcMon) { const string sql = @" SELECT Id_PorcMon AS IdPorcMon, Id_Publicacion AS IdPublicacion, Id_Canilla AS IdCanilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje FROM dbo.dist_PorcMonPagoCanilla WHERE Id_PorcMon = @IdPorcMonParam"; try { using var connection = _cf.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { IdPorcMonParam = idPorcMon }); } catch (Exception ex) { _log.LogError(ex, "Error al obtener PorcMonCanilla por ID: {IdPorcMon}", idPorcMon); return null; } } public async Task GetActiveByPublicacionCanillaAndDateAsync(int idPublicacion, int idCanilla, DateTime fecha, IDbTransaction? transaction = null) { const string sql = @" SELECT TOP 1 Id_PorcMon AS IdPorcMon, Id_Publicacion AS IdPublicacion, Id_Canilla AS IdCanilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje FROM dbo.dist_PorcMonPagoCanilla WHERE Id_Publicacion = @IdPublicacionParam AND Id_Canilla = @IdCanillaParam AND VigenciaD <= @FechaParam AND (VigenciaH IS NULL OR VigenciaH >= @FechaParam) ORDER BY VigenciaD DESC;"; var cn = transaction?.Connection ?? _cf.CreateConnection(); bool ownConnection = transaction == null; PorcMonCanilla? result = null; try { if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); result = await cn.QuerySingleOrDefaultAsync(sql, new { IdPublicacionParam = idPublicacion, IdCanillaParam = idCanilla, FechaParam = fecha.Date }, transaction); } finally { if (ownConnection && cn.State == ConnectionState.Open && cn is System.Data.Common.DbConnection dbConnClose) await dbConnClose.CloseAsync(); if (ownConnection) (cn as IDisposable)?.Dispose(); } return result; } public async Task GetPreviousActiveAsync(int idPublicacion, int idCanilla, DateTime vigenciaDNuevo, IDbTransaction transaction) { const string sql = @" SELECT TOP 1 Id_PorcMon AS IdPorcMon, Id_Publicacion AS IdPublicacion, Id_Canilla AS IdCanilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje FROM dbo.dist_PorcMonPagoCanilla WHERE Id_Publicacion = @IdPublicacionParam AND Id_Canilla = @IdCanillaParam AND VigenciaD < @VigenciaDNuevoParam AND VigenciaH IS NULL ORDER BY VigenciaD DESC;"; return await transaction.Connection!.QuerySingleOrDefaultAsync(sql, new { IdPublicacionParam = idPublicacion, IdCanillaParam = idCanilla, VigenciaDNuevoParam = vigenciaDNuevo.Date }, transaction); } public async Task CreateAsync(PorcMonCanilla nuevoItem, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.dist_PorcMonPagoCanilla (Id_Publicacion, Id_Canilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje) OUTPUT INSERTED.Id_PorcMon AS IdPorcMon, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Id_Canilla AS IdCanilla, INSERTED.VigenciaD, INSERTED.VigenciaH, INSERTED.PorcMon, INSERTED.EsPorcentaje VALUES (@IdPublicacion, @IdCanilla, @VigenciaD, @VigenciaH, @PorcMon, @EsPorcentaje);"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_PorcMonPagoCanilla_H (Id_PorcMon, Id_Publicacion, Id_Canilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcMonParam, @IdPublicacionParam, @IdCanillaParam, @VigenciaDParam, @VigenciaHParam, @PorcMonParam, @EsPorcentajeParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevoItem, transaction); if (inserted == null || inserted.IdPorcMon == 0) throw new DataException("Error al crear PorcMonCanilla o ID no generado."); await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdPorcMonParam = inserted.IdPorcMon, IdPublicacionParam = inserted.IdPublicacion, IdCanillaParam = inserted.IdCanilla, VigenciaDParam = inserted.VigenciaD, VigenciaHParam = inserted.VigenciaH, PorcMonParam = inserted.PorcMon, EsPorcentajeParam = inserted.EsPorcentaje, IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Creado" }, transaction); return inserted; } public async Task UpdateAsync(PorcMonCanilla itemAActualizar, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_PorcMon AS IdPorcMon, Id_Publicacion AS IdPublicacion, Id_Canilla AS IdCanilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje FROM dbo.dist_PorcMonPagoCanilla WHERE Id_PorcMon = @IdPorcMonParam", new { IdPorcMonParam = itemAActualizar.IdPorcMon }, transaction); if (actual == null) throw new KeyNotFoundException("Registro PorcMonCanilla no encontrado."); const string sqlUpdate = @" UPDATE dbo.dist_PorcMonPagoCanilla SET PorcMon = @PorcMon, EsPorcentaje = @EsPorcentaje, VigenciaH = @VigenciaH WHERE Id_PorcMon = @IdPorcMon;"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_PorcMonPagoCanilla_H (Id_PorcMon, Id_Publicacion, Id_Canilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcMonParam, @IdPublicacionParam, @IdCanillaParam, @VigenciaDParam, @VigenciaHParam, @PorcMonParam, @EsPorcentajeParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdPorcMonParam = actual.IdPorcMon, IdPublicacionParam = actual.IdPublicacion, IdCanillaParam = actual.IdCanilla, VigenciaDParam = actual.VigenciaD, VigenciaHParam = actual.VigenciaH, // Valor ANTERIOR PorcMonParam = actual.PorcMon, // Valor ANTERIOR EsPorcentajeParam = actual.EsPorcentaje, // Valor ANTERIOR IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Actualizado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, itemAActualizar, transaction); return rowsAffected == 1; } public async Task DeleteAsync(int idPorcMon, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_PorcMon AS IdPorcMon, Id_Publicacion AS IdPublicacion, Id_Canilla AS IdCanilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje FROM dbo.dist_PorcMonPagoCanilla WHERE Id_PorcMon = @IdPorcMonParam", new { IdPorcMonParam = idPorcMon }, transaction); if (actual == null) throw new KeyNotFoundException("Registro PorcMonCanilla no encontrado para eliminar."); const string sqlDelete = "DELETE FROM dbo.dist_PorcMonPagoCanilla WHERE Id_PorcMon = @IdPorcMonParam"; const string sqlInsertHistorico = @" INSERT INTO dbo.dist_PorcMonPagoCanilla_H (Id_PorcMon, Id_Publicacion, Id_Canilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcMonParam, @IdPublicacionParam, @IdCanillaParam, @VigenciaDParam, @VigenciaHParam, @PorcMonParam, @EsPorcentajeParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdPorcMonParam = actual.IdPorcMon, IdPublicacionParam = actual.IdPublicacion, IdCanillaParam = actual.IdCanilla, VigenciaDParam = actual.VigenciaD, VigenciaHParam = actual.VigenciaH, PorcMonParam = actual.PorcMon, EsPorcentajeParam = actual.EsPorcentaje, IdUsuarioParam = idUsuario, FechaModParam = DateTime.Now, TipoModParam = "Eliminado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdPorcMonParam = idPorcMon }, transaction); return rowsAffected == 1; } public async Task DeleteByPublicacionIdAsync(int idPublicacion, int idUsuarioAuditoria, IDbTransaction transaction) { const string selectSql = @" SELECT Id_PorcMon AS IdPorcMon, Id_Publicacion AS IdPublicacion, Id_Canilla AS IdCanilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje FROM dbo.dist_PorcMonPagoCanilla WHERE Id_Publicacion = @IdPublicacionParam"; var itemsToDelete = await transaction.Connection!.QueryAsync(selectSql, new { IdPublicacionParam = idPublicacion }, transaction); if (itemsToDelete.Any()) { const string insertHistoricoSql = @" INSERT INTO dbo.dist_PorcMonPagoCanilla_H (Id_PorcMon, Id_Publicacion, Id_Canilla, VigenciaD, VigenciaH, PorcMon, EsPorcentaje, Id_Usuario, FechaMod, TipoMod) VALUES (@IdPorcMonParam, @IdPublicacionParam, @IdCanillaParam, @VigenciaDParam, @VigenciaHParam, @PorcMonParam, @EsPorcentajeParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);"; foreach (var item in itemsToDelete) { await transaction.Connection!.ExecuteAsync(insertHistoricoSql, new { IdPorcMonParam = item.IdPorcMon, IdPublicacionParam = item.IdPublicacion, IdCanillaParam = item.IdCanilla, VigenciaDParam = item.VigenciaD, VigenciaHParam = item.VigenciaH, PorcMonParam = item.PorcMon, EsPorcentajeParam = item.EsPorcentaje, IdUsuarioParam = idUsuarioAuditoria, FechaModParam = DateTime.Now, TipoModParam = "Eliminado (Cascada)" }, transaction); } } const string deleteSql = "DELETE FROM dbo.dist_PorcMonPagoCanilla WHERE Id_Publicacion = @IdPublicacionParam"; try { await transaction.Connection!.ExecuteAsync(deleteSql, new { IdPublicacionParam = idPublicacion }, transaction: transaction); return true; } catch (System.Exception ex) { _log.LogError(ex, "Error al eliminar PorcMonCanilla por IdPublicacion: {idPublicacion}", idPublicacion); throw; } } public async Task> GetHistorialAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, int? idPorcMonOriginal, int? idPublicacionOriginal, int? idCanillaOriginal) { using var connection = _cf.CreateConnection(); var sqlBuilder = new StringBuilder(@" SELECT h.Id_PorcMon, h.Id_Publicacion, h.Id_Canilla, h.VigenciaD, h.VigenciaH, h.PorcMon, h.EsPorcentaje, h.Id_Usuario, h.FechaMod, h.TipoMod, u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico FROM dbo.dist_PorcMonPagoCanilla_H h JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id WHERE 1=1"); var parameters = new DynamicParameters(); if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); } if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); } if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); } if (idPorcMonOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_PorcMon = @IdPorcMonOriginalParam"); parameters.Add("IdPorcMonOriginalParam", idPorcMonOriginal.Value); } if (idPublicacionOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionOriginalParam"); parameters.Add("IdPublicacionOriginalParam", idPublicacionOriginal.Value); } if (idCanillaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Canilla = @IdCanillaOriginalParam"); parameters.Add("IdCanillaOriginalParam", idCanillaOriginal.Value); } sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); try { var result = await connection.QueryAsync( sqlBuilder.ToString(), (hist, userName) => (hist, userName), parameters, splitOn: "NombreUsuarioModifico" ); return result; } catch (Exception ex) { _log.LogError(ex, "Error al obtener historial de Porcentajes/Montos Canillita."); return Enumerable.Empty<(PorcMonCanillaHistorico, string)>(); } } } }