< 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: 46
Uncovered lines: 42
Coverable lines: 88
Total lines: 178
Line coverage: 52.2%
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%2293.54%
ObtenerPorIdAsync()100%210%
ActualizarAsync()0%620%
ObtenerPaginadoAsync()100%210%

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
 217    public IrpfService(
 218        IrpfCalculator calculator,
 219        IIrpfRepository repository,
 220        IAuditoriaService auditoriaService,
 221        ICurrentUserContext currentUser)
 22    {
 223        _calculator = calculator;
 224        _repository = repository;
 225        _auditoriaService = auditoriaService;
 226        _currentUser = currentUser;
 227    }
 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
 240            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
 144            var resultadoCalculo = _calculator.Calcular(request);
 45
 46            // 3. Mapear resultado a entidad IrpfMensual
 147            var irpfMensual = new IrpfMensual
 148            {
 149                LiquidacionId = liquidacionId,
 150                PersonaId = personaId,
 151                IngresosGravados = resultadoCalculo.IngresoGravado,
 152                DeduccionAportes = resultadoCalculo.Deducciones.Aportes,
 153                DeduccionHijos = resultadoCalculo.Deducciones.Hijos,
 154                DeduccionConyuge = resultadoCalculo.Deducciones.Conyuge,
 155                DeduccionProfesional = resultadoCalculo.Deducciones.Profesional,
 156                TasaDeduccionProfesional = resultadoCalculo.TasaDeduccionProfesional,
 157                TotalDeducciones = resultadoCalculo.TotalDeducciones,
 158                BaseImponibleNeta = resultadoCalculo.BaseImponibleNeta,
 159                TotalIrpf = resultadoCalculo.TotalIrpf
 160            };
 61
 62            // 4. Mapear detalles por franja
 163            var detalleFranjas = resultadoCalculo.DetallePorFranja
 264                .Select(d => new IrpfDetalleFranjas
 265                {
 266                    FranjaIrpfId = d.FranjaId,
 267                    BaseEnFranja = d.BaseFranja,
 268                    Tasa = d.Tasa,
 269                    Impuesto = d.Impuesto
 270                })
 171                .ToList();
 72
 73            // 5. Persistir
 174            var irpfCreado = await _repository.CrearIrpfMensualAsync(irpfMensual, detalleFranjas);
 75
 76            // 6. Auditar (crear entidad BitacoraAuditoria)
 177            await _auditoriaService.LogAuditoriaAsync(
 178                usuarioId: _currentUser.UserId ?? 0,
 179                accion: AccionEnum.CrearIrpf,
 180                contexto: ContextoEnum.IRPF,
 181                entidad: "irpf_mensual",
 182                entidadId: irpfCreado.Id,
 183                detalle: $"LiquidacionId={liquidacionId}, PersonaId={personaId}, TotalIrpf={irpfCreado.TotalIrpf}");
 84
 185            return (true, irpfCreado.Id, null);
 86        }
 087        catch (Exception ex)
 88        {
 089            return (false, null, $"Error al calcular IRPF: {ex.Message}");
 90        }
 291    }
 92
 93    /// <summary>
 94    /// Obtiene un IRPF por ID con sus detalles.
 95    /// </summary>
 96    public async Task<IrpfMensual?> ObtenerPorIdAsync(long id)
 97    {
 098        return await _repository.ObtenerIrpfMensualPorIdAsync(id);
 099    }
 100
 101    /// <summary>
 102    /// Actualiza un IRPF (retroactividad, etc).
 103    /// Recalcula y reemplaza valores y detalles por franja.
 104    /// </summary>
 105    public async Task<(bool Exito, long? IdIrpfMensual, string? Error)> ActualizarAsync(
 106        long id,
 107        CalcularIrpfRequest request)
 108    {
 109        try
 110        {
 111            // 1. Obtener IRPF existente
 0112            var irpfExistente = await _repository.ObtenerIrpfMensualPorIdAsync(id);
 0113            if (irpfExistente == null)
 114            {
 0115                return (false, null, "IRPF no encontrado");
 116            }
 117
 118            // 2. Guardar datos antiguos para auditoría
 0119            var datosAntiguos = $"TotalIrpf={irpfExistente.TotalIrpf}, BaseImponibleNeta={irpfExistente.BaseImponibleNet
 120
 121            // 3. Recalcular
 0122            var resultadoCalculo = _calculator.Calcular(request);
 123
 124            // 4. Actualizar entidad IrpfMensual
 0125            irpfExistente.IngresosGravados = resultadoCalculo.IngresoGravado;
 0126            irpfExistente.DeduccionAportes = resultadoCalculo.Deducciones.Aportes;
 0127            irpfExistente.DeduccionHijos = resultadoCalculo.Deducciones.Hijos;
 0128            irpfExistente.DeduccionConyuge = resultadoCalculo.Deducciones.Conyuge;
 0129            irpfExistente.DeduccionProfesional = resultadoCalculo.Deducciones.Profesional;
 0130            irpfExistente.TasaDeduccionProfesional = resultadoCalculo.TasaDeduccionProfesional;
 0131            irpfExistente.TotalDeducciones = resultadoCalculo.TotalDeducciones;
 0132            irpfExistente.BaseImponibleNeta = resultadoCalculo.BaseImponibleNeta;
 0133            irpfExistente.TotalIrpf = resultadoCalculo.TotalIrpf;
 134
 135            // 5. Mapear nuevos detalles
 0136            var nuevoDetalle = resultadoCalculo.DetallePorFranja
 0137                .Select(d => new IrpfDetalleFranjas
 0138                {
 0139                    FranjaIrpfId = d.FranjaId,
 0140                    BaseEnFranja = d.BaseFranja,
 0141                    Tasa = d.Tasa,
 0142                    Impuesto = d.Impuesto
 0143                })
 0144                .ToList();
 145
 146            // 6. Persistir
 0147            var irpfActualizado = await _repository.ActualizarIrpfMensualAsync(irpfExistente, nuevoDetalle);
 148
 149            // 7. Auditar
 0150            var datosNuevos = $"TotalIrpf={irpfActualizado.TotalIrpf}, BaseImponibleNeta={irpfActualizado.BaseImponibleN
 0151            await _auditoriaService.LogAuditoriaAsync(
 0152                usuarioId: _currentUser.UserId ?? 0,
 0153                accion: AccionEnum.ActualizarIrpf,
 0154                contexto: ContextoEnum.IRPF,
 0155                entidad: "irpf_mensual",
 0156                entidadId: irpfActualizado.Id,
 0157                detalle: new { Anterior = datosAntiguos, Nuevo = datosNuevos });
 158
 0159            return (true, irpfActualizado.Id, null);
 160        }
 0161        catch (Exception ex)
 162        {
 0163            return (false, null, $"Error al actualizar IRPF: {ex.Message}");
 164        }
 0165    }
 166
 167    /// <summary>
 168    /// Obtiene IRPF paginado para una liquidación.
 169    /// </summary>
 170    public async Task<(IEnumerable<IrpfMensual> Items, int TotalCount)> ObtenerPaginadoAsync(
 171        long liquidacionId,
 172        int pagina,
 173        int tamano)
 174    {
 0175        return await _repository.ObtenerIrpfsMensualPorLiquidacionAsync(liquidacionId, pagina, tamano);
 0176    }
 177}
 178