using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Contables; using GestionIntegral.Api.Data.Repositories.Distribucion; using GestionIntegral.Api.Dtos.Contables; using GestionIntegral.Api.Models.Contables; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; namespace GestionIntegral.Api.Services.Contables { public class PagoDistribuidorService : IPagoDistribuidorService { private readonly IPagoDistribuidorRepository _pagoRepo; private readonly IDistribuidorRepository _distribuidorRepo; private readonly ITipoPagoRepository _tipoPagoRepo; private readonly IEmpresaRepository _empresaRepo; private readonly ISaldoRepository _saldoRepo; private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public PagoDistribuidorService( IPagoDistribuidorRepository pagoRepo, IDistribuidorRepository distribuidorRepo, ITipoPagoRepository tipoPagoRepo, IEmpresaRepository empresaRepo, ISaldoRepository saldoRepo, DbConnectionFactory connectionFactory, ILogger logger) { _pagoRepo = pagoRepo; _distribuidorRepo = distribuidorRepo; _tipoPagoRepo = tipoPagoRepo; _empresaRepo = empresaRepo; _saldoRepo = saldoRepo; _connectionFactory = connectionFactory; _logger = logger; } private async Task MapToDto(PagoDistribuidor pago) { if (pago == null) return null!; var distribuidorData = await _distribuidorRepo.GetByIdAsync(pago.IdDistribuidor); var tipoPago = await _tipoPagoRepo.GetByIdAsync(pago.IdTipoPago); var empresa = await _empresaRepo.GetByIdAsync(pago.IdEmpresa); return new PagoDistribuidorDto { IdPago = pago.IdPago, IdDistribuidor = pago.IdDistribuidor, NombreDistribuidor = distribuidorData.Distribuidor?.Nombre ?? "N/A", Fecha = pago.Fecha.ToString("yyyy-MM-dd"), TipoMovimiento = pago.TipoMovimiento, Recibo = pago.Recibo, Monto = pago.Monto, IdTipoPago = pago.IdTipoPago, NombreTipoPago = tipoPago?.Nombre ?? "N/A", Detalle = pago.Detalle, IdEmpresa = pago.IdEmpresa, NombreEmpresa = empresa?.Nombre ?? "N/A" }; } public async Task> ObtenerTodosAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idDistribuidor, int? idEmpresa, string? tipoMovimiento) { var pagos = await _pagoRepo.GetAllAsync(fechaDesde, fechaHasta, idDistribuidor, idEmpresa, tipoMovimiento); var dtos = new List(); foreach (var pago in pagos) { dtos.Add(await MapToDto(pago)); } return dtos; } public async Task ObtenerPorIdAsync(int idPago) { var pago = await _pagoRepo.GetByIdAsync(idPago); return pago == null ? null : await MapToDto(pago); } public async Task<(PagoDistribuidorDto? Pago, string? Error)> CrearAsync(CreatePagoDistribuidorDto createDto, int idUsuario) { if (await _distribuidorRepo.GetByIdSimpleAsync(createDto.IdDistribuidor) == null) return (null, "Distribuidor no válido."); if (await _tipoPagoRepo.GetByIdAsync(createDto.IdTipoPago) == null) return (null, "Tipo de pago no válido."); if (await _empresaRepo.GetByIdAsync(createDto.IdEmpresa) == null) return (null, "Empresa no válida."); if (await _pagoRepo.ExistsByReciboAndTipoMovimientoAsync(createDto.Recibo, createDto.TipoMovimiento)) return (null, $"Ya existe un pago '{createDto.TipoMovimiento}' con el número de recibo '{createDto.Recibo}'."); var nuevoPago = new PagoDistribuidor { IdDistribuidor = createDto.IdDistribuidor, Fecha = createDto.Fecha.Date, TipoMovimiento = createDto.TipoMovimiento, Recibo = createDto.Recibo, Monto = createDto.Monto, IdTipoPago = createDto.IdTipoPago, Detalle = createDto.Detalle, IdEmpresa = createDto.IdEmpresa }; using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try { var pagoCreado = await _pagoRepo.CreateAsync(nuevoPago, idUsuario, transaction); if (pagoCreado == null) throw new DataException("Error al registrar el pago."); // Afectar Saldo // Si TipoMovimiento es "Recibido", el monto DISMINUYE la deuda del distribuidor (monto positivo para el servicio de saldo). // Si TipoMovimiento es "Realizado" (empresa paga a distribuidor), el monto AUMENTA la deuda (monto negativo para el servicio de saldo). decimal montoAjusteSaldo = createDto.TipoMovimiento == "Recibido" ? createDto.Monto : -createDto.Monto; bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync("Distribuidores", pagoCreado.IdDistribuidor, pagoCreado.IdEmpresa, montoAjusteSaldo, transaction); if (!saldoActualizado) throw new DataException("Error al actualizar el saldo del distribuidor."); transaction.Commit(); _logger.LogInformation("PagoDistribuidor ID {Id} creado y saldo afectado por Usuario ID {UserId}.", pagoCreado.IdPago, idUsuario); return (await MapToDto(pagoCreado), null); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error CrearAsync PagoDistribuidor."); return (null, $"Error interno: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> ActualizarAsync(int idPago, UpdatePagoDistribuidorDto updateDto, int idUsuario) { using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try { var pagoExistente = await _pagoRepo.GetByIdAsync(idPago); if (pagoExistente == null) return (false, "Pago no encontrado."); if (await _tipoPagoRepo.GetByIdAsync(updateDto.IdTipoPago) == null) return (false, "Tipo de pago no válido."); // Calcular la diferencia de monto para ajustar el saldo decimal montoOriginal = pagoExistente.TipoMovimiento == "Recibido" ? pagoExistente.Monto : -pagoExistente.Monto; decimal montoNuevo = pagoExistente.TipoMovimiento == "Recibido" ? updateDto.Monto : -updateDto.Monto; decimal diferenciaAjusteSaldo = montoNuevo - montoOriginal; // Actualizar campos permitidos pagoExistente.Monto = updateDto.Monto; pagoExistente.IdTipoPago = updateDto.IdTipoPago; pagoExistente.Detalle = updateDto.Detalle; var actualizado = await _pagoRepo.UpdateAsync(pagoExistente, idUsuario, transaction); if (!actualizado) throw new DataException("Error al actualizar el pago."); if (diferenciaAjusteSaldo != 0) { bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync("Distribuidores", pagoExistente.IdDistribuidor, pagoExistente.IdEmpresa, diferenciaAjusteSaldo, transaction); if (!saldoActualizado) throw new DataException("Error al ajustar el saldo del distribuidor tras la actualización del pago."); } transaction.Commit(); _logger.LogInformation("PagoDistribuidor ID {Id} actualizado por Usuario ID {UserId}.", idPago, idUsuario); return (true, null); } catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Pago no encontrado."); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error ActualizarAsync PagoDistribuidor ID: {Id}", idPago); return (false, $"Error interno: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> EliminarAsync(int idPago, int idUsuario) { using var connection = _connectionFactory.CreateConnection(); if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); using var transaction = connection.BeginTransaction(); try { var pagoExistente = await _pagoRepo.GetByIdAsync(idPago); if (pagoExistente == null) return (false, "Pago no encontrado."); // Revertir el efecto en el saldo // Si fue "Recibido", el saldo disminuyó (montoAjusteSaldo fue +Monto). Al eliminar, revertimos sumando -Monto (o restando +Monto). // Si fue "Realizado", el saldo aumentó (montoAjusteSaldo fue -Monto). Al eliminar, revertimos sumando +Monto (o restando -Monto). decimal montoReversion = pagoExistente.TipoMovimiento == "Recibido" ? -pagoExistente.Monto : pagoExistente.Monto; var eliminado = await _pagoRepo.DeleteAsync(idPago, idUsuario, transaction); if (!eliminado) throw new DataException("Error al eliminar el pago."); bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync("Distribuidores", pagoExistente.IdDistribuidor, pagoExistente.IdEmpresa, montoReversion, transaction); if (!saldoActualizado) throw new DataException("Error al revertir el saldo del distribuidor tras la eliminación del pago."); transaction.Commit(); _logger.LogInformation("PagoDistribuidor ID {Id} eliminado por Usuario ID {UserId}.", idPago, idUsuario); return (true, null); } catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Pago no encontrado."); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error EliminarAsync PagoDistribuidor ID: {Id}", idPago); return (false, $"Error interno: {ex.Message}"); } } } }