234 lines
12 KiB
C#
234 lines
12 KiB
C#
|
|
using GestionIntegral.Api.Data;
|
||
|
|
using GestionIntegral.Api.Data.Repositories.Distribucion;
|
||
|
|
using GestionIntegral.Api.Dtos.Distribucion;
|
||
|
|
using GestionIntegral.Api.Models.Distribucion;
|
||
|
|
using Microsoft.Extensions.Logging;
|
||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Data;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Threading.Tasks;
|
||
|
|
|
||
|
|
namespace GestionIntegral.Api.Services.Distribucion
|
||
|
|
{
|
||
|
|
public class PorcPagoService : IPorcPagoService
|
||
|
|
{
|
||
|
|
private readonly IPorcPagoRepository _porcPagoRepository;
|
||
|
|
private readonly IPublicacionRepository _publicacionRepository;
|
||
|
|
private readonly IDistribuidorRepository _distribuidorRepository;
|
||
|
|
private readonly DbConnectionFactory _connectionFactory;
|
||
|
|
private readonly ILogger<PorcPagoService> _logger;
|
||
|
|
|
||
|
|
public PorcPagoService(
|
||
|
|
IPorcPagoRepository porcPagoRepository,
|
||
|
|
IPublicacionRepository publicacionRepository,
|
||
|
|
IDistribuidorRepository distribuidorRepository,
|
||
|
|
DbConnectionFactory connectionFactory,
|
||
|
|
ILogger<PorcPagoService> logger)
|
||
|
|
{
|
||
|
|
_porcPagoRepository = porcPagoRepository;
|
||
|
|
_publicacionRepository = publicacionRepository;
|
||
|
|
_distribuidorRepository = distribuidorRepository;
|
||
|
|
_connectionFactory = connectionFactory;
|
||
|
|
_logger = logger;
|
||
|
|
}
|
||
|
|
|
||
|
|
private PorcPagoDto MapToDto((PorcPago PorcPago, string NombreDistribuidor) data) => new PorcPagoDto
|
||
|
|
{
|
||
|
|
IdPorcentaje = data.PorcPago.IdPorcentaje,
|
||
|
|
IdPublicacion = data.PorcPago.IdPublicacion,
|
||
|
|
IdDistribuidor = data.PorcPago.IdDistribuidor,
|
||
|
|
NombreDistribuidor = data.NombreDistribuidor,
|
||
|
|
VigenciaD = data.PorcPago.VigenciaD.ToString("yyyy-MM-dd"),
|
||
|
|
VigenciaH = data.PorcPago.VigenciaH?.ToString("yyyy-MM-dd"),
|
||
|
|
Porcentaje = data.PorcPago.Porcentaje
|
||
|
|
};
|
||
|
|
|
||
|
|
private async Task<PorcPagoDto> MapToDtoWithLookup(PorcPago porcPago)
|
||
|
|
{
|
||
|
|
var distribuidorData = await _distribuidorRepository.GetByIdAsync(porcPago.IdDistribuidor);
|
||
|
|
return new PorcPagoDto
|
||
|
|
{
|
||
|
|
IdPorcentaje = porcPago.IdPorcentaje,
|
||
|
|
IdPublicacion = porcPago.IdPublicacion,
|
||
|
|
IdDistribuidor = porcPago.IdDistribuidor,
|
||
|
|
NombreDistribuidor = distribuidorData.Distribuidor?.Nombre ?? "Distribuidor Desconocido",
|
||
|
|
VigenciaD = porcPago.VigenciaD.ToString("yyyy-MM-dd"),
|
||
|
|
VigenciaH = porcPago.VigenciaH?.ToString("yyyy-MM-dd"),
|
||
|
|
Porcentaje = porcPago.Porcentaje
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<IEnumerable<PorcPagoDto>> ObtenerPorPublicacionIdAsync(int idPublicacion)
|
||
|
|
{
|
||
|
|
var data = await _porcPagoRepository.GetByPublicacionIdAsync(idPublicacion);
|
||
|
|
return data.Select(MapToDto);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<PorcPagoDto?> ObtenerPorIdAsync(int idPorcentaje)
|
||
|
|
{
|
||
|
|
var porcPago = await _porcPagoRepository.GetByIdAsync(idPorcentaje);
|
||
|
|
if (porcPago == null) return null;
|
||
|
|
return await MapToDtoWithLookup(porcPago);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<(PorcPagoDto? PorcPago, string? Error)> CrearAsync(CreatePorcPagoDto createDto, int idUsuario)
|
||
|
|
{
|
||
|
|
if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null)
|
||
|
|
return (null, "La publicación especificada no existe.");
|
||
|
|
var distribuidorData = await _distribuidorRepository.GetByIdAsync(createDto.IdDistribuidor);
|
||
|
|
if (distribuidorData.Distribuidor == null)
|
||
|
|
return (null, "El distribuidor especificado no existe.");
|
||
|
|
|
||
|
|
var porcPagoActivo = await _porcPagoRepository.GetActiveByPublicacionDistribuidorAndDateAsync(createDto.IdPublicacion, createDto.IdDistribuidor, createDto.VigenciaD.Date);
|
||
|
|
if (porcPagoActivo != null)
|
||
|
|
{
|
||
|
|
return (null, $"Ya existe un porcentaje de pago activo para esta publicación y distribuidor en la fecha {createDto.VigenciaD:dd/MM/yyyy}. Cierre el período anterior primero.");
|
||
|
|
}
|
||
|
|
|
||
|
|
var nuevoPorcPago = new PorcPago
|
||
|
|
{
|
||
|
|
IdPublicacion = createDto.IdPublicacion,
|
||
|
|
IdDistribuidor = createDto.IdDistribuidor,
|
||
|
|
VigenciaD = createDto.VigenciaD.Date,
|
||
|
|
VigenciaH = null,
|
||
|
|
Porcentaje = createDto.Porcentaje
|
||
|
|
};
|
||
|
|
|
||
|
|
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 porcPagoAnterior = await _porcPagoRepository.GetPreviousActivePorcPagoAsync(createDto.IdPublicacion, createDto.IdDistribuidor, nuevoPorcPago.VigenciaD, transaction);
|
||
|
|
if (porcPagoAnterior != null)
|
||
|
|
{
|
||
|
|
if (porcPagoAnterior.VigenciaD.Date >= nuevoPorcPago.VigenciaD.Date)
|
||
|
|
{
|
||
|
|
transaction.Rollback();
|
||
|
|
return (null, $"La fecha de inicio del nuevo porcentaje ({nuevoPorcPago.VigenciaD:dd/MM/yyyy}) no puede ser anterior o igual a la del último porcentaje vigente ({porcPagoAnterior.VigenciaD:dd/MM/yyyy}) para este distribuidor.");
|
||
|
|
}
|
||
|
|
porcPagoAnterior.VigenciaH = nuevoPorcPago.VigenciaD.AddDays(-1);
|
||
|
|
await _porcPagoRepository.UpdateAsync(porcPagoAnterior, idUsuario, transaction);
|
||
|
|
}
|
||
|
|
|
||
|
|
var porcPagoCreado = await _porcPagoRepository.CreateAsync(nuevoPorcPago, idUsuario, transaction);
|
||
|
|
if (porcPagoCreado == null) throw new DataException("Error al crear el porcentaje de pago.");
|
||
|
|
|
||
|
|
transaction.Commit();
|
||
|
|
_logger.LogInformation("PorcPago ID {Id} creado por Usuario ID {UserId}.", porcPagoCreado.IdPorcentaje, idUsuario);
|
||
|
|
return (new PorcPagoDto { // Construir DTO manualmente ya que tenemos NombreDistribuidor
|
||
|
|
IdPorcentaje = porcPagoCreado.IdPorcentaje,
|
||
|
|
IdPublicacion = porcPagoCreado.IdPublicacion,
|
||
|
|
IdDistribuidor = porcPagoCreado.IdDistribuidor,
|
||
|
|
NombreDistribuidor = distribuidorData.Distribuidor.Nombre,
|
||
|
|
VigenciaD = porcPagoCreado.VigenciaD.ToString("yyyy-MM-dd"),
|
||
|
|
VigenciaH = porcPagoCreado.VigenciaH?.ToString("yyyy-MM-dd"),
|
||
|
|
Porcentaje = porcPagoCreado.Porcentaje
|
||
|
|
}, null);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
try { transaction.Rollback(); } catch { }
|
||
|
|
_logger.LogError(ex, "Error CrearAsync PorcPago para Pub ID {IdPub}, Dist ID {IdDist}", createDto.IdPublicacion, createDto.IdDistribuidor);
|
||
|
|
return (null, $"Error interno: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<(bool Exito, string? Error)> ActualizarAsync(int idPorcentaje, UpdatePorcPagoDto 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 porcPagoExistente = await _porcPagoRepository.GetByIdAsync(idPorcentaje);
|
||
|
|
if (porcPagoExistente == null) return (false, "Porcentaje de pago no encontrado.");
|
||
|
|
|
||
|
|
if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < porcPagoExistente.VigenciaD.Date)
|
||
|
|
return (false, "Vigencia Hasta no puede ser anterior a Vigencia Desde.");
|
||
|
|
|
||
|
|
if (updateDto.VigenciaH.HasValue)
|
||
|
|
{
|
||
|
|
var porcentajesPubDist = await _porcPagoRepository.GetByPublicacionIdAsync(porcPagoExistente.IdPublicacion);
|
||
|
|
var porcentajesPosteriores = porcentajesPubDist
|
||
|
|
.Where(p => p.PorcPago.IdDistribuidor == porcPagoExistente.IdDistribuidor &&
|
||
|
|
p.PorcPago.IdPorcentaje != idPorcentaje &&
|
||
|
|
p.PorcPago.VigenciaD.Date <= updateDto.VigenciaH.Value.Date &&
|
||
|
|
p.PorcPago.VigenciaD.Date > porcPagoExistente.VigenciaD.Date);
|
||
|
|
if(porcentajesPosteriores.Any())
|
||
|
|
{
|
||
|
|
return (false, "No se puede cerrar este período porque existen porcentajes posteriores para este distribuidor que se solaparían.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
porcPagoExistente.Porcentaje = updateDto.Porcentaje;
|
||
|
|
if (updateDto.VigenciaH.HasValue)
|
||
|
|
{
|
||
|
|
porcPagoExistente.VigenciaH = updateDto.VigenciaH.Value.Date;
|
||
|
|
}
|
||
|
|
|
||
|
|
var actualizado = await _porcPagoRepository.UpdateAsync(porcPagoExistente, idUsuario, transaction);
|
||
|
|
if (!actualizado) throw new DataException("Error al actualizar porcentaje de pago.");
|
||
|
|
|
||
|
|
transaction.Commit();
|
||
|
|
_logger.LogInformation("PorcPago ID {Id} actualizado por Usuario ID {UserId}.", idPorcentaje, idUsuario);
|
||
|
|
return (true, null);
|
||
|
|
}
|
||
|
|
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Porcentaje no encontrado."); }
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
try { transaction.Rollback(); } catch { }
|
||
|
|
_logger.LogError(ex, "Error ActualizarAsync PorcPago ID: {Id}", idPorcentaje);
|
||
|
|
return (false, $"Error interno: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<(bool Exito, string? Error)> EliminarAsync(int idPorcentaje, 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 porcPagoAEliminar = await _porcPagoRepository.GetByIdAsync(idPorcentaje);
|
||
|
|
if (porcPagoAEliminar == null) return (false, "Porcentaje de pago no encontrado.");
|
||
|
|
|
||
|
|
if (porcPagoAEliminar.VigenciaH == null)
|
||
|
|
{
|
||
|
|
var todosPorcPubDistData = await _porcPagoRepository.GetByPublicacionIdAsync(porcPagoAEliminar.IdPublicacion);
|
||
|
|
var todosPorcPubDist = todosPorcPubDistData
|
||
|
|
.Where(p => p.PorcPago.IdDistribuidor == porcPagoAEliminar.IdDistribuidor)
|
||
|
|
.Select(p => p.PorcPago)
|
||
|
|
.OrderByDescending(p => p.VigenciaD).ToList();
|
||
|
|
|
||
|
|
var indiceActual = todosPorcPubDist.FindIndex(p => p.IdPorcentaje == idPorcentaje);
|
||
|
|
if(indiceActual != -1 && (indiceActual + 1) < todosPorcPubDist.Count)
|
||
|
|
{
|
||
|
|
var porcPagoAnteriorDirecto = todosPorcPubDist[indiceActual + 1];
|
||
|
|
if(porcPagoAnteriorDirecto.VigenciaH.HasValue &&
|
||
|
|
porcPagoAnteriorDirecto.VigenciaH.Value.Date == porcPagoAEliminar.VigenciaD.AddDays(-1).Date)
|
||
|
|
{
|
||
|
|
porcPagoAnteriorDirecto.VigenciaH = null;
|
||
|
|
await _porcPagoRepository.UpdateAsync(porcPagoAnteriorDirecto, idUsuario, transaction);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var eliminado = await _porcPagoRepository.DeleteAsync(idPorcentaje, idUsuario, transaction);
|
||
|
|
if (!eliminado) throw new DataException("Error al eliminar porcentaje de pago.");
|
||
|
|
|
||
|
|
transaction.Commit();
|
||
|
|
_logger.LogInformation("PorcPago ID {Id} eliminado por Usuario ID {UserId}.", idPorcentaje, idUsuario);
|
||
|
|
return (true, null);
|
||
|
|
}
|
||
|
|
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Porcentaje no encontrado."); }
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
try { transaction.Rollback(); } catch { }
|
||
|
|
_logger.LogError(ex, "Error EliminarAsync PorcPago ID: {Id}", idPorcentaje);
|
||
|
|
return (false, $"Error interno: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|