< Summary

Information
Class: FAU.DataAccess.Repositories.ReciboRepository
Assembly: FAU.DataAccess
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.DataAccess/Repositories/ReciboRepository.cs
Line coverage
70%
Covered lines: 138
Uncovered lines: 57
Coverable lines: 195
Total lines: 269
Line coverage: 70.7%
Branch coverage
46%
Covered branches: 48
Total branches: 104
Branch coverage: 46.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ObtenerVersionMaximaAsync()100%210%
ObtenerResumenesAsync()0%156120%
ObtenerDatosReciboAsync()59.21%767698.3%
EsDescuentoLegalParaRecibo(...)66.66%66100%
EsFictoParaRecibo(...)50%22100%
get_Tipo()100%11100%

File(s)

/home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.DataAccess/Repositories/ReciboRepository.cs

#LineLine coverage
 1using FAU.Entidades;
 2using Microsoft.EntityFrameworkCore;
 3
 4namespace FAU.DataAccess.Repositories;
 5
 6public class ReciboRepository : IReciboRepository
 7{
 8    private readonly ApplicationDbContext _context;
 9
 110    public ReciboRepository(ApplicationDbContext context)
 11    {
 112        _context = context;
 113    }
 14
 15    public async Task<int> ObtenerVersionMaximaAsync(long periodoId)
 16    {
 017        return await _context.LiquidacionItems
 018            .AsNoTracking()
 019            .Where(i => i.PeriodoId == periodoId)
 020            .MaxAsync(i => (int?)i.Version) ?? 0;
 021    }
 22
 23    public async Task<List<ReciboResumenDto>> ObtenerResumenesAsync(long periodoId, int version, ReciboFiltroRequest fil
 24    {
 025        var query = _context.RelacionesLaborales
 026            .AsNoTracking()
 027            .Include(r => r.Persona)
 028                .ThenInclude(p => p.CuentasBancarias)
 029                    .ThenInclude(c => c.Banco)
 030            .Include(r => r.Regimen)
 031            .Include(r => r.Programa)
 032            .Include(r => r.Unidad)
 033            .Where(r => r.Estado == EstadoRelacion.Activo);
 34
 035        if (!string.IsNullOrWhiteSpace(filtro.Cedula))
 036            query = query.Where(r => r.Persona.Cedula == filtro.Cedula);
 37
 038        if (filtro.ProgramaId.HasValue)
 039            query = query.Where(r => r.ProgramaId == filtro.ProgramaId.Value);
 40
 041        if (filtro.UnidadId.HasValue)
 042            query = query.Where(r => r.UnidadId == filtro.UnidadId.Value);
 43
 044        var relaciones = await query.ToListAsync();
 45
 46        // Obtener cédulas que tienen liquidación en el período/versión
 047        var cedulasConLiquidacion = await _context.LiquidacionEstadosPersonal
 048            .AsNoTracking()
 049            .Where(e => e.PeriodoId == periodoId && e.Version == version)
 050            .Select(e => e.Cedula)
 051            .ToHashSetAsync();
 52
 53        // Calcular líquido pagable (suma de todos los ítems por cédula)
 054        var liquidosPorCedula = await _context.LiquidacionItems
 055            .AsNoTracking()
 056            .Where(i => i.PeriodoId == periodoId && i.Version == version)
 057            .GroupBy(i => i.Cedula)
 058            .Select(g => new { Cedula = g.Key, Liquido = g.Sum(i => i.Importe) })
 059            .ToDictionaryAsync(x => x.Cedula, x => x.Liquido);
 60
 061        var resultados = relaciones
 062            .Where(r => cedulasConLiquidacion.Contains(r.Persona.Cedula))
 063            .ToList();
 64
 065        if (!string.IsNullOrWhiteSpace(filtro.Regimen))
 66        {
 067            var regimenNorm = filtro.Regimen.Replace('_', '-').ToLowerInvariant();
 068            resultados = regimenNorm switch
 069            {
 070                "ley-vieja" => resultados.Where(r => r.Regimen.EsLeyVieja).ToList(),
 071                "ley-nueva" => resultados.Where(r => !r.Regimen.EsLeyVieja).ToList(),
 072                _ => resultados.Where(r => r.Regimen.NumeroLey.Contains(filtro.Regimen, StringComparison.OrdinalIgnoreCa
 073            };
 74        }
 75
 076        return resultados.Select(r => new ReciboResumenDto
 077        {
 078            RelacionLaboralId   = r.Id,
 079            Cedula       = r.Persona.Cedula,
 080            Nombre       = r.Persona.PrimerNombre,
 081            Apellido     = r.Persona.PrimerApellido,
 082            Regimen      = r.Regimen.NumeroLey,
 083            Programa     = r.Programa?.Denominacion ?? string.Empty,
 084            Unidad       = r.Unidad?.Denominacion ?? string.Empty,
 085            LiquidoPagable = liquidosPorCedula.TryGetValue(r.Persona.Cedula, out var liq) ? liq : 0m
 086        }).ToList();
 087    }
 88
 89    public async Task<ReciboDto?> ObtenerDatosReciboAsync(long periodoId, int version, long relacionId)
 90    {
 191        var relacion = await _context.RelacionesLaborales
 192            .AsNoTracking()
 193            .Include(r => r.Persona)
 194                .ThenInclude(p => p.CuentasBancarias.Where(c => c.Estado == "activa"))
 195                    .ThenInclude(c => c.Banco)
 196            .Include(r => r.Regimen)
 197            .Include(r => r.Programa)
 198            .Include(r => r.Unidad)
 199            .Include(r => r.SubUnidad)
 1100            .Include(r => r.Grado)
 1101            .Include(r => r.Escalafon)
 1102            .FirstOrDefaultAsync(r => r.Id == relacionId);
 103
 1104        if (relacion == null) return null;
 105
 1106        var cedula = relacion.Persona.Cedula;
 107
 1108        var items = await _context.LiquidacionItems
 1109            .AsNoTracking()
 1110            .Where(i => i.PeriodoId == periodoId && i.Version == version && i.Cedula == cedula)
 1111            .ToListAsync();
 112
 1113        if (!items.Any()) return null;
 114
 1115        var periodo = await _context.PeriodosLiquidacion
 1116            .AsNoTracking()
 1117            .Where(p => p.Id == periodoId)
 1118            .Select(p => new { p.Anio, p.Mes })
 1119            .FirstOrDefaultAsync();
 120
 1121        var situacionCodigo = await _context.Situaciones
 1122            .AsNoTracking()
 1123            .Where(s => s.Id == relacion.SituacionId)
 1124            .Select(s => s.Codigo)
 1125            .FirstOrDefaultAsync() ?? string.Empty;
 126
 127        // Obtener tipos de catálogo para clasificar ítems
 1128        var codigosNumericos = items
 4129            .Select(i => i.CodigoConcepto)
 4130            .Where(c => int.TryParse(c, out _))
 1131            .Select(int.Parse)
 1132            .ToHashSet();
 133
 1134        var catalogoPorCodigo = await _context.CatalogoItems
 1135            .AsNoTracking()
 1136            .Where(c => codigosNumericos.Contains(c.Codigo))
 1137            .ToDictionaryAsync(
 4138                c => c.Codigo.ToString(),
 5139                c => new CatalogoReciboItem(c.Tipo, c.ItemParFictoId));
 140
 1141        var idsParesFictos = catalogoPorCodigo.Values
 4142            .Where(c => c.ItemParFictoId.HasValue)
 2143            .Select(c => c.ItemParFictoId!.Value)
 1144            .ToHashSet();
 145
 1146        var tiposPorId = idsParesFictos.Count == 0
 1147            ? new Dictionary<long, string>()
 1148            : await _context.CatalogoItems
 1149                .AsNoTracking()
 1150                .Where(c => idsParesFictos.Contains(c.Id))
 5151                .ToDictionaryAsync(c => c.Id, c => c.Tipo);
 152
 1153        var haberes              = new List<ReciboItemDto>();
 1154        var beneficios           = new List<ReciboItemDto>();
 1155        var descuentosLegales    = new List<ReciboItemDto>();
 1156        var descuentosPersonales = new List<ReciboItemDto>();
 157
 14158        foreach (var item in items.OrderBy(i => i.CodigoConcepto))
 159        {
 4160            var dto = new ReciboItemDto
 4161            {
 4162                Codigo        = item.CodigoConcepto,
 4163                Descripcion   = item.NombreConcepto,
 4164                RubroContable = item.RubroContable,
 4165                Monto         = item.Importe
 4166            };
 167
 4168            if (item.Importe > 0)
 169            {
 2170                var catalogoItem = catalogoPorCodigo.TryGetValue(item.CodigoConcepto, out var t) ? t : null;
 2171                var tipo = catalogoItem?.Tipo ?? "haber_normal";
 2172                if (string.Equals(tipo, "ajuste", StringComparison.OrdinalIgnoreCase))
 173                    { /* el ajuste se muestra por separado en columna 3, no suma a haberes */ }
 2174                else if (EsFictoParaRecibo(catalogoItem))
 1175                    beneficios.Add(dto);
 1176                else if (tipo.StartsWith("beneficio_social", StringComparison.OrdinalIgnoreCase))
 0177                    beneficios.Add(dto);
 178                else
 1179                    haberes.Add(dto);
 180            }
 2181            else if (catalogoPorCodigo.TryGetValue(item.CodigoConcepto, out var catalogoItem) &&
 2182                     EsDescuentoLegalParaRecibo(catalogoItem, tiposPorId))
 183            {
 2184                descuentosLegales.Add(dto);
 185            }
 186            else
 187            {
 0188                descuentosPersonales.Add(dto);
 189            }
 190        }
 191
 2192        var totalHaberes              = haberes.Sum(i => i.Monto);
 2193        var totalBeneficios           = beneficios.Sum(i => i.Monto);
 3194        var totalDescuentosLegales    = Math.Abs(descuentosLegales.Sum(i => i.Monto));
 1195        var totalDescuentosPersonales = Math.Abs(descuentosPersonales.Sum(i => i.Monto));
 1196        var totalDescuentos           = totalDescuentosLegales + totalDescuentosPersonales;
 197
 1198        var nominal     = totalHaberes + totalBeneficios;
 1199        var exactLiquid = nominal - totalDescuentosLegales - totalDescuentosPersonales;
 1200        var aCobrar     = Math.Round(exactLiquid, 0, MidpointRounding.AwayFromZero);
 1201        var ajuste      = aCobrar - exactLiquid;
 202
 203        // Civil: SIT19 (presupuestado), SIT20 (baja civil), SIT21 (contratado)
 1204        var esCivil = situacionCodigo is "SIT19" or "SIT20" or "SIT21";
 205
 1206        var cuentaActiva = relacion.Persona.CuentasBancarias.FirstOrDefault();
 207
 1208        return new ReciboDto
 1209        {
 1210            Anio            = periodo?.Anio ?? 0,
 1211            Mes             = periodo?.Mes ?? 0,
 1212            RelacionLaboralId      = relacion.Id,
 1213            Cedula          = cedula,
 1214            PrimerNombre    = relacion.Persona.PrimerNombre,
 1215            SegundoNombre   = relacion.Persona.SegundoNombre ?? string.Empty,
 1216            PrimerApellido  = relacion.Persona.PrimerApellido,
 1217            SegundoApellido = relacion.Persona.SegundoApellido ?? string.Empty,
 1218            Grado           = relacion.Grado?.Denominacion ?? string.Empty,
 1219            Escalafon       = relacion.Escalafon?.Denominacion ?? string.Empty,
 1220            Regimen         = relacion.Regimen.NumeroLey,
 1221            Unidad          = relacion.Unidad?.Denominacion ?? string.Empty,
 1222            Programa        = relacion.Programa?.Denominacion ?? string.Empty,
 1223            UnidadCodigo    = relacion.Unidad?.Codigo ?? string.Empty,
 1224            Compania        = relacion.SubUnidad?.Codigo ?? string.Empty,
 1225            EsOficial       = relacion.Grado?.EsOficial ?? false,
 1226            EsSubalterno    = relacion.Grado?.EsSubalterno ?? false,
 1227            EsCivil         = esCivil,
 1228            FechaInicio     = relacion.FechaInicio,
 1229            FechaUltimoAscenso = relacion.FechaUltimoAscenso,
 1230            Haberes               = haberes,
 1231            Beneficios            = beneficios,
 1232            DescuentosLegales     = descuentosLegales,
 1233            DescuentosPersonales  = descuentosPersonales,
 1234            TotalHaberes          = totalHaberes,
 1235            TotalBeneficios       = totalBeneficios,
 1236            TotalDescuentosLegales    = totalDescuentosLegales,
 1237            TotalDescuentosPersonales = totalDescuentosPersonales,
 1238            TotalDescuentos       = totalDescuentos,
 1239            Nominal               = nominal,
 1240            LiquidoPagable        = aCobrar,
 1241            ACobrar               = aCobrar,
 1242            Ajuste                = ajuste,
 1243            DatosBancarios = cuentaActiva != null
 1244                ? new ReciboDatosBancariosDto
 1245                  {
 1246                      Banco  = cuentaActiva.Banco?.Nombre,
 1247                      Cuenta = cuentaActiva.NumeroCuenta
 1248                  }
 1249                : new ReciboDatosBancariosDto { Observacion = "Sin cuenta registrada" }
 1250        };
 1251    }
 252
 253    private static bool EsDescuentoLegalParaRecibo(
 254        CatalogoReciboItem catalogoItem,
 255        IReadOnlyDictionary<long, string> tiposPorId)
 256    {
 2257        if (string.Equals(catalogoItem.Tipo, "descuento_legal", StringComparison.OrdinalIgnoreCase))
 1258            return true;
 259
 1260        return catalogoItem.ItemParFictoId is long itemParFictoId
 1261               && tiposPorId.TryGetValue(itemParFictoId, out var tipoPar)
 1262               && tipoPar.StartsWith("ficto_", StringComparison.OrdinalIgnoreCase);
 263    }
 264
 265    private static bool EsFictoParaRecibo(CatalogoReciboItem? catalogoItem) =>
 2266        catalogoItem is not null && catalogoItem.Tipo.StartsWith("ficto_", StringComparison.OrdinalIgnoreCase);
 267
 17268    private sealed record CatalogoReciboItem(string Tipo, long? ItemParFictoId);
 269}