< Summary

Information
Class: FAU.Logica.Services.IrpfService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/IrpfService.cs
Line coverage
52%
Covered lines: 38
Uncovered lines: 34
Coverable lines: 72
Total lines: 162
Line coverage: 52.7%
Branch coverage
50%
Covered branches: 2
Total branches: 4
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
CalcularYPersistirAsync()100%2291.3%
ObtenerPorIdAsync()100%210%
ActualizarAsync()0%620%
ObtenerPaginadoAsync()100%210%
MapearDetallesFranjas(...)100%11100%

File(s)

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

#LineLine coverage
 1namespace FAU.Logica.Services;
 2
 3using FAU.DataAccess.Repositories;
 4using FAU.Entidades;
 5using FAU.Logica.DTOs;
 6
 7/// <summary>
 8/// Servicio IRPF - orquesta cálculo + persistencia + auditoría.
 9/// </summary>
 10public class IrpfService : IIrpfService
 11{
 12    private readonly IrpfCalculator _calculator;
 13    private readonly IIrpfRepository _repository;
 14    private readonly IAuditoriaService _auditoriaService;
 15    private readonly ICurrentUserContext _currentUser;
 16
 317    public IrpfService(
 318        IrpfCalculator calculator,
 319        IIrpfRepository repository,
 320        IAuditoriaService auditoriaService,
 321        ICurrentUserContext currentUser)
 22    {
 323        _calculator = calculator;
 324        _repository = repository;
 325        _auditoriaService = auditoriaService;
 326        _currentUser = currentUser;
 327    }
 28
 29    /// <summary>
 30    /// Calcula IRPF y lo persiste en BD con auditoría.
 31    /// </summary>
 32    public async Task<(bool Exito, long? IdIrpfMensual, string? Error)> CalcularYPersistirAsync(
 33        long liquidacionId,
 34        long personaId,
 35        CalcularIrpfRequest request)
 36    {
 37        try
 38        {
 39            // 1. Verificar si ya existe
 340            if (await _repository.ExisteIrpfMensualAsync(liquidacionId, personaId))
 141                return (false, null, "Ya existe un IRPF calculado para esta liquidación y persona");
 42
 43            // 2. Calcular
 244            var resultadoCalculo = _calculator.Calcular(request);
 45
 46            // 3. Mapear resultado a entidad IrpfMensual
 247            var irpfMensual = new IrpfMensual
 248            {
 249                LiquidacionId = liquidacionId,
 250                PersonaId = personaId,
 251                IngresosGravados = resultadoCalculo.IngresoGravado,
 252                DeduccionAportes = resultadoCalculo.Deducciones.Aportes,
 253                DeduccionHijos = resultadoCalculo.Deducciones.Hijos,
 254                DeduccionConyuge = resultadoCalculo.Deducciones.Conyuge,
 255                DeduccionProfesional = resultadoCalculo.Deducciones.Profesional,
 256                TasaDeduccionProfesional = resultadoCalculo.TasaCredito,
 257                TotalDeducciones = resultadoCalculo.TotalDeducciones,
 258                BaseImponibleNeta = resultadoCalculo.IrpfBruto,
 259                TotalIrpf = resultadoCalculo.TotalIrpf
 260            };
 61
 62            // 4. Mapear detalles por franja
 263            var detalleFranjas = MapearDetallesFranjas(resultadoCalculo.DetallePorFranja);
 64
 65            // 5. Persistir
 266            var irpfCreado = await _repository.CrearIrpfMensualAsync(irpfMensual, detalleFranjas);
 67
 268            return (true, irpfCreado.Id, null);
 69        }
 070        catch (Exception ex)
 71        {
 072            return (false, null, $"Error al calcular IRPF: {ex.Message}");
 73        }
 374    }
 75
 76    /// <summary>
 77    /// Obtiene un IRPF por ID con sus detalles.
 78    /// </summary>
 79    public async Task<IrpfMensual?> ObtenerPorIdAsync(long id)
 80    {
 081        return await _repository.ObtenerIrpfMensualPorIdAsync(id);
 082    }
 83
 84    /// <summary>
 85    /// Actualiza un IRPF (retroactividad, etc).
 86    /// Recalcula y reemplaza valores y detalles por franja.
 87    /// </summary>
 88    public async Task<(bool Exito, long? IdIrpfMensual, string? Error)> ActualizarAsync(
 89        long id,
 90        CalcularIrpfRequest request)
 91    {
 92        try
 93        {
 94            // 1. Obtener IRPF existente
 095            var irpfExistente = await _repository.ObtenerIrpfMensualPorIdAsync(id);
 096            if (irpfExistente == null)
 97            {
 098                return (false, null, "IRPF no encontrado");
 99            }
 100
 101            // 2. Guardar datos antiguos para auditoría
 0102            var datosAntiguos = $"TotalIrpf={irpfExistente.TotalIrpf}, BaseImponibleNeta={irpfExistente.BaseImponibleNet
 103
 104            // 3. Recalcular
 0105            var resultadoCalculo = _calculator.Calcular(request);
 106
 107            // 4. Actualizar entidad IrpfMensual
 0108            irpfExistente.IngresosGravados = resultadoCalculo.IngresoGravado;
 0109            irpfExistente.DeduccionAportes = resultadoCalculo.Deducciones.Aportes;
 0110            irpfExistente.DeduccionHijos = resultadoCalculo.Deducciones.Hijos;
 0111            irpfExistente.DeduccionConyuge = resultadoCalculo.Deducciones.Conyuge;
 0112            irpfExistente.DeduccionProfesional = resultadoCalculo.Deducciones.Profesional;
 0113            irpfExistente.TasaDeduccionProfesional = resultadoCalculo.TasaCredito;
 0114            irpfExistente.TotalDeducciones = resultadoCalculo.TotalDeducciones;
 0115            irpfExistente.BaseImponibleNeta = resultadoCalculo.IrpfBruto;
 0116            irpfExistente.TotalIrpf = resultadoCalculo.TotalIrpf;
 117
 118            // 5. Mapear nuevos detalles
 0119            var nuevoDetalle = MapearDetallesFranjas(resultadoCalculo.DetallePorFranja);
 120
 121            // 6. Persistir
 0122            var irpfActualizado = await _repository.ActualizarIrpfMensualAsync(irpfExistente, nuevoDetalle);
 123
 124            // 7. Auditar
 0125            var datosNuevos = $"TotalIrpf={irpfActualizado.TotalIrpf}, BaseImponibleNeta={irpfActualizado.BaseImponibleN
 0126            await _auditoriaService.LogAuditoriaAsync(
 0127                usuarioId: _currentUser.UserId ?? 0,
 0128                accion: AccionEnum.ActualizarIrpf,
 0129                contexto: ContextoEnum.IRPF,
 0130                entidad: "irpf_mensual",
 0131                entidadId: irpfActualizado.Id,
 0132                detalle: new { Anterior = datosAntiguos, Nuevo = datosNuevos });
 133
 0134            return (true, irpfActualizado.Id, null);
 135        }
 0136        catch (Exception ex)
 137        {
 0138            return (false, null, $"Error al actualizar IRPF: {ex.Message}");
 139        }
 0140    }
 141
 142    /// <summary>
 143    /// Obtiene IRPF paginado para una liquidación.
 144    /// </summary>
 145    public async Task<(IEnumerable<IrpfMensual> Items, int TotalCount)> ObtenerPaginadoAsync(
 146        long liquidacionId,
 147        int pagina,
 148        int tamano)
 149    {
 0150        return await _repository.ObtenerIrpfsMensualPorLiquidacionAsync(liquidacionId, pagina, tamano);
 0151    }
 152
 153    private static List<IrpfDetalleFranjas> MapearDetallesFranjas(IEnumerable<DetalleFranjaResultado> detalles) =>
 6154        detalles.Select(d => new IrpfDetalleFranjas
 6155        {
 6156            FranjaIrpfId = d.FranjaId,
 6157            BaseEnFranja = d.BaseFranja,
 6158            Tasa = d.Tasa,
 6159            Impuesto = d.Impuesto
 6160        }).ToList();
 161}
 162