240 lines
12 KiB
C#
240 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 PorcMonCanillaService : IPorcMonCanillaService
|
||
|
|
{
|
||
|
|
private readonly IPorcMonCanillaRepository _porcMonCanillaRepository;
|
||
|
|
private readonly IPublicacionRepository _publicacionRepository;
|
||
|
|
private readonly ICanillaRepository _canillaRepository; // Para validar IdCanilla y obtener nombre
|
||
|
|
private readonly DbConnectionFactory _connectionFactory;
|
||
|
|
private readonly ILogger<PorcMonCanillaService> _logger;
|
||
|
|
|
||
|
|
public PorcMonCanillaService(
|
||
|
|
IPorcMonCanillaRepository porcMonCanillaRepository,
|
||
|
|
IPublicacionRepository publicacionRepository,
|
||
|
|
ICanillaRepository canillaRepository,
|
||
|
|
DbConnectionFactory connectionFactory,
|
||
|
|
ILogger<PorcMonCanillaService> logger)
|
||
|
|
{
|
||
|
|
_porcMonCanillaRepository = porcMonCanillaRepository;
|
||
|
|
_publicacionRepository = publicacionRepository;
|
||
|
|
_canillaRepository = canillaRepository;
|
||
|
|
_connectionFactory = connectionFactory;
|
||
|
|
_logger = logger;
|
||
|
|
}
|
||
|
|
|
||
|
|
private PorcMonCanillaDto MapToDto((PorcMonCanilla Item, string NomApeCanilla) data) => new PorcMonCanillaDto
|
||
|
|
{
|
||
|
|
IdPorcMon = data.Item.IdPorcMon,
|
||
|
|
IdPublicacion = data.Item.IdPublicacion,
|
||
|
|
IdCanilla = data.Item.IdCanilla,
|
||
|
|
NomApeCanilla = data.NomApeCanilla,
|
||
|
|
VigenciaD = data.Item.VigenciaD.ToString("yyyy-MM-dd"),
|
||
|
|
VigenciaH = data.Item.VigenciaH?.ToString("yyyy-MM-dd"),
|
||
|
|
PorcMon = data.Item.PorcMon,
|
||
|
|
EsPorcentaje = data.Item.EsPorcentaje
|
||
|
|
};
|
||
|
|
|
||
|
|
private async Task<PorcMonCanillaDto?> MapToDtoWithLookup(PorcMonCanilla? item)
|
||
|
|
{
|
||
|
|
if (item == null) return null; // Si el item es null, devuelve null
|
||
|
|
|
||
|
|
var canillaData = await _canillaRepository.GetByIdAsync(item.IdCanilla);
|
||
|
|
return new PorcMonCanillaDto
|
||
|
|
{
|
||
|
|
IdPorcMon = item.IdPorcMon,
|
||
|
|
IdPublicacion = item.IdPublicacion,
|
||
|
|
IdCanilla = item.IdCanilla,
|
||
|
|
NomApeCanilla = canillaData.Canilla?.NomApe ?? "Canillita Desconocido",
|
||
|
|
VigenciaD = item.VigenciaD.ToString("yyyy-MM-dd"),
|
||
|
|
VigenciaH = item.VigenciaH?.ToString("yyyy-MM-dd"),
|
||
|
|
PorcMon = item.PorcMon,
|
||
|
|
EsPorcentaje = item.EsPorcentaje
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
public async Task<IEnumerable<PorcMonCanillaDto>> ObtenerPorPublicacionIdAsync(int idPublicacion)
|
||
|
|
{
|
||
|
|
var data = await _porcMonCanillaRepository.GetByPublicacionIdAsync(idPublicacion);
|
||
|
|
// Filtrar los nulos que MapToDto podría devolver (aunque no debería en este caso si GetAllWithProfileNameAsync no devuelve usuarios nulos en la tupla)
|
||
|
|
return data.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<PorcMonCanillaDto?> ObtenerPorIdAsync(int idPorcMon)
|
||
|
|
{
|
||
|
|
var item = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon);
|
||
|
|
return await MapToDtoWithLookup(item);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<(PorcMonCanillaDto? Item, string? Error)> CrearAsync(CreatePorcMonCanillaDto createDto, int idUsuario)
|
||
|
|
{
|
||
|
|
if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null)
|
||
|
|
return (null, "La publicación especificada no existe.");
|
||
|
|
|
||
|
|
var canillaData = await _canillaRepository.GetByIdAsync(createDto.IdCanilla);
|
||
|
|
if (canillaData.Canilla == null) // GetByIdAsync devuelve una tupla
|
||
|
|
return (null, "El canillita especificado no existe o no está activo.");
|
||
|
|
// Validar que solo canillitas accionistas pueden tener porcentaje/monto (o la regla de negocio que aplique)
|
||
|
|
// Por ejemplo, si solo los Accionistas pueden tener esta configuración:
|
||
|
|
// if (!canillaData.Canilla.Accionista) {
|
||
|
|
// return (null, "Solo los canillitas accionistas pueden tener un porcentaje/monto de pago configurado.");
|
||
|
|
// }
|
||
|
|
|
||
|
|
|
||
|
|
var itemActivo = await _porcMonCanillaRepository.GetActiveByPublicacionCanillaAndDateAsync(createDto.IdPublicacion, createDto.IdCanilla, createDto.VigenciaD.Date);
|
||
|
|
if (itemActivo != null)
|
||
|
|
{
|
||
|
|
return (null, $"Ya existe un porcentaje/monto activo para esta publicación y canillita en la fecha {createDto.VigenciaD:dd/MM/yyyy}. Cierre el período anterior.");
|
||
|
|
}
|
||
|
|
|
||
|
|
var nuevoItem = new PorcMonCanilla
|
||
|
|
{
|
||
|
|
IdPublicacion = createDto.IdPublicacion,
|
||
|
|
IdCanilla = createDto.IdCanilla,
|
||
|
|
VigenciaD = createDto.VigenciaD.Date,
|
||
|
|
VigenciaH = null,
|
||
|
|
PorcMon = createDto.PorcMon,
|
||
|
|
EsPorcentaje = createDto.EsPorcentaje
|
||
|
|
};
|
||
|
|
|
||
|
|
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 itemAnterior = await _porcMonCanillaRepository.GetPreviousActiveAsync(createDto.IdPublicacion, createDto.IdCanilla, nuevoItem.VigenciaD, transaction);
|
||
|
|
if (itemAnterior != null)
|
||
|
|
{
|
||
|
|
if (itemAnterior.VigenciaD.Date >= nuevoItem.VigenciaD.Date)
|
||
|
|
{
|
||
|
|
transaction.Rollback();
|
||
|
|
return (null, $"La fecha de inicio ({nuevoItem.VigenciaD:dd/MM/yyyy}) no puede ser anterior o igual a la del último período vigente ({itemAnterior.VigenciaD:dd/MM/yyyy}).");
|
||
|
|
}
|
||
|
|
itemAnterior.VigenciaH = nuevoItem.VigenciaD.AddDays(-1);
|
||
|
|
await _porcMonCanillaRepository.UpdateAsync(itemAnterior, idUsuario, transaction);
|
||
|
|
}
|
||
|
|
|
||
|
|
var itemCreado = await _porcMonCanillaRepository.CreateAsync(nuevoItem, idUsuario, transaction);
|
||
|
|
if (itemCreado == null) throw new DataException("Error al crear el registro.");
|
||
|
|
|
||
|
|
transaction.Commit();
|
||
|
|
_logger.LogInformation("PorcMonCanilla ID {Id} creado por Usuario ID {UserId}.", itemCreado.IdPorcMon, idUsuario);
|
||
|
|
return (await MapToDtoWithLookup(itemCreado), null);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
try { transaction.Rollback(); } catch { }
|
||
|
|
_logger.LogError(ex, "Error CrearAsync PorcMonCanilla para Pub ID {IdPub}, Canilla ID {IdCan}", createDto.IdPublicacion, createDto.IdCanilla);
|
||
|
|
return (null, $"Error interno: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<(bool Exito, string? Error)> ActualizarAsync(int idPorcMon, UpdatePorcMonCanillaDto 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 itemExistente = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon);
|
||
|
|
if (itemExistente == null) return (false, "Registro de porcentaje/monto no encontrado.");
|
||
|
|
|
||
|
|
if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < itemExistente.VigenciaD.Date)
|
||
|
|
return (false, "Vigencia Hasta no puede ser anterior a Vigencia Desde.");
|
||
|
|
|
||
|
|
if (updateDto.VigenciaH.HasValue)
|
||
|
|
{
|
||
|
|
var itemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemExistente.IdPublicacion);
|
||
|
|
var itemsPosteriores = itemsPubCanillaData
|
||
|
|
.Where(i => i.Item.IdCanilla == itemExistente.IdCanilla &&
|
||
|
|
i.Item.IdPorcMon != idPorcMon &&
|
||
|
|
i.Item.VigenciaD.Date <= updateDto.VigenciaH.Value.Date &&
|
||
|
|
i.Item.VigenciaD.Date > itemExistente.VigenciaD.Date);
|
||
|
|
if(itemsPosteriores.Any())
|
||
|
|
{
|
||
|
|
return (false, "No se puede cerrar este período porque existen configuraciones posteriores para este canillita que se solaparían.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
itemExistente.PorcMon = updateDto.PorcMon;
|
||
|
|
itemExistente.EsPorcentaje = updateDto.EsPorcentaje;
|
||
|
|
if (updateDto.VigenciaH.HasValue)
|
||
|
|
{
|
||
|
|
itemExistente.VigenciaH = updateDto.VigenciaH.Value.Date;
|
||
|
|
}
|
||
|
|
|
||
|
|
var actualizado = await _porcMonCanillaRepository.UpdateAsync(itemExistente, idUsuario, transaction);
|
||
|
|
if (!actualizado) throw new DataException("Error al actualizar registro.");
|
||
|
|
|
||
|
|
transaction.Commit();
|
||
|
|
_logger.LogInformation("PorcMonCanilla ID {Id} actualizado por Usuario ID {UserId}.", idPorcMon, idUsuario);
|
||
|
|
return (true, null);
|
||
|
|
}
|
||
|
|
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); }
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
try { transaction.Rollback(); } catch { }
|
||
|
|
_logger.LogError(ex, "Error ActualizarAsync PorcMonCanilla ID: {Id}", idPorcMon);
|
||
|
|
return (false, $"Error interno: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<(bool Exito, string? Error)> EliminarAsync(int idPorcMon, 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 itemAEliminar = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon);
|
||
|
|
if (itemAEliminar == null) return (false, "Registro no encontrado.");
|
||
|
|
|
||
|
|
if (itemAEliminar.VigenciaH == null)
|
||
|
|
{
|
||
|
|
var todosItemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemAEliminar.IdPublicacion);
|
||
|
|
var todosItemsPubCanilla = todosItemsPubCanillaData
|
||
|
|
.Where(i => i.Item.IdCanilla == itemAEliminar.IdCanilla)
|
||
|
|
.Select(i => i.Item)
|
||
|
|
.OrderByDescending(i => i.VigenciaD).ToList();
|
||
|
|
|
||
|
|
var indiceActual = todosItemsPubCanilla.FindIndex(i => i.IdPorcMon == idPorcMon);
|
||
|
|
if(indiceActual != -1 && (indiceActual + 1) < todosItemsPubCanilla.Count)
|
||
|
|
{
|
||
|
|
var itemAnteriorDirecto = todosItemsPubCanilla[indiceActual + 1];
|
||
|
|
if(itemAnteriorDirecto.VigenciaH.HasValue &&
|
||
|
|
itemAnteriorDirecto.VigenciaH.Value.Date == itemAEliminar.VigenciaD.AddDays(-1).Date)
|
||
|
|
{
|
||
|
|
itemAnteriorDirecto.VigenciaH = null;
|
||
|
|
await _porcMonCanillaRepository.UpdateAsync(itemAnteriorDirecto, idUsuario, transaction);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var eliminado = await _porcMonCanillaRepository.DeleteAsync(idPorcMon, idUsuario, transaction);
|
||
|
|
if (!eliminado) throw new DataException("Error al eliminar registro.");
|
||
|
|
|
||
|
|
transaction.Commit();
|
||
|
|
_logger.LogInformation("PorcMonCanilla ID {Id} eliminado por Usuario ID {UserId}.", idPorcMon, idUsuario);
|
||
|
|
return (true, null);
|
||
|
|
}
|
||
|
|
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); }
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
try { transaction.Rollback(); } catch { }
|
||
|
|
_logger.LogError(ex, "Error EliminarAsync PorcMonCanilla ID: {Id}", idPorcMon);
|
||
|
|
return (false, $"Error interno: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|