< 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
100%
Covered lines: 205
Uncovered lines: 0
Coverable lines: 205
Total lines: 286
Line coverage: 100%
Branch coverage
79%
Covered branches: 92
Total branches: 116
Branch coverage: 79.3%
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%11100%
ObtenerResumenesAsync()100%2020100%
ObtenerDatosReciboAsync()78.75%8080100%
EsDescuentoLegalParaRecibo(...)66.66%66100%
EsFictoParaRecibo(...)100%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
 2110    public ReciboRepository(ApplicationDbContext context)
 11    {
 2112        _context = context;
 2113    }
 14
 15    public async Task<int> ObtenerVersionMaximaAsync(long periodoId)
 16    {
 217        return await _context.LiquidacionItems
 218            .AsNoTracking()
 219            .Where(i => i.PeriodoId == periodoId)
 220            .MaxAsync(i => (int?)i.Version) ?? 0;
 221    }
 22
 23    public async Task<(List<ReciboResumenDto> Items, int TotalCount)> ObtenerResumenesAsync(long periodoId, int version,
 24    {
 1025        var query = _context.RelacionesLaborales
 1026            .AsNoTracking()
 1027            .Include(r => r.Persona)
 1028                .ThenInclude(p => p.CuentasBancarias)
 1029                    .ThenInclude(c => c.Banco)
 1030            .Include(r => r.Regimen)
 1031            .Include(r => r.Programa)
 1032            .Include(r => r.Unidad)
 1033            .Where(r => r.Estado == EstadoRelacion.Activo);
 34
 1035        if (!string.IsNullOrWhiteSpace(filtro.Cedula))
 36        {
 237            if (filtro.Cedula.Length >= 7)
 138                query = query.Where(r => r.Persona.Cedula == filtro.Cedula);
 139            else if (filtro.Cedula.Length >= 3)
 140                query = query.Where(r => r.Persona.Cedula.Contains(filtro.Cedula));
 41        }
 42
 1043        if (filtro.ProgramaId.HasValue)
 144            query = query.Where(r => r.ProgramaId == filtro.ProgramaId.Value);
 45
 1046        if (filtro.UnidadId.HasValue)
 147            query = query.Where(r => r.UnidadId == filtro.UnidadId.Value);
 48
 1049        var relaciones = await query.ToListAsync();
 50
 51        // Obtener cédulas que tienen liquidación en el período/versión
 1052        var cedulasConLiquidacion = await _context.LiquidacionEstadosPersonal
 1053            .AsNoTracking()
 1054            .Where(e => e.PeriodoId == periodoId && e.Version == version)
 1055            .Select(e => e.Cedula)
 1056            .ToHashSetAsync();
 57
 58        // Calcular líquido pagable (suma de todos los ítems por cédula)
 1059        var liquidosPorCedula = await _context.LiquidacionItems
 1060            .AsNoTracking()
 1061            .Where(i => i.PeriodoId == periodoId && i.Version == version)
 1062            .GroupBy(i => i.Cedula)
 1063            .Select(g => new { Cedula = g.Key, Liquido = g.Sum(i => i.Importe) })
 2864            .ToDictionaryAsync(x => x.Cedula, x => x.Liquido);
 65
 1066        var resultados = relaciones
 867            .Where(r => cedulasConLiquidacion.Contains(r.Persona.Cedula))
 1068            .ToList();
 69
 1070        if (!string.IsNullOrWhiteSpace(filtro.Regimen))
 71        {
 372            var regimenNorm = filtro.Regimen.Replace('_', '-').ToLowerInvariant();
 373            resultados = regimenNorm switch
 374            {
 275                "ley-vieja" => resultados.Where(r => r.Regimen.EsLeyVieja).ToList(),
 276                "ley-nueva" => resultados.Where(r => !r.Regimen.EsLeyVieja).ToList(),
 277                _ => resultados.Where(r => r.Regimen.NumeroLey.Contains(filtro.Regimen, StringComparison.OrdinalIgnoreCa
 378            };
 79        }
 80
 1781        var todos = resultados.Select(r => new ReciboResumenDto
 1782        {
 1783            RelacionLaboralId   = r.Id,
 1784            Cedula       = r.Persona.Cedula,
 1785            Nombre       = r.Persona.PrimerNombre,
 1786            Apellido     = r.Persona.PrimerApellido,
 1787            Regimen      = r.Regimen.NumeroLey,
 1788            Programa     = r.Programa?.Denominacion ?? string.Empty,
 1789            Unidad       = r.Unidad?.Denominacion ?? string.Empty,
 1790            LiquidoPagable = liquidosPorCedula.TryGetValue(r.Persona.Cedula, out var liq) ? liq : 0m
 1791        }).ToList();
 92
 1093        var totalCount = todos.Count;
 94
 1095        if (filtro.Page.HasValue && filtro.PageSize.HasValue)
 96        {
 197            var skip = (filtro.Page.Value - 1) * filtro.PageSize.Value;
 198            todos = todos.Skip(skip).Take(filtro.PageSize.Value).ToList();
 99        }
 100
 10101        return (todos, totalCount);
 10102    }
 103
 104    public async Task<ReciboDto?> ObtenerDatosReciboAsync(long periodoId, int version, long relacionId)
 105    {
 9106        var relacion = await _context.RelacionesLaborales
 9107            .AsNoTracking()
 9108            .Include(r => r.Persona)
 9109                .ThenInclude(p => p.CuentasBancarias.Where(c => c.Estado == "activa"))
 9110                    .ThenInclude(c => c.Banco)
 9111            .Include(r => r.Regimen)
 9112            .Include(r => r.Programa)
 9113            .Include(r => r.Unidad)
 9114            .Include(r => r.SubUnidad)
 9115            .Include(r => r.Grado)
 9116            .Include(r => r.Escalafon)
 9117            .FirstOrDefaultAsync(r => r.Id == relacionId);
 118
 10119        if (relacion == null) return null;
 120
 8121        var cedula = relacion.Persona.Cedula;
 122
 8123        var items = await _context.LiquidacionItems
 8124            .AsNoTracking()
 8125            .Where(i => i.PeriodoId == periodoId && i.Version == version && i.Cedula == cedula)
 8126            .ToListAsync();
 127
 9128        if (!items.Any()) return null;
 129
 7130        var periodo = await _context.PeriodosLiquidacion
 7131            .AsNoTracking()
 7132            .Where(p => p.Id == periodoId)
 7133            .Select(p => new { p.Anio, p.Mes })
 7134            .FirstOrDefaultAsync();
 135
 7136        var situacionCodigo = await _context.Situaciones
 7137            .AsNoTracking()
 7138            .Where(s => s.Id == relacion.SituacionId)
 7139            .Select(s => s.Codigo)
 7140            .FirstOrDefaultAsync() ?? string.Empty;
 141
 142        // Obtener tipos de catálogo para clasificar ítems
 7143        var codigosNumericos = items
 10144            .Select(i => i.CodigoConcepto)
 10145            .Where(c => int.TryParse(c, out _))
 7146            .Select(int.Parse)
 7147            .ToHashSet();
 148
 7149        var catalogoPorCodigo = await _context.CatalogoItems
 7150            .AsNoTracking()
 7151            .Where(c => codigosNumericos.Contains(c.Codigo))
 7152            .ToDictionaryAsync(
 7153                c => c.Codigo.ToString(),
 14154                c => new CatalogoReciboItem(c.Tipo, c.ItemParFictoId));
 155
 7156        var idsParesFictos = catalogoPorCodigo.Values
 7157            .Where(c => c.ItemParFictoId.HasValue)
 2158            .Select(c => c.ItemParFictoId!.Value)
 7159            .ToHashSet();
 160
 7161        var tiposPorId = idsParesFictos.Count == 0
 7162            ? new Dictionary<long, string>()
 7163            : await _context.CatalogoItems
 7164                .AsNoTracking()
 7165                .Where(c => idsParesFictos.Contains(c.Id))
 11166                .ToDictionaryAsync(c => c.Id, c => c.Tipo);
 167
 7168        var haberes              = new List<ReciboItemDto>();
 7169        var beneficios           = new List<ReciboItemDto>();
 7170        var descuentosLegales    = new List<ReciboItemDto>();
 7171        var descuentosPersonales = new List<ReciboItemDto>();
 172
 44173        foreach (var item in items.OrderBy(i => i.CodigoConcepto))
 174        {
 10175            var dto = new ReciboItemDto
 10176            {
 10177                Codigo        = item.CodigoConcepto,
 10178                Descripcion   = item.NombreConcepto,
 10179                RubroContable = item.RubroContable,
 10180                Monto         = item.Importe
 10181            };
 182
 10183            if (item.Importe > 0)
 184            {
 6185                var catalogoItem = catalogoPorCodigo.TryGetValue(item.CodigoConcepto, out var t) ? t : null;
 6186                var tipo = catalogoItem?.Tipo ?? "haber_normal";
 6187                if (string.Equals(tipo, "ajuste", StringComparison.OrdinalIgnoreCase))
 188                    { /* el ajuste se muestra por separado en columna 3, no suma a haberes */ }
 5189                else if (EsFictoParaRecibo(catalogoItem))
 1190                    beneficios.Add(dto);
 4191                else if (tipo.StartsWith("beneficio_social", StringComparison.OrdinalIgnoreCase))
 1192                    beneficios.Add(dto);
 193                else
 3194                    haberes.Add(dto);
 195            }
 196            else
 197            {
 4198                catalogoPorCodigo.TryGetValue(item.CodigoConcepto, out var catalogoItem);
 4199                var tipoNeg = catalogoItem?.Tipo;
 4200                if (string.Equals(tipoNeg, "ajuste", StringComparison.OrdinalIgnoreCase))
 201                    { /* ajuste negativo: se muestra por separado en columna 3, no suma a descuentos */ }
 3202                else if (catalogoItem != null && EsDescuentoLegalParaRecibo(catalogoItem, tiposPorId))
 2203                    descuentosLegales.Add(dto);
 204                else
 1205                    descuentosPersonales.Add(dto);
 206            }
 207        }
 208
 10209        var totalHaberes              = haberes.Sum(i => i.Monto);
 9210        var totalBeneficios           = beneficios.Sum(i => i.Monto);
 9211        var totalDescuentosLegales    = Math.Abs(descuentosLegales.Sum(i => i.Monto));
 8212        var totalDescuentosPersonales = Math.Abs(descuentosPersonales.Sum(i => i.Monto));
 7213        var totalDescuentos           = totalDescuentosLegales + totalDescuentosPersonales;
 214
 7215        var nominal     = totalHaberes + totalBeneficios;
 7216        var exactLiquid = nominal - totalDescuentosLegales - totalDescuentosPersonales;
 7217        var aCobrar     = Math.Round(exactLiquid, 0, MidpointRounding.AwayFromZero);
 7218        var ajuste      = aCobrar - exactLiquid;
 219
 220        // Civil: SIT19 (presupuestado), SIT20 (baja civil), SIT21 (contratado)
 7221        var esCivil = situacionCodigo is "SIT19" or "SIT20" or "SIT21";
 222
 7223        var cuentaActiva = relacion.Persona.CuentasBancarias.FirstOrDefault();
 224
 7225        return new ReciboDto
 7226        {
 7227            Anio            = periodo?.Anio ?? 0,
 7228            Mes             = periodo?.Mes ?? 0,
 7229            RelacionLaboralId      = relacion.Id,
 7230            Cedula          = cedula,
 7231            PrimerNombre    = relacion.Persona.PrimerNombre,
 7232            SegundoNombre   = relacion.Persona.SegundoNombre ?? string.Empty,
 7233            PrimerApellido  = relacion.Persona.PrimerApellido,
 7234            SegundoApellido = relacion.Persona.SegundoApellido ?? string.Empty,
 7235            Grado           = relacion.Grado?.Denominacion ?? string.Empty,
 7236            Escalafon       = relacion.Escalafon?.Denominacion ?? string.Empty,
 7237            Regimen         = relacion.Regimen.NumeroLey,
 7238            Unidad          = relacion.Unidad?.Denominacion ?? string.Empty,
 7239            Programa        = relacion.Programa?.Denominacion ?? string.Empty,
 7240            UnidadCodigo    = relacion.Unidad?.Codigo ?? string.Empty,
 7241            Compania        = relacion.SubUnidad?.Codigo ?? string.Empty,
 7242            EsOficial       = relacion.Grado?.EsOficial ?? false,
 7243            EsSubalterno    = relacion.Grado?.EsSubalterno ?? false,
 7244            EsCivil         = esCivil,
 7245            FechaInicio     = relacion.FechaInicio,
 7246            FechaUltimoAscenso = relacion.FechaUltimoAscenso,
 7247            Haberes               = haberes,
 7248            Beneficios            = beneficios,
 7249            DescuentosLegales     = descuentosLegales,
 7250            DescuentosPersonales  = descuentosPersonales,
 7251            TotalHaberes          = totalHaberes,
 7252            TotalBeneficios       = totalBeneficios,
 7253            TotalDescuentosLegales    = totalDescuentosLegales,
 7254            TotalDescuentosPersonales = totalDescuentosPersonales,
 7255            TotalDescuentos       = totalDescuentos,
 7256            Nominal               = nominal,
 7257            LiquidoPagable        = aCobrar,
 7258            ACobrar               = aCobrar,
 7259            Ajuste                = ajuste,
 7260            DatosBancarios = cuentaActiva != null
 7261                ? new ReciboDatosBancariosDto
 7262                  {
 7263                      Banco  = cuentaActiva.Banco?.Nombre,
 7264                      Cuenta = cuentaActiva.NumeroCuenta
 7265                  }
 7266                : new ReciboDatosBancariosDto { Observacion = "Sin cuenta registrada" }
 7267        };
 9268    }
 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) =>
 5283        catalogoItem is not null && catalogoItem.Tipo.StartsWith("ficto_", StringComparison.OrdinalIgnoreCase);
 284
 29285    private sealed record CatalogoReciboItem(string Tipo, long? ItemParFictoId);
 286}