< Summary

Information
Class: FAU.Logica.Services.AguinaldoPadronService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/AguinaldoPadronService.cs
Line coverage
99%
Covered lines: 250
Uncovered lines: 1
Coverable lines: 251
Total lines: 356
Line coverage: 99.6%
Branch coverage
90%
Covered branches: 49
Total branches: 54
Branch coverage: 90.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GenerarPadronExcelAsync()100%88100%
ObtenerCodigoSanidad(...)90%1010100%
GenerarExcel(...)100%1212100%
EscribirFilaPadron(...)87.5%88100%
EscribirSubtotalPadron(...)100%44100%
FormatApellidosNombres(...)50%44100%
.ctor(...)100%11100%
get_Cedula()100%11100%
get_NombreCompleto()100%11100%
get_GradoDenominacion()100%210%
get_GradoOrden()100%11100%
get_EsOficial()100%11100%
get_EsSubalterno()100%11100%
get_SituacionCodigo()100%11100%
get_SituacionDenominacion()100%11100%
get_ProgramaCodigo()100%11100%
get_ProgramaDenominacion()100%11100%
get_Nominal()100%11100%
get_SueldoRetiro()100%11100%
get_Montepio()100%11100%
get_Sanidad()100%11100%
get_CodigoSanidad()100%11100%
get_Liquido()100%11100%
get_Nominal()100%11100%
get_SueldoRetiro()100%11100%
get_MontepiSFun()100%11100%
get_MontepiOficial()100%11100%
get_MontepiCivil()100%11100%
get_San5010()100%11100%
get_San5011()100%11100%
get_San5012()100%11100%
get_Liquido()100%11100%
Acumular(...)87.5%88100%
Acumular(...)100%11100%

File(s)

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

#LineLine coverage
 1using ClosedXML.Excel;
 2using FAU.DataAccess;
 3using FAU.Entidades;
 4using Microsoft.EntityFrameworkCore;
 5
 6namespace FAU.Logica.Services;
 7
 8public class AguinaldoPadronService : IAguinaldoPadronService
 9{
 10    private readonly ApplicationDbContext _context;
 11
 1012    public AguinaldoPadronService(ApplicationDbContext context)
 13    {
 1014        _context = context;
 1015    }
 16
 17    public async Task<(byte[] Bytes, string FileName)> GenerarPadronExcelAsync(int anio, int mes, bool esLeyVieja)
 18    {
 1019        if (mes != 6 && mes != 12)
 120            throw new ArgumentException("El mes debe ser 6 o 12.", nameof(mes));
 21
 922        var semestre = (short)(mes == 6 ? 1 : 2);
 923        var periodoDate = new DateTime(anio, mes, 1);
 24
 25        // Cargar tramos de sanidad vigentes al período (mismo patrón que InsumosPeriodoLoader)
 926        var tramosRaw = await _context.TramosSanidadMilitar
 927            .AsNoTracking()
 928            .Where(t => t.VigenteDesde <= periodoDate && (t.VigenteHasta == null || t.VigenteHasta >= periodoDate))
 929            .ToListAsync();
 930        var tramos = tramosRaw
 231            .GroupBy(t => t.CodigoRetencion)
 432            .Select(g => g.OrderByDescending(t => t.VigenteDesde).First())
 233            .OrderBy(t => t.Desde)
 934            .ToList();
 35
 936        var rawData = await (
 937            from a in _context.Aguinaldos
 938            where a.Anio == (short)anio && a.Semestre == semestre
 939            join rl in _context.RelacionesLaborales on a.PersonaId equals rl.PersonaId
 940            where rl.Estado == EstadoRelacion.Activo && rl.Regimen.EsLeyVieja == esLeyVieja
 941            select new
 942            {
 943                rl.Persona.Cedula,
 944                rl.Persona.PrimerNombre,
 945                rl.Persona.SegundoNombre,
 946                rl.Persona.PrimerApellido,
 947                rl.Persona.SegundoApellido,
 948                GradoDenominacion = rl.Grado.Denominacion,
 949                GradoOrden = rl.Grado.Orden,
 950                EsOficial = rl.Grado.EsOficial,
 951                EsSubalterno = rl.Grado.EsSubalterno,
 952                SituacionCodigo = rl.Situacion.Codigo,
 953                SituacionDenominacion = rl.Situacion.Denominacion,
 954                ProgramaCodigo = rl.Programa.Codigo,
 955                ProgramaDenominacion = rl.Programa.Denominacion,
 956                Nominal = a.Nominal ?? 0m,
 957                Montepio = a.Montepio ?? 0m,
 958                Sanidad = a.Sanidad ?? 0m,
 959            }
 960        ).ToListAsync();
 61
 62        const decimal TasaSueldoRetiro = 0.01m;
 63
 964        var filas = rawData.Select(d =>
 965        {
 666            var nominal = d.Nominal;
 667            var sueldoRetiro = Math.Round(nominal * TasaSueldoRetiro, 2);
 668            var codigoSanidad = ObtenerCodigoSanidad(nominal, tramos);
 669            var liquido = Math.Round(nominal - sueldoRetiro - d.Montepio - d.Sanidad, 2);
 670            return new FilaPadron(
 671                d.Cedula,
 672                FormatApellidosNombres(d.PrimerApellido, d.SegundoApellido, d.PrimerNombre, d.SegundoNombre),
 673                d.GradoDenominacion,
 674                d.GradoOrden,
 675                d.EsOficial,
 676                d.EsSubalterno,
 677                d.SituacionCodigo,
 678                d.SituacionDenominacion,
 679                d.ProgramaCodigo,
 680                d.ProgramaDenominacion,
 681                nominal,
 682                sueldoRetiro,
 683                d.Montepio,
 684                d.Sanidad,
 685                codigoSanidad,
 686                liquido);
 987        }).ToList();
 88
 989        var leyLabel = esLeyVieja ? "ley_vieja" : "ley_nueva";
 990        var bytes = GenerarExcel(filas, anio, mes, esLeyVieja);
 991        return (bytes, $"padron_aguinaldo_{anio}_{mes:D2}_{leyLabel}.xlsx");
 992    }
 93
 94    // Devuelve el CodigoRetencion del tramo que contiene el nominal (5010 / 5011 / 5012)
 95    private static int ObtenerCodigoSanidad(decimal nominal, IReadOnlyList<TramoSanidadMilitar> tramos)
 96    {
 1397        foreach (var t in tramos)
 198            if (nominal >= t.Desde && (t.Hasta == null || nominal <= t.Hasta))
 199                return t.CodigoRetencion;
 5100        return tramos.Count > 0 ? tramos[^1].CodigoRetencion : 5012;
 1101    }
 102
 103    private static byte[] GenerarExcel(List<FilaPadron> filas, int anio, int mes, bool esLeyVieja)
 104    {
 9105        using var wb = new XLWorkbook();
 9106        var ws = wb.Worksheets.Add("Padrón");
 107
 9108        var semNombre = mes == 6 ? "1er Semestre" : "2do Semestre";
 9109        var leyNombre = esLeyVieja ? "Ley Vieja (14.157)" : "Ley Nueva (19.695)";
 110        const int TotalCols = 13;
 111        const int RightCol = 11;
 9112        int row = 1;
 113
 114        // ── Cabezal ──────────────────────────────────────────────────────────
 9115        ws.Cell(row, 1).Value = "F.A.U. Dirección de Economía y Finanzas";
 9116        ws.Range(row, 1, row, RightCol - 1).Merge();
 9117        ws.Cell(row, RightCol).Value = $"Fecha: {DateTime.Now:dd/MM/yyyy}";
 9118        ws.Range(row, RightCol, row, TotalCols).Merge();
 9119        ws.Cell(row, RightCol).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right;
 9120        row++;
 121
 9122        ws.Cell(row, 1).Value = "Departamento de Administración Presupuestal";
 9123        ws.Range(row, 1, row, RightCol - 1).Merge();
 9124        ws.Cell(row, RightCol).Value = $"Hora : {DateTime.Now:HH:mm:ss}";
 9125        ws.Range(row, RightCol, row, TotalCols).Merge();
 9126        ws.Cell(row, RightCol).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right;
 9127        row++;
 128
 9129        ws.Cell(row, 1).Value = $"PADRÓN DE AGUINALDO | {leyNombre} | {semNombre} {anio}";
 9130        ws.Range(row, 1, row, TotalCols).Merge();
 9131        ws.Cell(row, 1).Style.Font.Bold = true;
 9132        ws.Cell(row, 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
 9133        row += 2;
 134
 135        // ── Cabecera de columnas (dos filas: grupos + individuales) ───────────
 9136        int groupRow = row;
 9137        int colRow = row + 1;
 9138        int dataStartRow = row + 2;
 139
 9140        var headerBg = XLColor.FromHtml("#4472C4");
 141
 9142        ws.Range(groupRow, 1, colRow, 1).Merge();
 9143        ws.Cell(groupRow, 1).Value = "Cédula";
 144
 9145        ws.Range(groupRow, 2, colRow, 2).Merge();
 9146        ws.Cell(groupRow, 2).Value = "Apellido y Nombre";
 147
 9148        ws.Cell(groupRow, 3).Value = "DISPON./";
 9149        ws.Range(groupRow, 3, groupRow, 6).Merge();
 150
 9151        ws.Cell(groupRow, 7).Value = "Montepíos";
 9152        ws.Range(groupRow, 7, groupRow, 9).Merge();
 153
 9154        ws.Cell(groupRow, 10).Value = "Sanidad";
 9155        ws.Range(groupRow, 10, groupRow, 12).Merge();
 156
 9157        ws.Range(groupRow, 13, colRow, 13).Merge();
 9158        ws.Cell(groupRow, 13).Value = "Líquido";
 159
 9160        ws.Cell(colRow, 3).Value = "Duodécimo";
 9161        ws.Cell(colRow, 4).Value = "SUB.TRAN.";
 9162        ws.Cell(colRow, 5).Value = "Suel. Ret.";
 9163        ws.Cell(colRow, 6).Value = "S.Fun";
 9164        ws.Cell(colRow, 7).Value = "Oficial";
 9165        ws.Cell(colRow, 8).Value = "Tropa";
 9166        ws.Cell(colRow, 9).Value = "Civil";
 9167        ws.Cell(colRow, 10).Value = "5.010";
 9168        ws.Cell(colRow, 11).Value = "5.011";
 9169        ws.Cell(colRow, 12).Value = "5.012";
 170
 54171        foreach (var r in new[] { groupRow, colRow })
 172        {
 18173            var rng = ws.Range(r, 1, r, TotalCols);
 18174            rng.Style.Font.Bold = true;
 18175            rng.Style.Fill.BackgroundColor = headerBg;
 18176            rng.Style.Font.FontColor = XLColor.White;
 18177            rng.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
 18178            rng.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
 18179            rng.Style.Alignment.WrapText = true;
 180        }
 181
 9182        row = dataStartRow;
 183
 9184        var totalGeneral = new TotalesPadron();
 185
 9186        var porPrograma = filas
 6187            .GroupBy(f => (f.ProgramaCodigo, f.ProgramaDenominacion))
 15188            .OrderBy(g => g.Key.ProgramaCodigo);
 189
 30190        foreach (var programaGroup in porPrograma)
 191        {
 6192            ws.Cell(row, 1).Value = $"Programa: {programaGroup.Key.ProgramaCodigo} - {programaGroup.Key.ProgramaDenomina
 6193            ws.Range(row, 1, row, TotalCols).Merge();
 6194            ws.Cell(row, 1).Style.Font.Bold = true;
 6195            ws.Cell(row, 1).Style.Fill.BackgroundColor = XLColor.FromHtml("#D9E1F2");
 6196            row++;
 197
 6198            var totalPrograma = new TotalesPadron();
 199
 6200            var porSituacion = programaGroup
 6201                .GroupBy(f => (f.SituacionCodigo, f.SituacionDenominacion))
 12202                .OrderBy(g => g.Key.SituacionCodigo);
 203
 24204            foreach (var situacionGroup in porSituacion)
 205            {
 6206                ws.Cell(row, 1).Value = $"  Situación: {situacionGroup.Key.SituacionCodigo} - {situacionGroup.Key.Situac
 6207                ws.Range(row, 1, row, TotalCols).Merge();
 6208                ws.Cell(row, 1).Style.Font.Italic = true;
 6209                ws.Cell(row, 1).Style.Fill.BackgroundColor = XLColor.FromHtml("#EEF2F9");
 6210                row++;
 211
 6212                var totalSituacion = new TotalesPadron();
 213
 36214                foreach (var fila in situacionGroup.OrderBy(f => f.GradoOrden).ThenBy(f => f.NombreCompleto))
 215                {
 6216                    EscribirFilaPadron(ws, row, fila);
 6217                    totalSituacion.Acumular(fila);
 6218                    row++;
 219                }
 220
 6221                EscribirSubtotalPadron(ws, row, TotalCols, $"Subtotal {situacionGroup.Key.SituacionDenominacion}", total
 6222                totalPrograma.Acumular(totalSituacion);
 6223                row++;
 224            }
 225
 6226            EscribirSubtotalPadron(ws, row, TotalCols, $"Subtotal Programa {programaGroup.Key.ProgramaCodigo}", totalPro
 6227            totalGeneral.Acumular(totalPrograma);
 6228            row++;
 6229            row++;
 230        }
 231
 9232        EscribirSubtotalPadron(ws, row, TotalCols, "TOTAL GENERAL", totalGeneral, XLColor.FromHtml("#4472C4"), XLColor.W
 233
 9234        ws.Range(dataStartRow, 3, row, TotalCols).Style.NumberFormat.Format = "#,##0.00";
 9235        ws.Columns().AdjustToContents();
 236
 9237        using var ms = new MemoryStream();
 9238        wb.SaveAs(ms);
 9239        return ms.ToArray();
 9240    }
 241
 242    private static void EscribirFilaPadron(IXLWorksheet ws, int row, FilaPadron f)
 243    {
 6244        ws.Cell(row, 1).Value = f.Cedula;
 6245        ws.Cell(row, 2).Value = f.NombreCompleto;
 6246        ws.Cell(row, 3).Value = f.Nominal;
 247        // col 4: DISPON./SUB.TRAN — reservada (vacía por ahora)
 6248        ws.Cell(row, 5).Value = f.SueldoRetiro;
 249
 250        // Montepío: solo una columna aplica por persona
 6251        if (f.EsOficial)
 1252            ws.Cell(row, 6).Value = f.Montepio;       // S.Fun
 5253        else if (f.EsSubalterno)
 4254            ws.Cell(row, 7).Value = f.Montepio;       // Oficial
 255        else
 1256            ws.Cell(row, 9).Value = f.Montepio;       // Civil (col 8 = Tropa, vacía)
 257
 258        // Sanidad: columna determinada por tramo (CodigoRetencion)
 7259        if (f.CodigoSanidad == 5010) ws.Cell(row, 10).Value = f.Sanidad;
 5260        else if (f.CodigoSanidad == 5011) ws.Cell(row, 11).Value = f.Sanidad;
 5261        else ws.Cell(row, 12).Value = f.Sanidad;
 262
 6263        ws.Cell(row, 13).Value = f.Liquido;
 6264    }
 265
 266    private static void EscribirSubtotalPadron(
 267        IXLWorksheet ws, int row, int totalCols, string etiqueta,
 268        TotalesPadron t, XLColor bgColor,
 269        XLColor? fontColor = null, int? fontSize = null)
 270    {
 21271        ws.Cell(row, 1).Value = etiqueta;
 21272        ws.Cell(row, 3).Value = t.Nominal;
 21273        ws.Cell(row, 5).Value = t.SueldoRetiro;
 21274        ws.Cell(row, 6).Value = t.MontepiSFun;
 21275        ws.Cell(row, 7).Value = t.MontepiOficial;
 21276        ws.Cell(row, 9).Value = t.MontepiCivil;
 21277        ws.Cell(row, 10).Value = t.San5010;
 21278        ws.Cell(row, 11).Value = t.San5011;
 21279        ws.Cell(row, 12).Value = t.San5012;
 21280        ws.Cell(row, 13).Value = t.Liquido;
 281
 21282        var rango = ws.Range(row, 1, row, totalCols);
 21283        rango.Style.Font.Bold = true;
 21284        rango.Style.Fill.BackgroundColor = bgColor;
 21285        if (fontColor != null)
 9286            rango.Style.Font.FontColor = fontColor;
 21287        if (fontSize.HasValue)
 9288            rango.Style.Font.FontSize = fontSize.Value;
 21289    }
 290
 291    private static string FormatApellidosNombres(
 292        string primerApellido, string? segundoApellido,
 293        string primerNombre, string? segundoNombre)
 294    {
 6295        var apellidos = primerApellido + (string.IsNullOrWhiteSpace(segundoApellido) ? "" : " " + segundoApellido);
 6296        var nombres = primerNombre + (string.IsNullOrWhiteSpace(segundoNombre) ? "" : " " + segundoNombre);
 6297        return $"{apellidos}, {nombres}";
 298    }
 299
 6300    private record FilaPadron(
 6301        string Cedula,
 12302        string NombreCompleto,
 0303        string GradoDenominacion,
 6304        short GradoOrden,
 12305        bool EsOficial,
 10306        bool EsSubalterno,
 6307        string SituacionCodigo,
 6308        string SituacionDenominacion,
 6309        string ProgramaCodigo,
 6310        string ProgramaDenominacion,
 12311        decimal Nominal,
 12312        decimal SueldoRetiro,
 12313        decimal Montepio,
 12314        decimal Sanidad,
 22315        int CodigoSanidad,   // 5010 / 5011 / 5012 según tramo
 18316        decimal Liquido);
 317
 318    private class TotalesPadron
 319    {
 69320        public decimal Nominal { get; private set; }
 69321        public decimal SueldoRetiro { get; private set; }
 59322        public decimal MontepiSFun { get; private set; }    // EsOficial → col S.Fun
 65323        public decimal MontepiOficial { get; private set; } // EsSubalterno → col Oficial
 59324        public decimal MontepiCivil { get; private set; }   // ni oficial ni subalterno → col Civil
 59325        public decimal San5010 { get; private set; }
 57326        public decimal San5011 { get; private set; }
 67327        public decimal San5012 { get; private set; }
 69328        public decimal Liquido { get; private set; }
 329
 330        public void Acumular(FilaPadron f)
 331        {
 6332            Nominal += f.Nominal;
 6333            SueldoRetiro += f.SueldoRetiro;
 7334            if (f.EsOficial) MontepiSFun += f.Montepio;
 9335            else if (f.EsSubalterno) MontepiOficial += f.Montepio;
 1336            else MontepiCivil += f.Montepio;
 7337            if (f.CodigoSanidad == 5010) San5010 += f.Sanidad;
 5338            else if (f.CodigoSanidad == 5011) San5011 += f.Sanidad;
 5339            else San5012 += f.Sanidad;
 6340            Liquido += f.Liquido;
 6341        }
 342
 343        public void Acumular(TotalesPadron t)
 344        {
 12345            Nominal += t.Nominal;
 12346            SueldoRetiro += t.SueldoRetiro;
 12347            MontepiSFun += t.MontepiSFun;
 12348            MontepiOficial += t.MontepiOficial;
 12349            MontepiCivil += t.MontepiCivil;
 12350            San5010 += t.San5010;
 12351            San5011 += t.San5011;
 12352            San5012 += t.San5012;
 12353            Liquido += t.Liquido;
 12354        }
 355    }
 356}

Methods/Properties

.ctor(FAU.DataAccess.ApplicationDbContext)
GenerarPadronExcelAsync()
ObtenerCodigoSanidad(System.Decimal,System.Collections.Generic.IReadOnlyList`1<FAU.Entidades.TramoSanidadMilitar>)
GenerarExcel(System.Collections.Generic.List`1<FAU.Logica.Services.AguinaldoPadronService/FilaPadron>,System.Int32,System.Int32,System.Boolean)
EscribirFilaPadron(ClosedXML.Excel.IXLWorksheet,System.Int32,FAU.Logica.Services.AguinaldoPadronService/FilaPadron)
EscribirSubtotalPadron(ClosedXML.Excel.IXLWorksheet,System.Int32,System.Int32,System.String,FAU.Logica.Services.AguinaldoPadronService/TotalesPadron,ClosedXML.Excel.XLColor,ClosedXML.Excel.XLColor,System.Nullable`1<System.Int32>)
FormatApellidosNombres(System.String,System.String,System.String,System.String)
.ctor(System.String,System.String,System.String,System.Int16,System.Boolean,System.Boolean,System.String,System.String,System.String,System.String,System.Decimal,System.Decimal,System.Decimal,System.Decimal,System.Int32,System.Decimal)
get_Cedula()
get_NombreCompleto()
get_GradoDenominacion()
get_GradoOrden()
get_EsOficial()
get_EsSubalterno()
get_SituacionCodigo()
get_SituacionDenominacion()
get_ProgramaCodigo()
get_ProgramaDenominacion()
get_Nominal()
get_SueldoRetiro()
get_Montepio()
get_Sanidad()
get_CodigoSanidad()
get_Liquido()
get_Nominal()
get_SueldoRetiro()
get_MontepiSFun()
get_MontepiOficial()
get_MontepiCivil()
get_San5010()
get_San5011()
get_San5012()
get_Liquido()
Acumular(FAU.Logica.Services.AguinaldoPadronService/FilaPadron)
Acumular(FAU.Logica.Services.AguinaldoPadronService/TotalesPadron)