< 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
68%
Covered lines: 140
Uncovered lines: 65
Coverable lines: 205
Total lines: 286
Line coverage: 68.2%
Branch coverage
43%
Covered branches: 51
Total branches: 116
Branch coverage: 43.9%
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%420200%
ObtenerDatosReciboAsync()60%808098.33%
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> Items, int TotalCount)> ObtenerResumenesAsync(long periodoId, int version,
 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))
 36        {
 037            if (filtro.Cedula.Length >= 7)
 038                query = query.Where(r => r.Persona.Cedula == filtro.Cedula);
 039            else if (filtro.Cedula.Length >= 3)
 040                query = query.Where(r => r.Persona.Cedula.Contains(filtro.Cedula));
 41        }
 42
 043        if (filtro.ProgramaId.HasValue)
 044            query = query.Where(r => r.ProgramaId == filtro.ProgramaId.Value);
 45
 046        if (filtro.UnidadId.HasValue)
 047            query = query.Where(r => r.UnidadId == filtro.UnidadId.Value);
 48
 049        var relaciones = await query.ToListAsync();
 50
 51        // Obtener cédulas que tienen liquidación en el período/versión
 052        var cedulasConLiquidacion = await _context.LiquidacionEstadosPersonal
 053            .AsNoTracking()
 054            .Where(e => e.PeriodoId == periodoId && e.Version == version)
 055            .Select(e => e.Cedula)
 056            .ToHashSetAsync();
 57
 58        // Calcular líquido pagable (suma de todos los ítems por cédula)
 059        var liquidosPorCedula = await _context.LiquidacionItems
 060            .AsNoTracking()
 061            .Where(i => i.PeriodoId == periodoId && i.Version == version)
 062            .GroupBy(i => i.Cedula)
 063            .Select(g => new { Cedula = g.Key, Liquido = g.Sum(i => i.Importe) })
 064            .ToDictionaryAsync(x => x.Cedula, x => x.Liquido);
 65
 066        var resultados = relaciones
 067            .Where(r => cedulasConLiquidacion.Contains(r.Persona.Cedula))
 068            .ToList();
 69
 070        if (!string.IsNullOrWhiteSpace(filtro.Regimen))
 71        {
 072            var regimenNorm = filtro.Regimen.Replace('_', '-').ToLowerInvariant();
 073            resultados = regimenNorm switch
 074            {
 075                "ley-vieja" => resultados.Where(r => r.Regimen.EsLeyVieja).ToList(),
 076                "ley-nueva" => resultados.Where(r => !r.Regimen.EsLeyVieja).ToList(),
 077                _ => resultados.Where(r => r.Regimen.NumeroLey.Contains(filtro.Regimen, StringComparison.OrdinalIgnoreCa
 078            };
 79        }
 80
 081        var todos = resultados.Select(r => new ReciboResumenDto
 082        {
 083            RelacionLaboralId   = r.Id,
 084            Cedula       = r.Persona.Cedula,
 085            Nombre       = r.Persona.PrimerNombre,
 086            Apellido     = r.Persona.PrimerApellido,
 087            Regimen      = r.Regimen.NumeroLey,
 088            Programa     = r.Programa?.Denominacion ?? string.Empty,
 089            Unidad       = r.Unidad?.Denominacion ?? string.Empty,
 090            LiquidoPagable = liquidosPorCedula.TryGetValue(r.Persona.Cedula, out var liq) ? liq : 0m
 091        }).ToList();
 92
 093        var totalCount = todos.Count;
 94
 095        if (filtro.Page.HasValue && filtro.PageSize.HasValue)
 96        {
 097            var skip = (filtro.Page.Value - 1) * filtro.PageSize.Value;
 098            todos = todos.Skip(skip).Take(filtro.PageSize.Value).ToList();
 99        }
 100
 0101        return (todos, totalCount);
 0102    }
 103
 104    public async Task<ReciboDto?> ObtenerDatosReciboAsync(long periodoId, int version, long relacionId)
 105    {
 1106        var relacion = await _context.RelacionesLaborales
 1107            .AsNoTracking()
 1108            .Include(r => r.Persona)
 1109                .ThenInclude(p => p.CuentasBancarias.Where(c => c.Estado == "activa"))
 1110                    .ThenInclude(c => c.Banco)
 1111            .Include(r => r.Regimen)
 1112            .Include(r => r.Programa)
 1113            .Include(r => r.Unidad)
 1114            .Include(r => r.SubUnidad)
 1115            .Include(r => r.Grado)
 1116            .Include(r => r.Escalafon)
 1117            .FirstOrDefaultAsync(r => r.Id == relacionId);
 118
 1119        if (relacion == null) return null;
 120
 1121        var cedula = relacion.Persona.Cedula;
 122
 1123        var items = await _context.LiquidacionItems
 1124            .AsNoTracking()
 1125            .Where(i => i.PeriodoId == periodoId && i.Version == version && i.Cedula == cedula)
 1126            .ToListAsync();
 127
 1128        if (!items.Any()) return null;
 129
 1130        var periodo = await _context.PeriodosLiquidacion
 1131            .AsNoTracking()
 1132            .Where(p => p.Id == periodoId)
 1133            .Select(p => new { p.Anio, p.Mes })
 1134            .FirstOrDefaultAsync();
 135
 1136        var situacionCodigo = await _context.Situaciones
 1137            .AsNoTracking()
 1138            .Where(s => s.Id == relacion.SituacionId)
 1139            .Select(s => s.Codigo)
 1140            .FirstOrDefaultAsync() ?? string.Empty;
 141
 142        // Obtener tipos de catálogo para clasificar ítems
 1143        var codigosNumericos = items
 4144            .Select(i => i.CodigoConcepto)
 4145            .Where(c => int.TryParse(c, out _))
 1146            .Select(int.Parse)
 1147            .ToHashSet();
 148
 1149        var catalogoPorCodigo = await _context.CatalogoItems
 1150            .AsNoTracking()
 1151            .Where(c => codigosNumericos.Contains(c.Codigo))
 1152            .ToDictionaryAsync(
 4153                c => c.Codigo.ToString(),
 5154                c => new CatalogoReciboItem(c.Tipo, c.ItemParFictoId));
 155
 1156        var idsParesFictos = catalogoPorCodigo.Values
 4157            .Where(c => c.ItemParFictoId.HasValue)
 2158            .Select(c => c.ItemParFictoId!.Value)
 1159            .ToHashSet();
 160
 1161        var tiposPorId = idsParesFictos.Count == 0
 1162            ? new Dictionary<long, string>()
 1163            : await _context.CatalogoItems
 1164                .AsNoTracking()
 1165                .Where(c => idsParesFictos.Contains(c.Id))
 5166                .ToDictionaryAsync(c => c.Id, c => c.Tipo);
 167
 1168        var haberes              = new List<ReciboItemDto>();
 1169        var beneficios           = new List<ReciboItemDto>();
 1170        var descuentosLegales    = new List<ReciboItemDto>();
 1171        var descuentosPersonales = new List<ReciboItemDto>();
 172
 14173        foreach (var item in items.OrderBy(i => i.CodigoConcepto))
 174        {
 4175            var dto = new ReciboItemDto
 4176            {
 4177                Codigo        = item.CodigoConcepto,
 4178                Descripcion   = item.NombreConcepto,
 4179                RubroContable = item.RubroContable,
 4180                Monto         = item.Importe
 4181            };
 182
 4183            if (item.Importe > 0)
 184            {
 2185                var catalogoItem = catalogoPorCodigo.TryGetValue(item.CodigoConcepto, out var t) ? t : null;
 2186                var tipo = catalogoItem?.Tipo ?? "haber_normal";
 2187                if (string.Equals(tipo, "ajuste", StringComparison.OrdinalIgnoreCase))
 188                    { /* el ajuste se muestra por separado en columna 3, no suma a haberes */ }
 2189                else if (EsFictoParaRecibo(catalogoItem))
 1190                    beneficios.Add(dto);
 1191                else if (tipo.StartsWith("beneficio_social", StringComparison.OrdinalIgnoreCase))
 0192                    beneficios.Add(dto);
 193                else
 1194                    haberes.Add(dto);
 195            }
 196            else
 197            {
 2198                catalogoPorCodigo.TryGetValue(item.CodigoConcepto, out var catalogoItem);
 2199                var tipoNeg = catalogoItem?.Tipo;
 2200                if (string.Equals(tipoNeg, "ajuste", StringComparison.OrdinalIgnoreCase))
 201                    { /* ajuste negativo: se muestra por separado en columna 3, no suma a descuentos */ }
 2202                else if (catalogoItem != null && EsDescuentoLegalParaRecibo(catalogoItem, tiposPorId))
 2203                    descuentosLegales.Add(dto);
 204                else
 0205                    descuentosPersonales.Add(dto);
 206            }
 207        }
 208
 2209        var totalHaberes              = haberes.Sum(i => i.Monto);
 2210        var totalBeneficios           = beneficios.Sum(i => i.Monto);
 3211        var totalDescuentosLegales    = Math.Abs(descuentosLegales.Sum(i => i.Monto));
 1212        var totalDescuentosPersonales = Math.Abs(descuentosPersonales.Sum(i => i.Monto));
 1213        var totalDescuentos           = totalDescuentosLegales + totalDescuentosPersonales;
 214
 1215        var nominal     = totalHaberes + totalBeneficios;
 1216        var exactLiquid = nominal - totalDescuentosLegales - totalDescuentosPersonales;
 1217        var aCobrar     = Math.Round(exactLiquid, 0, MidpointRounding.AwayFromZero);
 1218        var ajuste      = aCobrar - exactLiquid;
 219
 220        // Civil: SIT19 (presupuestado), SIT20 (baja civil), SIT21 (contratado)
 1221        var esCivil = situacionCodigo is "SIT19" or "SIT20" or "SIT21";
 222
 1223        var cuentaActiva = relacion.Persona.CuentasBancarias.FirstOrDefault();
 224
 1225        return new ReciboDto
 1226        {
 1227            Anio            = periodo?.Anio ?? 0,
 1228            Mes             = periodo?.Mes ?? 0,
 1229            RelacionLaboralId      = relacion.Id,
 1230            Cedula          = cedula,
 1231            PrimerNombre    = relacion.Persona.PrimerNombre,
 1232            SegundoNombre   = relacion.Persona.SegundoNombre ?? string.Empty,
 1233            PrimerApellido  = relacion.Persona.PrimerApellido,
 1234            SegundoApellido = relacion.Persona.SegundoApellido ?? string.Empty,
 1235            Grado           = relacion.Grado?.Denominacion ?? string.Empty,
 1236            Escalafon       = relacion.Escalafon?.Denominacion ?? string.Empty,
 1237            Regimen         = relacion.Regimen.NumeroLey,
 1238            Unidad          = relacion.Unidad?.Denominacion ?? string.Empty,
 1239            Programa        = relacion.Programa?.Denominacion ?? string.Empty,
 1240            UnidadCodigo    = relacion.Unidad?.Codigo ?? string.Empty,
 1241            Compania        = relacion.SubUnidad?.Codigo ?? string.Empty,
 1242            EsOficial       = relacion.Grado?.EsOficial ?? false,
 1243            EsSubalterno    = relacion.Grado?.EsSubalterno ?? false,
 1244            EsCivil         = esCivil,
 1245            FechaInicio     = relacion.FechaInicio,
 1246            FechaUltimoAscenso = relacion.FechaUltimoAscenso,
 1247            Haberes               = haberes,
 1248            Beneficios            = beneficios,
 1249            DescuentosLegales     = descuentosLegales,
 1250            DescuentosPersonales  = descuentosPersonales,
 1251            TotalHaberes          = totalHaberes,
 1252            TotalBeneficios       = totalBeneficios,
 1253            TotalDescuentosLegales    = totalDescuentosLegales,
 1254            TotalDescuentosPersonales = totalDescuentosPersonales,
 1255            TotalDescuentos       = totalDescuentos,
 1256            Nominal               = nominal,
 1257            LiquidoPagable        = aCobrar,
 1258            ACobrar               = aCobrar,
 1259            Ajuste                = ajuste,
 1260            DatosBancarios = cuentaActiva != null
 1261                ? new ReciboDatosBancariosDto
 1262                  {
 1263                      Banco  = cuentaActiva.Banco?.Nombre,
 1264                      Cuenta = cuentaActiva.NumeroCuenta
 1265                  }
 1266                : new ReciboDatosBancariosDto { Observacion = "Sin cuenta registrada" }
 1267        };
 1268    }
 269
 270    private static bool EsDescuentoLegalParaRecibo(
 271        CatalogoReciboItem catalogoItem,
 272        IReadOnlyDictionary<long, string> tiposPorId)
 273    {
 2274        if (string.Equals(catalogoItem.Tipo, "descuento_legal", StringComparison.OrdinalIgnoreCase))
 1275            return true;
 276
 1277        return catalogoItem.ItemParFictoId is long itemParFictoId
 1278               && tiposPorId.TryGetValue(itemParFictoId, out var tipoPar)
 1279               && tipoPar.StartsWith("ficto_", StringComparison.OrdinalIgnoreCase);
 280    }
 281
 282    private static bool EsFictoParaRecibo(CatalogoReciboItem? catalogoItem) =>
 2283        catalogoItem is not null && catalogoItem.Tipo.StartsWith("ficto_", StringComparison.OrdinalIgnoreCase);
 284
 19285    private sealed record CatalogoReciboItem(string Tipo, long? ItemParFictoId);
 286}