< Summary

Information
Class: FAU.Logica.Services.AguinaldoReporteControlService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/AguinaldoReporteControlService.cs
Line coverage
100%
Covered lines: 285
Uncovered lines: 0
Coverable lines: 285
Total lines: 399
Line coverage: 100%
Branch coverage
85%
Covered branches: 80
Total branches: 94
Branch coverage: 85.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%
GenerarExcelAsync()77.77%5454100%
GetHistoricoDict()100%22100%
GetMesesData(...)100%66100%
ObtenerMesesSemestre(...)100%22100%
GetMesHeaders(...)100%11100%
FormatFechaIngreso(...)100%11100%
FormatApellidosNombres(...)50%44100%
GenerarExcel(...)100%2424100%
EscribirSubtotal(...)100%22100%
.ctor(...)100%11100%
get_PersonaId()100%11100%
get_Cedula()100%11100%
get_NombreCompleto()100%11100%
get_GradoDenominacion()100%11100%
get_GradoOrden()100%11100%
get_FechaIngreso()100%11100%
get_SituacionCodigo()100%11100%
get_SituacionDenominacion()100%11100%
get_ProgramaCodigo()100%11100%
get_ProgramaDenominacion()100%11100%
get_MesesData()100%11100%
get_TotalSemestral()100%11100%
get_Duodecimo()100%11100%
get_EsBaja()100%11100%

File(s)

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

#LineLine coverage
 1using ClosedXML.Excel;
 2using FAU.DataAccess;
 3using FAU.Entidades;
 4using Microsoft.EntityFrameworkCore;
 5
 6namespace FAU.Logica.Services;
 7
 8public class AguinaldoReporteControlService : IAguinaldoReporteControlService
 9{
 10    private readonly ApplicationDbContext _context;
 11
 1112    public AguinaldoReporteControlService(ApplicationDbContext context)
 13    {
 1114        _context = context;
 1115    }
 16
 17    public async Task<(byte[] Bytes, string FileName)> GenerarExcelAsync(int anio, int mes, string variante, bool esLeyV
 18    {
 1119        if (mes != 6 && mes != 12)
 120            throw new ArgumentException("El mes debe ser 6 o 12.", nameof(mes));
 21
 1022        variante = variante.ToUpper();
 1023        if (variante != "TODOS" && variante != "REVISTAN" && variante != "BAJAS")
 124            throw new ArgumentException("La variante debe ser TODOS, REVISTAN o BAJAS.", nameof(variante));
 25
 926        var semestre = (short)(mes == 6 ? 1 : 2);
 927        var mesesSemestre = ObtenerMesesSemestre(anio, mes);
 928        var filas = new List<FilaControl>();
 29
 930        if (variante == "REVISTAN" || variante == "TODOS")
 31        {
 732            var semestralData = await (
 733                from a in _context.Aguinaldos
 734                where a.Anio == (short)anio && a.Semestre == semestre
 735                join rl in _context.RelacionesLaborales on a.PersonaId equals rl.PersonaId
 736                where rl.Estado == EstadoRelacion.Activo && rl.Regimen.EsLeyVieja == esLeyVieja
 737                select new
 738                {
 739                    a.PersonaId,
 740                    rl.Persona.Cedula,
 741                    rl.Persona.PrimerNombre,
 742                    rl.Persona.SegundoNombre,
 743                    rl.Persona.PrimerApellido,
 744                    rl.Persona.SegundoApellido,
 745                    GradoDenominacion = rl.Grado.Denominacion,
 746                    GradoOrden = rl.Grado.Orden,
 747                    SituacionCodigo = rl.Situacion.Codigo,
 748                    SituacionDenominacion = rl.Situacion.Denominacion,
 749                    ProgramaCodigo = rl.Programa.Codigo,
 750                    ProgramaDenominacion = rl.Programa.Denominacion,
 751                    FechaIngreso = rl.FechaInicio,
 752                }
 753            ).ToListAsync();
 54
 1155            var semestralIds = semestralData.Select(d => d.PersonaId).ToList();
 756            var historicoSemestral = await GetHistoricoDict(semestralIds, mesesSemestre);
 57
 2258            foreach (var d in semestralData)
 59            {
 460                var mesesData = GetMesesData(d.PersonaId, historicoSemestral, mesesSemestre, null);
 461                var total = mesesData.Sum();
 462                filas.Add(new FilaControl(
 463                    d.PersonaId,
 464                    d.Cedula,
 465                    FormatApellidosNombres(d.PrimerApellido, d.SegundoApellido, d.PrimerNombre, d.SegundoNombre),
 466                    d.GradoDenominacion,
 467                    d.GradoOrden,
 468                    FormatFechaIngreso(d.FechaIngreso),
 469                    d.SituacionCodigo,
 470                    d.SituacionDenominacion,
 471                    d.ProgramaCodigo,
 472                    d.ProgramaDenominacion,
 473                    mesesData,
 474                    total,
 475                    Math.Round(total / 12m, 2),
 476                    false));
 77            }
 778        }
 79
 980        if (variante == "BAJAS" || variante == "TODOS")
 81        {
 482            var bajaRecords = await _context.AguinaldosBaja
 483                .Where(b => b.Anio == (short)anio && b.Semestre == semestre)
 484                .ToListAsync();
 85
 686            var bajaPersonaIds = bajaRecords.Select(b => b.PersonaId).Distinct().ToList();
 87
 488            var relacionesBajaRaw = await _context.RelacionesLaborales
 489                .Where(rl => bajaPersonaIds.Contains(rl.PersonaId) && rl.Regimen.EsLeyVieja == esLeyVieja)
 490                .Select(rl => new
 491                {
 492                    rl.PersonaId,
 493                    rl.FechaInicio,
 494                    GradoDenominacion = rl.Grado.Denominacion,
 495                    GradoOrden = rl.Grado.Orden,
 496                    SituacionCodigo = rl.Situacion.Codigo,
 497                    SituacionDenominacion = rl.Situacion.Denominacion,
 498                    ProgramaCodigo = rl.Programa.Codigo,
 499                    ProgramaDenominacion = rl.Programa.Denominacion,
 4100                    PersFechaInicio = rl.FechaInicio
 4101                })
 4102                .ToListAsync();
 103
 4104            var relacionesBajaDict = relacionesBajaRaw
 2105                .GroupBy(rl => rl.PersonaId)
 10106                .ToDictionary(g => g.Key, g => g.OrderByDescending(rl => rl.FechaInicio).First());
 107
 4108            var historicoBaja = await GetHistoricoDict(bajaPersonaIds, mesesSemestre);
 109
 5110            var semestralPersonaIds = filas.Select(f => f.PersonaId).ToHashSet();
 111
 12112            foreach (var b in bajaRecords)
 113            {
 2114                if (variante == "TODOS" && semestralPersonaIds.Contains(b.PersonaId))
 115                    continue;
 116
 1117                relacionesBajaDict.TryGetValue(b.PersonaId, out var rl);
 118
 1119                var mesesData = GetMesesData(b.PersonaId, historicoBaja, mesesSemestre, b.FechaBaja);
 1120                var total = mesesData.Sum();
 121
 1122                filas.Add(new FilaControl(
 1123                    b.PersonaId,
 1124                    b.Cedula,
 1125                    b.NombreCompleto + " (BAJA)",
 1126                    rl?.GradoDenominacion ?? "",
 1127                    rl?.GradoOrden ?? 0,
 1128                    rl != null ? FormatFechaIngreso(rl.PersFechaInicio) : "",
 1129                    rl?.SituacionCodigo ?? "",
 1130                    rl?.SituacionDenominacion ?? "",
 1131                    rl?.ProgramaCodigo ?? "",
 1132                    rl?.ProgramaDenominacion ?? "",
 1133                    mesesData,
 1134                    total,
 1135                    Math.Round(total / 12m, 2),
 1136                    true));
 137            }
 4138        }
 139
 9140        var leyLabel = esLeyVieja ? "ley_vieja" : "ley_nueva";
 9141        var bytes = GenerarExcel(filas, anio, mes, variante, esLeyVieja, mesesSemestre);
 9142        return (bytes, $"control_aguinaldo_{anio}_{mes:D2}_{variante.ToLower()}_{leyLabel}.xlsx");
 9143    }
 144
 145    private async Task<Dictionary<long, Dictionary<(short Anio, short Mes), decimal>>> GetHistoricoDict(
 146        List<long> personaIds,
 147        IReadOnlyList<(short Anio, short Mes)> mesesSemestre)
 148    {
 11149        if (personaIds.Count == 0)
 5150            return new Dictionary<long, Dictionary<(short Anio, short Mes), decimal>>();
 151
 42152        var aniosSemestre = mesesSemestre.Select(m => m.Anio).Distinct().ToList();
 42153        var mesesIds = mesesSemestre.Select(m => m.Mes).Distinct().ToList();
 154
 6155        var historicos = await _context.HistoricoLiquidaciones
 6156            .Where(h => personaIds.Contains(h.PersonaId)
 6157                && aniosSemestre.Contains(h.Anio)
 6158                && mesesIds.Contains(h.Mes))
 6159            .Select(h => new { h.PersonaId, h.Anio, h.Mes, h.HaberesGravados })
 6160            .ToListAsync();
 161
 6162        var mesesSet = mesesSemestre.ToHashSet();
 6163        return historicos
 1164            .Where(h => mesesSet.Contains((h.Anio, h.Mes)))
 1165            .GroupBy(h => h.PersonaId)
 6166            .ToDictionary(
 1167                g => g.Key,
 9168                g => g.ToDictionary(h => (h.Anio, h.Mes), h => h.HaberesGravados));
 11169    }
 170
 171    private static decimal[] GetMesesData(
 172        long personaId,
 173        Dictionary<long, Dictionary<(short Anio, short Mes), decimal>> dict,
 174        IReadOnlyList<(short Anio, short Mes)> mesesSemestre,
 175        DateTime? fechaBaja)
 176    {
 5177        var personaDict = dict.GetValueOrDefault(personaId) ?? new Dictionary<(short Anio, short Mes), decimal>();
 5178        return mesesSemestre.Select(m =>
 5179        {
 30180            if (fechaBaja.HasValue)
 5181            {
 6182                var mesDate = new DateTime(m.Anio, m.Mes, 1);
 6183                var bajaMonth = new DateTime(fechaBaja.Value.Year, fechaBaja.Value.Month, 1);
 6184                if (mesDate > bajaMonth)
 2185                    return 0m;
 5186            }
 28187            return personaDict.GetValueOrDefault(m, 0m);
 5188        }).ToArray();
 189    }
 190
 191    private static IReadOnlyList<(short Anio, short Mes)> ObtenerMesesSemestre(int anio, int mes)
 192    {
 9193        if (mes == 6)
 8194            return new[]
 8195            {
 8196                ((short)(anio - 1), (short)12),
 8197                ((short)anio, (short)1),
 8198                ((short)anio, (short)2),
 8199                ((short)anio, (short)3),
 8200                ((short)anio, (short)4),
 8201                ((short)anio, (short)5)
 8202            };
 203
 1204        return new[]
 1205        {
 1206            ((short)anio, (short)6),
 1207            ((short)anio, (short)7),
 1208            ((short)anio, (short)8),
 1209            ((short)anio, (short)9),
 1210            ((short)anio, (short)10),
 1211            ((short)anio, (short)11)
 1212        };
 213    }
 214
 215    private static string[] GetMesHeaders(IReadOnlyList<(short Anio, short Mes)> meses)
 216    {
 9217        string[] nombres = ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"];
 63218        return meses.Select(m => $"{nombres[m.Mes - 1]} {m.Anio}").ToArray();
 219    }
 220
 5221    private static string FormatFechaIngreso(DateTime fecha) => $"{fecha:MM/yyyy}";
 222
 223    private static string FormatApellidosNombres(
 224        string primerApellido, string? segundoApellido,
 225        string primerNombre, string? segundoNombre)
 226    {
 4227        var apellidos = primerApellido + (string.IsNullOrWhiteSpace(segundoApellido) ? "" : " " + segundoApellido);
 4228        var nombres = primerNombre + (string.IsNullOrWhiteSpace(segundoNombre) ? "" : " " + segundoNombre);
 4229        return $"{apellidos}, {nombres}";
 230    }
 231
 232    private static byte[] GenerarExcel(
 233        List<FilaControl> filas,
 234        int anio, int mes, string variante, bool esLeyVieja,
 235        IReadOnlyList<(short Anio, short Mes)> mesesSemestre)
 236    {
 9237        using var wb = new XLWorkbook();
 9238        var ws = wb.Worksheets.Add("Control");
 239
 9240        var mesHeaders = GetMesHeaders(mesesSemestre);
 9241        var semNombre = mes == 6 ? "1er Semestre" : "2do Semestre";
 9242        var leyNombre = esLeyVieja ? "Ley Vieja (14.157)" : "Ley Nueva (19.695)";
 243        const int TotalCols = 12;
 244        const int RightCol = 10;
 9245        int row = 1;
 246
 247        // ── Cabezal ──────────────────────────────────────────────────────────
 9248        ws.Cell(row, 1).Value = "F.A.U. Dirección de Economía y Finanzas";
 9249        ws.Range(row, 1, row, RightCol - 1).Merge();
 9250        ws.Cell(row, RightCol).Value = $"Fecha: {DateTime.Now:dd/MM/yyyy}";
 9251        ws.Range(row, RightCol, row, TotalCols).Merge();
 9252        ws.Cell(row, RightCol).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right;
 9253        row++;
 254
 9255        ws.Cell(row, 1).Value = "Departamento de Administración Presupuestal";
 9256        ws.Range(row, 1, row, RightCol - 1).Merge();
 9257        ws.Cell(row, RightCol).Value = $"Hora : {DateTime.Now:HH:mm:ss}";
 9258        ws.Range(row, RightCol, row, TotalCols).Merge();
 9259        ws.Cell(row, RightCol).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right;
 9260        row++;
 261
 9262        ws.Cell(row, 1).Value = $"{variante} | {leyNombre} | {semNombre} {anio}";
 9263        ws.Range(row, 1, row, TotalCols).Merge();
 9264        ws.Cell(row, 1).Style.Font.Bold = true;
 9265        ws.Cell(row, 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
 9266        row += 2;
 267
 268        // ── Cabecera de columnas ──────────────────────────────────────────────
 9269        string[] headers = [
 9270            "Ingreso FAU", "Grado", "Cédula", "Apellidos y Nombres",
 9271            mesHeaders[0], mesHeaders[1], mesHeaders[2],
 9272            mesHeaders[3], mesHeaders[4], mesHeaders[5],
 9273            "Total Semestral", "1/12"
 9274        ];
 275
 234276        for (int c = 0; c < headers.Length; c++)
 277        {
 108278            var cell = ws.Cell(row, c + 1);
 108279            cell.Value = headers[c];
 108280            cell.Style.Font.Bold = true;
 108281            cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#4472C4");
 108282            cell.Style.Font.FontColor = XLColor.White;
 108283            cell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
 108284            cell.Style.Alignment.WrapText = true;
 285        }
 9286        int headerRow = row;
 9287        row++;
 288
 9289        var totalGeneral = new decimal[8];
 290
 9291        var porPrograma = filas
 5292            .GroupBy(f => (f.ProgramaCodigo, f.ProgramaDenominacion))
 14293            .OrderBy(g => g.Key.ProgramaCodigo);
 294
 28295        foreach (var programaGroup in porPrograma)
 296        {
 5297            ws.Cell(row, 1).Value = $"Programa: {programaGroup.Key.ProgramaCodigo} - {programaGroup.Key.ProgramaDenomina
 5298            ws.Range(row, 1, row, TotalCols).Merge();
 5299            ws.Cell(row, 1).Style.Font.Bold = true;
 5300            ws.Cell(row, 1).Style.Fill.BackgroundColor = XLColor.FromHtml("#D9E1F2");
 5301            row++;
 302
 5303            var totalPrograma = new decimal[8];
 304
 5305            var porSituacion = programaGroup
 5306                .GroupBy(f => (f.SituacionCodigo, f.SituacionDenominacion))
 10307                .OrderBy(g => g.Key.SituacionCodigo);
 308
 20309            foreach (var situacionGroup in porSituacion)
 310            {
 5311                ws.Cell(row, 1).Value = $"  Situación: {situacionGroup.Key.SituacionCodigo} - {situacionGroup.Key.Situac
 5312                ws.Range(row, 1, row, TotalCols).Merge();
 5313                ws.Cell(row, 1).Style.Font.Italic = true;
 5314                ws.Cell(row, 1).Style.Fill.BackgroundColor = XLColor.FromHtml("#EEF2F9");
 5315                row++;
 316
 5317                var totalSituacion = new decimal[8];
 318
 30319                foreach (var fila in situacionGroup.OrderBy(f => f.GradoOrden).ThenBy(f => f.NombreCompleto))
 320                {
 5321                    ws.Cell(row, 1).Value = fila.FechaIngreso;
 5322                    ws.Cell(row, 2).Value = fila.GradoDenominacion;
 5323                    ws.Cell(row, 3).Value = fila.Cedula;
 5324                    ws.Cell(row, 4).Value = fila.NombreCompleto;
 70325                    for (int i = 0; i < 6; i++)
 30326                        ws.Cell(row, 5 + i).Value = fila.MesesData[i];
 5327                    ws.Cell(row, 11).Value = fila.TotalSemestral;
 5328                    ws.Cell(row, 12).Value = fila.Duodecimo;
 329
 5330                    if (fila.EsBaja)
 1331                        ws.Range(row, 1, row, TotalCols).Style.Font.Italic = true;
 332
 100333                    for (int i = 0; i < 6; i++) totalSituacion[i] += fila.MesesData[i];
 5334                    totalSituacion[6] += fila.TotalSemestral;
 5335                    totalSituacion[7] += fila.Duodecimo;
 5336                    row++;
 337                }
 338
 5339                EscribirSubtotal(ws, row, TotalCols,
 5340                    $"Subtotal {situacionGroup.Key.SituacionDenominacion}", totalSituacion,
 5341                    XLColor.FromHtml("#EEF2F9"));
 130342                for (int i = 0; i < 8; i++) totalPrograma[i] += totalSituacion[i];
 5343                row++;
 344            }
 345
 5346            EscribirSubtotal(ws, row, TotalCols,
 5347                $"Subtotal Programa {programaGroup.Key.ProgramaCodigo}", totalPrograma,
 5348                XLColor.FromHtml("#D9E1F2"));
 130349            for (int i = 0; i < 8; i++) totalGeneral[i] += totalPrograma[i];
 5350            row++;
 5351            row++;
 352        }
 353
 9354        ws.Cell(row, 4).Value = "TOTAL GENERAL";
 9355        ws.Cell(row, 4).Style.Font.Bold = true;
 9356        ws.Cell(row, 4).Style.Font.FontSize = 12;
 126357        for (int i = 0; i < 6; i++)
 54358            ws.Cell(row, 5 + i).Value = totalGeneral[i];
 9359        ws.Cell(row, 11).Value = totalGeneral[6];
 9360        ws.Cell(row, 12).Value = totalGeneral[7];
 9361        ws.Range(row, 1, row, TotalCols).Style.Font.Bold = true;
 9362        ws.Range(row, 1, row, TotalCols).Style.Fill.BackgroundColor = XLColor.FromHtml("#4472C4");
 9363        ws.Range(row, 1, row, TotalCols).Style.Font.FontColor = XLColor.White;
 364
 9365        ws.Range(headerRow, 5, row, 12).Style.NumberFormat.Format = "#,##0.00";
 9366        ws.Columns().AdjustToContents();
 367
 9368        using var ms = new MemoryStream();
 9369        wb.SaveAs(ms);
 9370        return ms.ToArray();
 9371    }
 372
 373    private static void EscribirSubtotal(IXLWorksheet ws, int row, int totalCols, string etiqueta, decimal[] totales, XL
 374    {
 10375        ws.Cell(row, 4).Value = etiqueta;
 140376        for (int i = 0; i < 6; i++)
 60377            ws.Cell(row, 5 + i).Value = totales[i];
 10378        ws.Cell(row, 11).Value = totales[6];
 10379        ws.Cell(row, 12).Value = totales[7];
 10380        ws.Range(row, 1, row, totalCols).Style.Font.Bold = true;
 10381        ws.Range(row, 1, row, totalCols).Style.Fill.BackgroundColor = bgColor;
 10382    }
 383
 5384    private record FilaControl(
 1385        long PersonaId,
 5386        string Cedula,
 10387        string NombreCompleto,
 5388        string GradoDenominacion,
 5389        short GradoOrden,
 5390        string FechaIngreso,
 5391        string SituacionCodigo,
 5392        string SituacionDenominacion,
 5393        string ProgramaCodigo,
 5394        string ProgramaDenominacion,
 60395        decimal[] MesesData,
 10396        decimal TotalSemestral,
 10397        decimal Duodecimo,
 10398        bool EsBaja);
 399}

Methods/Properties

.ctor(FAU.DataAccess.ApplicationDbContext)
GenerarExcelAsync()
GetHistoricoDict()
GetMesesData(System.Int64,System.Collections.Generic.Dictionary`2<System.Int64,System.Collections.Generic.Dictionary`2<System.ValueTuple`2<System.Int16,System.Int16>,System.Decimal>>,System.Collections.Generic.IReadOnlyList`1<System.ValueTuple`2<System.Int16,System.Int16>>,System.Nullable`1<System.DateTime>)
ObtenerMesesSemestre(System.Int32,System.Int32)
GetMesHeaders(System.Collections.Generic.IReadOnlyList`1<System.ValueTuple`2<System.Int16,System.Int16>>)
FormatFechaIngreso(System.DateTime)
FormatApellidosNombres(System.String,System.String,System.String,System.String)
GenerarExcel(System.Collections.Generic.List`1<FAU.Logica.Services.AguinaldoReporteControlService/FilaControl>,System.Int32,System.Int32,System.String,System.Boolean,System.Collections.Generic.IReadOnlyList`1<System.ValueTuple`2<System.Int16,System.Int16>>)
EscribirSubtotal(ClosedXML.Excel.IXLWorksheet,System.Int32,System.Int32,System.String,System.Decimal[],ClosedXML.Excel.XLColor)
.ctor(System.Int64,System.String,System.String,System.String,System.Int16,System.String,System.String,System.String,System.String,System.String,System.Decimal[],System.Decimal,System.Decimal,System.Boolean)
get_PersonaId()
get_Cedula()
get_NombreCompleto()
get_GradoDenominacion()
get_GradoOrden()
get_FechaIngreso()
get_SituacionCodigo()
get_SituacionDenominacion()
get_ProgramaCodigo()
get_ProgramaDenominacion()
get_MesesData()
get_TotalSemestral()
get_Duodecimo()
get_EsBaja()