// Archivo: GestionIntegral.Api/Data/Repositories/Suscripciones/FacturaRepository.cs using Dapper; using GestionIntegral.Api.Models.Suscripciones; using System.Data; namespace GestionIntegral.Api.Data.Repositories.Suscripciones { public class FacturaRepository : IFacturaRepository { private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public FacturaRepository(DbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory; _logger = logger; } public async Task GetByIdAsync(int idFactura) { const string sql = "SELECT * FROM dbo.susc_Facturas WHERE IdFactura = @IdFactura;"; using var connection = _connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { idFactura }); } public async Task> GetByPeriodoAsync(string periodo) { const string sql = "SELECT * FROM dbo.susc_Facturas WHERE Periodo = @Periodo ORDER BY IdFactura;"; using var connection = _connectionFactory.CreateConnection(); return await connection.QueryAsync(sql, new { Periodo = periodo }); } public async Task GetBySuscripcionYPeriodoAsync(int idSuscripcion, string periodo, IDbTransaction transaction) { const string sql = "SELECT TOP 1 * FROM dbo.susc_Facturas WHERE IdSuscripcion = @IdSuscripcion AND Periodo = @Periodo;"; if (transaction == null || transaction.Connection == null) { throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas."); } return await transaction.Connection.QuerySingleOrDefaultAsync(sql, new { IdSuscripcion = idSuscripcion, Periodo = periodo }, transaction); } public async Task CreateAsync(Factura nuevaFactura, IDbTransaction transaction) { if (transaction == null || transaction.Connection == null) { throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas."); } const string sqlInsert = @" INSERT INTO dbo.susc_Facturas (IdSuscripcion, Periodo, FechaEmision, FechaVencimiento, ImporteBruto, DescuentoAplicado, ImporteFinal, Estado) OUTPUT INSERTED.* VALUES (@IdSuscripcion, @Periodo, @FechaEmision, @FechaVencimiento, @ImporteBruto, @DescuentoAplicado, @ImporteFinal, @Estado);"; return await transaction.Connection.QuerySingleAsync(sqlInsert, nuevaFactura, transaction); } public async Task UpdateEstadoAsync(int idFactura, string nuevoEstado, IDbTransaction transaction) { if (transaction == null || transaction.Connection == null) { throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas."); } const string sql = "UPDATE dbo.susc_Facturas SET Estado = @NuevoEstado WHERE IdFactura = @IdFactura;"; var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { NuevoEstado = nuevoEstado, IdFactura = idFactura }, transaction); return rowsAffected == 1; } public async Task UpdateNumeroFacturaAsync(int idFactura, string numeroFactura, IDbTransaction transaction) { if (transaction == null || transaction.Connection == null) { throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas."); } const string sql = "UPDATE dbo.susc_Facturas SET NumeroFactura = @NumeroFactura, Estado = 'Pendiente de Cobro' WHERE IdFactura = @IdFactura;"; var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { NumeroFactura = numeroFactura, IdFactura = idFactura }, transaction); return rowsAffected == 1; } public async Task UpdateLoteDebitoAsync(IEnumerable idsFacturas, int idLoteDebito, IDbTransaction transaction) { if (transaction == null || transaction.Connection == null) { throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas."); } const string sql = "UPDATE dbo.susc_Facturas SET IdLoteDebito = @IdLoteDebito, Estado = 'Enviada a Débito' WHERE IdFactura IN @IdsFacturas;"; var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { IdLoteDebito = idLoteDebito, IdsFacturas = idsFacturas }, transaction); return rowsAffected == idsFacturas.Count(); } public async Task> GetByPeriodoEnrichedAsync(string periodo) { const string sql = @" SELECT f.*, s.NombreCompleto AS NombreSuscriptor, p.Nombre AS NombrePublicacion FROM dbo.susc_Facturas f JOIN dbo.susc_Suscripciones sc ON f.IdSuscripcion = sc.IdSuscripcion JOIN dbo.susc_Suscriptores s ON sc.IdSuscriptor = s.IdSuscriptor JOIN dbo.dist_dtPublicaciones p ON sc.IdPublicacion = p.Id_Publicacion WHERE f.Periodo = @Periodo ORDER BY s.NombreCompleto; "; try { using var connection = _connectionFactory.CreateConnection(); var result = await connection.QueryAsync( sql, (factura, suscriptor, publicacion) => (factura, suscriptor, publicacion), new { Periodo = periodo }, splitOn: "NombreSuscriptor,NombrePublicacion" ); return result; } catch (Exception ex) { _logger.LogError(ex, "Error al obtener facturas enriquecidas para el período {Periodo}", periodo); return Enumerable.Empty<(Factura, string, string)>(); } } public async Task UpdateEstadoYMotivoAsync(int idFactura, string nuevoEstado, string? motivoRechazo, IDbTransaction transaction) { if (transaction == null || transaction.Connection == null) { throw new ArgumentNullException(nameof(transaction), "La transacción o su conexión no pueden ser nulas."); } const string sql = @" UPDATE dbo.susc_Facturas SET Estado = @NuevoEstado, MotivoRechazo = @MotivoRechazo WHERE IdFactura = @IdFactura;"; var rowsAffected = await transaction.Connection.ExecuteAsync( sql, new { NuevoEstado = nuevoEstado, MotivoRechazo = motivoRechazo, IdFactura = idFactura }, transaction ); return rowsAffected == 1; } } }