< Summary

Information
Class: FAU.Logica.Services.IrpfCalculator
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/IrpfCalculator.cs
Line coverage
100%
Covered lines: 65
Uncovered lines: 0
Coverable lines: 65
Total lines: 159
Line coverage: 100%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
CalcularDeducciones(...)100%1010100%
AplicarFranjas(...)100%1010100%
Calcular(...)100%22100%

File(s)

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

#LineLine coverage
 1namespace FAU.Logica.Services;
 2
 3using FAU.Logica.DTOs;
 4
 5/// <summary>
 6/// Calculadora IRPF stateless.
 7/// Implementa la lógica de cálculo de deducciones IRPF y aplicación de franjas progresivas.
 8/// No accede a la BD - recibe todos los datos por parámetro.
 9///
 10/// Flujo:
 11/// 1. CalcularDeducciones() - calcula aportes, hijos, cónyuge, profesional
 12/// 2. AplicarFranjas() - aplica 7 franjas progresivas a la base imponible neta
 13/// 3. Calcular() - orquesta el flujo completo y devuelve ResultadoIrpf
 14/// </summary>
 15public class IrpfCalculator
 16{
 17    /// <summary>
 18    /// Calcula las deducciones IRPF: aportes jubilatorios y dependientes.
 19    /// La deducción ficta profesional (Ded_Prof en FoxPro) no se incluye aquí
 20    /// porque se carga por persona desde tabla externa; por defecto es 0.
 21    /// </summary>
 22    public DetalleDeduccionesResultado CalcularDeducciones(
 23        decimal ingresoGravado,
 24        decimal montepio,
 25        decimal fonasa,
 26        decimal frl,
 27        List<DeduccionDto> deducciones,
 28        ParametrosIrpfDto parametros)
 29    {
 1430        var resultado = new DetalleDeduccionesResultado();
 31
 32        // 1. Deducción por aportes jubilatorios (montepío + FONASA/Sanidad + FRL)
 1433        resultado.Aportes = montepio + fonasa + frl;
 34
 35        // 2. Deducción por dependientes (hijos y cónyuge)
 3836        foreach (var deduccion in deducciones)
 37        {
 538            if (deduccion.Tipo == "hijo_menor")
 39            {
 240                resultado.Hijos += deduccion.Cantidad * (parametros.BpcMensual * 20 / 12);
 41            }
 342            else if (deduccion.Tipo == "hijo_discapacidad")
 43            {
 144                resultado.Hijos += deduccion.Cantidad * (parametros.BpcMensual * 40 / 12);
 45            }
 246            else if (deduccion.Tipo == "conyuge_sin_ingresos")
 47            {
 248                if (deduccion.Cantidad > 0)
 249                    resultado.Conyuge = parametros.BpcMensual * 20 / 12;
 50            }
 51        }
 52
 53        // Profesional queda en 0 (se carga por persona si corresponde)
 1454        return resultado;
 55    }
 56
 57    /// <summary>
 58    /// Aplica las franjas progresivas IRPF a la base dada.
 59    /// Retorna (TotalIrpf, DetallePorFranja[]).
 60    /// </summary>
 61    public (decimal TotalIrpf, List<DetalleFranjaResultado> Detalles) AplicarFranjas(
 62        decimal baseParaFranjas,
 63        List<FranjaIrpfCalculoDto> franjas,
 64        decimal bpcMensual)
 65    {
 1366        var totalIrpf = decimal.Zero;
 1367        var detalles = new List<DetalleFranjaResultado>();
 68
 1469        if (baseParaFranjas < 0) baseParaFranjas = 0;
 70
 1371        var restante = baseParaFranjas;
 72
 11673        foreach (var franja in franjas.OrderBy(f => f.NumeroFranja))
 74        {
 3175            if (restante <= 0) break;
 76
 1977            var limiteInf = franja.DesdeBpc * bpcMensual;
 1978            var limiteSup = franja.HastaBpc.HasValue
 1979                ? franja.HastaBpc.Value * bpcMensual
 1980                : decimal.MaxValue;
 81
 1982            var ancho = limiteSup - limiteInf;
 1983            var montoEnFranja = Math.Min(restante, ancho);
 1984            var impuestoFranja = montoEnFranja * franja.Tasa;
 85
 1986            if (montoEnFranja > 0)
 87            {
 1988                detalles.Add(new DetalleFranjaResultado
 1989                {
 1990                    FranjaId = franja.FranjaId,
 1991                    NumeroFranja = franja.NumeroFranja,
 1992                    BaseFranja = montoEnFranja,
 1993                    Tasa = franja.Tasa,
 1994                    Impuesto = impuestoFranja
 1995                });
 96
 1997                totalIrpf += impuestoFranja;
 98            }
 99
 19100            restante -= ancho;
 101        }
 102
 13103        return (totalIrpf, detalles);
 104    }
 105
 106    /// <summary>
 107    /// Calcula IRPF con el enfoque de crédito fiscal (igual a FoxPro IRPF_DED.PRG):
 108    /// 1. Aplica franjas sobre el ingreso BRUTO → IRPF bruto
 109    /// 2. Crédito = (aportes + hijos + cónyuge) × tasa_crédito (8% ó 10%)
 110    /// 3. IRPF final = max(0, IRPF bruto − crédito)
 111    ///
 112    /// Tasa de crédito: 8% si ingreso >= 15 BPC, 10% si ingreso &lt; 15 BPC.
 113    /// (TasaDeduccionAlta=8%, TasaDeduccionBaja=10% en parámetros del período)
 114    /// </summary>
 115    public ResultadoIrpf Calcular(CalcularIrpfRequest request)
 116    {
 9117        var bpcMensual = request.Parametros.BpcMensual;
 118
 119        // 1. IRPF bruto aplicando franjas sobre ingreso completo (sin restar deducciones)
 9120        var (irpfBruto, detalleFranjas) = AplicarFranjas(
 9121            request.IngresoGravados,
 9122            request.Franjas,
 9123            bpcMensual);
 124
 125        // 2. Deducciones: aportes + dependientes (profesional = 0 por defecto)
 9126        var deducciones = CalcularDeducciones(
 9127            request.IngresoGravados,
 9128            request.Montepio,
 9129            request.Fonasa,
 9130            request.Frl,
 9131            request.Deducciones,
 9132            request.Parametros);
 133
 9134        var totalDeducciones = deducciones.Aportes + deducciones.Hijos + deducciones.Conyuge + deducciones.Profesional;
 135
 136        // 3. Tasa de crédito: 8% si ingreso >= 15 BPC, 10% si menor
 9137        var umbral15Bpc = request.Parametros.UmbralDeduccionBpc * bpcMensual;
 9138        var tasaCredito = request.IngresoGravados >= umbral15Bpc
 9139            ? request.Parametros.TasaDeduccionAlta   // 8%
 9140            : request.Parametros.TasaDeduccionBaja;  // 10%
 141
 142        // 4. Crédito fiscal = deducciones × tasa_crédito
 9143        var credito = totalDeducciones * tasaCredito;
 144
 145        // 5. IRPF final
 9146        var totalIrpf = Math.Max(0m, irpfBruto - credito);
 147
 9148        return new ResultadoIrpf
 9149        {
 9150            IngresoGravado = request.IngresoGravados,
 9151            Deducciones = deducciones,
 9152            TasaCredito = tasaCredito,
 9153            TotalDeducciones = totalDeducciones,
 9154            IrpfBruto = irpfBruto,
 9155            TotalIrpf = totalIrpf,
 9156            DetallePorFranja = detalleFranjas
 9157        };
 158    }
 159}