< 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
91%
Covered lines: 101
Uncovered lines: 9
Coverable lines: 110
Total lines: 279
Line coverage: 91.8%
Branch coverage
85%
Covered branches: 36
Total branches: 42
Branch coverage: 85.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_PorcentajeDedicacionIntegral()100%11100%
CalcularAsync(...)100%1010100%
AgregarItemsDecreto(...)100%11100%
AgregarItemsCondicionales(...)72.22%351862.5%
CalcularDedicacionIntegral(...)50%22100%
AgregarSiExiste(...)100%22100%
AgregarItem(...)100%66100%
CalcularAniosServicio(...)100%11100%
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    /// <summary>Porcentaje de Dedicación Integral. Override por subclase.</summary>
 322    protected virtual decimal PorcentajeDedicacionIntegral => 0.10m; // 10% Personal Superior
 23
 24    public Task<ResultadoCalculo> CalcularAsync(
 25        RelacionLaboral funcionario, InsumosPeriodo insumos, Acumuladores acumuladores)
 26    {
 4827        var resultado = new ResultadoCalculo();
 28        try
 29        {
 30            // 1. Ítems base de la situación (SIN SDL)
 4831            var wpara53 = CalcularItemsBase(funcionario, insumos, acumuladores, resultado);
 32
 33            // 2. Decretos y recuperaciones fijas por grado
 34            //    Retorna el extra que TAMBIÉN entra en la base SDL
 4635            wpara53 += AgregarItemsDecreto(funcionario, insumos, acumuladores, resultado);
 36
 37            // 3. Ítems condicionales (Riesgo Vuelo, Progresivo, etc.)
 38            //    También pueden sumar a wpara53
 4639            wpara53 += AgregarItemsCondicionales(funcionario, insumos, acumuladores, resultado);
 40
 41            // 4. SDL 5,3% — calculado con TODA la base
 4642            if (wpara53 > 0)
 43            {
 4344                var sdl = Math.Round(wpara53 * 0.053m, 2);
 4345                AgregarItem(48012, sdl, insumos, acumuladores, resultado);
 46
 4347                var wPara10 = wpara53 + sdl;
 48
 49                // 5. Dedicación Integral (virtual — cada situación usa su porcentaje)
 4350                CalcularDedicacionIntegral(wPara10, funcionario, insumos, acumuladores, resultado);
 51            }
 52
 53            // 6. Mod. Aportes Ley 19.695 (solo Ley Nueva)
 4654            if (!funcionario.Regimen.EsLeyVieja)
 55            {
 1956                CalcularModAportes19695(acumuladores, insumos, resultado);
 1957                CalcularReintegroAportes(acumuladores, insumos, resultado);
 58            }
 59
 4660            if (!funcionario.Situacion.AfectaCobro)
 1061                foreach (var item in resultado.Items)
 462                    item.EsReferencia = true;
 63
 4664            resultado.Estado = resultado.Incidencias.Count > 0
 4665                ? EstadoCalculo.Revisar
 4666                : EstadoCalculo.Calculado;
 4667        }
 268        catch (Exception ex)
 69        {
 270            resultado.Estado = EstadoCalculo.Error;
 271            resultado.MensajeError = ex.Message;
 272        }
 73
 4874        return Task.FromResult(resultado);
 75    }
 76
 77    // ── Métodos abstractos/virtuales que cada calculadora puede sobreescribir ──────
 78
 79    /// <summary>
 80    /// Calcula los ítems propios de la situación.
 81    /// NO incluye SDL (048.012) ni DI (043.004) — los maneja la clase base.
 82    /// Retorna wpara53: suma de los ítems que entran en la base del SDL.
 83    /// </summary>
 84    protected abstract decimal CalcularItemsBase(
 85        RelacionLaboral funcionario, InsumosPeriodo insumos,
 86        Acumuladores acumuladores, ResultadoCalculo resultado);
 87
 88    /// <summary>
 89    /// Agrega decretos y recuperaciones fijas por grado (lookup en remuneraciones_grado).
 90    /// Fórmula SDL PDF: 048.004, 048.023, 048.026 SÍ entran en base SDL.
 91    ///                  048.009, 048.015, 048.017, 042.403 NO entran en base SDL.
 92    /// Retorna el extra a sumar a wpara53 (solo ítems que entran en SDL).
 93    /// </summary>
 94    protected virtual decimal AgregarItemsDecreto(
 95        RelacionLaboral funcionario, InsumosPeriodo insumos,
 96        Acumuladores acumuladores, ResultadoCalculo resultado)
 97    {
 4398        decimal extraWPara53 = 0m;
 99
 100        // ── Entran en base SDL (confirmado por fórmula PDF 048.012) ────────────
 43101        extraWPara53 += AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48004);  // Dec.180/985
 43102        extraWPara53 += AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48023);  // Rec.Salarial 2007
 43103        extraWPara53 += AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48026);  // Rec.Salarial 4.66%
 43104        extraWPara53 += AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 42103);  // Mayor Resp. MDN
 43105        extraWPara53 += AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48038);  // Incr. Esc. K
 43106        extraWPara53 += AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 42067);  // Equipo SS.OO (PDF: en
 107
 108        // ── NO entran en base SDL (según fórmula PDF) ──────────────────────────
 43109        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48009);  // Dec.203/992
 43110        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48015);  // Ley 17.269
 43111        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48017);  // Dec.191/003
 43112        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48040);  // Ley 19.355
 43113        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 48042);  // Incr. Sub Esc.K
 43114        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 42536);  // Comp. Ley 18.834
 43115        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 42403);  // Ley 20.075 Art.80
 43116        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 69006);  // Dif. Equipo Oficiales
 43117        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 69010);  // Viático por Traslado
 43118        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 42571);  // Comp. PAN (Policía Aérea Nacional)
 43119        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 42572);  // Comp. SAR (Búsqueda y Rescate)
 120        // Complemento Dedicación Integral (código catálogo 299=Oficial, 315=Subalterno)
 43121        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 299);    // Comp. DI Oficial (043.003)
 43122        AgregarSiExiste(funcionario, insumos, acumuladores, resultado, 315);    // Comp. DI Subalterno (043.003)
 123        // TODO: confirmar OG para los códigos internos 39 y 154 con DFC si no están alineados
 124
 43125        return extraWPara53;
 126    }
 127
 128    /// <summary>
 129    /// Ítems condicionales: Riesgo de Vuelo, Progresivo, Prima Técnica, etc.
 130    /// Override por subclase si la situación los tiene.
 131    /// Retorna el extra a sumar a wpara53 (solo ítems en base SDL).
 132    /// </summary>
 133    protected virtual decimal AgregarItemsCondicionales(
 134        RelacionLaboral funcionario, InsumosPeriodo insumos,
 135        Acumuladores acumuladores, ResultadoCalculo resultado)
 136    {
 46137        decimal extraWPara53 = 0m;
 138
 139        // 042.092 — Prima Técnica PTI/PT (categorías A/B/C designadas por Orden del Comando)
 140        // El campo prima_tecnica de RelacionLaboral indica si aplica ('A','B','C' o 'VACIO').
 141        // El monto viene de remuneraciones_grado por grado (DFC carga el monto correcto).
 142        // Sujeto a Montepío. TODO: implementar diferenciación por categoría si los montos difieren.
 46143        if (!string.IsNullOrEmpty(funcionario.PrimaTecnica) && funcionario.PrimaTecnica != "VACIO")
 144        {
 3145            var remPT = insumos.ObtenerRemuneracion(funcionario.GradoId, 42092);
 3146            if (remPT is not null)
 1147                AgregarItem(42092, remPT.Monto, insumos, acumuladores, resultado);
 148        }
 149
 150        // 042.025 — Prima Técnica Permanente
 151        // Confirmado DFC 2026-06-10: "Se calcula automáticamente" para SOM y Sgto.1ro.
 152        // La data-driven check: si el grado tiene remuneración para 42025, se emite.
 153        // No entra en base SDL (no figura en fórmula de 048.012).
 46154        var remPrimaTec = insumos.ObtenerRemuneracion(funcionario.GradoId, 42025);
 46155        if (remPrimaTec is not null)
 0156            AgregarItem(42025, remPrimaTec.Monto, insumos, acumuladores, resultado);
 157
 158        // 042.019 — Riesgo de Vuelo (si relacion_laboral.riesgo_vuelo > 0)
 159        // PDF: SÍ entra en base SDL y en base DI
 46160        if (funcionario.RiesgoVuelo.HasValue && funcionario.RiesgoVuelo.Value > 0)
 161        {
 0162            var remVuelo = insumos.ObtenerRemuneracion(funcionario.GradoId, 42019);
 0163            if (remVuelo is not null)
 164            {
 0165                AgregarItem(42019, remVuelo.Monto, insumos, acumuladores, resultado);
 0166                extraWPara53 += remVuelo.Monto;
 167            }
 168        }
 169
 170        // 044.001 — Progresivo / Prima por Antigüedad
 171        // Equivalente a Personal.Anios_Trop del FoxPro:
 172        //   años de servicio efectivo = (fecha_periodo - fecha_inicio) - anios_inactivos
 173        // anios_inactivos: períodos de baja/inactividad ya registrados en relacion_laboral
 174        // Fórmula: min(años_efectivos, 30) × monto_unitario
 46175        var fechaPeriodo2 = new DateTime(insumos.ParametroPeriodo.Anio, insumos.ParametroPeriodo.Mes, 1);
 46176        var aniosBrutos = Math.Max(0, (int)((fechaPeriodo2 - funcionario.FechaInicio).TotalDays / 365.25));
 46177        var anios = Math.Max(0, aniosBrutos - (funcionario.AniosInactivos ?? 0));
 46178        if (anios > 0)
 179        {
 46180            var remProg = insumos.ObtenerRemuneracion(funcionario.GradoId, 44001);
 46181            if (remProg is not null)
 182            {
 0183                var acum = Math.Min(anios, 30);
 0184                var montoProg = remProg.Monto * acum;
 0185                AgregarItem(44001, montoProg, insumos, acumuladores, resultado);
 0186                extraWPara53 += montoProg;
 187            }
 188        }
 189
 46190        return extraWPara53;
 191    }
 192
 193    /// <summary>
 194    /// Calcula Dedicación Integral (043.004).
 195    /// Override en subclases que usan porcentaje distinto o ítem diferente (p.ej. PAO).
 196    /// </summary>
 197    protected virtual void CalcularDedicacionIntegral(
 198        decimal wPara10, RelacionLaboral funcionario,
 199        InsumosPeriodo insumos, Acumuladores acumuladores, ResultadoCalculo resultado)
 200    {
 34201        if (wPara10 <= 0) return;
 34202        var di = Math.Round(wPara10 * PorcentajeDedicacionIntegral, 2);
 34203        AgregarItem(43004, di, insumos, acumuladores, resultado);
 34204    }
 205
 206    // ── Helpers internos ──────────────────────────────────────────────────────────
 207
 208    /// <summary>Agrega el ítem si existe en remuneraciones. Retorna el monto agregado (o 0).</summary>
 209    protected static decimal AgregarSiExiste(
 210        RelacionLaboral funcionario, InsumosPeriodo insumos,
 211        Acumuladores acumuladores, ResultadoCalculo resultado, int codigo)
 212    {
 817213        var rem = insumos.ObtenerRemuneracion(funcionario.GradoId, codigo);
 1625214        if (rem is null) return 0m;
 9215        AgregarItem(codigo, rem.Monto, insumos, acumuladores, resultado);
 9216        return rem.Monto;
 217    }
 218
 219    protected static void AgregarItem(
 220        int codigo, decimal monto, InsumosPeriodo insumos,
 221        Acumuladores acumuladores, ResultadoCalculo resultado)
 222    {
 1429223        var catalogoItem = insumos.CatalogoItems.FirstOrDefault(c => c.Codigo == codigo);
 215224        resultado.Items.Add(new ItemCalculado
 215225        {
 215226            CodigoConcepto = codigo,
 215227            NombreConcepto = catalogoItem?.Nombre ?? $"Concepto {codigo}",
 215228            Importe = monto
 215229        });
 215230        if (catalogoItem is not null)
 210231            acumuladores.AgregarItem(monto, catalogoItem);
 215232    }
 233
 234    // No se necesita FechaBaseProgresivo — ver AgregarItemsCondicionales donde se calcula
 235    // Anios_Trop = años desde fecha_inicio MENOS anios_inactivos (campo en relacion_laboral)
 236
 237    protected static int CalcularAniosServicio(RelacionLaboral funcionario, InsumosPeriodo insumos)
 238    {
 43239        var fechaPeriodo = new DateTime(insumos.ParametroPeriodo.Anio, insumos.ParametroPeriodo.Mes, 1);
 43240        return Math.Max(0, (int)((fechaPeriodo - funcionario.FechaInicio).TotalDays / 365.25));
 241    }
 242
 243    private static void CalcularModAportes19695(
 244        Acumuladores acumuladores, InsumosPeriodo insumos, ResultadoCalculo resultado)
 245    {
 107246        var monto048042 = resultado.Items.Where(i => i.CodigoConcepto == 48042).Sum(i => i.Importe);
 106247        var monto042403 = resultado.Items.Where(i => i.CodigoConcepto == 42403).Sum(i => i.Importe);
 19248        var baseCalculo = acumuladores.WNominal - monto048042 - monto042403;
 20249        if (baseCalculo <= 0) return;
 18250        var monto = Math.Round(baseCalculo * 0.0235m, 2);
 18251        AgregarItem(42413, monto, insumos, acumuladores, resultado);
 18252    }
 253
 254    /// <summary>
 255    /// 042.414 — Reintegro de Aportes por Cambio de Régimen de Aportación.
 256    /// Fórmula PDF: Σ(069.006 + 069.008 + 069.010 + 042.092×2 + 043.002 + 042.571 + 042.572) / 0.87 × 13%
 257    /// 042.092 cuenta doble porque el régimen nuevo aplica aporte doble sobre esa compensación.
 258    /// Los insumos deben haberse emitido previamente en resultado.Items.
 259    /// Solo aplica en régimen Ley Nueva (llamado desde CalcularAsync junto con 042.413).
 260    /// </summary>
 261    private static void CalcularReintegroAportes(
 262        Acumuladores acumuladores, InsumosPeriodo insumos, ResultadoCalculo resultado)
 263    {
 864264        decimal Suma(int codigo) => resultado.Items.Where(i => i.CodigoConcepto == codigo).Sum(i => i.Importe);
 265
 19266        var base042414 =
 19267            Suma(69006)   +    // 069.006 Dif. Equipo Oficiales
 19268            Suma(69008)   +    // 069.008 Viático de Alimentación
 19269            Suma(69010)   +    // 069.010 Viático por Traslado
 19270            Suma(42092) * 2m + // 042.092 Prima Técnica (×2 según fórmula PDF)
 19271            Suma(43002)   +    // 043.002 Complemento DI
 19272            Suma(42571)   +    // 042.571 Comp. PAN
 19273            Suma(42572);       // 042.572 Comp. SAR
 274
 36275        if (base042414 <= 0m) return;
 2276        var monto = Math.Round(base042414 / 0.87m * 0.13m, 2);
 2277        AgregarItem(42414, monto, insumos, acumuladores, resultado);
 2278    }
 279}