< Summary

Information
Class: FAU.Logica.Services.LiquidacionService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/LiquidacionService.cs
Line coverage
83%
Covered lines: 344
Uncovered lines: 69
Coverable lines: 413
Total lines: 658
Line coverage: 83.2%
Branch coverage
87%
Covered branches: 77
Total branches: 88
Branch coverage: 87.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

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

#LineLine coverage
 1using FAU.DataAccess;
 2using FAU.DataAccess.Repositories;
 3using FAU.Entidades;
 4using FAU.Logica.Calculators;
 5using FAU.Logica.DTOs;
 6using Microsoft.EntityFrameworkCore;
 7
 8namespace FAU.Logica.Services;
 9
 10public class LiquidacionService : ILiquidacionService
 11{
 12    private const int CodigoIrpf = 517;
 13    private const int CodigoMontepioSubalterno = 550;
 14    private const int CodigoSanidad = 520;
 15    private const string CodigoFrl = "FRL";
 16
 17    private readonly ApplicationDbContext _context;
 18    private readonly IAuditoriaService _auditoriaService;
 19    private readonly ICurrentUserContext _currentUser;
 20    private readonly IInsumosPeriodoLoader _insumosLoader;
 21    private readonly IProcesadorBeneficiosService _procesadorBeneficios;
 22    private readonly IDescuentoPersonalRepository _descuentoPersonalRepository;
 23    private readonly IDependienteRepository _dependienteRepository;
 24    private readonly IDeficitCalculator _deficitCalculator;
 25    private readonly IDescuentosLegalesCalculator _descuentosLegalesCalculator;
 26    private readonly IIrpfService _irpfService;
 27
 2028    public LiquidacionService(
 2029        ApplicationDbContext context,
 2030        IAuditoriaService auditoriaService,
 2031        ICurrentUserContext currentUser,
 2032        IInsumosPeriodoLoader insumosLoader,
 2033        IProcesadorBeneficiosService procesadorBeneficios,
 2034        IDescuentoPersonalRepository descuentoPersonalRepository,
 2035        IDependienteRepository dependienteRepository,
 2036        IDeficitCalculator deficitCalculator,
 2037        IDescuentosLegalesCalculator descuentosLegalesCalculator,
 2038        IIrpfService irpfService)
 39    {
 2040        _context = context;
 2041        _auditoriaService = auditoriaService;
 2042        _currentUser = currentUser;
 2043        _insumosLoader = insumosLoader;
 2044        _procesadorBeneficios = procesadorBeneficios;
 2045        _descuentoPersonalRepository = descuentoPersonalRepository;
 2046        _dependienteRepository = dependienteRepository;
 2047        _deficitCalculator = deficitCalculator;
 2048        _descuentosLegalesCalculator = descuentosLegalesCalculator;
 2049        _irpfService = irpfService;
 2050    }
 51
 52    public async Task<(bool Exito, string? Error)> GenerarLiquidacionAsync(long periodoId, int version)
 53    {
 2054        var periodo = await _context.PeriodosLiquidacion
 2055            .AsNoTracking()
 2056            .FirstOrDefaultAsync(p => p.Id == periodoId && p.Activo);
 57
 2058        if (periodo is null)
 159            return (false, "Período de liquidación no encontrado o no está activo");
 60
 1961        if (version <= 0)
 162            return (false, "La versión debe ser mayor a cero");
 63
 1864        var existItems = await _context.LiquidacionItems
 1865            .AnyAsync(li => li.PeriodoId == periodoId && li.Version == version);
 66
 1867        if (existItems)
 168            return (false, "Ya existe una liquidación generada para ese período y versión");
 69
 70        InsumosPeriodo insumos;
 71        try
 72        {
 1773            insumos = await _insumosLoader.CargarAsync(periodoId);
 1774        }
 075        catch (InvalidOperationException ex) when (ex.Message.Contains("arámetro"))
 76        {
 077            return (false, $"No se encontraron parámetros para el período {periodo.Anio}/{periodo.Mes}");
 78        }
 79
 1780        var personas = await _context.RelacionesLaborales
 1781            .AsNoTracking()
 1782            .Where(r => r.Estado == EstadoRelacion.Activo)
 1783            .Join(
 1784                _context.Personas.AsNoTracking(),
 1785                r => r.PersonaId,
 1786                p => p.Id,
 1787                (r, p) => new { p.Id, p.Cedula, r.GradoId, r.RegimenId }
 1788            )
 1789            .Distinct()
 1790            .ToListAsync();
 91
 1792        if (!personas.Any())
 193            return (false, "No se encontraron relaciones laborales activas para generar la liquidación");
 94
 1695        var primerDiaPeriodo = new DateOnly(periodo.Anio, periodo.Mes, 1);
 1696        var itemsToSave = new List<LiquidacionItem>();
 1697        var incidenciasToSave = new List<IncidenciaCalculo>();
 98
 99        // Pre-load historico de primas únicas para todas las personas en una sola consulta
 32100        var cedulasPersonas = personas.Select(p => p.Cedula).ToHashSet();
 16101        var itemsHistoricosGlobal = await _context.LiquidacionItems
 16102            .AsNoTracking()
 16103            .Where(li => cedulasPersonas.Contains(li.Cedula) &&
 16104                         (li.CodigoConcepto == "340" || li.CodigoConcepto == "350" || li.CodigoConcepto == "370"))
 16105            .ToListAsync();
 106
 64107        foreach (var persona in personas)
 108        {
 109            try
 110            {
 16111                var beneficios = insumos.ObtenerBeneficios(persona.Id);
 16112                var itemsHistoricoPersona = itemsHistoricosGlobal
 0113                    .Where(h => h.Cedula == persona.Cedula)
 16114                    .ToList();
 115
 116                // Calcular sueldo base para Prima Traslado:
 117                // sueldo base (código 11000) + compensación de cargo/tropa (42012 oficial, 42022 subalterno)
 16118                var sueldoBasePersona = CalcularSueldoBaseParaTraslado(insumos, persona.GradoId);
 119
 16120                var resultado = _procesadorBeneficios.ProcesarBeneficios(
 16121                    persona.Id,
 16122                    beneficios,
 16123                    insumos.ParametroPeriodo,
 16124                    insumos.CatalogoItems,
 16125                    itemsHistoricoPersona,
 16126                    primerDiaPeriodo,
 16127                    insumos.BeneficiariosPorBeneficio,
 16128                    insumos.InstructivoAsigFamiliar,
 16129                    insumos.InstructivoPsf,
 16130                    insumos.TablaPorHogar,
 16131                    sueldoBaseParaTraslado: sueldoBasePersona);
 132
 16133                var itemsCalculados = resultado.Items.ToList();
 16134                var catalogoPorCodigo = insumos.CatalogoItems
 41135                    .GroupBy(i => i.Codigo)
 98136                    .ToDictionary(g => g.Key, g => g.First());
 137
 16138                var bases = CalcularBasesLiquidacion(itemsCalculados, catalogoPorCodigo);
 16139                var aportes = await CalcularAportesLegalesAsync(persona.RegimenId, bases.BaseSdl, insumos);
 16140                var itemsAportesLegales = CrearItemsAportesLegales(periodoId, persona.Cedula, version, aportes, catalogo
 16141                itemsToSave.AddRange(itemsAportesLegales);
 142
 16143                var resultadoIrpf = await CalcularIrpfAsync(
 16144                    periodoId,
 16145                    persona.Id,
 16146                    persona.Cedula,
 16147                    version,
 16148                    bases.NominalBruto,
 16149                    aportes,
 16150                    insumos,
 16151                    catalogoPorCodigo);
 152
 16153                if (resultadoIrpf.Item is not null)
 3154                    itemsToSave.Add(resultadoIrpf.Item);
 155
 16156                var codigosAExcluir = ObtenerCodigosAExcluirDeDescuentosLegales(itemsAportesLegales, resultadoIrpf.Item)
 16157                var descuentosLegales = CalcularDescuentosLegales(
 16158                    itemsCalculados,
 16159                    catalogoPorCodigo,
 16160                    codigosAExcluir) + aportes.Total + resultadoIrpf.TotalIrpf;
 161
 16162                itemsToSave.AddRange(await CrearItemsDescuentosPersonalesAsync(
 16163                    periodoId,
 16164                    persona.Id,
 16165                    persona.Cedula,
 16166                    version,
 16167                    bases.NominalBruto,
 16168                    descuentosLegales));
 169
 78170                foreach (var item in resultado.Items)
 171                {
 23172                    itemsToSave.Add(new LiquidacionItem
 23173                    {
 23174                        PeriodoId = periodoId,
 23175                        Cedula = persona.Cedula,
 23176                        Version = version,
 23177                        CodigoConcepto = item.CodigoConcepto.ToString(),
 23178                        NombreConcepto = item.NombreConcepto,
 23179                        Importe = item.Importe,
 23180                        RubroContable = item.RubroContable,
 23181                        Rubro083 = false,
 23182                        ClaveEvento = item.ClaveEvento,
 23183                        FechaCreacion = DateTime.UtcNow
 23184                    });
 185                }
 186
 32187                foreach (var incidencia in resultado.Incidencias)
 188                {
 0189                    incidenciasToSave.Add(new IncidenciaCalculo
 0190                    {
 0191                        PeriodoId = periodoId,
 0192                        Version = version,
 0193                        PersonaId = persona.Id,
 0194                        Tipo = incidencia.Tipo,
 0195                        Descripcion = incidencia.Descripcion,
 0196                        CreadoEn = DateTime.UtcNow
 0197                    });
 198                }
 16199            }
 0200            catch (Exception ex)
 201            {
 0202                incidenciasToSave.Add(new IncidenciaCalculo
 0203                {
 0204                    PeriodoId = periodoId,
 0205                    Version = version,
 0206                    PersonaId = persona.Id,
 0207                    Tipo = TipoIncidenciaCalculo.ErrorCalculo,
 0208                    Descripcion = $"Error al calcular beneficios: {ex.Message}",
 0209                    CreadoEn = DateTime.UtcNow
 0210                });
 0211            }
 16212        }
 213
 32214        var estados = personas.Select(p => new LiquidacionEstadoPersonal
 32215        {
 32216            PeriodoId = periodoId,
 32217            Cedula = p.Cedula,
 32218            Version = version,
 32219            Estado = "PENDIENTE",
 32220            FechaActualizacion = DateTime.UtcNow
 32221        }).ToList();
 222
 16223        await _context.LiquidacionEstadosPersonal.AddRangeAsync(estados);
 224
 16225        if (itemsToSave.Count > 0)
 16226            await _context.LiquidacionItems.AddRangeAsync(itemsToSave);
 227
 16228        if (incidenciasToSave.Count > 0)
 0229            await _context.IncidenciasCalculo.AddRangeAsync(incidenciasToSave);
 230
 81231        var totalImporte = itemsToSave.Sum(i => i.Importe);
 16232        var resumen = new LiquidacionResumen
 16233        {
 16234            PeriodoId = periodoId,
 16235            Version = version,
 16236            TotalImporte = totalImporte,
 16237            Rubro083 = false,
 16238            Descripcion = "Liquidación generada"
 16239        };
 16240        await _context.LiquidacionResumenes.AddAsync(resumen);
 241
 16242        await _context.SaveChangesAsync();
 243
 16244        await _auditoriaService.LogAuditoriaAsync(
 16245            _currentUser.UserId ?? 0,
 16246            AccionEnum.GenerarLiquidacion,
 16247            ContextoEnum.Liquidaciones,
 16248            _currentUser.Host,
 16249            entidad: nameof(LiquidacionEstadoPersonal),
 16250            entidadId: periodoId,
 16251            detalle: new { periodoId, version, registros = estados.Count, items = itemsToSave.Count, incidencias = incid
 252
 16253        return (true, null);
 20254    }
 255
 256    public async Task<IEnumerable<CalculoEstadoDto>> ObtenerEstadosAsync(long periodoId, int version)
 257    {
 0258        return await _context.LiquidacionEstadosPersonal
 0259            .AsNoTracking()
 0260            .Where(e => e.PeriodoId == periodoId && e.Version == version)
 0261            .Select(e => new CalculoEstadoDto
 0262            {
 0263                Cedula = e.Cedula,
 0264                Version = e.Version,
 0265                Estado = e.Estado,
 0266                MensajeError = e.MensajeError
 0267            })
 0268            .ToListAsync();
 0269    }
 270
 271    public async Task<IEnumerable<CalculoItemDto>> ObtenerItemsAsync(long periodoId, int version)
 272    {
 0273        return await _context.LiquidacionItems
 0274            .AsNoTracking()
 0275            .Where(i => i.PeriodoId == periodoId && i.Version == version)
 0276            .Select(i => new CalculoItemDto
 0277            {
 0278                Cedula = i.Cedula,
 0279                Version = i.Version,
 0280                CodigoConcepto = i.CodigoConcepto,
 0281                NombreConcepto = i.NombreConcepto,
 0282                Importe = i.Importe,
 0283                RubroContable = i.RubroContable,
 0284                ObjetoGasto = i.ObjetoGasto
 0285            })
 0286            .ToListAsync();
 0287    }
 288
 289    public async Task<IEnumerable<LiquidacionResumenDto>> ObtenerResumenAsync(long periodoId, int version)
 290    {
 0291        return await _context.LiquidacionResumenes
 0292            .AsNoTracking()
 0293            .Where(r => r.PeriodoId == periodoId && r.Version == version)
 0294            .Select(r => new LiquidacionResumenDto
 0295            {
 0296                Version = r.Version,
 0297                ProgramaId = r.ProgramaId,
 0298                RegimenId = r.RegimenId,
 0299                Rubro083 = r.Rubro083,
 0300                TotalImporte = r.TotalImporte,
 0301                Descripcion = r.Descripcion
 0302            })
 0303            .ToListAsync();
 0304    }
 305
 306    private async Task<AportesLegales> CalcularAportesLegalesAsync(long regimenId, decimal baseSdl, InsumosPeriodo insum
 307    {
 16308        var regimen = await _context.Regimenes.AsNoTracking().FirstOrDefaultAsync(r => r.Id == regimenId);
 16309        var tasaMontepio = regimen is not null && regimen.EsLeyVieja
 16310            ? insumos.ParametroPeriodo.TasaMontepioLeyVieja
 16311            : insumos.ParametroPeriodo.TasaMontepioLeyNueva;
 312
 16313        return new AportesLegales(
 16314            _descuentosLegalesCalculator.CalcularMontepio(baseSdl, tasaMontepio),
 16315            _descuentosLegalesCalculator.CalcularFonasa(baseSdl, insumos.ParametroPeriodo.TasaFonasa),
 16316            _descuentosLegalesCalculator.CalcularFrl(baseSdl, insumos.ParametroPeriodo.TasaFrl));
 16317    }
 318
 319    private async Task<ResultadoIrpfLiquidacion> CalcularIrpfAsync(
 320        long periodoId,
 321        long personaId,
 322        string cedula,
 323        int version,
 324        decimal nominalBruto,
 325        AportesLegales aportes,
 326        InsumosPeriodo insumos,
 327        IReadOnlyDictionary<int, CatalogoItem> catalogoPorCodigo)
 328    {
 16329        var request = new CalcularIrpfRequest
 16330        {
 16331            IngresoGravados = nominalBruto,
 16332            Montepio = aportes.Montepio,
 16333            Fonasa = aportes.Fonasa,
 16334            Frl = aportes.Frl,
 16335            Deducciones = await CrearDeduccionesIrpfAsync(personaId),
 16336            Parametros = new ParametrosIrpfDto
 16337            {
 16338                BpcMensual = insumos.ParametroPeriodo.BpcAnual / 12m,
 16339                UmbralDeduccionBpc = insumos.ParametroPeriodo.UmbralDeduccionBpc,
 16340                TasaDeduccionAlta = insumos.ParametroPeriodo.TasaDeduccionAlta,
 16341                TasaDeduccionBaja = insumos.ParametroPeriodo.TasaDeduccionBaja
 16342            },
 16343            Franjas = insumos.FranjasIrpf
 42344                .Select(f => new FranjaIrpfCalculoDto
 42345                {
 42346                    FranjaId = f.Id,
 42347                    NumeroFranja = f.NumeroFranja,
 42348                    DesdeBpc = f.DesdeBpc,
 42349                    HastaBpc = f.HastaBpc,
 42350                    Tasa = f.Tasa
 42351                })
 16352                .ToList()
 16353        };
 354
 16355        var (exito, idIrpf, _) = await _irpfService.CalcularYPersistirAsync(periodoId, personaId, request);
 16356        if (!exito || !idIrpf.HasValue)
 12357            return new ResultadoIrpfLiquidacion(0m, null);
 358
 4359        var irpf = await _irpfService.ObtenerPorIdAsync(idIrpf.Value);
 4360        if (irpf is null)
 0361            return new ResultadoIrpfLiquidacion(0m, null);
 362
 4363        var item = catalogoPorCodigo.TryGetValue(CodigoIrpf, out var catalogoIrpf)
 4364            ? CrearItemLiquidacion(periodoId, cedula, version, catalogoIrpf, -irpf.TotalIrpf)
 4365            : null;
 366
 4367        return new ResultadoIrpfLiquidacion(irpf.TotalIrpf, item);
 16368    }
 369
 370    private async Task<List<DeduccionDto>> CrearDeduccionesIrpfAsync(long personaId)
 371    {
 16372        var dependientes = await _dependienteRepository.GetDependientesByPersonaAsync(personaId);
 373
 16374        return dependientes
 3375            .Where(d => d.Activo)
 16376            .SelectMany(d =>
 16377            {
 3378                if (string.Equals(d.Tipo, "conyuge_sin_ingresos", StringComparison.OrdinalIgnoreCase))
 1379                    return [new DeduccionDto { Tipo = "conyuge_sin_ingresos", Cantidad = 1 }];
 16380
 2381                if (string.Equals(d.Tipo, "hijo", StringComparison.OrdinalIgnoreCase))
 16382                {
 2383                    var tipoHijo = d.Discapacitado ? "hijo_discapacidad" : "hijo_menor";
 2384                    return [new DeduccionDto { Tipo = tipoHijo, Cantidad = 1 }];
 16385                }
 16386
 0387                return Array.Empty<DeduccionDto>();
 16388            })
 16389            .ToList();
 16390    }
 391
 392    private async Task<IReadOnlyList<LiquidacionItem>> CrearItemsDescuentosPersonalesAsync(
 393        long periodoId,
 394        long personaId,
 395        string cedula,
 396        int version,
 397        decimal nominalBruto,
 398        decimal descuentosLegales)
 399    {
 16400        var descuentosConfirmados = (await _descuentoPersonalRepository
 16401                .GetConfirmadosPorPersonaAsync(periodoId, personaId))
 9402            .Where(d => d.CatalogoItem is not null)
 16403            .ToList();
 404
 16405        if (descuentosConfirmados.Count == 0)
 8406            return Array.Empty<LiquidacionItem>();
 407
 8408        var entradasDeficit = descuentosConfirmados
 9409            .Select(d => CrearEntradaDeficit(d, personaId))
 8410            .ToList();
 411
 8412        var resultadosDeficit = await _deficitCalculator.AplicarAsync(
 8413            nominalBruto,
 8414            descuentosLegales,
 8415            entradasDeficit);
 416
 17417        var descuentosPorId = descuentosConfirmados.ToDictionary(d => d.Id);
 8418        var items = new List<LiquidacionItem>();
 419
 34420        foreach (var resultadoDeficit in resultadosDeficit)
 421        {
 9422            if (resultadoDeficit.DescuentoPeriodoId is null || resultadoDeficit.MontoAplicado <= 0m)
 423                continue;
 424
 9425            if (!descuentosPorId.TryGetValue(resultadoDeficit.DescuentoPeriodoId.Value, out var descuento))
 426                continue;
 427
 9428            items.Add(CrearItemLiquidacion(
 9429                periodoId,
 9430                cedula,
 9431                version,
 9432                descuento.CatalogoItem,
 9433                -resultadoDeficit.MontoAplicado));
 434        }
 435
 8436        return items;
 16437    }
 438
 439    private static DescuentoPersonalInput CrearEntradaDeficit(DescuentoPersonalPeriodo descuento, long personaId)
 440    {
 9441        if (descuento.AcreedorId is null)
 0442            throw new InvalidOperationException($"El descuento personal {descuento.Id} no tiene acreedor asignado.");
 443
 9444        if (descuento.Acreedor is null)
 0445            throw new InvalidOperationException($"El descuento personal {descuento.Id} no tiene acreedor cargado.");
 446
 9447        return new DescuentoPersonalInput(
 9448            descuento.Id,
 9449            personaId,
 9450            descuento.CatalogoItem.Codigo,
 9451            descuento.Importe,
 9452            descuento.AcreedorId.Value,
 9453            descuento.Acreedor.PrioridadDeficit);
 454    }
 455
 456    private static BasesLiquidacion CalcularBasesLiquidacion(
 457        IReadOnlyList<ItemBeneficioCalculado> items,
 458        IReadOnlyDictionary<int, CatalogoItem> catalogoPorCodigo)
 459    {
 16460        return new BasesLiquidacion(
 16461            CalcularNominalBruto(items),
 16462            CalcularBaseSdl(items, catalogoPorCodigo));
 463    }
 464
 465    private static decimal CalcularBaseSdl(
 466        IEnumerable<ItemBeneficioCalculado> items,
 467        IReadOnlyDictionary<int, CatalogoItem> catalogoPorCodigo)
 468    {
 16469        return items
 23470            .Where(i => i.Importe > 0m && catalogoPorCodigo.TryGetValue(i.CodigoConcepto, out var catalogoItem) &&
 23471                        (catalogoItem.EsGrabado || catalogoItem.ContribuyeWNominal))
 32472            .Sum(i => i.Importe);
 473    }
 474
 475    private static IReadOnlyList<LiquidacionItem> CrearItemsAportesLegales(
 476        long periodoId,
 477        string cedula,
 478        int version,
 479        AportesLegales aportes,
 480        IReadOnlyDictionary<int, CatalogoItem> catalogoPorCodigo)
 481    {
 16482        var items = new List<LiquidacionItem>();
 483
 16484        AgregarItemAporteLegal(
 16485            items,
 16486            periodoId,
 16487            cedula,
 16488            version,
 16489            aportes.Montepio,
 16490            BuscarCatalogoItem(catalogoPorCodigo, CodigoMontepioSubalterno, 545, 555),
 16491            codigoFallback: CodigoMontepioSubalterno.ToString(),
 16492            nombreFallback: "MONTEPIO",
 16493            rubroFallback: "5.04");
 494
 16495        AgregarItemAporteLegal(
 16496            items,
 16497            periodoId,
 16498            cedula,
 16499            version,
 16500            aportes.Fonasa,
 16501            BuscarCatalogoItem(catalogoPorCodigo, CodigoSanidad, 521, 522, 523, 524, 525, 526, 527, 528),
 16502            codigoFallback: CodigoSanidad.ToString(),
 16503            nombreFallback: "FONASA",
 16504            rubroFallback: "5.010");
 505
 16506        AgregarItemAporteLegal(
 16507            items,
 16508            periodoId,
 16509            cedula,
 16510            version,
 16511            aportes.Frl,
 16512            null,
 16513            codigoFallback: CodigoFrl,
 16514            nombreFallback: "FRL",
 16515            rubroFallback: "000.000");
 516
 16517        return items;
 518    }
 519
 520    private static void AgregarItemAporteLegal(
 521        ICollection<LiquidacionItem> items,
 522        long periodoId,
 523        string cedula,
 524        int version,
 525        decimal importe,
 526        CatalogoItem? catalogoItem,
 527        string codigoFallback,
 528        string nombreFallback,
 529        string rubroFallback)
 530    {
 48531        if (importe <= 0m)
 18532            return;
 533
 30534        if (catalogoItem is not null)
 535        {
 7536            items.Add(CrearItemLiquidacion(periodoId, cedula, version, catalogoItem, -importe));
 7537            return;
 538        }
 539
 23540        items.Add(CrearItemLiquidacion(periodoId, cedula, version, codigoFallback, nombreFallback, -importe, rubroFallba
 23541    }
 542
 543    private static CatalogoItem? BuscarCatalogoItem(
 544        IReadOnlyDictionary<int, CatalogoItem> catalogoPorCodigo,
 545        params int[] codigos)
 546    {
 385547        foreach (var codigo in codigos)
 548        {
 164549            if (catalogoPorCodigo.TryGetValue(codigo, out var catalogoItem))
 7550                return catalogoItem;
 551        }
 552
 25553        return null;
 554    }
 555
 556    private static LiquidacionItem CrearItemLiquidacion(
 557        long periodoId,
 558        string cedula,
 559        int version,
 560        CatalogoItem catalogoItem,
 561        decimal importe)
 562    {
 19563        return CrearItemLiquidacion(
 19564            periodoId,
 19565            cedula,
 19566            version,
 19567            catalogoItem.Codigo.ToString(),
 19568            catalogoItem.Nombre,
 19569            importe,
 19570            catalogoItem.ClasificacionSiif ?? "000.000");
 571    }
 572
 573    private static LiquidacionItem CrearItemLiquidacion(
 574        long periodoId,
 575        string cedula,
 576        int version,
 577        string codigoConcepto,
 578        string nombreConcepto,
 579        decimal importe,
 580        string rubroContable)
 581    {
 42582        return new LiquidacionItem
 42583        {
 42584            PeriodoId = periodoId,
 42585            Cedula = cedula,
 42586            Version = version,
 42587            CodigoConcepto = codigoConcepto,
 42588            NombreConcepto = nombreConcepto,
 42589            Importe = importe,
 42590            RubroContable = rubroContable,
 42591            Rubro083 = false,
 42592            FechaCreacion = DateTime.UtcNow
 42593        };
 594    }
 595
 596    private static decimal CalcularNominalBruto(IEnumerable<ItemBeneficioCalculado> items)
 597    {
 16598        return items
 23599            .Where(i => i.Importe > 0m)
 35600            .Sum(i => i.Importe);
 601    }
 602
 603    private static decimal CalcularDescuentosLegales(
 604        IEnumerable<ItemBeneficioCalculado> items,
 605        IReadOnlyDictionary<int, CatalogoItem> catalogoPorCodigo,
 606        IReadOnlySet<string> codigosAExcluir)
 607    {
 16608        return items
 23609            .Where(i => catalogoPorCodigo.TryGetValue(i.CodigoConcepto, out var catalogoItem) &&
 23610                        string.Equals(catalogoItem.Tipo, "descuento_legal", StringComparison.OrdinalIgnoreCase) &&
 23611                        !codigosAExcluir.Contains(i.CodigoConcepto.ToString()))
 19612            .Sum(i => Math.Abs(i.Importe));
 613    }
 614
 615    private static IReadOnlySet<string> ObtenerCodigosAExcluirDeDescuentosLegales(
 616        IEnumerable<LiquidacionItem> itemsAportesLegales,
 617        LiquidacionItem? itemIrpf)
 618    {
 16619        var codigos = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 620
 92621        foreach (var item in itemsAportesLegales)
 622        {
 30623            if (!string.IsNullOrWhiteSpace(item.CodigoConcepto))
 30624                codigos.Add(item.CodigoConcepto);
 625        }
 626
 16627        if (!string.IsNullOrWhiteSpace(itemIrpf?.CodigoConcepto))
 3628            codigos.Add(itemIrpf.CodigoConcepto);
 629
 16630        return codigos;
 631    }
 632
 64633    private sealed record BasesLiquidacion(decimal NominalBruto, decimal BaseSdl);
 634
 160635    private sealed record AportesLegales(decimal Montepio, decimal Fonasa, decimal Frl)
 636    {
 16637        public decimal Total => Montepio + Fonasa + Frl;
 638    }
 639
 67640    private sealed record ResultadoIrpfLiquidacion(decimal TotalIrpf, LiquidacionItem? Item);
 641
 642    // Sueldo Base + Compensación para Prima Traslado
 643    // Oficial (grados 01-09): sueldo 11000 + compensación cargo 42012
 644    // Subalterno (grados 10-17): sueldo 11000 + compensación tropa 42022
 645    private static decimal CalcularSueldoBaseParaTraslado(InsumosPeriodo insumos, long gradoId)
 646    {
 647        const int CodigoSueldoBase    = 11000;
 648        const int CodigoCompOficial    = 42012;
 649        const int CodigoCompSubalterno = 42022;
 650
 16651        var sueldoBase = insumos.ObtenerRemuneracion(gradoId, CodigoSueldoBase)?.Monto ?? 0m;
 16652        var compOficial    = insumos.ObtenerRemuneracion(gradoId, CodigoCompOficial)?.Monto ?? 0m;
 16653        var compSubalterno = insumos.ObtenerRemuneracion(gradoId, CodigoCompSubalterno)?.Monto ?? 0m;
 654
 655        // Toma la compensación que corresponda según el grado (solo uno de los dos tendrá valor)
 16656        return sueldoBase + compOficial + compSubalterno;
 657    }
 658}

Methods/Properties

.ctor(FAU.DataAccess.ApplicationDbContext,FAU.Logica.Services.IAuditoriaService,FAU.Logica.Services.ICurrentUserContext,FAU.Logica.Services.IInsumosPeriodoLoader,FAU.Logica.Services.IProcesadorBeneficiosService,FAU.DataAccess.Repositories.IDescuentoPersonalRepository,FAU.DataAccess.Repositories.IDependienteRepository,FAU.Logica.Calculators.IDeficitCalculator,FAU.Logica.Services.IDescuentosLegalesCalculator,FAU.Logica.Services.IIrpfService)
GenerarLiquidacionAsync()
ObtenerEstadosAsync()
ObtenerItemsAsync()
ObtenerResumenAsync()
CalcularAportesLegalesAsync()
CalcularIrpfAsync()
CrearDeduccionesIrpfAsync()
CrearItemsDescuentosPersonalesAsync()
CrearEntradaDeficit(FAU.Entidades.DescuentoPersonalPeriodo,System.Int64)
CalcularBasesLiquidacion(System.Collections.Generic.IReadOnlyList`1<FAU.Logica.DTOs.ItemBeneficioCalculado>,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,FAU.Entidades.CatalogoItem>)
CalcularBaseSdl(System.Collections.Generic.IEnumerable`1<FAU.Logica.DTOs.ItemBeneficioCalculado>,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,FAU.Entidades.CatalogoItem>)
CrearItemsAportesLegales(System.Int64,System.String,System.Int32,FAU.Logica.Services.LiquidacionService/AportesLegales,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,FAU.Entidades.CatalogoItem>)
AgregarItemAporteLegal(System.Collections.Generic.ICollection`1<FAU.Entidades.LiquidacionItem>,System.Int64,System.String,System.Int32,System.Decimal,FAU.Entidades.CatalogoItem,System.String,System.String,System.String)
BuscarCatalogoItem(System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,FAU.Entidades.CatalogoItem>,System.Int32[])
CrearItemLiquidacion(System.Int64,System.String,System.Int32,FAU.Entidades.CatalogoItem,System.Decimal)
CrearItemLiquidacion(System.Int64,System.String,System.Int32,System.String,System.String,System.Decimal,System.String)
CalcularNominalBruto(System.Collections.Generic.IEnumerable`1<FAU.Logica.DTOs.ItemBeneficioCalculado>)
CalcularDescuentosLegales(System.Collections.Generic.IEnumerable`1<FAU.Logica.DTOs.ItemBeneficioCalculado>,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,FAU.Entidades.CatalogoItem>,System.Collections.Generic.IReadOnlySet`1<System.String>)
ObtenerCodigosAExcluirDeDescuentosLegales(System.Collections.Generic.IEnumerable`1<FAU.Entidades.LiquidacionItem>,FAU.Entidades.LiquidacionItem)
get_NominalBruto()
get_Montepio()
get_Total()
get_TotalIrpf()
CalcularSueldoBaseParaTraslado(FAU.Logica.DTOs.InsumosPeriodo,System.Int64)