< Summary

Information
Class: FAU.Logica.Calculadores.CalculadorHaberesBase
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Calculadores/CalculadorHaberesBase.cs
Line coverage
96%
Covered lines: 120
Uncovered lines: 5
Coverable lines: 125
Total lines: 335
Line coverage: 96%
Branch coverage
90%
Covered branches: 49
Total branches: 54
Branch coverage: 90.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_CodigosExcluidosDecreto()100%11100%
get_CodigosExcluidosSDL()100%11100%
get_PorcentajeDedicacionIntegral()100%11100%
CalcularAsync(...)100%1010100%
AgregarItemsDecreto(...)100%11100%
AgregarItemsCondicionales(...)83.33%272482.14%
CalcularDedicacionIntegral(...)50%22100%
AgregarSiExiste(...)100%22100%
AgregarItem(...)100%66100%
CalcularAniosServicio(...)100%11100%
AgregarDecretoSdl(...)100%44100%
AgregarDecretoNoSdl(...)100%22100%
CalcularModAportes19695(...)100%22100%
Suma()100%11100%
CalcularReintegroAportes(...)100%22100%

File(s)

/home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Calculadores/CalculadorHaberesBase.cs

#LineLine coverage
 1using FAU.Entidades;
 2using FAU.Logica.DTOs;
 3
 4namespace FAU.Logica.Calculadores;
 5
 6/// <summary>
 7/// Clase base de todas las calculadoras de haberes.
 8///
 9/// Flujo de cálculo (basado en SUM33100.PRG FoxPro):
 10///   1. CalcularItemsBase     → ítems propios de cada situación, retorna wpara53 (SIN SDL)
 11///   2. AgregarItemsDecreto   → ítems fijos por grado (decretos + recuperaciones + etc.),
 12///                              actualiza wpara53 si el ítem entra en la base SDL
 13///   3. CalcularItemsCondicionales → Riesgo de Vuelo, Progresivo, etc.
 14///   4. CalcularSDL           → 048.012 = wpara53 × 5,3%  (DESPUÉS de todos los ítems base)
 15///   5. CalcularDI            → 043.004 = wPara10 × porcentaje  (virtual, override por situación)
 16///   6. CalcularModAportes19695 → 042.413 (solo Ley Nueva)
 17///   7. CalcularReintegroAportes → 042.414 (solo Ley Nueva)
 18/// </summary>
 19public abstract class CalculadorHaberesBase : ICalculadorHaberes
 20{
 21    protected static class Codigos
 22    {
 23        internal const int SueldoBase          = 11000;
 24        internal const int SueldoReservista    = 11001;
 25        internal const int SueldoReservaJornal = 20;
 26        internal const int GastosRepresentacion = 15000;
 27        internal const int Permanencia         = 41003;
 28        internal const int CompCargo           = 42012;
 29        internal const int PermanenciaOrden    = 42014;
 30        internal const int RiesgoVuelo         = 42019;
 31        internal const int CompSuboficiales    = 42022;
 32        internal const int PrimaTecnicaPerm    = 42025;
 33        internal const int EquipoOficiales     = 42067;
 34        internal const int PrimaTecnicaPTI     = 42092;
 35        internal const int PrimaIdoneidad      = 42093;
 36        internal const int CompCombatientes    = 42103;
 37        internal const int CompLey18834        = 42536;
 38        internal const int Ley20075Art80       = 42403;
 39        internal const int ModAportes          = 42413;
 40        internal const int ReintegroAportes    = 42414;
 41        internal const int CompDI              = 43002;
 42        internal const int DedicacionIntegral  = 43004;
 43        internal const int Progresivo          = 44001;
 44        internal const int AumEsp85Mil         = 48004;
 45        internal const int AumEsp92            = 48009;
 46        internal const int Sdl                 = 48012;
 47        internal const int Ley17269            = 48015;
 48        internal const int AumDec191_003       = 48017;
 49        internal const int RecSalarial2007     = 48023;
 50        internal const int RecSalarial466      = 48026;
 51        internal const int PartEspLey18719     = 48038;
 52        internal const int Ley19355            = 48040;
 53        internal const int IncrSubEscK         = 48042;
 54        internal const int CompPAN             = 42571;
 55        internal const int CompSAR             = 42572;
 56        internal const int PermanenciaOrdenSup = 60;
 57        internal const int CompDiOficial       = 299;
 58        internal const int CompDiSubalterno    = 315;
 59        internal const int DifEquipoSinAportes = 69005;
 60        internal const int DifEquipoConAportes = 69006;
 61        internal const int ViaticoAlimentacion = 69008;
 62        internal const int ViaticossinAportes  = 69009;
 63        internal const int ViaticosConAportes  = 69010;
 64    }
 65
 166    private static readonly IReadOnlySet<int> EmptySet = new HashSet<int>();
 67
 68    /// <summary>Ítems excluidos completamente de AgregarItemsDecreto. Override por subclase.</summary>
 100869    protected virtual IReadOnlySet<int> CodigosExcluidosDecreto => EmptySet;
 70
 71    /// <summary>Ítems emitidos pero que NO contribuyen a la base SDL. Override por subclase.</summary>
 28872    protected virtual IReadOnlySet<int> CodigosExcluidosSDL => EmptySet;
 73
 74    /// <summary>Porcentaje de Dedicación Integral. Override por subclase.</summary>
 375    protected virtual decimal PorcentajeDedicacionIntegral => 0.10m;
 76
 77    public Task<ResultadoCalculo> CalcularAsync(
 78        RelacionLaboral funcionario, InsumosPeriodo insumos, Acumuladores acumuladores)
 79    {
 5580        var resultado = new ResultadoCalculo();
 81        try
 82        {
 5583            var wpara53 = CalcularItemsBase(funcionario, insumos, acumuladores, resultado);
 5384            wpara53 += AgregarItemsDecreto(funcionario, insumos, acumuladores, resultado);
 5385            wpara53 += AgregarItemsCondicionales(funcionario, insumos, acumuladores, resultado);
 86
 5387            if (wpara53 > 0)
 88            {
 5089                var sdl = Math.Round(wpara53 * 0.053m, 2);
 5090                AgregarItem(Codigos.Sdl, sdl, insumos, acumuladores, resultado);
 91
 5092                var wPara10 = wpara53 + sdl;
 5093                CalcularDedicacionIntegral(wPara10, funcionario, insumos, acumuladores, resultado);
 94            }
 95
 5396            if (!funcionario.Regimen.EsLeyVieja)
 97            {
 2098                CalcularModAportes19695(acumuladores, insumos, resultado);
 2099                CalcularReintegroAportes(acumuladores, insumos, resultado);
 100            }
 101
 53102            if (!funcionario.Situacion.AfectaCobro)
 10103                foreach (var item in resultado.Items)
 4104                    item.EsReferencia = true;
 105
 53106            resultado.Estado = resultado.Incidencias.Count > 0
 53107                ? EstadoCalculo.Revisar
 53108                : EstadoCalculo.Calculado;
 53109        }
 2110        catch (Exception ex)
 111        {
 2112            resultado.Estado = EstadoCalculo.Error;
 2113            resultado.MensajeError = ex.Message;
 2114        }
 115
 55116        return Task.FromResult(resultado);
 117    }
 118
 119    // ── Métodos abstractos/virtuales que cada calculadora puede sobreescribir ──────
 120
 121    /// <summary>
 122    /// Calcula los ítems propios de la situación.
 123    /// NO incluye SDL (048.012) ni DI (043.004) — los maneja la clase base.
 124    /// Retorna wpara53: suma de los ítems que entran en la base del SDL.
 125    /// </summary>
 126    protected abstract decimal CalcularItemsBase(
 127        RelacionLaboral funcionario, InsumosPeriodo insumos,
 128        Acumuladores acumuladores, ResultadoCalculo resultado);
 129
 130    /// <summary>
 131    /// Agrega decretos y recuperaciones fijas por grado (lookup en remuneraciones_grado).
 132    /// Subclases pueden excluir ítems via CodigosExcluidosDecreto / CodigosExcluidosSDL.
 133    /// Retorna el extra a sumar a wpara53 (solo ítems que entran en SDL).
 134    /// </summary>
 135    protected virtual decimal AgregarItemsDecreto(
 136        RelacionLaboral funcionario, InsumosPeriodo insumos,
 137        Acumuladores acumuladores, ResultadoCalculo resultado)
 138    {
 50139        decimal extraWPara53 = 0m;
 140
 141        // Entran en base SDL (confirmado por fórmula PDF 048.012)
 50142        extraWPara53 += AgregarDecretoSdl(Codigos.AumEsp85Mil, funcionario, insumos, acumuladores, resultado);
 50143        extraWPara53 += AgregarDecretoSdl(Codigos.RecSalarial2007, funcionario, insumos, acumuladores, resultado);
 50144        extraWPara53 += AgregarDecretoSdl(Codigos.RecSalarial466, funcionario, insumos, acumuladores, resultado);
 50145        extraWPara53 += AgregarDecretoSdl(Codigos.CompCombatientes, funcionario, insumos, acumuladores, resultado);
 50146        extraWPara53 += AgregarDecretoSdl(Codigos.PartEspLey18719, funcionario, insumos, acumuladores, resultado);
 50147        extraWPara53 += AgregarDecretoSdl(Codigos.EquipoOficiales, funcionario, insumos, acumuladores, resultado);
 148
 149        // NO entran en base SDL
 50150        AgregarDecretoNoSdl(Codigos.AumEsp92, funcionario, insumos, acumuladores, resultado);
 50151        AgregarDecretoNoSdl(Codigos.Ley17269, funcionario, insumos, acumuladores, resultado);
 50152        AgregarDecretoNoSdl(Codigos.AumDec191_003, funcionario, insumos, acumuladores, resultado);
 50153        AgregarDecretoNoSdl(Codigos.Ley19355, funcionario, insumos, acumuladores, resultado);
 50154        AgregarDecretoNoSdl(Codigos.IncrSubEscK, funcionario, insumos, acumuladores, resultado);
 50155        AgregarDecretoNoSdl(Codigos.CompLey18834, funcionario, insumos, acumuladores, resultado);
 50156        AgregarDecretoNoSdl(Codigos.Ley20075Art80, funcionario, insumos, acumuladores, resultado);
 50157        AgregarDecretoNoSdl(Codigos.DifEquipoSinAportes, funcionario, insumos, acumuladores, resultado);
 50158        AgregarDecretoNoSdl(Codigos.DifEquipoConAportes, funcionario, insumos, acumuladores, resultado);
 50159        AgregarDecretoNoSdl(Codigos.ViaticossinAportes, funcionario, insumos, acumuladores, resultado);
 50160        AgregarDecretoNoSdl(Codigos.ViaticosConAportes, funcionario, insumos, acumuladores, resultado);
 50161        AgregarDecretoNoSdl(Codigos.CompPAN, funcionario, insumos, acumuladores, resultado);
 50162        AgregarDecretoNoSdl(Codigos.CompSAR, funcionario, insumos, acumuladores, resultado);
 50163        AgregarDecretoNoSdl(Codigos.CompDiOficial, funcionario, insumos, acumuladores, resultado);
 50164        AgregarDecretoNoSdl(Codigos.CompDiSubalterno, funcionario, insumos, acumuladores, resultado);
 165
 50166        return extraWPara53;
 167    }
 168
 169    /// <summary>
 170    /// Ítems condicionales: Riesgo de Vuelo, Progresivo, Prima Técnica, etc.
 171    /// Override por subclase si la situación los tiene.
 172    /// Retorna el extra a sumar a wpara53 (solo ítems en base SDL).
 173    /// </summary>
 174    protected virtual decimal AgregarItemsCondicionales(
 175        RelacionLaboral funcionario, InsumosPeriodo insumos,
 176        Acumuladores acumuladores, ResultadoCalculo resultado)
 177    {
 53178        decimal extraWPara53 = 0m;
 179
 180        // 042.092 — Prima Técnica PTI/PT (categorías A/B/C designadas por Orden del Comando)
 181        // El monto viene de remuneraciones_grado por grado. Sujeto a Montepío.
 53182        if (!string.IsNullOrEmpty(funcionario.PrimaTecnica) && funcionario.PrimaTecnica != "VACIO")
 183        {
 3184            var remPT = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.PrimaTecnicaPTI);
 3185            if (remPT is not null)
 2186                AgregarItem(Codigos.PrimaTecnicaPTI, remPT.Monto, insumos, acumuladores, resultado);
 187        }
 188
 189        // 042.093 — Prima Idoneidad (sin aporte), si el funcionario tiene superprima.
 53190        if (funcionario.Superprima)
 191        {
 1192            var remIdoneidad = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.PrimaIdoneidad);
 1193            if (remIdoneidad is not null)
 1194                AgregarItem(Codigos.PrimaIdoneidad, remIdoneidad.Monto, insumos, acumuladores, resultado);
 195        }
 196
 197        // 042.025 — Prima Técnica Permanente (data-driven: si el grado tiene remuneración, se emite)
 53198        var remPrimaTec = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.PrimaTecnicaPerm);
 53199        if (remPrimaTec is not null)
 0200            AgregarItem(Codigos.PrimaTecnicaPerm, remPrimaTec.Monto, insumos, acumuladores, resultado);
 201
 202        // 042.019 — Riesgo de Vuelo (si relacion_laboral.riesgo_vuelo > 0); PDF: SÍ entra en SDL y DI
 53203        if (funcionario.RiesgoVuelo.HasValue && funcionario.RiesgoVuelo.Value > 0)
 204        {
 0205            var remVuelo = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.RiesgoVuelo);
 0206            if (remVuelo is not null)
 207            {
 0208                AgregarItem(Codigos.RiesgoVuelo, remVuelo.Monto, insumos, acumuladores, resultado);
 0209                extraWPara53 += remVuelo.Monto;
 210            }
 211        }
 212
 213        // 044.001 — Progresivo / Prima por Antigüedad
 214        // FechaBase: oficiales desde FechaAscensoOficial; tropa desde FechaInicio.
 215        // anios_inactivos: períodos de baja ya registrados en relacion_laboral.
 53216        var fechaPeriodo = new DateTime(insumos.ParametroPeriodo.Anio, insumos.ParametroPeriodo.Mes, 1);
 53217        var fechaBaseProgresivo = funcionario.FechaAscensoOficial ?? funcionario.FechaInicio;
 53218        var aniosBrutos = Math.Max(0, (int)((fechaPeriodo - fechaBaseProgresivo).TotalDays / 365.25));
 53219        var anios = Math.Max(0, aniosBrutos - (funcionario.AniosInactivos ?? 0));
 53220        if (anios > 0)
 221        {
 53222            var remProg = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.Progresivo);
 53223            if (remProg is not null)
 224            {
 3225                var montoProg = remProg.Monto * Math.Min(anios, 30);
 3226                AgregarItem(Codigos.Progresivo, montoProg, insumos, acumuladores, resultado);
 3227                extraWPara53 += montoProg;
 228            }
 229        }
 230
 53231        return extraWPara53;
 232    }
 233
 234    /// <summary>
 235    /// Calcula Dedicación Integral (043.004).
 236    /// Override en subclases que usan porcentaje distinto o ítem diferente (p.ej. PAO).
 237    /// </summary>
 238    protected virtual void CalcularDedicacionIntegral(
 239        decimal wPara10, RelacionLaboral funcionario,
 240        InsumosPeriodo insumos, Acumuladores acumuladores, ResultadoCalculo resultado)
 241    {
 39242        if (wPara10 <= 0) return;
 39243        var di = Math.Round(wPara10 * PorcentajeDedicacionIntegral, 2);
 39244        AgregarItem(Codigos.DedicacionIntegral, di, insumos, acumuladores, resultado);
 39245    }
 246
 247    // ── Helpers internos ──────────────────────────────────────────────────────────
 248
 249    /// <summary>Agrega el ítem si existe en remuneraciones. Retorna el monto agregado (o 0).</summary>
 250    protected static decimal AgregarSiExiste(
 251        RelacionLaboral funcionario, InsumosPeriodo insumos,
 252        Acumuladores acumuladores, ResultadoCalculo resultado, int codigo)
 253    {
 1044254        var rem = insumos.ObtenerRemuneracion(funcionario.GradoId, codigo);
 2073255        if (rem is null) return 0m;
 15256        AgregarItem(codigo, rem.Monto, insumos, acumuladores, resultado);
 15257        return rem.Monto;
 258    }
 259
 260    protected static void AgregarItem(
 261        int codigo, decimal monto, InsumosPeriodo insumos,
 262        Acumuladores acumuladores, ResultadoCalculo resultado)
 263    {
 2239264        var catalogoItem = insumos.CatalogoItems.FirstOrDefault(c => c.Codigo == codigo);
 253265        resultado.Items.Add(new ItemCalculado
 253266        {
 253267            CodigoConcepto = codigo,
 253268            NombreConcepto = catalogoItem?.Nombre ?? $"Concepto {codigo}",
 253269            Importe = monto
 253270        });
 253271        if (catalogoItem is not null)
 248272            acumuladores.AgregarItem(monto, catalogoItem);
 253273    }
 274
 275    protected static int CalcularAniosServicio(RelacionLaboral funcionario, InsumosPeriodo insumos)
 276    {
 48277        var fechaPeriodo = new DateTime(insumos.ParametroPeriodo.Anio, insumos.ParametroPeriodo.Mes, 1);
 48278        return Math.Max(0, (int)((fechaPeriodo - funcionario.FechaInicio).TotalDays / 365.25));
 279    }
 280
 281    // Emite el ítem y acumula a la base SDL, respetando CodigosExcluidosDecreto/CodigosExcluidosSDL.
 282    private decimal AgregarDecretoSdl(
 283        int codigo, RelacionLaboral funcionario, InsumosPeriodo insumos,
 284        Acumuladores acumuladores, ResultadoCalculo resultado)
 285    {
 302286        if (CodigosExcluidosDecreto.Contains(codigo)) return 0m;
 298287        var monto = AgregarSiExiste(funcionario, insumos, acumuladores, resultado, codigo);
 298288        return CodigosExcluidosSDL.Contains(codigo) ? 0m : monto;
 289    }
 290
 291    // Emite el ítem sin acumularlo a la base SDL, respetando CodigosExcluidosDecreto.
 292    private void AgregarDecretoNoSdl(
 293        int codigo, RelacionLaboral funcionario, InsumosPeriodo insumos,
 294        Acumuladores acumuladores, ResultadoCalculo resultado)
 295    {
 750296        if (!CodigosExcluidosDecreto.Contains(codigo))
 746297            AgregarSiExiste(funcionario, insumos, acumuladores, resultado, codigo);
 750298    }
 299
 300    private static void CalcularModAportes19695(
 301        Acumuladores acumuladores, InsumosPeriodo insumos, ResultadoCalculo resultado)
 302    {
 112303        var montoIncrSubEscK = resultado.Items.Where(i => i.CodigoConcepto == Codigos.IncrSubEscK).Sum(i => i.Importe);
 111304        var montoArt80 = resultado.Items.Where(i => i.CodigoConcepto == Codigos.Ley20075Art80).Sum(i => i.Importe);
 20305        var baseCalculo = acumuladores.WNominal - montoIncrSubEscK - montoArt80;
 21306        if (baseCalculo <= 0) return;
 19307        var monto = Math.Round(baseCalculo * 0.0235m, 2);
 19308        AgregarItem(Codigos.ModAportes, monto, insumos, acumuladores, resultado);
 19309    }
 310
 311    /// <summary>
 312    /// 042.414 — Reintegro de Aportes por Cambio de Régimen de Aportación.
 313    /// Fórmula PDF: Σ(069.006 + 069.008 + 069.010 + 042.092×2 + 043.002 + 042.571 + 042.572) / 0.87 × 13%
 314    /// 042.092 cuenta doble porque el régimen nuevo aplica aporte doble sobre esa compensación.
 315    /// Solo aplica en régimen Ley Nueva (llamado desde CalcularAsync junto con 042.413).
 316    /// </summary>
 317    private static void CalcularReintegroAportes(
 318        Acumuladores acumuladores, InsumosPeriodo insumos, ResultadoCalculo resultado)
 319    {
 906320        decimal Suma(int codigo) => resultado.Items.Where(i => i.CodigoConcepto == codigo).Sum(i => i.Importe);
 321
 20322        var base042414 =
 20323            Suma(Codigos.DifEquipoConAportes)  +
 20324            Suma(Codigos.ViaticoAlimentacion)  +
 20325            Suma(Codigos.ViaticosConAportes)   +
 20326            Suma(Codigos.PrimaTecnicaPTI) * 2m +
 20327            Suma(Codigos.CompDI)               +
 20328            Suma(Codigos.CompPAN)              +
 20329            Suma(Codigos.CompSAR);
 330
 38331        if (base042414 <= 0m) return;
 2332        var monto = Math.Round(base042414 / 0.87m * 0.13m, 2);
 2333        AgregarItem(Codigos.ReintegroAportes, monto, insumos, acumuladores, resultado);
 2334    }
 335}

Methods/Properties

.cctor()
get_CodigosExcluidosDecreto()
get_CodigosExcluidosSDL()
get_PorcentajeDedicacionIntegral()
CalcularAsync(FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo,FAU.Entidades.Acumuladores)
AgregarItemsDecreto(FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo,FAU.Entidades.Acumuladores,FAU.Logica.Calculadores.ResultadoCalculo)
AgregarItemsCondicionales(FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo,FAU.Entidades.Acumuladores,FAU.Logica.Calculadores.ResultadoCalculo)
CalcularDedicacionIntegral(System.Decimal,FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo,FAU.Entidades.Acumuladores,FAU.Logica.Calculadores.ResultadoCalculo)
AgregarSiExiste(FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo,FAU.Entidades.Acumuladores,FAU.Logica.Calculadores.ResultadoCalculo,System.Int32)
AgregarItem(System.Int32,System.Decimal,FAU.Logica.DTOs.InsumosPeriodo,FAU.Entidades.Acumuladores,FAU.Logica.Calculadores.ResultadoCalculo)
CalcularAniosServicio(FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo)
AgregarDecretoSdl(System.Int32,FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo,FAU.Entidades.Acumuladores,FAU.Logica.Calculadores.ResultadoCalculo)
AgregarDecretoNoSdl(System.Int32,FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo,FAU.Entidades.Acumuladores,FAU.Logica.Calculadores.ResultadoCalculo)
CalcularModAportes19695(FAU.Entidades.Acumuladores,FAU.Logica.DTOs.InsumosPeriodo,FAU.Logica.Calculadores.ResultadoCalculo)
Suma()
CalcularReintegroAportes(FAU.Entidades.Acumuladores,FAU.Logica.DTOs.InsumosPeriodo,FAU.Logica.Calculadores.ResultadoCalculo)