< Summary

Information
Class: FAU.Logica.Services.AguinaldoSemestralService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/AguinaldoSemestralService.cs
Line coverage
97%
Covered lines: 131
Uncovered lines: 3
Coverable lines: 134
Total lines: 204
Line coverage: 97.7%
Branch coverage
88%
Covered branches: 16
Total branches: 18
Branch coverage: 88.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ValidarHistoricoAsync()100%11100%
CalcularAguinaldoSemestralAsync()85.71%141497.43%
CalcularSemestre(...)100%22100%
ObtenerParametrosPeriodoAsync()100%11100%
ObtenerMesesSemestre(...)75%4495.23%

File(s)

/home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/AguinaldoSemestralService.cs

#LineLine coverage
 1using FAU.DataAccess;
 2using FAU.DataAccess.Repositories;
 3using FAU.Entidades;
 4using FAU.Logica.DTOs;
 5using Microsoft.EntityFrameworkCore;
 6
 7namespace FAU.Logica.Services;
 8
 9public class AguinaldoSemestralService : IAguinaldoSemestralService
 10{
 11    private readonly IAguinaldoValidador _validador;
 12    private readonly IAguinaldosRepository _aguinaldosRepository;
 13    private readonly IPersonalRepository _personalRepository;
 14    private readonly ApplicationDbContext _context;
 15    private readonly AguinaldoCalculator _calculator;
 16    private readonly IAuditoriaService _auditoriaService;
 17    private readonly ICurrentUserContext _currentUser;
 18
 619    public AguinaldoSemestralService(
 620        IAguinaldoValidador validador,
 621        IAguinaldosRepository aguinaldosRepository,
 622        IPersonalRepository personalRepository,
 623        ApplicationDbContext context,
 624        AguinaldoCalculator calculator,
 625        IAuditoriaService auditoriaService,
 626        ICurrentUserContext currentUser)
 27    {
 628        _validador = validador;
 629        _aguinaldosRepository = aguinaldosRepository;
 630        _personalRepository = personalRepository;
 631        _context = context;
 632        _calculator = calculator;
 633        _auditoriaService = auditoriaService;
 634        _currentUser = currentUser;
 635    }
 36
 37    public async Task<AguinaldoValidacionDto> ValidarHistoricoAsync(CalcularAguinaldoSemestralRequest request)
 38    {
 139        return await _validador.ValidarHistoricoSemestralAsync(request);
 140    }
 41
 42    public async Task<(bool Success, IEnumerable<AguinaldoSemestralDto> Resultados, string? Error)> CalcularAguinaldoSem
 43    {
 544        var validacion = await _validador.ValidarHistoricoSemestralAsync(request);
 545        var cedulasConHistoricoIncompleto = validacion.Incidencias
 146            .Where(i => i.Tipo == "HistoricoIncompleto")
 147            .Select(i => i.Cedula)
 548            .ToHashSet();
 49
 550        var parametros = await ObtenerParametrosPeriodoAsync(request);
 551        if (parametros is null)
 152            return (false, Array.Empty<AguinaldoSemestralDto>(), "No existe configuración de parámetros para el período 
 53
 454        var activos = (await _personalRepository.GetActivosAsync()).ToList();
 55
 56        // Cargar todo el histórico del semestre en una sola query para evitar N+1
 457        var mesesSemestre = ObtenerMesesSemestre(request);
 458        var mesesSet = mesesSemestre.ToHashSet();
 859        var personaIds = activos.Select(a => a.PersonaId).ToList();
 2860        var aniosSemestre = mesesSemestre.Select(m => m.Anio).Distinct().ToList();
 2861        var mesesIds = mesesSemestre.Select(m => m.Mes).Distinct().ToList();
 62
 463        var todosHistoricos = await _context.HistoricoLiquidaciones
 464            .Where(h => personaIds.Contains(h.PersonaId)
 465                && aniosSemestre.Contains(h.Anio)
 466                && mesesIds.Contains(h.Mes))
 467            .Select(h => new { h.PersonaId, h.Anio, h.Mes, h.HaberesGravados })
 468            .ToListAsync();
 69
 470        var historicosPorPersona = todosHistoricos
 1871            .Where(h => mesesSet.Contains((h.Anio, h.Mes)))
 1872            .GroupBy(h => h.PersonaId)
 1073            .ToDictionary(g => g.Key, g => g.ToList());
 74
 475        var semestre = CalcularSemestre(request);
 76
 477        var aguinaldosExistentes = await _context.Set<Aguinaldo>()
 478            .Where(a => personaIds.Contains(a.PersonaId) && a.Anio == (short)request.Anio && a.Semestre == semestre)
 579            .ToDictionaryAsync(a => a.PersonaId);
 80
 481        var resultados = new List<AguinaldoSemestralDto>();
 82
 1683        foreach (var relacion in activos)
 84        {
 485            if (cedulasConHistoricoIncompleto.Contains(relacion.Persona.Cedula))
 86                continue;
 87
 388            var historicoPersona = historicosPorPersona.GetValueOrDefault(relacion.PersonaId);
 2189            var totalHaberesGravadosSemestre = historicoPersona?.Sum(x => x.HaberesGravados) ?? 0m;
 390            var mesesTrabajados = historicoPersona?.Count ?? 0;
 91
 392            var tasaMontepio = relacion.Regimen.EsLeyVieja
 393                ? parametros.TasaMontepioLeyVieja
 394                : parametros.TasaMontepioLeyNueva;
 95
 396            var resultado = _calculator.Calcular(
 397                relacion.PersonaId,
 398                relacion.Persona.Cedula,
 399                relacion.Persona.NombreCompleto,
 3100                (short)request.Anio,
 3101                semestre,
 3102                totalHaberesGravadosSemestre,
 3103                (short)mesesTrabajados,
 3104                tasaMontepio,
 3105                parametros.TasaFonasa);
 106
 3107            resultados.Add(resultado);
 108
 109            long aguinaldoId;
 3110            if (aguinaldosExistentes.TryGetValue(relacion.PersonaId, out var existente))
 111            {
 1112                existente.TotalHaberesGravadosSemestre = totalHaberesGravadosSemestre;
 1113                existente.MesesTrabajados = (short)mesesTrabajados;
 1114                existente.Nominal = resultado.Nominal;
 1115                existente.Montepio = resultado.Montepio;
 1116                existente.Sanidad = resultado.Sanidad;
 1117                existente.TotalDescuentos = resultado.TotalDescuentos;
 1118                existente.Liquido = resultado.Liquido;
 1119                existente.Redondeo = resultado.Redondeo;
 1120                await _context.SaveChangesAsync();
 1121                aguinaldoId = existente.Id;
 122            }
 123            else
 124            {
 2125                var aguinaldo = new Aguinaldo
 2126                {
 2127                    PersonaId = relacion.PersonaId,
 2128                    Anio = (short)request.Anio,
 2129                    Semestre = semestre,
 2130                    TotalHaberesGravadosSemestre = totalHaberesGravadosSemestre,
 2131                    MesesTrabajados = (short)mesesTrabajados,
 2132                    Nominal = resultado.Nominal,
 2133                    Montepio = resultado.Montepio,
 2134                    Sanidad = resultado.Sanidad,
 2135                    TotalDescuentos = resultado.TotalDescuentos,
 2136                    Liquido = resultado.Liquido,
 2137                    Redondeo = resultado.Redondeo
 2138                };
 2139                var creado = await _aguinaldosRepository.CreateAsync(aguinaldo);
 2140                aguinaldoId = creado.Id;
 141            }
 142
 143            try
 144            {
 3145                await _auditoriaService.LogAuditoriaAsync(
 3146                    _currentUser.UserId ?? 0,
 3147                    AccionEnum.GenerarLiquidacion,
 3148                    ContextoEnum.Liquidaciones,
 3149                    entidad: "aguinaldos",
 3150                    entidadId: aguinaldoId,
 3151                    detalle: new { Resultado = resultado });
 3152            }
 0153            catch
 154            {
 155                // No bloquear el proceso por fallos en auditoría
 0156            }
 3157        }
 158
 4159        return (true, resultados, null);
 5160    }
 161
 162    private static short CalcularSemestre(CalcularAguinaldoSemestralRequest request)
 4163        => request.Mes == 6 ? (short)1 : (short)2;
 164
 165    private async Task<ParametroPeriodo?> ObtenerParametrosPeriodoAsync(CalcularAguinaldoSemestralRequest request)
 166    {
 5167        return await _context.ParametrosPeriodo
 5168            .Where(p => p.Anio < request.Anio || (p.Anio == request.Anio && p.Mes <= request.Mes))
 5169            .OrderByDescending(p => p.Anio)
 5170            .ThenByDescending(p => p.Mes)
 5171            .FirstOrDefaultAsync();
 5172    }
 173
 174    private static IReadOnlyList<(short Anio, short Mes)> ObtenerMesesSemestre(CalcularAguinaldoSemestralRequest request
 175    {
 4176        if (request.Mes == 6)
 177        {
 3178            return new List<(short Anio, short Mes)>
 3179            {
 3180                ((short)(request.Anio - 1), (short)12),
 3181                ((short)request.Anio, (short)1),
 3182                ((short)request.Anio, (short)2),
 3183                ((short)request.Anio, (short)3),
 3184                ((short)request.Anio, (short)4),
 3185                ((short)request.Anio, (short)5)
 3186            };
 187        }
 188
 1189        if (request.Mes == 12)
 190        {
 1191            return new List<(short Anio, short Mes)>
 1192            {
 1193                ((short)request.Anio, (short)6),
 1194                ((short)request.Anio, (short)7),
 1195                ((short)request.Anio, (short)8),
 1196                ((short)request.Anio, (short)9),
 1197                ((short)request.Anio, (short)10),
 1198                ((short)request.Anio, (short)11)
 1199            };
 200        }
 201
 0202        throw new InvalidOperationException("El mes proporcionado no corresponde a un cierre de aguinaldo semestral.");
 203    }
 204}