< 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
0%
Covered lines: 0
Uncovered lines: 134
Coverable lines: 134
Total lines: 204
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 18
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
ValidarHistoricoAsync()100%210%
CalcularAguinaldoSemestralAsync()0%210140%
CalcularSemestre(...)0%620%
ObtenerParametrosPeriodoAsync()100%210%
ObtenerMesesSemestre(...)0%2040%

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
 019    public AguinaldoSemestralService(
 020        IAguinaldoValidador validador,
 021        IAguinaldosRepository aguinaldosRepository,
 022        IPersonalRepository personalRepository,
 023        ApplicationDbContext context,
 024        AguinaldoCalculator calculator,
 025        IAuditoriaService auditoriaService,
 026        ICurrentUserContext currentUser)
 27    {
 028        _validador = validador;
 029        _aguinaldosRepository = aguinaldosRepository;
 030        _personalRepository = personalRepository;
 031        _context = context;
 032        _calculator = calculator;
 033        _auditoriaService = auditoriaService;
 034        _currentUser = currentUser;
 035    }
 36
 37    public async Task<AguinaldoValidacionDto> ValidarHistoricoAsync(CalcularAguinaldoSemestralRequest request)
 38    {
 039        return await _validador.ValidarHistoricoSemestralAsync(request);
 040    }
 41
 42    public async Task<(bool Success, IEnumerable<AguinaldoSemestralDto> Resultados, string? Error)> CalcularAguinaldoSem
 43    {
 044        var validacion = await _validador.ValidarHistoricoSemestralAsync(request);
 045        var cedulasConHistoricoIncompleto = validacion.Incidencias
 046            .Where(i => i.Tipo == "HistoricoIncompleto")
 047            .Select(i => i.Cedula)
 048            .ToHashSet();
 49
 050        var parametros = await ObtenerParametrosPeriodoAsync(request);
 051        if (parametros is null)
 052            return (false, Array.Empty<AguinaldoSemestralDto>(), "No existe configuración de parámetros para el período 
 53
 054        var activos = (await _personalRepository.GetActivosAsync()).ToList();
 55
 56        // Cargar todo el histórico del semestre en una sola query para evitar N+1
 057        var mesesSemestre = ObtenerMesesSemestre(request);
 058        var mesesSet = mesesSemestre.ToHashSet();
 059        var personaIds = activos.Select(a => a.PersonaId).ToList();
 060        var aniosSemestre = mesesSemestre.Select(m => m.Anio).Distinct().ToList();
 061        var mesesIds = mesesSemestre.Select(m => m.Mes).Distinct().ToList();
 62
 063        var todosHistoricos = await _context.HistoricoLiquidaciones
 064            .Where(h => personaIds.Contains(h.PersonaId)
 065                && aniosSemestre.Contains(h.Anio)
 066                && mesesIds.Contains(h.Mes))
 067            .Select(h => new { h.PersonaId, h.Anio, h.Mes, h.HaberesGravados })
 068            .ToListAsync();
 69
 070        var historicosPorPersona = todosHistoricos
 071            .Where(h => mesesSet.Contains((h.Anio, h.Mes)))
 072            .GroupBy(h => h.PersonaId)
 073            .ToDictionary(g => g.Key, g => g.ToList());
 74
 075        var semestre = CalcularSemestre(request);
 76
 077        var aguinaldosExistentes = await _context.Set<Aguinaldo>()
 078            .Where(a => personaIds.Contains(a.PersonaId) && a.Anio == (short)request.Anio && a.Semestre == semestre)
 079            .ToDictionaryAsync(a => a.PersonaId);
 80
 081        var resultados = new List<AguinaldoSemestralDto>();
 82
 083        foreach (var relacion in activos)
 84        {
 085            if (cedulasConHistoricoIncompleto.Contains(relacion.Persona.Cedula))
 86                continue;
 87
 088            var historicoPersona = historicosPorPersona.GetValueOrDefault(relacion.PersonaId);
 089            var totalHaberesGravadosSemestre = historicoPersona?.Sum(x => x.HaberesGravados) ?? 0m;
 090            var mesesTrabajados = historicoPersona?.Count ?? 0;
 91
 092            var tasaMontepio = relacion.Regimen.EsLeyVieja
 093                ? parametros.TasaMontepioLeyVieja
 094                : parametros.TasaMontepioLeyNueva;
 95
 096            var resultado = _calculator.Calcular(
 097                relacion.PersonaId,
 098                relacion.Persona.Cedula,
 099                relacion.Persona.NombreCompleto,
 0100                (short)request.Anio,
 0101                semestre,
 0102                totalHaberesGravadosSemestre,
 0103                (short)mesesTrabajados,
 0104                tasaMontepio,
 0105                parametros.TasaFonasa);
 106
 0107            resultados.Add(resultado);
 108
 109            long aguinaldoId;
 0110            if (aguinaldosExistentes.TryGetValue(relacion.PersonaId, out var existente))
 111            {
 0112                existente.TotalHaberesGravadosSemestre = totalHaberesGravadosSemestre;
 0113                existente.MesesTrabajados = (short)mesesTrabajados;
 0114                existente.Nominal = resultado.Nominal;
 0115                existente.Montepio = resultado.Montepio;
 0116                existente.Sanidad = resultado.Sanidad;
 0117                existente.TotalDescuentos = resultado.TotalDescuentos;
 0118                existente.Liquido = resultado.Liquido;
 0119                existente.Redondeo = resultado.Redondeo;
 0120                await _context.SaveChangesAsync();
 0121                aguinaldoId = existente.Id;
 122            }
 123            else
 124            {
 0125                var aguinaldo = new Aguinaldo
 0126                {
 0127                    PersonaId = relacion.PersonaId,
 0128                    Anio = (short)request.Anio,
 0129                    Semestre = semestre,
 0130                    TotalHaberesGravadosSemestre = totalHaberesGravadosSemestre,
 0131                    MesesTrabajados = (short)mesesTrabajados,
 0132                    Nominal = resultado.Nominal,
 0133                    Montepio = resultado.Montepio,
 0134                    Sanidad = resultado.Sanidad,
 0135                    TotalDescuentos = resultado.TotalDescuentos,
 0136                    Liquido = resultado.Liquido,
 0137                    Redondeo = resultado.Redondeo
 0138                };
 0139                var creado = await _aguinaldosRepository.CreateAsync(aguinaldo);
 0140                aguinaldoId = creado.Id;
 141            }
 142
 143            try
 144            {
 0145                await _auditoriaService.LogAuditoriaAsync(
 0146                    _currentUser.UserId ?? 0,
 0147                    AccionEnum.GenerarLiquidacion,
 0148                    ContextoEnum.Liquidaciones,
 0149                    entidad: "aguinaldos",
 0150                    entidadId: aguinaldoId,
 0151                    detalle: new { Resultado = resultado });
 0152            }
 0153            catch
 154            {
 155                // No bloquear el proceso por fallos en auditoría
 0156            }
 0157        }
 158
 0159        return (true, resultados, null);
 0160    }
 161
 162    private static short CalcularSemestre(CalcularAguinaldoSemestralRequest request)
 0163        => request.Mes == 6 ? (short)1 : (short)2;
 164
 165    private async Task<ParametroPeriodo?> ObtenerParametrosPeriodoAsync(CalcularAguinaldoSemestralRequest request)
 166    {
 0167        return await _context.ParametrosPeriodo
 0168            .Where(p => p.Anio < request.Anio || (p.Anio == request.Anio && p.Mes <= request.Mes))
 0169            .OrderByDescending(p => p.Anio)
 0170            .ThenByDescending(p => p.Mes)
 0171            .FirstOrDefaultAsync();
 0172    }
 173
 174    private static IReadOnlyList<(short Anio, short Mes)> ObtenerMesesSemestre(CalcularAguinaldoSemestralRequest request
 175    {
 0176        if (request.Mes == 6)
 177        {
 0178            return new List<(short Anio, short Mes)>
 0179            {
 0180                ((short)(request.Anio - 1), (short)12),
 0181                ((short)request.Anio, (short)1),
 0182                ((short)request.Anio, (short)2),
 0183                ((short)request.Anio, (short)3),
 0184                ((short)request.Anio, (short)4),
 0185                ((short)request.Anio, (short)5)
 0186            };
 187        }
 188
 0189        if (request.Mes == 12)
 190        {
 0191            return new List<(short Anio, short Mes)>
 0192            {
 0193                ((short)request.Anio, (short)6),
 0194                ((short)request.Anio, (short)7),
 0195                ((short)request.Anio, (short)8),
 0196                ((short)request.Anio, (short)9),
 0197                ((short)request.Anio, (short)10),
 0198                ((short)request.Anio, (short)11)
 0199            };
 200        }
 201
 0202        throw new InvalidOperationException("El mes proporcionado no corresponde a un cierre de aguinaldo semestral.");
 203    }
 204}