< 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: 47
Uncovered lines: 42
Coverable lines: 89
Total lines: 181
Line coverage: 52.8%
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.75%
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 que no exista ya un IRPF para esta persona en esta liquidación
 240            var existe = await _repository.ExisteIrpfMensualAsync(liquidacionId, personaId);
 241            if (existe)
 42            {
 143                return (false, null, "Ya existe un cálculo IRPF para esta persona en esta liquidación");
 44            }
 45
 46            // 2. Calcular
 147            var resultadoCalculo = _calculator.Calcular(request);
 48
 49            // 3. Mapear resultado a entidad IrpfMensual
 150            var irpfMensual = new IrpfMensual
 151            {
 152                LiquidacionId = liquidacionId,
 153                PersonaId = personaId,
 154                IngresosGravados = resultadoCalculo.IngresoGravado,
 155                DeduccionAportes = resultadoCalculo.Deducciones.Aportes,
 156                DeduccionHijos = resultadoCalculo.Deducciones.Hijos,
 157                DeduccionConyuge = resultadoCalculo.Deducciones.Conyuge,
 158                DeduccionProfesional = resultadoCalculo.Deducciones.Profesional,
 159                TasaDeduccionProfesional = resultadoCalculo.TasaDeduccionProfesional,
 160                TotalDeducciones = resultadoCalculo.TotalDeducciones,
 161                BaseImponibleNeta = resultadoCalculo.BaseImponibleNeta,
 162                TotalIrpf = resultadoCalculo.TotalIrpf
 163            };
 64
 65            // 4. Mapear detalles por franja
 166            var detalleFranjas = resultadoCalculo.DetallePorFranja
 267                .Select(d => new IrpfDetalleFranjas
 268                {
 269                    FranjaIrpfId = d.FranjaId,
 270                    BaseEnFranja = d.BaseFranja,
 271                    Tasa = d.Tasa,
 272                    Impuesto = d.Impuesto
 273                })
 174                .ToList();
 75
 76            // 5. Persistir
 177            var irpfCreado = await _repository.CrearIrpfMensualAsync(irpfMensual, detalleFranjas);
 78
 79            // 6. Auditar (crear entidad BitacoraAuditoria)
 180            await _auditoriaService.LogAuditoriaAsync(
 181                usuarioId: _currentUser.UserId ?? 0,
 182                accion: AccionEnum.CrearIrpf,
 183                contexto: ContextoEnum.IRPF,
 184                entidad: "irpf_mensual",
 185                entidadId: irpfCreado.Id,
 186                detalle: $"LiquidacionId={liquidacionId}, PersonaId={personaId}, TotalIrpf={irpfCreado.TotalIrpf}");
 87
 188            return (true, irpfCreado.Id, null);
 89        }
 090        catch (Exception ex)
 91        {
 092            return (false, null, $"Error al calcular IRPF: {ex.Message}");
 93        }
 294    }
 95
 96    /// <summary>
 97    /// Obtiene un IRPF por ID con sus detalles.
 98    /// </summary>
 99    public async Task<IrpfMensual?> ObtenerPorIdAsync(long id)
 100    {
 0101        return await _repository.ObtenerIrpfMensualPorIdAsync(id);
 0102    }
 103
 104    /// <summary>
 105    /// Actualiza un IRPF (retroactividad, etc).
 106    /// Recalcula y reemplaza valores y detalles por franja.
 107    /// </summary>
 108    public async Task<(bool Exito, long? IdIrpfMensual, string? Error)> ActualizarAsync(
 109        long id,
 110        CalcularIrpfRequest request)
 111    {
 112        try
 113        {
 114            // 1. Obtener IRPF existente
 0115            var irpfExistente = await _repository.ObtenerIrpfMensualPorIdAsync(id);
 0116            if (irpfExistente == null)
 117            {
 0118                return (false, null, "IRPF no encontrado");
 119            }
 120
 121            // 2. Guardar datos antiguos para auditoría
 0122            var datosAntiguos = $"TotalIrpf={irpfExistente.TotalIrpf}, BaseImponibleNeta={irpfExistente.BaseImponibleNet
 123
 124            // 3. Recalcular
 0125            var resultadoCalculo = _calculator.Calcular(request);
 126
 127            // 4. Actualizar entidad IrpfMensual
 0128            irpfExistente.IngresosGravados = resultadoCalculo.IngresoGravado;
 0129            irpfExistente.DeduccionAportes = resultadoCalculo.Deducciones.Aportes;
 0130            irpfExistente.DeduccionHijos = resultadoCalculo.Deducciones.Hijos;
 0131            irpfExistente.DeduccionConyuge = resultadoCalculo.Deducciones.Conyuge;
 0132            irpfExistente.DeduccionProfesional = resultadoCalculo.Deducciones.Profesional;
 0133            irpfExistente.TasaDeduccionProfesional = resultadoCalculo.TasaDeduccionProfesional;
 0134            irpfExistente.TotalDeducciones = resultadoCalculo.TotalDeducciones;
 0135            irpfExistente.BaseImponibleNeta = resultadoCalculo.BaseImponibleNeta;
 0136            irpfExistente.TotalIrpf = resultadoCalculo.TotalIrpf;
 137
 138            // 5. Mapear nuevos detalles
 0139            var nuevoDetalle = resultadoCalculo.DetallePorFranja
 0140                .Select(d => new IrpfDetalleFranjas
 0141                {
 0142                    FranjaIrpfId = d.FranjaId,
 0143                    BaseEnFranja = d.BaseFranja,
 0144                    Tasa = d.Tasa,
 0145                    Impuesto = d.Impuesto
 0146                })
 0147                .ToList();
 148
 149            // 6. Persistir
 0150            var irpfActualizado = await _repository.ActualizarIrpfMensualAsync(irpfExistente, nuevoDetalle);
 151
 152            // 7. Auditar
 0153            var datosNuevos = $"TotalIrpf={irpfActualizado.TotalIrpf}, BaseImponibleNeta={irpfActualizado.BaseImponibleN
 0154            await _auditoriaService.LogAuditoriaAsync(
 0155                usuarioId: _currentUser.UserId ?? 0,
 0156                accion: AccionEnum.ActualizarIrpf,
 0157                contexto: ContextoEnum.IRPF,
 0158                entidad: "irpf_mensual",
 0159                entidadId: irpfActualizado.Id,
 0160                detalle: new { Anterior = datosAntiguos, Nuevo = datosNuevos });
 161
 0162            return (true, irpfActualizado.Id, null);
 163        }
 0164        catch (Exception ex)
 165        {
 0166            return (false, null, $"Error al actualizar IRPF: {ex.Message}");
 167        }
 0168    }
 169
 170    /// <summary>
 171    /// Obtiene IRPF paginado para una liquidación.
 172    /// </summary>
 173    public async Task<(IEnumerable<IrpfMensual> Items, int TotalCount)> ObtenerPaginadoAsync(
 174        long liquidacionId,
 175        int pagina,
 176        int tamano)
 177    {
 0178        return await _repository.ObtenerIrpfsMensualPorLiquidacionAsync(liquidacionId, pagina, tamano);
 0179    }
 180}
 181