< Summary

Information
Class: FAU.Logica.Services.PeriodoService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/PeriodoService.cs
Line coverage
94%
Covered lines: 177
Uncovered lines: 10
Coverable lines: 187
Total lines: 277
Line coverage: 94.6%
Branch coverage
64%
Covered branches: 18
Total branches: 28
Branch coverage: 64.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
AbrirPeriodoAsync()64.28%292889.74%
ObtenerActivoAsync()100%210%
ObtenerPorIdAsync(...)100%210%
SerializarSnapshotAsync()100%11100%
CalcularHash(...)100%11100%

File(s)

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

#LineLine coverage
 1using System.Security.Cryptography;
 2using System.Text;
 3using System.Text.Json;
 4using FAU.DataAccess;
 5using FAU.DataAccess.Repositories;
 6using FAU.Entidades;
 7using Microsoft.EntityFrameworkCore;
 8
 9namespace FAU.Logica.Services;
 10
 11public class PeriodoService : IPeriodoService
 12{
 13    private readonly IPeriodosRepository _periodosRepository;
 14    private readonly ISnapshotsRepository _snapshotsRepository;
 15    private readonly ApplicationDbContext _context;
 16    private readonly IAuditoriaService _auditoriaService;
 17    private readonly ICurrentUserContext _currentUser;
 18
 419    public PeriodoService(
 420        IPeriodosRepository periodosRepository,
 421        ISnapshotsRepository snapshotsRepository,
 422        ApplicationDbContext context,
 423        IAuditoriaService auditoriaService,
 424        ICurrentUserContext currentUser)
 25    {
 426        _periodosRepository = periodosRepository;
 427        _snapshotsRepository = snapshotsRepository;
 428        _context = context;
 429        _auditoriaService = auditoriaService;
 430        _currentUser = currentUser;
 431    }
 32
 33    public async Task<(bool Exito, Periodo? Periodo, string? Error)> AbrirPeriodoAsync(short anio, short mes)
 34    {
 535        if (anio < 2000 || anio > 2100)
 36        {
 037            return (false, null, "El año debe estar entre 2000 y 2100");
 38        }
 39
 540        if (mes < 1 || mes > 12)
 41        {
 042            return (false, null, "El mes debe estar entre 1 y 12");
 43        }
 44
 545        var periodoExistente = await _periodosRepository.ObtenerPorAnioMesAsync(anio, mes);
 546        if (periodoExistente is not null && periodoExistente.Estado == "ABIERTA" && periodoExistente.Activo)
 47        {
 148            return (true, periodoExistente, null);
 49        }
 50
 451        var periodoActivo = await _periodosRepository.ObtenerActivoAsync();
 452        if (periodoActivo is not null)
 53        {
 054            return (false, null, "Ya existe un período abierto");
 55        }
 56
 457        var parametrosPeriodo = await _context.ParametrosPeriodo
 458            .AsNoTracking()
 459            .FirstOrDefaultAsync(p => p.Anio == anio && p.Mes == mes);
 60
 461        if (parametrosPeriodo is null)
 62        {
 163            return (false, null, "No existen parámetros de liquidación para el período");
 64        }
 65
 366        var fechaInicioPeriodo = new DateTime(anio, mes, 1);
 367        var fechaFinPeriodo = fechaInicioPeriodo.AddMonths(1).AddTicks(-1);
 68
 369        var tieneMovimientosLaborales = await _context.MovimientosLaborales
 370            .AsNoTracking()
 371            .AnyAsync(m => m.FechaMovimiento.Year == anio && m.FechaMovimiento.Month == mes);
 72
 373        if (tieneMovimientosLaborales)
 74        {
 075            return (false, null, "No se puede abrir el período porque ya existen novedades laborales para ese mes");
 76        }
 77
 378        var existeLoteAprobado = await _context.LotesCompensacion
 379            .AsNoTracking()
 380            .AnyAsync(l => l.Periodo.Year == anio && l.Periodo.Month == mes && l.Estado == "aprobado");
 81
 382        if (!existeLoteAprobado)
 83        {
 084            return (false, null, "Debe existir al menos un lote de compensación aprobado para el período");
 85        }
 86
 387        var existeBeneficio = await _context.BeneficiosSociales
 388            .AsNoTracking()
 389            .AnyAsync(b => b.Estado == "activo" && b.VigenteDesde <= fechaFinPeriodo && (b.VigenteHasta == null || b.Vig
 90
 391        if (!existeBeneficio)
 92        {
 093            return (false, null, "Debe existir al menos un beneficio social vigente para el período");
 94        }
 95
 396        var existeRemuneraciones = await _context.RemuneracionesGrado
 397            .AsNoTracking()
 398            .AnyAsync(r => r.VigenteDesde <= fechaInicioPeriodo && (r.VigenteHasta == null || r.VigenteHasta >= fechaIni
 99
 3100        if (!existeRemuneraciones)
 101        {
 0102            return (false, null, "No existen tablas de remuneración vigentes para el período");
 103        }
 104
 3105        var existeFranjaIrpf = await _context.FranjasIrpf
 3106            .AsNoTracking()
 3107            .AnyAsync(f => f.VigenteDesde <= fechaInicioPeriodo && (f.VigenteHasta == null || f.VigenteHasta >= fechaIni
 108
 3109        if (!existeFranjaIrpf)
 110        {
 0111            return (false, null, "No existen franjas IRPF vigentes para el período");
 112        }
 113
 3114        var snapshotJson = await SerializarSnapshotAsync(anio, mes, parametrosPeriodo, fechaInicioPeriodo, fechaFinPerio
 3115        var hashSnapshot = CalcularHash(snapshotJson);
 116
 3117        var snapshot = new SnapshotInsumos
 3118        {
 3119            ContenidoJson = snapshotJson,
 3120            HashIntegridad = hashSnapshot
 3121        };
 122
 3123        var snapshotCreado = await _snapshotsRepository.CrearAsync(snapshot);
 124
 3125        var periodo = new Periodo
 3126        {
 3127            Anio = anio,
 3128            Mes = mes,
 3129            Estado = "ABIERTA",
 3130            SnapshotId = snapshotCreado.Id,
 3131            FechaApertura = DateTime.UtcNow,
 3132            UsuarioApertura = _currentUser.UserId ?? 0,
 3133            HashSnapshot = hashSnapshot,
 3134            Activo = true
 3135        };
 136
 3137        var periodoCreado = await _periodosRepository.CrearAsync(periodo);
 138
 3139        await _auditoriaService.LogAuditoriaAsync(
 3140            _currentUser.UserId ?? 0,
 3141            AccionEnum.AbrirPeriodoLiquidacion,
 3142            ContextoEnum.Liquidaciones,
 3143            _currentUser.Host,
 3144            entidad: nameof(Periodo),
 3145            entidadId: periodoCreado.Id,
 3146            detalle: new
 3147            {
 3148                anio,
 3149                mes,
 3150                snapshotId = snapshotCreado.Id,
 3151                hashSnapshot
 3152            });
 153
 3154        return (true, periodoCreado, null);
 5155    }
 156
 157    public Task<Periodo?> ObtenerActivoAsync()
 158    {
 0159        return _periodosRepository.ObtenerActivoAsync();
 160    }
 161
 162    public Task<Periodo?> ObtenerPorIdAsync(long id)
 163    {
 0164        return _periodosRepository.ObtenerPorIdAsync(id);
 165    }
 166
 167    private async Task<string> SerializarSnapshotAsync(
 168        short anio,
 169        short mes,
 170        ParametroPeriodo parametrosPeriodo,
 171        DateTime fechaInicioPeriodo,
 172        DateTime fechaFinPeriodo)
 173    {
 3174        var lotes = await _context.LotesCompensacion
 3175            .AsNoTracking()
 3176            .Where(l => l.Periodo.Year == anio && l.Periodo.Month == mes && l.Estado == "aprobado")
 3177            .Select(l => new
 3178            {
 3179                l.Id,
 3180                l.TipoCompensacionId,
 3181                l.Periodo,
 3182                l.Estado,
 3183                l.Fuente,
 3184                l.NombreArchivo,
 3185                l.HashArchivo,
 3186                l.UsuarioId,
 3187                l.AprobadoPor,
 3188                l.AprobadoEn
 3189            })
 3190            .ToListAsync();
 191
 3192        var beneficios = await _context.BeneficiosSociales
 3193            .AsNoTracking()
 3194            .Where(b => b.Estado == "activo" && b.VigenteDesde <= fechaFinPeriodo && (b.VigenteHasta == null || b.Vigent
 3195            .Select(b => new
 3196            {
 3197                b.Id,
 3198                b.PersonaId,
 3199                b.TipoBeneficioId,
 3200                b.Cantidad,
 3201                b.VigenteDesde,
 3202                b.VigenteHasta,
 3203                b.Estado,
 3204                b.ClaveEvento,
 3205                b.FechaEvento,
 3206                b.RetroactividadPendiente
 3207            })
 3208            .ToListAsync();
 209
 3210        var remuneraciones = await _context.RemuneracionesGrado
 3211            .AsNoTracking()
 3212            .Where(r => r.VigenteDesde <= fechaInicioPeriodo && (r.VigenteHasta == null || r.VigenteHasta >= fechaInicio
 3213            .Select(r => new
 3214            {
 3215                r.Id,
 3216                r.GradoId,
 3217                r.ItemId,
 3218                r.Monto,
 3219                r.VigenteDesde,
 3220                r.VigenteHasta
 3221            })
 3222            .ToListAsync();
 223
 3224        var franjasIrpf = await _context.FranjasIrpf
 3225            .AsNoTracking()
 3226            .Where(f => f.VigenteDesde <= fechaInicioPeriodo && (f.VigenteHasta == null || f.VigenteHasta >= fechaInicio
 3227            .Select(f => new
 3228            {
 3229                f.Id,
 3230                f.NumeroFranja,
 3231                f.DesdeBpc,
 3232                f.HastaBpc,
 3233                f.Tasa,
 3234                f.VigenteDesde,
 3235                f.VigenteHasta
 3236            })
 3237            .ToListAsync();
 238
 3239        var snapshot = new
 3240        {
 3241            anio,
 3242            mes,
 3243            fechaCreacion = DateTime.UtcNow,
 3244            parametros = new
 3245            {
 3246                parametrosPeriodo.Anio,
 3247                parametrosPeriodo.Mes,
 3248                parametrosPeriodo.BpcAnual,
 3249                parametrosPeriodo.TasaMontepioLeyVieja,
 3250                parametrosPeriodo.TasaMontepioLeyNueva,
 3251                parametrosPeriodo.TasaFonasa,
 3252                parametrosPeriodo.TasaFrl,
 3253                parametrosPeriodo.UmbralDeduccionBpc,
 3254                parametrosPeriodo.TasaDeduccionAlta,
 3255                parametrosPeriodo.TasaDeduccionBaja,
 3256                parametrosPeriodo.PorcentajeMinimoDeficit
 3257            },
 3258            lotesCompensacion = lotes,
 3259            beneficiosSociales = beneficios,
 3260            remuneracionesGrado = remuneraciones,
 3261            franjasIrpf = franjasIrpf
 3262        };
 263
 3264        return JsonSerializer.Serialize(snapshot, new JsonSerializerOptions
 3265        {
 3266            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
 3267            WriteIndented = false
 3268        });
 3269    }
 270
 271    private static string CalcularHash(string contenidoJson)
 272    {
 3273        using var sha256 = SHA256.Create();
 3274        var bytes = Encoding.UTF8.GetBytes(contenidoJson);
 3275        return Convert.ToHexString(sha256.ComputeHash(bytes)).ToLowerInvariant();
 3276    }
 277}