< 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: 153
Coverable lines: 153
Total lines: 226
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 franjasIrpf = await _context.FranjasIrpf
 055            .OrderBy(f => f.NumeroFranja)
 056            .ToListAsync();
 57
 058        var franjasCalculo = franjasIrpf.Select(f => new FranjaIrpfCalculoDto
 059        {
 060            FranjaId = f.Id,
 061            NumeroFranja = f.NumeroFranja,
 062            DesdeBpc = f.DesdeBpc,
 063            HastaBpc = f.HastaBpc,
 064            Tasa = f.Tasa
 065        }).ToList();
 66
 067        var parametrosIrpf = new ParametrosIrpfDto
 068        {
 069            BpcMensual = parametros.BpcAnual / 12m,
 070            UmbralDeduccionBpc = parametros.UmbralDeduccionBpc,
 071            TasaDeduccionAlta = parametros.TasaDeduccionAlta,
 072            TasaDeduccionBaja = parametros.TasaDeduccionBaja
 073        };
 74
 075        var activos = (await _personalRepository.GetActivosAsync()).ToList();
 76
 77        // Cargar todo el histórico del semestre en una sola query para evitar N+1
 078        var mesesSemestre = ObtenerMesesSemestre(request);
 079        var mesesSet = mesesSemestre.ToHashSet();
 080        var personaIds = activos.Select(a => a.PersonaId).ToList();
 081        var aniosSemestre = mesesSemestre.Select(m => m.Anio).Distinct().ToList();
 082        var mesesIds = mesesSemestre.Select(m => m.Mes).Distinct().ToList();
 83
 084        var todosHistoricos = await _context.HistoricoLiquidaciones
 085            .Where(h => personaIds.Contains(h.PersonaId)
 086                && aniosSemestre.Contains(h.Anio)
 087                && mesesIds.Contains(h.Mes))
 088            .Select(h => new { h.PersonaId, h.Anio, h.Mes, h.HaberesGravados })
 089            .ToListAsync();
 90
 091        var historicosPorPersona = todosHistoricos
 092            .Where(h => mesesSet.Contains((h.Anio, h.Mes)))
 093            .GroupBy(h => h.PersonaId)
 094            .ToDictionary(g => g.Key, g => g.ToList());
 95
 096        var semestre = CalcularSemestre(request);
 97
 098        var aguinaldosExistentes = await _context.Set<Aguinaldo>()
 099            .Where(a => personaIds.Contains(a.PersonaId) && a.Anio == (short)request.Anio && a.Semestre == semestre)
 0100            .ToDictionaryAsync(a => a.PersonaId);
 101
 0102        var resultados = new List<AguinaldoSemestralDto>();
 103
 0104        foreach (var relacion in activos)
 105        {
 0106            if (cedulasConHistoricoIncompleto.Contains(relacion.Persona.Cedula))
 107                continue;
 108
 0109            var historicoPersona = historicosPorPersona.GetValueOrDefault(relacion.PersonaId);
 0110            var totalHaberesGravadosSemestre = historicoPersona?.Sum(x => x.HaberesGravados) ?? 0m;
 0111            var mesesTrabajados = historicoPersona?.Count ?? 0;
 112
 0113            var tasaMontepio = relacion.Regimen.EsLeyVieja
 0114                ? parametros.TasaMontepioLeyVieja
 0115                : parametros.TasaMontepioLeyNueva;
 116
 0117            var resultado = _calculator.Calcular(
 0118                relacion.PersonaId,
 0119                relacion.Persona.Cedula,
 0120                relacion.Persona.NombreCompleto,
 0121                (short)request.Anio,
 0122                semestre,
 0123                totalHaberesGravadosSemestre,
 0124                (short)mesesTrabajados,
 0125                franjasCalculo,
 0126                parametrosIrpf,
 0127                tasaMontepio,
 0128                parametros.TasaFonasa);
 129
 0130            resultados.Add(resultado);
 131
 132            long aguinaldoId;
 0133            if (aguinaldosExistentes.TryGetValue(relacion.PersonaId, out var existente))
 134            {
 0135                existente.TotalHaberesGravadosSemestre = totalHaberesGravadosSemestre;
 0136                existente.MesesTrabajados = (short)mesesTrabajados;
 0137                existente.IrpfAguinaldo = resultado.Irpf;
 0138                existente.Nominal = resultado.Nominal;
 0139                existente.Montepio = resultado.Montepio;
 0140                existente.Sanidad = resultado.Sanidad;
 0141                existente.TotalDescuentos = resultado.TotalDescuentos;
 0142                existente.Liquido = resultado.Liquido;
 0143                existente.Redondeo = resultado.Redondeo;
 0144                await _context.SaveChangesAsync();
 0145                aguinaldoId = existente.Id;
 146            }
 147            else
 148            {
 0149                var aguinaldo = new Aguinaldo
 0150                {
 0151                    PersonaId = relacion.PersonaId,
 0152                    Anio = (short)request.Anio,
 0153                    Semestre = semestre,
 0154                    TotalHaberesGravadosSemestre = totalHaberesGravadosSemestre,
 0155                    MesesTrabajados = (short)mesesTrabajados,
 0156                    IrpfAguinaldo = resultado.Irpf,
 0157                    Nominal = resultado.Nominal,
 0158                    Montepio = resultado.Montepio,
 0159                    Sanidad = resultado.Sanidad,
 0160                    TotalDescuentos = resultado.TotalDescuentos,
 0161                    Liquido = resultado.Liquido,
 0162                    Redondeo = resultado.Redondeo
 0163                };
 0164                var creado = await _aguinaldosRepository.CreateAsync(aguinaldo);
 0165                aguinaldoId = creado.Id;
 166            }
 167
 168            try
 169            {
 0170                await _auditoriaService.LogAuditoriaAsync(
 0171                    _currentUser.UserId ?? 0,
 0172                    AccionEnum.GenerarLiquidacion,
 0173                    ContextoEnum.Liquidaciones,
 0174                    entidad: "aguinaldos",
 0175                    entidadId: aguinaldoId,
 0176                    detalle: new { Resultado = resultado });
 0177            }
 0178            catch
 179            {
 180                // No bloquear el proceso por fallos en auditoría
 0181            }
 0182        }
 183
 0184        return (true, resultados, null);
 0185    }
 186
 187    private static short CalcularSemestre(CalcularAguinaldoSemestralRequest request)
 0188        => request.Mes == 6 ? (short)1 : (short)2;
 189
 190    private async Task<ParametroPeriodo?> ObtenerParametrosPeriodoAsync(CalcularAguinaldoSemestralRequest request)
 191    {
 0192        return await _context.ParametrosPeriodo
 0193            .FirstOrDefaultAsync(p => p.Anio == request.Anio && p.Mes == request.Mes);
 0194    }
 195
 196    private static IReadOnlyList<(short Anio, short Mes)> ObtenerMesesSemestre(CalcularAguinaldoSemestralRequest request
 197    {
 0198        if (request.Mes == 6)
 199        {
 0200            return new List<(short Anio, short Mes)>
 0201            {
 0202                ((short)(request.Anio - 1), (short)12),
 0203                ((short)request.Anio, (short)1),
 0204                ((short)request.Anio, (short)2),
 0205                ((short)request.Anio, (short)3),
 0206                ((short)request.Anio, (short)4),
 0207                ((short)request.Anio, (short)5)
 0208            };
 209        }
 210
 0211        if (request.Mes == 12)
 212        {
 0213            return new List<(short Anio, short Mes)>
 0214            {
 0215                ((short)request.Anio, (short)6),
 0216                ((short)request.Anio, (short)7),
 0217                ((short)request.Anio, (short)8),
 0218                ((short)request.Anio, (short)9),
 0219                ((short)request.Anio, (short)10),
 0220                ((short)request.Anio, (short)11)
 0221            };
 222        }
 223
 0224        throw new InvalidOperationException("El mes proporcionado no corresponde a un cierre de aguinaldo semestral.");
 225    }
 226}