< 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
89%
Covered lines: 144
Uncovered lines: 17
Coverable lines: 161
Total lines: 433
Line coverage: 89.4%
Branch coverage
86%
Covered branches: 74
Total branches: 86
Branch coverage: 86%
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%
DebeCalcularSDL(...)100%11100%
CalcularAsync(...)100%1414100%
AgregarItemsDecreto(...)100%11100%
AgregarItemsCondicionales(...)83.33%884874.13%
CalcularDedicacionIntegral(...)50%22100%
AgregarSiExiste(...)100%22100%
AgregarItem(...)100%66100%
CalcularAniosServicio(...)100%210%
CalcularMesesPermanencia(...)50%22100%
AgregarDecretoSdl(...)75%44100%
AgregarDecretoNoSdl(...)100%22100%
Suma()100%11100%
CalcularReintegroAportes(...)83.33%66100%

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 CompPersonal        = 210;   // 048.018 "COMP PERS #400 Y 250"
 36        internal const int PrimaIdoneidad      = 42093;
 37        internal const int CompCombatientes    = 42103;
 38        internal const int CompLey18834        = 42536;
 39        internal const int Ley20075Art80       = 42403;
 40        internal const int Ley20075Art80Sub    = 42623;  // versión subalternos (Art.80 Ley 20.075)
 41        internal const int ModAportes          = 42413;
 42        internal const int ReintegroAportes    = 42414;
 43        internal const int CompDI              = 43002;
 44        internal const int DedicacionIntegral  = 43004;
 45        internal const int Progresivo          = 44001;
 46        internal const int AumEsp85Mil         = 48004;
 47        internal const int AumEsp92            = 48009;
 48        internal const int Sdl                 = 48012;
 49        internal const int Ley17269            = 48015;
 50        internal const int AumDec191_003       = 48017;
 51        internal const int RecSalarial2007     = 48023;
 52        internal const int RecSalarial466      = 48026;
 53        internal const int PartEspLey18719     = 48038;
 54        internal const int Ley19355            = 48040;
 55        internal const int IncrSubEscK         = 48042;
 56        internal const int CompPAN             = 42571;
 57        internal const int CompSAR             = 42572;
 58        internal const int PermanenciaOrdenSup = 60;
 59        internal const int CompDiOficial       = 299;
 60        internal const int CompDiSubalterno    = 315;
 61        internal const int DifEquipoSinAportes = 69005;
 62        internal const int DifEquipoConAportes = 69006;
 63        internal const int ViaticoAlimentacion = 69008;
 64        internal const int ViaticossinAportes  = 69009;
 65        internal const int ViaticosConAportes  = 69010;
 66    }
 67
 168    private static readonly IReadOnlySet<int> EmptySet = new HashSet<int>();
 69
 70    /// <summary>Ítems excluidos completamente de AgregarItemsDecreto. Override por subclase.</summary>
 74471    protected virtual IReadOnlySet<int> CodigosExcluidosDecreto => EmptySet;
 72
 73    /// <summary>Ítems emitidos pero que NO contribuyen a la base SDL. Override por subclase.</summary>
 36174    protected virtual IReadOnlySet<int> CodigosExcluidosSDL => EmptySet;
 75
 76    /// <summary>Porcentaje de Dedicación Integral. Override por subclase.</summary>
 377    protected virtual decimal PorcentajeDedicacionIntegral => 0.10m;
 78
 79    /// <summary>
 80    /// Controla si se calcula el SDL (048.012 = 5.3%) para este funcionario.
 81    /// Por defecto true. Sit30Calculator lo sobreescribe para excluir grados de orden < 2.
 82    /// </summary>
 5083    protected virtual bool DebeCalcularSDL(RelacionLaboral funcionario, InsumosPeriodo insumos) => true;
 84
 85    public Task<ResultadoCalculo> CalcularAsync(
 86        RelacionLaboral funcionario, InsumosPeriodo insumos, Acumuladores acumuladores)
 87    {
 7088        var resultado = new ResultadoCalculo();
 89        try
 90        {
 7091            var wpara53 = CalcularItemsBase(funcionario, insumos, acumuladores, resultado);
 6892            wpara53 += AgregarItemsDecreto(funcionario, insumos, acumuladores, resultado);
 6893            wpara53 += AgregarItemsCondicionales(funcionario, insumos, acumuladores, resultado);
 94
 6895            if (wpara53 > 0)
 96            {
 97                decimal wPara10;
 6798                if (DebeCalcularSDL(funcionario, insumos))
 99                {
 58100                    var sdl = Math.Round(wpara53 * 0.053m, 2);
 58101                    AgregarItem(Codigos.Sdl, sdl, insumos, acumuladores, resultado);
 58102                    wPara10 = wpara53 + sdl;
 103                }
 104                else
 105                {
 9106                    wPara10 = wpara53;
 107                }
 67108                CalcularDedicacionIntegral(wPara10, funcionario, insumos, acumuladores, resultado);
 109            }
 110
 111            // 042.414 — Reintegro de Aportes: aplica a TODOS los regímenes.
 112            // FoxPro SUM33316.PRG no verifica Ley Vieja/Nueva — confirmado por recibos
 113            // legacy donde aparece 042.414 sin 042.413 (que sí es exclusivo de Ley Nueva).
 68114            var esOficial = insumos.OrdenPorGrado.TryGetValue(funcionario.GradoId, out var gradoOrden)
 68115                ? gradoOrden >= 7
 68116                : (funcionario.Grado?.EsOficial ?? false);
 68117            CalcularReintegroAportes(esOficial, acumuladores, insumos, resultado);
 118
 119            // 042.413 (Mod.Aportes) se calcula en LiquidacionService DESPUÉS del paso de
 120            // compensaciones para que entren en WNominal. No calcular aquí (evita duplicado).
 121            // Solo Ley Nueva (19.695) — FoxPro SUM33315.PRG confirma condición de régimen.
 122
 68123            if (!funcionario.Situacion.AfectaCobro)
 10124                foreach (var item in resultado.Items)
 4125                    item.EsReferencia = true;
 126
 68127            resultado.Estado = resultado.Incidencias.Count > 0
 68128                ? EstadoCalculo.Revisar
 68129                : EstadoCalculo.Calculado;
 68130        }
 2131        catch (Exception ex)
 132        {
 2133            resultado.Estado = EstadoCalculo.Error;
 2134            resultado.MensajeError = ex.Message;
 2135        }
 136
 70137        return Task.FromResult(resultado);
 138    }
 139
 140    // ── Métodos abstractos/virtuales que cada calculadora puede sobreescribir ──────
 141
 142    /// <summary>
 143    /// Calcula los ítems propios de la situación.
 144    /// NO incluye SDL (048.012) ni DI (043.004) — los maneja la clase base.
 145    /// Retorna wpara53: suma de los ítems que entran en la base del SDL.
 146    /// </summary>
 147    protected abstract decimal CalcularItemsBase(
 148        RelacionLaboral funcionario, InsumosPeriodo insumos,
 149        Acumuladores acumuladores, ResultadoCalculo resultado);
 150
 151    /// <summary>
 152    /// Agrega decretos y recuperaciones fijas por grado (lookup en remuneraciones_grado).
 153    /// Subclases pueden excluir ítems via CodigosExcluidosDecreto / CodigosExcluidosSDL.
 154    /// Retorna el extra a sumar a wpara53 (solo ítems que entran en SDL).
 155    /// </summary>
 156    protected virtual decimal AgregarItemsDecreto(
 157        RelacionLaboral funcionario, InsumosPeriodo insumos,
 158        Acumuladores acumuladores, ResultadoCalculo resultado)
 159    {
 68160        decimal extraWPara53 = 0m;
 161
 162        // Entran en base SDL (confirmado por fórmula PDF 048.012)
 68163        extraWPara53 += AgregarDecretoSdl(Codigos.CompCargo, funcionario, insumos, acumuladores, resultado);       // 04
 68164        extraWPara53 += AgregarDecretoSdl(Codigos.AumEsp85Mil, funcionario, insumos, acumuladores, resultado);
 68165        extraWPara53 += AgregarDecretoSdl(Codigos.RecSalarial2007, funcionario, insumos, acumuladores, resultado);
 68166        extraWPara53 += AgregarDecretoSdl(Codigos.RecSalarial466, funcionario, insumos, acumuladores, resultado);
 68167        extraWPara53 += AgregarDecretoSdl(Codigos.CompCombatientes, funcionario, insumos, acumuladores, resultado);
 68168        extraWPara53 += AgregarDecretoSdl(Codigos.EquipoOficiales, funcionario, insumos, acumuladores, resultado);
 169
 170        // NO entran en base SDL (Calc_Hab.prg confirmado)
 68171        AgregarDecretoNoSdl(Codigos.AumEsp92, funcionario, insumos, acumuladores, resultado);
 68172        AgregarDecretoNoSdl(Codigos.Ley17269, funcionario, insumos, acumuladores, resultado);
 68173        AgregarDecretoNoSdl(Codigos.AumDec191_003, funcionario, insumos, acumuladores, resultado);
 68174        AgregarDecretoNoSdl(Codigos.Ley19355, funcionario, insumos, acumuladores, resultado);
 68175        AgregarDecretoNoSdl(Codigos.IncrSubEscK, funcionario, insumos, acumuladores, resultado);    // 048.042 — "No va?
 68176        AgregarDecretoNoSdl(Codigos.PartEspLey18719, funcionario, insumos, acumuladores, resultado); // 048.038 — NO ent
 68177        AgregarDecretoNoSdl(Codigos.CompLey18834, funcionario, insumos, acumuladores, resultado);
 68178        AgregarDecretoNoSdl(Codigos.Ley20075Art80, funcionario, insumos, acumuladores, resultado);    // 042.403 Oficial
 68179        AgregarDecretoNoSdl(Codigos.Ley20075Art80Sub, funcionario, insumos, acumuladores, resultado); // 042.623 Subalte
 68180        AgregarDecretoNoSdl(Codigos.DifEquipoSinAportes, funcionario, insumos, acumuladores, resultado);
 68181        AgregarDecretoNoSdl(Codigos.DifEquipoConAportes, funcionario, insumos, acumuladores, resultado);
 68182        AgregarDecretoNoSdl(Codigos.ViaticossinAportes, funcionario, insumos, acumuladores, resultado);
 68183        AgregarDecretoNoSdl(Codigos.ViaticosConAportes, funcionario, insumos, acumuladores, resultado);
 68184        AgregarDecretoNoSdl(Codigos.CompPAN, funcionario, insumos, acumuladores, resultado);
 68185        AgregarDecretoNoSdl(Codigos.CompSAR, funcionario, insumos, acumuladores, resultado);
 68186        AgregarDecretoNoSdl(Codigos.CompDI, funcionario, insumos, acumuladores, resultado);         // 043.002 Compl. DI
 68187        AgregarDecretoNoSdl(Codigos.CompDiOficial, funcionario, insumos, acumuladores, resultado);  // 299 043.003 Ofici
 68188        AgregarDecretoNoSdl(Codigos.CompDiSubalterno, funcionario, insumos, acumuladores, resultado); // 315 043.003 Sub
 189
 68190        return extraWPara53;
 191    }
 192
 193    /// <summary>
 194    /// Ítems condicionales: Riesgo de Vuelo, Progresivo, Prima Técnica, etc.
 195    /// Override por subclase si la situación los tiene.
 196    /// Retorna el extra a sumar a wpara53 (solo ítems en base SDL).
 197    /// </summary>
 198    protected virtual decimal AgregarItemsCondicionales(
 199        RelacionLaboral funcionario, InsumosPeriodo insumos,
 200        Acumuladores acumuladores, ResultadoCalculo resultado)
 201    {
 68202        decimal extraWPara53 = 0m;
 203
 204        // 042.092 — Prima Técnica (categorías A/B/C designadas por Orden del Comando)
 205        // El monto depende de la CATEGORÍA del funcionario (A/B/C), no del grado — confirmado
 206        // contra recibo real: A=3040.33, B=1520.16, C=912.10 (tarifas_por_categoria).
 207        // Solo aplica de Sdo.1ra a S.O.M. (orden 1-6); oficiales no la cobran.
 68208        if (!string.IsNullOrEmpty(funcionario.PrimaTecnica) && funcionario.PrimaTecnica != "VACIO")
 209        {
 8210            var ordenPT = insumos.OrdenPorGrado.TryGetValue(funcionario.GradoId, out var oPT) ? oPT : (short)0;
 8211            if (ordenPT is >= 1 and <= 6)
 212            {
 6213                var tarifaPT = insumos.ObtenerTarifaCategoria(funcionario.PrimaTecnica, Codigos.PrimaTecnicaPTI);
 6214                if (tarifaPT is not null)
 5215                    AgregarItem(Codigos.PrimaTecnicaPTI, tarifaPT.Monto, insumos, acumuladores, resultado);
 216            }
 217        }
 218
 219        // 048.018 — Compensación Personal ("COMP PERS #400 Y 250", cód. legacy 210).
 220        // Importe individual por persona (tabla comp_personal_048018), cargado a mano,
 221        // NO se calcula por grado ni por fórmula. Común a todas las situaciones — confirmado
 222        // en Calc_Hab.prg (compartido) y SUM33101.PRG.
 223        // NO entra en wpara53/DI: el bloque "do grabo_codigo" de este ítem en Calc_Hab.prg
 224        // no incrementa m.para_5_3/m.para_10 (a diferencia de AumEsp85Mil/RecSalarial, que sí
 225        // lo hacen explícitamente). El "sdl_lq = .t." de ACUM_400.PRG es solo una clasificación
 226        // de reporte en un script offline aparte, no alimenta la fórmula de 048.012/043.004.
 227        // Verificado contra recibo real: incluirlo en wpara53 infla la Dedicación Integral
 228        // (35% de más) por encima del valor real.
 68229        var compPersonal = insumos.ObtenerCompPersonal048018(funcionario.PersonaId);
 68230        if (compPersonal is not null)
 2231            AgregarItem(Codigos.CompPersonal, compPersonal.Importe, insumos, acumuladores, resultado);
 232
 233        // 042.093 — Prima Idoneidad (sin aporte), si el funcionario tiene superprima.
 68234        if (funcionario.Superprima)
 235        {
 1236            var remIdoneidad = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.PrimaIdoneidad);
 1237            if (remIdoneidad is not null)
 1238                AgregarItem(Codigos.PrimaIdoneidad, remIdoneidad.Monto, insumos, acumuladores, resultado);
 239        }
 240
 241        // 042.025 — Prima Técnica Permanente (entra en base SDL/DI — confirmado Calc_Hab.prg)
 68242        var remPrimaTec = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.PrimaTecnicaPerm);
 68243        if (remPrimaTec is not null)
 244        {
 0245            AgregarItem(Codigos.PrimaTecnicaPerm, remPrimaTec.Monto, insumos, acumuladores, resultado);
 0246            extraWPara53 += remPrimaTec.Monto;
 247        }
 248
 249        // 042.019 — Riesgo de Vuelo (si relacion_laboral.riesgo_vuelo > 0); PDF: SÍ entra en SDL y DI
 68250        if (funcionario.RiesgoVuelo.HasValue && funcionario.RiesgoVuelo.Value > 0)
 251        {
 0252            var remVuelo = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.RiesgoVuelo);
 0253            if (remVuelo is not null)
 254            {
 0255                AgregarItem(Codigos.RiesgoVuelo, remVuelo.Monto, insumos, acumuladores, resultado);
 0256                extraWPara53 += remVuelo.Monto;
 257            }
 258        }
 259
 260        // 044.001 — Progresivo / Prima por Antigüedad
 261        // Para tropa (FechaAscensoOficial == null): FoxPro divide el cómputo en dos tramos cortando en
 262        // FechaUltimoAscenso. Esto evita que una promoción reciente "redondee hacia arriba" el año parcial.
 263        //   años_base   = int((UltAsc − Inicio).TotalDays / 365.25)
 264        //   años_actual = int((Período − UltAsc).TotalDays / 365.25)
 265        //   total       = años_base + años_actual
 266        // Si UltAsc == Inicio (o es null), reduce a la fórmula simple desde Inicio.
 267        // Para oficiales (FechaAscensoOficial != null): usa esa fecha como base sin split.
 68268        var fechaPeriodo = new DateTime(insumos.ParametroPeriodo.Anio, insumos.ParametroPeriodo.Mes, 1);
 269        int aniosBrutos;
 68270        if (funcionario.FechaAscensoOficial == null
 68271            && funcionario.FechaUltimoAscenso.HasValue
 68272            && funcionario.FechaUltimoAscenso.Value > funcionario.FechaInicio)
 273        {
 0274            var ultAsc = funcionario.FechaUltimoAscenso.Value;
 0275            var aBase   = Math.Max(0, (int)((ultAsc      - funcionario.FechaInicio).TotalDays / 365.25));
 0276            var aActual = Math.Max(0, (int)((fechaPeriodo - ultAsc).TotalDays / 365.25));
 0277            aniosBrutos = aBase + aActual;
 278        }
 279        else
 280        {
 68281            var fechaBase = funcionario.FechaAscensoOficial ?? funcionario.FechaInicio;
 68282            aniosBrutos = Math.Max(0, (int)((fechaPeriodo - fechaBase).TotalDays / 365.25));
 283        }
 68284        var anios = Math.Max(0, aniosBrutos - (funcionario.AniosInactivos ?? 0));
 68285        if (anios > 0)
 286        {
 68287            var remProg = insumos.ObtenerRemuneracion(funcionario.GradoId, Codigos.Progresivo);
 68288            if (remProg is not null)
 289            {
 3290                var montoProg = remProg.Monto * Math.Min(anios, 30);
 3291                AgregarItem(Codigos.Progresivo, montoProg, insumos, acumuladores, resultado);
 3292                extraWPara53 += montoProg;
 293            }
 294        }
 295
 296        // PROGRE.TROPA: tramo subalterno para oficiales que ascendieron desde tropa.
 297        // FoxPro: m.anios = Anios_Trop (= fecha_asc_oficial - fecha_sub en años).
 298        // Tasa: la del grado subalterno de menor orden (SDO_1RA = $114.01/año en 2026).
 68299        if (funcionario.FechaAscensoOficial.HasValue &&
 68300            funcionario.FechaAscensoOficial.Value > funcionario.FechaInicio)
 301        {
 1302            var aniosTropa = Math.Max(0,
 1303                (int)((funcionario.FechaAscensoOficial.Value - funcionario.FechaInicio).TotalDays / 365.25));
 1304            aniosTropa = Math.Min(aniosTropa + funcionario.AniosServicioAnterior, 30);
 1305            if (aniosTropa > 0)
 306            {
 307                // Tasa subalterno: progresivo del grado con menor orden (SDO_1RA, orden=1)
 1308                var gradoId = insumos.OrdenPorGrado
 0309                    .Where(kv => kv.Value == 1)
 0310                    .Select(kv => (long?)kv.Key)
 1311                    .FirstOrDefault();
 1312                var remTropa = gradoId.HasValue
 1313                    ? insumos.ObtenerRemuneracion(gradoId.Value, Codigos.Progresivo)
 1314                    : null;
 1315                if (remTropa is not null)
 316                {
 0317                    var montoTropa = remTropa.Monto * aniosTropa;
 0318                    AgregarItem(Codigos.Progresivo, montoTropa, insumos, acumuladores, resultado);
 0319                    extraWPara53 += montoTropa;
 320                }
 321            }
 322        }
 323
 68324        return extraWPara53;
 325    }
 326
 327    /// <summary>
 328    /// Calcula Dedicación Integral (043.004).
 329    /// Override en subclases que usan porcentaje distinto o ítem diferente (p.ej. PAO).
 330    /// </summary>
 331    protected virtual void CalcularDedicacionIntegral(
 332        decimal wPara10, RelacionLaboral funcionario,
 333        InsumosPeriodo insumos, Acumuladores acumuladores, ResultadoCalculo resultado)
 334    {
 39335        if (wPara10 <= 0) return;
 39336        var di = Math.Round(wPara10 * PorcentajeDedicacionIntegral, 2);
 39337        AgregarItem(Codigos.DedicacionIntegral, di, insumos, acumuladores, resultado);
 39338    }
 339
 340    // ── Helpers internos ──────────────────────────────────────────────────────────
 341
 342    /// <summary>Agrega el ítem si existe en remuneraciones. Retorna el monto agregado (o 0).</summary>
 343    protected static decimal AgregarSiExiste(
 344        RelacionLaboral funcionario, InsumosPeriodo insumos,
 345        Acumuladores acumuladores, ResultadoCalculo resultado, int codigo)
 346    {
 1589347        var rem = insumos.ObtenerRemuneracion(funcionario.GradoId, codigo);
 3141348        if (rem is null) return 0m;
 37349        AgregarItem(codigo, rem.Monto, insumos, acumuladores, resultado);
 37350        return rem.Monto;
 351    }
 352
 353    protected static void AgregarItem(
 354        int codigo, decimal monto, InsumosPeriodo insumos,
 355        Acumuladores acumuladores, ResultadoCalculo resultado)
 356    {
 2682357        var catalogoItem = insumos.CatalogoItems.FirstOrDefault(c => c.Codigo == codigo);
 300358        resultado.Items.Add(new ItemCalculado
 300359        {
 300360            CodigoConcepto = codigo,
 300361            NombreConcepto = catalogoItem?.Nombre ?? $"Concepto {codigo}",
 300362            Importe = monto
 300363        });
 300364        if (catalogoItem is not null)
 296365            acumuladores.AgregarItem(monto, catalogoItem);
 300366    }
 367
 368    protected static int CalcularAniosServicio(RelacionLaboral funcionario, InsumosPeriodo insumos)
 369    {
 0370        var fechaPeriodo = new DateTime(insumos.ParametroPeriodo.Anio, insumos.ParametroPeriodo.Mes, 1);
 0371        return Math.Max(0, (int)((fechaPeriodo - funcionario.FechaInicio).TotalDays / 365.25));
 372    }
 373
 374    protected static int CalcularMesesPermanencia(RelacionLaboral funcionario, InsumosPeriodo insumos)
 375    {
 376        // 041.003 mide tiempo en el grado actual, no tiempo total de servicio.
 65377        var fechaBase = funcionario.FechaUltimoAscenso ?? funcionario.FechaInicio;
 65378        var fechaPeriodo = new DateTime(insumos.ParametroPeriodo.Anio, insumos.ParametroPeriodo.Mes, 1);
 65379        var meses = (fechaPeriodo.Year - fechaBase.Year) * 12
 65380                    + fechaPeriodo.Month - fechaBase.Month;
 65381        return Math.Max(0, meses);
 382    }
 383
 384    // Emite el ítem y acumula a la base SDL, respetando CodigosExcluidosDecreto/CodigosExcluidosSDL.
 385    private decimal AgregarDecretoSdl(
 386        int codigo, RelacionLaboral funcionario, InsumosPeriodo insumos,
 387        Acumuladores acumuladores, ResultadoCalculo resultado)
 388    {
 447389        if (CodigosExcluidosDecreto.Contains(codigo)) return 0m;
 369390        var monto = AgregarSiExiste(funcionario, insumos, acumuladores, resultado, codigo);
 369391        return CodigosExcluidosSDL.Contains(codigo) ? 0m : monto;
 392    }
 393
 394    // Emite el ítem sin acumularlo a la base SDL, respetando CodigosExcluidosDecreto.
 395    private void AgregarDecretoNoSdl(
 396        int codigo, RelacionLaboral funcionario, InsumosPeriodo insumos,
 397        Acumuladores acumuladores, ResultadoCalculo resultado)
 398    {
 1224399        if (!CodigosExcluidosDecreto.Contains(codigo))
 1220400            AgregarSiExiste(funcionario, insumos, acumuladores, resultado, codigo);
 1224401    }
 402
 403    /// <summary>
 404    /// 042.414 — Reintegro de Aportes por Cambio de Régimen de Aportación.
 405    /// Fórmula PDF: Σ(069.006 + 069.008 + 069.010 + 042.092×2 + 043.002 + 042.571 + 042.572) / 0.87 × 13%
 406    /// 042.092 cuenta doble porque el régimen nuevo aplica aporte doble sobre esa compensación.
 407    /// Solo aplica en régimen Ley Nueva (llamado desde CalcularAsync junto con 042.413).
 408    /// </summary>
 409    private static void CalcularReintegroAportes(
 410        bool esOficial, Acumuladores acumuladores, InsumosPeriodo insumos, ResultadoCalculo resultado)
 411    {
 2189412        decimal Suma(int codigo) => resultado.Items.Where(i => i.CodigoConcepto == codigo).Sum(i => i.Importe);
 413
 414        // Fórmula SUM33316.PRG: acumula (item/0.87) para códigos específicos, × 13%.
 415        // 069.008 (subalterno c/aportes) o 069.006 (oficial c/aportes) — excluyentes.
 416        // Subalternos sin CAT_VIAT (cadetes, aprendices, SDO sin viático): $0 para 069.x.
 417        // 042.092 se agrega UNA vez (verificado en legacy con dos personas).
 68418        var suma69 = Suma(Codigos.ViaticoAlimentacion) > 0m ? Suma(Codigos.ViaticoAlimentacion)
 68419                   : (esOficial ? Suma(Codigos.DifEquipoConAportes) : 0m);
 420
 68421        var base042414 =
 68422            suma69                    +
 68423            Suma(Codigos.ViaticosConAportes) +
 68424            Suma(Codigos.PrimaTecnicaPTI)    +    // × 1 (no ×2, verificado legacy)
 68425            Suma(Codigos.CompDI)             +
 68426            Suma(Codigos.CompPAN)            +    // solo vía lote
 68427            Suma(Codigos.CompSAR);                // solo vía lote
 428
 130429        if (base042414 <= 0m) return;
 6430        var monto = Math.Round(base042414 / 0.87m * 0.13m, 2);
 6431        AgregarItem(Codigos.ReintegroAportes, monto, insumos, acumuladores, resultado);
 6432    }
 433}

Methods/Properties

.cctor()
get_CodigosExcluidosDecreto()
get_CodigosExcluidosSDL()
get_PorcentajeDedicacionIntegral()
DebeCalcularSDL(FAU.Entidades.RelacionLaboral,FAU.Logica.DTOs.InsumosPeriodo)
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)
CalcularMesesPermanencia(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)
Suma()
CalcularReintegroAportes(System.Boolean,FAU.Entidades.Acumuladores,FAU.Logica.DTOs.InsumosPeriodo,FAU.Logica.Calculadores.ResultadoCalculo)