< Summary

Information
Class: FAU.Logica.Services.InsumosPeriodoLoader
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/InsumosPeriodoLoader.cs
Line coverage
90%
Covered lines: 217
Uncovered lines: 24
Coverable lines: 241
Total lines: 325
Line coverage: 90%
Branch coverage
40%
Covered branches: 8
Total branches: 20
Branch coverage: 40%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
CargarAsync()60%101098.23%

File(s)

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

#LineLine coverage
 1using FAU.DataAccess;
 2using FAU.Entidades;
 3using FAU.Logica.DTOs;
 4using Microsoft.EntityFrameworkCore;
 5
 6namespace FAU.Logica.Services;
 7
 8public class InsumosPeriodoLoader : IInsumosPeriodoLoader
 9{
 10    private readonly ApplicationDbContext _context;
 11
 12    // Mapeo escalafón (código) → grupo de remuneración, hardcodeado (no en la base).
 13    // Confirmado con recibos reales (no solo el instructivo): el driver de Cuerpo Comando
 14    // vs SS.GG. Y Equiparados en 042.536/048.015 NO es el escalafón técnico de la persona
 15    // (AV/SG/NAV/AT/...) — personal activo en escalafón SG usa Cuerpo Comando igual que
 16    // cualquier otro (cédulas 53764283, 52106008, 43111769). El único caso real confirmado
 17    // con tarifa SS.GG. es un reservista superior (SIT05), cuyo escalafón queda tageado
 18    // "RVA" en este modelo (cédula 40211677). Todo lo que no sea RVA cae al valor general
 19    // (= Cuerpo Comando).
 120    private static readonly Dictionary<string, string> GrupoPorCodigoEscalafon = new(StringComparer.OrdinalIgnoreCase)
 121    {
 122        ["RVA"] = "SSGG",
 123    };
 24
 1625    public InsumosPeriodoLoader(ApplicationDbContext context)
 26    {
 1627        _context = context;
 1628    }
 29
 30    public async Task<InsumosPeriodo> CargarAsync(long periodoId)
 31    {
 1632        var periodo = await _context.PeriodosLiquidacion
 1633            .AsNoTracking()
 1634            .FirstOrDefaultAsync(p => p.Id == periodoId && p.Activo);
 35
 1636        if (periodo is null)
 37        {
 038            throw new InvalidOperationException($"Periodo de liquidación {periodoId} no encontrado o no está activo.");
 39        }
 40
 1641        var periodoDate = new DateTime(periodo.Anio, periodo.Mes, 1);
 1642        var ultimoDiaPeriodo = new DateTime(periodo.Anio, periodo.Mes, DateTime.DaysInMonth(periodo.Anio, periodo.Mes));
 43
 1644        var parametros = await _context.ParametrosPeriodo
 1645            .AsNoTracking()
 1646            .Where(pp => pp.Anio < periodo.Anio || (pp.Anio == periodo.Anio && pp.Mes <= periodo.Mes))
 1647            .OrderByDescending(pp => pp.Anio)
 1648            .ThenByDescending(pp => pp.Mes)
 1649            .FirstOrDefaultAsync();
 50
 1651        if (parametros is null)
 52        {
 053            throw new InvalidOperationException($"No hay parámetros de período configurados para {periodo.Anio}/{periodo
 54        }
 55
 1656        var catalogoItems = await _context.CatalogoItems
 1657            .AsNoTracking()
 1658            .ToListAsync();
 59
 1660        var remuneraciones = await _context.RemuneracionesGrado
 1661            .AsNoTracking()
 1662            .Include(r => r.Item)
 1663            .Where(r => r.VigenteDesde <= periodoDate && (r.VigenteHasta == null || r.VigenteHasta >= periodoDate))
 1664            .ToListAsync();
 65
 1666        var escalafones = await _context.Escalafones
 1667            .AsNoTracking()
 1668            .ToListAsync();
 69
 1670        var tarifasCategoria = await _context.TarifasPorCategoria
 1671            .AsNoTracking()
 1672            .Include(t => t.Item)
 1673            .Where(t => t.VigenteDesde <= periodoDate && (t.VigenteHasta == null || t.VigenteHasta >= periodoDate))
 1674            .ToListAsync();
 75
 1676        var compPersonal048018 = await _context.CompPersonal048018
 1677            .AsNoTracking()
 1678            .Where(c => c.Estado == "activo"
 1679                     && c.VigenteDesde <= periodoDate && (c.VigenteHasta == null || c.VigenteHasta >= periodoDate))
 1680            .ToListAsync();
 81
 1682        var permanencias = await _context.Set<TablaPermanencia>()
 1683            .AsNoTracking()
 1684            .Where(t => t.Activo)
 1685            .Where(t => t.VigenteDesde <= periodoDate && (t.VigenteHasta == null || t.VigenteHasta >= periodoDate))
 1686            .ToListAsync();
 87
 1688        var franjasIrpfRaw = await _context.FranjasIrpf
 1689            .AsNoTracking()
 1690            .Where(f => f.VigenteDesde <= periodoDate && (f.VigenteHasta == null || f.VigenteHasta >= periodoDate))
 1691            .ToListAsync();
 92
 93        // Deduplicar: una franja por NumeroFranja, la más reciente según VigenteDesde
 1694        var franjasIrpf = franjasIrpfRaw
 3295            .GroupBy(f => f.NumeroFranja)
 6496            .Select(g => g.OrderByDescending(f => f.VigenteDesde).First())
 3297            .OrderBy(f => f.NumeroFranja)
 1698            .ToList();
 99
 100        // Tramos de Sanidad Militar vigentes (Art. 94 Ley 18.834)
 16101        var tramosSanidadRaw = await _context.TramosSanidadMilitar
 16102            .AsNoTracking()
 16103            .Where(t => t.VigenteDesde <= periodoDate && (t.VigenteHasta == null || t.VigenteHasta >= periodoDate))
 16104            .ToListAsync();
 105
 106        // Deduplicar por código de retención, el más reciente según VigenteDesde; ordenar por Desde
 16107        var tramosSanidad = tramosSanidadRaw
 0108            .GroupBy(t => t.CodigoRetencion)
 0109            .Select(g => g.OrderByDescending(t => t.VigenteDesde).First())
 0110            .OrderBy(t => t.Desde)
 16111            .ToList();
 112
 16113        var beneficios = await _context.BeneficiosSociales
 16114            .AsNoTracking()
 16115            .Where(b => b.Estado == "activo"
 16116                     && b.VigenteDesde <= ultimoDiaPeriodo          // inició antes del fin del período
 16117                     && (b.VigenteHasta == null || b.VigenteHasta >= periodoDate))  // no terminó antes del inicio
 16118            .ToListAsync();
 119
 16120        var tiposBeneficio = await _context.TiposBeneficio
 16121            .AsNoTracking()
 16122            .ToDictionaryAsync(t => t.Id);
 123
 34124        foreach (var b in beneficios)
 125        {
 1126            if (tiposBeneficio.TryGetValue(b.TipoBeneficioId, out var tipo))
 0127                b.TipoBeneficio = tipo;
 128        }
 129
 130        // Cargar hijos registrados para beneficios de tipo Asignación Familiar
 16131        var idsAsigFamiliar = beneficios
 1132            .Where(b => b.TipoBeneficio is not null &&
 1133                        (b.TipoBeneficio.Codigo == "ASIGNACION_FAMILIAR" ||
 1134                         b.TipoBeneficio.Codigo.StartsWith("300") ||
 1135                         b.TipoBeneficio.Nombre.ToUpperInvariant().Contains("ASIGNACION FAMILIAR") ||
 1136                         b.TipoBeneficio.Nombre.ToUpperInvariant().Contains("ASIGNACIÓN FAMILIAR")))
 0137            .Select(b => b.Id)
 16138            .ToHashSet();
 139
 16140        var beneficiariosAsigFam = idsAsigFamiliar.Count > 0
 16141            ? await _context.BeneficioSocialBeneficiarios
 16142                .AsNoTracking()
 16143                .Where(bh => idsAsigFamiliar.Contains(bh.BeneficioId) &&
 16144                             bh.Activo &&
 16145                             bh.VigenteDesde <= ultimoDiaPeriodo &&
 16146                             (bh.VigenteHasta == null || bh.VigenteHasta >= periodoDate))
 16147                .ToListAsync()
 16148            : [];
 149
 16150        var beneficiariosPorBeneficio = beneficiariosAsigFam
 0151            .GroupBy(bh => bh.BeneficioId)
 16152            .ToDictionary(g => g.Key, g => (IReadOnlyList<BeneficioSocialBeneficiario>)g.ToList());
 153
 16154        var compensaciones = await _context.ItemsLoteCompensacion
 16155            .AsNoTracking()
 16156            .Include(i => i.LoteCompensacion)
 16157                .ThenInclude(l => l.TipoCompensacion)
 16158                .ThenInclude(t => t.Configuraciones)
 16159                    .ThenInclude(c => c.CatalogoItem)
 16160            .Where(i => i.LoteCompensacion.Estado == "aprobado"
 16161                        && i.LoteCompensacion.PeriodoId == periodoId)
 16162            .ToListAsync();
 163
 16164        var novedadesConfirmadas = await _context.NovedadesPeriodo
 16165            .AsNoTracking()
 16166            .Where(n => n.PeriodoId == periodoId && n.Estado == "confirmada")
 16167            .ToListAsync();
 168
 16169        var remuneracionesPorGrado = remuneraciones
 37170            .Where(r => r.GrupoRemuneracion is null)
 37171            .GroupBy(r => r.GradoId)
 16172            .ToDictionary(
 17173                g => g.Key,
 33174                g => (IReadOnlyDictionary<int, RemuneracionGrado>)g
 37175                    .GroupBy(r => r.Item.Codigo)
 33176                    .ToDictionary(
 37177                        gg => gg.Key,
 107178                        gg => gg.OrderByDescending(r => r.VigenteDesde).First()));
 179
 16180        var remuneracionesPorGradoYGrupo = remuneraciones
 37181            .Where(r => r.GrupoRemuneracion is not null)
 0182            .GroupBy(r => (r.GradoId, r.Item.Codigo, Grupo: r.GrupoRemuneracion!))
 16183            .ToDictionary(
 0184                g => g.Key,
 16185                g => g.OrderByDescending(r => r.VigenteDesde).First());
 186
 16187        var grupoPorEscalafon = escalafones
 16188            .ToDictionary(
 16189                e => e.Id,
 32190                e => GrupoPorCodigoEscalafon.TryGetValue(e.Codigo, out var grupo) ? grupo : null);
 191
 16192        var tarifasPorCategoria = tarifasCategoria
 0193            .GroupBy(t => t.Categoria)
 16194            .ToDictionary(
 0195                g => g.Key,
 16196                g => (IReadOnlyDictionary<int, TarifaPorCategoria>)g
 0197                    .GroupBy(t => t.Item.Codigo)
 16198                    .ToDictionary(
 0199                        gg => gg.Key,
 16200                        gg => gg.OrderByDescending(t => t.VigenteDesde).First()));
 201
 16202        var compPersonal048018PorPersona = compPersonal048018
 1203            .GroupBy(c => c.PersonaId)
 19204            .ToDictionary(g => g.Key, g => g.OrderByDescending(c => c.VigenteDesde).First());
 205
 16206        var permanenciasPorEscalafon = permanencias
 1207            .GroupBy(t => t.EscalafonId)
 19208            .ToDictionary(g => g.Key, g => (IReadOnlyList<TablaPermanencia>)g.OrderBy(t => t.MesesHasta).ToList());
 209
 16210        var beneficiosPorPersona = beneficios
 1211            .GroupBy(b => b.PersonaId)
 18212            .ToDictionary(g => g.Key, g => (IReadOnlyList<BeneficioSocial>)g.ToList());
 213
 16214        var compensacionesPorPersona = compensaciones
 3215            .GroupBy(i => i.PersonaId)
 22216            .ToDictionary(g => g.Key, g => (IReadOnlyList<ItemLoteCompensacion>)g.ToList());
 217
 16218        var novedadesPorPersona = novedadesConfirmadas
 4219            .GroupBy(n => n.PersonaId)
 24220            .ToDictionary(g => g.Key, g => (IReadOnlyList<NovedadPeriodo>)g.ToList());
 221
 222        // Instructivo Asignación Familiar: fila más reciente por tipo_franja
 16223        var instructivoAsigFamiliarRaw = await _context.InstructivosAsigFamiliar
 16224            .AsNoTracking()
 16225            .Where(i => i.VigenteDesde <= periodoDate)
 16226            .ToListAsync();
 227
 16228        var instructivoAsigFamiliar = instructivoAsigFamiliarRaw
 0229            .GroupBy(i => i.TipoFranja)
 16230            .ToDictionary(
 0231                g => g.Key,
 16232                g => g.OrderByDescending(i => i.VigenteDesde).First());
 233
 234        // Instructivo PSF: fila más reciente por numero_categoria
 16235        var instructivoPsfRaw = await _context.InstructivosPsf
 16236            .AsNoTracking()
 16237            .Where(i => i.VigenteDesde <= periodoDate)
 16238            .ToListAsync();
 239
 16240        var instructivoPsf = instructivoPsfRaw
 0241            .GroupBy(i => (int)i.NumeroCategoria)
 16242            .ToDictionary(
 0243                g => g.Key,
 16244                g => g.OrderByDescending(i => i.VigenteDesde).First());
 245
 246        // Tabla Hogar: tramos vigentes, ordenados ASC con NULL al final
 16247        var tablaPorHogarRaw = await _context.TablasHogarConstituido
 16248            .AsNoTracking()
 16249            .Where(t => t.VigenteDesde <= periodoDate)
 16250            .ToListAsync();
 251
 16252        var tablaPorHogar = tablaPorHogarRaw
 0253            .GroupBy(t => t.HastaHaberes)
 0254            .Select(g => g.OrderByDescending(t => t.VigenteDesde).First())
 0255            .OrderBy(t => t.HastaHaberes.HasValue ? 0 : 1)
 0256            .ThenBy(t => t.HastaHaberes ?? decimal.MaxValue)
 16257            .ToList();
 258
 16259        var aguinaldos = await _context.Set<Aguinaldo>()
 16260            .AsNoTracking()
 16261            .Where(a => a.Anio == periodo.Anio)
 16262            .ToListAsync();
 263
 16264        var aguinaldosPorPersona = aguinaldos
 0265            .GroupBy(a => a.PersonaId)
 16266            .ToDictionary(g => g.Key, g => (IReadOnlyList<Aguinaldo>)g.ToList());
 267
 16268        var ordenPorGrado = await _context.Grados
 16269            .AsNoTracking()
 16270            .Where(g => g.Vigente)
 48271            .ToDictionaryAsync(g => g.Id, g => g.Orden);
 272
 16273        var diferenciaAscenso = await _context.CompensacionesDiferenciaAscenso
 16274            .AsNoTracking()
 16275            .Where(c => c.PeriodoId == periodoId && c.Estado == "pendiente")
 17276            .ToDictionaryAsync(c => c.PersonaId);
 277
 16278        var descuentosPersonales = await _context.DescuentosPersonalesPeriodo
 16279            .AsNoTracking()
 16280            .Where(d => d.PeriodoId == periodoId && d.Estado == "aprobado")
 16281            .ToListAsync();
 282
 16283        var descuentosPersonalesPorPersona = descuentosPersonales
 0284            .GroupBy(d => d.PersonaId)
 16285            .ToDictionary(g => g.Key, g => (IReadOnlyList<DescuentoPersonalPeriodo>)g.ToList());
 286
 16287        var fictos = await _context.FictosPersona
 16288            .AsNoTracking()
 16289            .Include(f => f.CatalogoItem)
 16290            .Include(f => f.Acreedor)
 16291            .Where(f => f.Activo)
 16292            .ToListAsync();
 293
 16294        var fictosPorPersona = fictos
 1295            .GroupBy(f => f.PersonaId)
 18296            .ToDictionary(g => g.Key, g => (IReadOnlyList<FictoPersona>)g.ToList());
 297
 16298        return new InsumosPeriodo
 16299        {
 16300            PeriodoId = periodoId,
 16301            ParametroPeriodo = parametros,
 16302            CatalogoItems = catalogoItems,
 16303            RemuneracionesPorGrado = remuneracionesPorGrado,
 16304            RemuneracionesPorGradoYGrupo = remuneracionesPorGradoYGrupo,
 16305            GrupoPorEscalafon = grupoPorEscalafon,
 16306            TarifasPorCategoria = tarifasPorCategoria,
 16307            CompPersonal048018PorPersona = compPersonal048018PorPersona,
 16308            PermanenciasPorEscalafon = permanenciasPorEscalafon,
 16309            FranjasIrpf = franjasIrpf,
 16310            TramosSanidad = tramosSanidad,
 16311            BeneficiosPorPersona = beneficiosPorPersona,
 16312            CompensacionesPorPersona = compensacionesPorPersona,
 16313            NovedadesPorPersona = novedadesPorPersona,
 16314            BeneficiariosPorBeneficio = beneficiariosPorBeneficio,
 16315            InstructivoAsigFamiliar = instructivoAsigFamiliar,
 16316            InstructivoPsf = instructivoPsf,
 16317            TablaPorHogar = tablaPorHogar,
 16318            AguinaldosPorPersona = aguinaldosPorPersona,
 16319            OrdenPorGrado = ordenPorGrado,
 16320            CompensacionesDiferenciaAscensoPorPersona = diferenciaAscenso,
 16321            DescuentosPersonalesPorPersona = descuentosPersonalesPorPersona,
 16322            FictosPorPersona = fictosPorPersona
 16323        };
 16324    }
 325}