Files
GestionIntegralWeb/Backend/GestionIntegral.Api/Services/Contables/PagoDistribuidorService.cs

273 lines
13 KiB
C#

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<PagoDistribuidorService> _logger;
public PagoDistribuidorService(
IPagoDistribuidorRepository pagoRepo,
IDistribuidorRepository distribuidorRepo,
ITipoPagoRepository tipoPagoRepo,
IEmpresaRepository empresaRepo,
ISaldoRepository saldoRepo,
DbConnectionFactory connectionFactory,
ILogger<PagoDistribuidorService> logger)
{
_pagoRepo = pagoRepo;
_distribuidorRepo = distribuidorRepo;
_tipoPagoRepo = tipoPagoRepo;
_empresaRepo = empresaRepo;
_saldoRepo = saldoRepo;
_connectionFactory = connectionFactory;
_logger = logger;
}
private async Task<PagoDistribuidorDto> 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<IEnumerable<PagoDistribuidorDto>> 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<PagoDistribuidorDto>();
foreach (var pago in pagos)
{
dtos.Add(await MapToDto(pago));
}
return dtos;
}
public async Task<PagoDistribuidorDto?> 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();
IDbTransaction? transaction = null; // Declarar fuera para el finally
try
{
if (connection.State != ConnectionState.Open)
{
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
}
transaction = connection.BeginTransaction();
var pagoCreado = await _pagoRepo.CreateAsync(nuevoPago, idUsuario, transaction);
if (pagoCreado == null) throw new DataException("Error al registrar el pago.");
decimal montoParaSaldo;
if (createDto.TipoMovimiento == "Recibido")
{
montoParaSaldo = -createDto.Monto;
}
else
{
montoParaSaldo = createDto.Monto;
}
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync("Distribuidores", pagoCreado.IdDistribuidor, pagoCreado.IdEmpresa, montoParaSaldo, 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 (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de CrearAsync PagoDistribuidor."); }
_logger.LogError(ex, "Error CrearAsync PagoDistribuidor.");
return (null, $"Error interno: {ex.Message}");
}
finally
{
if (connection.State == ConnectionState.Open)
{
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close();
}
}
}
public async Task<(bool Exito, string? Error)> ActualizarAsync(int idPago, UpdatePagoDistribuidorDto updateDto, int idUsuario)
{
using var connection = _connectionFactory.CreateConnection();
IDbTransaction? transaction = null;
try
{
if (connection.State != ConnectionState.Open)
{
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
}
transaction = connection.BeginTransaction();
var pagoExistente = await _pagoRepo.GetByIdAsync(idPago);
if (pagoExistente == null)
{
transaction.Rollback(); // Rollback si no se encuentra
return (false, "Pago no encontrado.");
}
if (await _tipoPagoRepo.GetByIdAsync(updateDto.IdTipoPago) == null)
{
transaction.Rollback();
return (false, "Tipo de pago no válido.");
}
decimal impactoOriginalSaldo = pagoExistente.TipoMovimiento == "Recibido" ? -pagoExistente.Monto : pagoExistente.Monto;
decimal impactoNuevoSaldo = pagoExistente.TipoMovimiento == "Recibido" ? -updateDto.Monto : updateDto.Monto;
decimal diferenciaAjusteSaldo = impactoNuevoSaldo - impactoOriginalSaldo;
var pagoParaActualizarEnRepo = new PagoDistribuidor
{
IdPago = pagoExistente.IdPago,
IdDistribuidor = pagoExistente.IdDistribuidor,
Fecha = pagoExistente.Fecha,
TipoMovimiento = pagoExistente.TipoMovimiento,
Recibo = pagoExistente.Recibo,
Monto = updateDto.Monto,
IdTipoPago = updateDto.IdTipoPago,
Detalle = updateDto.Detalle,
IdEmpresa = pagoExistente.IdEmpresa
};
var actualizado = await _pagoRepo.UpdateAsync(pagoParaActualizarEnRepo, idUsuario, transaction);
if (!actualizado) throw new DataException("Error al actualizar el pago en la base de datos.");
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 (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de ActualizarAsync PagoDistribuidor (KeyNotFound)."); } return (false, "Pago no encontrado."); }
catch (Exception ex)
{
try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de ActualizarAsync PagoDistribuidor."); }
_logger.LogError(ex, "Error ActualizarAsync PagoDistribuidor ID: {Id}", idPago);
return (false, $"Error interno: {ex.Message}");
}
finally
{
if (connection.State == ConnectionState.Open)
{
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close();
}
}
}
public async Task<(bool Exito, string? Error)> EliminarAsync(int idPago, int idUsuario)
{
using var connection = _connectionFactory.CreateConnection();
IDbTransaction? transaction = null;
try
{
if (connection.State != ConnectionState.Open)
{
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
}
transaction = connection.BeginTransaction();
var pagoExistente = await _pagoRepo.GetByIdAsync(idPago);
if (pagoExistente == null)
{
transaction.Rollback();
return (false, "Pago no encontrado.");
}
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 de la base de datos.");
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 (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de EliminarAsync PagoDistribuidor (KeyNotFound)."); } return (false, "Pago no encontrado."); }
catch (Exception ex)
{
try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de EliminarAsync PagoDistribuidor."); }
_logger.LogError(ex, "Error EliminarAsync PagoDistribuidor ID: {Id}", idPago);
return (false, $"Error interno: {ex.Message}");
}
finally
{
if (connection.State == ConnectionState.Open)
{
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close();
}
}
}
}
}