using Dapper; using GestionIntegral.Api.Models.Contables; 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.Contables { public class NotaCreditoDebitoRepository : INotaCreditoDebitoRepository { private readonly DbConnectionFactory _cf; private readonly ILogger _log; public NotaCreditoDebitoRepository(DbConnectionFactory cf, ILogger log) { _cf = cf; _log = log; } private string SelectQueryBase() => @" SELECT Id_Nota AS IdNota, Destino, Id_Destino AS IdDestino, Referencia, Tipo, Fecha, Monto, Observaciones, Id_Empresa AS IdEmpresa FROM dbo.cue_CreditosDebitos"; public async Task> GetAllAsync( DateTime? fechaDesde, DateTime? fechaHasta, string? destino, int? idDestino, int? idEmpresa, string? tipoNota) { var sqlBuilder = new StringBuilder(SelectQueryBase()); sqlBuilder.Append(" WHERE 1=1"); var parameters = new DynamicParameters(); if (fechaDesde.HasValue) { sqlBuilder.Append(" AND Fecha >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } if (fechaHasta.HasValue) { sqlBuilder.Append(" AND Fecha <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date); } if (!string.IsNullOrWhiteSpace(destino)) { sqlBuilder.Append(" AND Destino = @DestinoParam"); parameters.Add("DestinoParam", destino); } if (idDestino.HasValue) { sqlBuilder.Append(" AND Id_Destino = @IdDestinoParam"); parameters.Add("IdDestinoParam", idDestino.Value); } if (idEmpresa.HasValue) { sqlBuilder.Append(" AND Id_Empresa = @IdEmpresaParam"); parameters.Add("IdEmpresaParam", idEmpresa.Value); } if (!string.IsNullOrWhiteSpace(tipoNota)) { sqlBuilder.Append(" AND Tipo = @TipoParam"); parameters.Add("TipoParam", tipoNota); } sqlBuilder.Append(" ORDER BY Fecha DESC, Id_Nota DESC;"); try { using var connection = _cf.CreateConnection(); return await connection.QueryAsync(sqlBuilder.ToString(), parameters); } catch (Exception ex) { _log.LogError(ex, "Error al obtener todas las Notas de Crédito/Débito."); return Enumerable.Empty(); } } public async Task GetByIdAsync(int idNota) { var sql = SelectQueryBase() + " WHERE Id_Nota = @IdNotaParam"; try { using var connection = _cf.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { IdNotaParam = idNota }); } catch (Exception ex) { _log.LogError(ex, "Error al obtener Nota C/D por ID: {IdNota}", idNota); return null; } } public async Task CreateAsync(NotaCreditoDebito nuevaNota, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.cue_CreditosDebitos (Destino, Id_Destino, Referencia, Tipo, Fecha, Monto, Observaciones, Id_Empresa) OUTPUT INSERTED.Id_Nota AS IdNota, INSERTED.Destino, INSERTED.Id_Destino AS IdDestino, INSERTED.Referencia, INSERTED.Tipo, INSERTED.Fecha, INSERTED.Monto, INSERTED.Observaciones, INSERTED.Id_Empresa AS IdEmpresa VALUES (@Destino, @IdDestino, @Referencia, @Tipo, @Fecha, @Monto, @Observaciones, @IdEmpresa);"; const string sqlHistorico = @" INSERT INTO dbo.cue_CreditosDebitos_H (Id_Nota, Destino, Id_Destino, Referencia, Tipo, Fecha, Monto, Observaciones, Id_Empresa, Id_Usuario, FechaMod, TipoMod) VALUES (@IdNotaHist, @DestinoHist, @IdDestinoHist, @ReferenciaHist, @TipoHist, @FechaHist, @MontoHist, @ObservacionesHist, @IdEmpresaHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevaNota, transaction); if (inserted == null || inserted.IdNota == 0) throw new DataException("Error al crear la nota o ID no generado."); await transaction.Connection!.ExecuteAsync(sqlHistorico, new { IdNotaHist = inserted.IdNota, DestinoHist = inserted.Destino, IdDestinoHist = inserted.IdDestino, ReferenciaHist = inserted.Referencia, TipoHist = inserted.Tipo, FechaHist = inserted.Fecha, MontoHist = inserted.Monto, ObservacionesHist = inserted.Observaciones, IdEmpresaHist = inserted.IdEmpresa, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Creada" }, transaction); return inserted; } public async Task UpdateAsync(NotaCreditoDebito notaAActualizar, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( SelectQueryBase() + " WHERE Id_Nota = @IdNotaParam", new { IdNotaParam = notaAActualizar.IdNota }, transaction); if (actual == null) throw new KeyNotFoundException("Nota de Crédito/Débito no encontrada."); // Solo se permite actualizar Monto y Observaciones const string sqlUpdate = @" UPDATE dbo.cue_CreditosDebitos SET Monto = @Monto, Observaciones = @Observaciones WHERE Id_Nota = @IdNota;"; const string sqlHistorico = @" INSERT INTO dbo.cue_CreditosDebitos_H (Id_Nota, Destino, Id_Destino, Referencia, Tipo, Fecha, Monto, Observaciones, Id_Empresa, Id_Usuario, FechaMod, TipoMod) VALUES (@IdNotaHist, @DestinoHist, @IdDestinoHist, @ReferenciaHist, @TipoHist, @FechaHist, @MontoHist, @ObservacionesHist, @IdEmpresaHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; await transaction.Connection!.ExecuteAsync(sqlHistorico, new { IdNotaHist = actual.IdNota, DestinoHist = actual.Destino, IdDestinoHist = actual.IdDestino, ReferenciaHist = actual.Referencia, TipoHist = actual.Tipo, FechaHist = actual.Fecha, MontoHist = actual.Monto, // Valor ANTERIOR ObservacionesHist = actual.Observaciones, IdEmpresaHist = actual.IdEmpresa, // Valor ANTERIOR IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Actualizada" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, new { notaAActualizar.Monto, notaAActualizar.Observaciones, notaAActualizar.IdNota }, transaction); return rowsAffected == 1; } public async Task DeleteAsync(int idNota, int idUsuario, IDbTransaction transaction) { var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( SelectQueryBase() + " WHERE Id_Nota = @IdNotaParam", new { IdNotaParam = idNota }, transaction); if (actual == null) throw new KeyNotFoundException("Nota de Crédito/Débito no encontrada para eliminar."); const string sqlDelete = "DELETE FROM dbo.cue_CreditosDebitos WHERE Id_Nota = @IdNotaParam"; const string sqlHistorico = @" INSERT INTO dbo.cue_CreditosDebitos_H (Id_Nota, Destino, Id_Destino, Referencia, Tipo, Fecha, Monto, Observaciones, Id_Empresa, Id_Usuario, FechaMod, TipoMod) VALUES (@IdNotaHist, @DestinoHist, @IdDestinoHist, @ReferenciaHist, @TipoHist, @FechaHist, @MontoHist, @ObservacionesHist, @IdEmpresaHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; await transaction.Connection!.ExecuteAsync(sqlHistorico, new { IdNotaHist = actual.IdNota, DestinoHist = actual.Destino, IdDestinoHist = actual.IdDestino, ReferenciaHist = actual.Referencia, TipoHist = actual.Tipo, FechaHist = actual.Fecha, MontoHist = actual.Monto, ObservacionesHist = actual.Observaciones, IdEmpresaHist = actual.IdEmpresa, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Eliminada" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdNotaParam = idNota }, transaction); return rowsAffected == 1; } public async Task> GetHistorialAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, int? idNotaOriginal) { using var connection = _cf.CreateConnection(); var sqlBuilder = new StringBuilder(@" SELECT h.Id_Nota, h.Destino, h.Id_Destino, h.Referencia, h.Tipo, h.Fecha, h.Monto, h.Observaciones, h.Id_Empresa, h.Id_Usuario, h.FechaMod, h.TipoMod, u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico FROM dbo.cue_CreditosDebitos_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 (idNotaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Nota = @IdNotaOriginalParam"); parameters.Add("IdNotaOriginalParam", idNotaOriginal.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 Notas C/D."); return Enumerable.Empty<(NotaCreditoDebitoHistorico, string)>(); } } } }