< 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
0%
Covered lines: 0
Uncovered lines: 285
Coverable lines: 285
Total lines: 399
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 94
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

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
 012    public AguinaldoReporteControlService(ApplicationDbContext context)
 13    {
 014        _context = context;
 015    }
 16
 17    public async Task<(byte[] Bytes, string FileName)> GenerarExcelAsync(int anio, int mes, string variante, bool esLeyV
 18    {
 019        if (mes != 6 && mes != 12)
 020            throw new ArgumentException("El mes debe ser 6 o 12.", nameof(mes));
 21
 022        variante = variante.ToUpper();
 023        if (variante != "TODOS" && variante != "REVISTAN" && variante != "BAJAS")
 024            throw new ArgumentException("La variante debe ser TODOS, REVISTAN o BAJAS.", nameof(variante));
 25
 026        var semestre = (short)(mes == 6 ? 1 : 2);
 027        var mesesSemestre = ObtenerMesesSemestre(anio, mes);
 028        var filas = new List<FilaControl>();
 29
 030        if (variante == "REVISTAN" || variante == "TODOS")
 31        {
 032            var semestralData = await (
 033                from a in _context.Aguinaldos
 034                where a.Anio == (short)anio && a.Semestre == semestre
 035                join rl in _context.RelacionesLaborales on a.PersonaId equals rl.PersonaId
 036                where rl.Estado == EstadoRelacion.Activo && rl.Regimen.EsLeyVieja == esLeyVieja
 037                select new
 038                {
 039                    a.PersonaId,
 040                    rl.Persona.Cedula,
 041                    rl.Persona.PrimerNombre,
 042                    rl.Persona.SegundoNombre,
 043                    rl.Persona.PrimerApellido,
 044                    rl.Persona.SegundoApellido,
 045                    GradoDenominacion = rl.Grado.Denominacion,
 046                    GradoOrden = rl.Grado.Orden,
 047                    SituacionCodigo = rl.Situacion.Codigo,
 048                    SituacionDenominacion = rl.Situacion.Denominacion,
 049                    ProgramaCodigo = rl.Programa.Codigo,
 050                    ProgramaDenominacion = rl.Programa.Denominacion,
 051                    FechaIngreso = rl.FechaInicio,
 052                }
 053            ).ToListAsync();
 54
 055            var semestralIds = semestralData.Select(d => d.PersonaId).ToList();
 056            var historicoSemestral = await GetHistoricoDict(semestralIds, mesesSemestre);
 57
 058            foreach (var d in semestralData)
 59            {
 060                var mesesData = GetMesesData(d.PersonaId, historicoSemestral, mesesSemestre, null);
 061                var total = mesesData.Sum();
 062                filas.Add(new FilaControl(
 063                    d.PersonaId,
 064                    d.Cedula,
 065                    FormatApellidosNombres(d.PrimerApellido, d.SegundoApellido, d.PrimerNombre, d.SegundoNombre),
 066                    d.GradoDenominacion,
 067                    d.GradoOrden,
 068                    FormatFechaIngreso(d.FechaIngreso),
 069                    d.SituacionCodigo,
 070                    d.SituacionDenominacion,
 071                    d.ProgramaCodigo,
 072                    d.ProgramaDenominacion,
 073                    mesesData,
 074                    total,
 075                    Math.Round(total / 12m, 2),
 076                    false));
 77            }
 078        }
 79
 080        if (variante == "BAJAS" || variante == "TODOS")
 81        {
 082            var bajaRecords = await _context.AguinaldosBaja
 083                .Where(b => b.Anio == (short)anio && b.Semestre == semestre)
 084                .ToListAsync();
 85
 086            var bajaPersonaIds = bajaRecords.Select(b => b.PersonaId).Distinct().ToList();
 87
 088            var relacionesBajaRaw = await _context.RelacionesLaborales
 089                .Where(rl => bajaPersonaIds.Contains(rl.PersonaId) && rl.Regimen.EsLeyVieja == esLeyVieja)
 090                .Select(rl => new
 091                {
 092                    rl.PersonaId,
 093                    rl.FechaInicio,
 094                    GradoDenominacion = rl.Grado.Denominacion,
 095                    GradoOrden = rl.Grado.Orden,
 096                    SituacionCodigo = rl.Situacion.Codigo,
 097                    SituacionDenominacion = rl.Situacion.Denominacion,
 098                    ProgramaCodigo = rl.Programa.Codigo,
 099                    ProgramaDenominacion = rl.Programa.Denominacion,
 0100                    PersFechaInicio = rl.FechaInicio
 0101                })
 0102                .ToListAsync();
 103
 0104            var relacionesBajaDict = relacionesBajaRaw
 0105                .GroupBy(rl => rl.PersonaId)
 0106                .ToDictionary(g => g.Key, g => g.OrderByDescending(rl => rl.FechaInicio).First());
 107
 0108            var historicoBaja = await GetHistoricoDict(bajaPersonaIds, mesesSemestre);
 109
 0110            var semestralPersonaIds = filas.Select(f => f.PersonaId).ToHashSet();
 111
 0112            foreach (var b in bajaRecords)
 113            {
 0114                if (variante == "TODOS" && semestralPersonaIds.Contains(b.PersonaId))
 115                    continue;
 116
 0117                relacionesBajaDict.TryGetValue(b.PersonaId, out var rl);
 118
 0119                var mesesData = GetMesesData(b.PersonaId, historicoBaja, mesesSemestre, b.FechaBaja);
 0120                var total = mesesData.Sum();
 121
 0122                filas.Add(new FilaControl(
 0123                    b.PersonaId,
 0124                    b.Cedula,
 0125                    b.NombreCompleto + " (BAJA)",
 0126                    rl?.GradoDenominacion ?? "",
 0127                    rl?.GradoOrden ?? 0,
 0128                    rl != null ? FormatFechaIngreso(rl.PersFechaInicio) : "",
 0129                    rl?.SituacionCodigo ?? "",
 0130                    rl?.SituacionDenominacion ?? "",
 0131                    rl?.ProgramaCodigo ?? "",
 0132                    rl?.ProgramaDenominacion ?? "",
 0133                    mesesData,
 0134                    total,
 0135                    Math.Round(total / 12m, 2),
 0136                    true));
 137            }
 0138        }
 139
 0140        var leyLabel = esLeyVieja ? "ley_vieja" : "ley_nueva";
 0141        var bytes = GenerarExcel(filas, anio, mes, variante, esLeyVieja, mesesSemestre);
 0142        return (bytes, $"control_aguinaldo_{anio}_{mes:D2}_{variante.ToLower()}_{leyLabel}.xlsx");
 0143    }
 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    {
 0149        if (personaIds.Count == 0)
 0150            return new Dictionary<long, Dictionary<(short Anio, short Mes), decimal>>();
 151
 0152        var aniosSemestre = mesesSemestre.Select(m => m.Anio).Distinct().ToList();
 0153        var mesesIds = mesesSemestre.Select(m => m.Mes).Distinct().ToList();
 154
 0155        var historicos = await _context.HistoricoLiquidaciones
 0156            .Where(h => personaIds.Contains(h.PersonaId)
 0157                && aniosSemestre.Contains(h.Anio)
 0158                && mesesIds.Contains(h.Mes))
 0159            .Select(h => new { h.PersonaId, h.Anio, h.Mes, h.HaberesGravados })
 0160            .ToListAsync();
 161
 0162        var mesesSet = mesesSemestre.ToHashSet();
 0163        return historicos
 0164            .Where(h => mesesSet.Contains((h.Anio, h.Mes)))
 0165            .GroupBy(h => h.PersonaId)
 0166            .ToDictionary(
 0167                g => g.Key,
 0168                g => g.ToDictionary(h => (h.Anio, h.Mes), h => h.HaberesGravados));
 0169    }
 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    {
 0177        var personaDict = dict.GetValueOrDefault(personaId) ?? new Dictionary<(short Anio, short Mes), decimal>();
 0178        return mesesSemestre.Select(m =>
 0179        {
 0180            if (fechaBaja.HasValue)
 0181            {
 0182                var mesDate = new DateTime(m.Anio, m.Mes, 1);
 0183                var bajaMonth = new DateTime(fechaBaja.Value.Year, fechaBaja.Value.Month, 1);
 0184                if (mesDate > bajaMonth)
 0185                    return 0m;
 0186            }
 0187            return personaDict.GetValueOrDefault(m, 0m);
 0188        }).ToArray();
 189    }
 190
 191    private static IReadOnlyList<(short Anio, short Mes)> ObtenerMesesSemestre(int anio, int mes)
 192    {
 0193        if (mes == 6)
 0194            return new[]
 0195            {
 0196                ((short)(anio - 1), (short)12),
 0197                ((short)anio, (short)1),
 0198                ((short)anio, (short)2),
 0199                ((short)anio, (short)3),
 0200                ((short)anio, (short)4),
 0201                ((short)anio, (short)5)
 0202            };
 203
 0204        return new[]
 0205        {
 0206            ((short)anio, (short)6),
 0207            ((short)anio, (short)7),
 0208            ((short)anio, (short)8),
 0209            ((short)anio, (short)9),
 0210            ((short)anio, (short)10),
 0211            ((short)anio, (short)11)
 0212        };
 213    }
 214
 215    private static string[] GetMesHeaders(IReadOnlyList<(short Anio, short Mes)> meses)
 216    {
 0217        string[] nombres = ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"];
 0218        return meses.Select(m => $"{nombres[m.Mes - 1]} {m.Anio}").ToArray();
 219    }
 220
 0221    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    {
 0227        var apellidos = primerApellido + (string.IsNullOrWhiteSpace(segundoApellido) ? "" : " " + segundoApellido);
 0228        var nombres = primerNombre + (string.IsNullOrWhiteSpace(segundoNombre) ? "" : " " + segundoNombre);
 0229        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    {
 0237        using var wb = new XLWorkbook();
 0238        var ws = wb.Worksheets.Add("Control");
 239
 0240        var mesHeaders = GetMesHeaders(mesesSemestre);
 0241        var semNombre = mes == 6 ? "1er Semestre" : "2do Semestre";
 0242        var leyNombre = esLeyVieja ? "Ley Vieja (14.157)" : "Ley Nueva (19.695)";
 243        const int TotalCols = 12;
 244        const int RightCol = 10;
 0245        int row = 1;
 246
 247        // ── Cabezal ──────────────────────────────────────────────────────────
 0248        ws.Cell(row, 1).Value = "F.A.U. Dirección de Economía y Finanzas";
 0249        ws.Range(row, 1, row, RightCol - 1).Merge();
 0250        ws.Cell(row, RightCol).Value = $"Fecha: {DateTime.Now:dd/MM/yyyy}";
 0251        ws.Range(row, RightCol, row, TotalCols).Merge();
 0252        ws.Cell(row, RightCol).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right;
 0253        row++;
 254
 0255        ws.Cell(row, 1).Value = "Departamento de Administración Presupuestal";
 0256        ws.Range(row, 1, row, RightCol - 1).Merge();
 0257        ws.Cell(row, RightCol).Value = $"Hora : {DateTime.Now:HH:mm:ss}";
 0258        ws.Range(row, RightCol, row, TotalCols).Merge();
 0259        ws.Cell(row, RightCol).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right;
 0260        row++;
 261
 0262        ws.Cell(row, 1).Value = $"{variante} | {leyNombre} | {semNombre} {anio}";
 0263        ws.Range(row, 1, row, TotalCols).Merge();
 0264        ws.Cell(row, 1).Style.Font.Bold = true;
 0265        ws.Cell(row, 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
 0266        row += 2;
 267
 268        // ── Cabecera de columnas ──────────────────────────────────────────────
 0269        string[] headers = [
 0270            "Ingreso FAU", "Grado", "Cédula", "Apellidos y Nombres",
 0271            mesHeaders[0], mesHeaders[1], mesHeaders[2],
 0272            mesHeaders[3], mesHeaders[4], mesHeaders[5],
 0273            "Total Semestral", "1/12"
 0274        ];
 275
 0276        for (int c = 0; c < headers.Length; c++)
 277        {
 0278            var cell = ws.Cell(row, c + 1);
 0279            cell.Value = headers[c];
 0280            cell.Style.Font.Bold = true;
 0281            cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#4472C4");
 0282            cell.Style.Font.FontColor = XLColor.White;
 0283            cell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
 0284            cell.Style.Alignment.WrapText = true;
 285        }
 0286        int headerRow = row;
 0287        row++;
 288
 0289        var totalGeneral = new decimal[8];
 290
 0291        var porPrograma = filas
 0292            .GroupBy(f => (f.ProgramaCodigo, f.ProgramaDenominacion))
 0293            .OrderBy(g => g.Key.ProgramaCodigo);
 294
 0295        foreach (var programaGroup in porPrograma)
 296        {
 0297            ws.Cell(row, 1).Value = $"Programa: {programaGroup.Key.ProgramaCodigo} - {programaGroup.Key.ProgramaDenomina
 0298            ws.Range(row, 1, row, TotalCols).Merge();
 0299            ws.Cell(row, 1).Style.Font.Bold = true;
 0300            ws.Cell(row, 1).Style.Fill.BackgroundColor = XLColor.FromHtml("#D9E1F2");
 0301            row++;
 302
 0303            var totalPrograma = new decimal[8];
 304
 0305            var porSituacion = programaGroup
 0306                .GroupBy(f => (f.SituacionCodigo, f.SituacionDenominacion))
 0307                .OrderBy(g => g.Key.SituacionCodigo);
 308
 0309            foreach (var situacionGroup in porSituacion)
 310            {
 0311                ws.Cell(row, 1).Value = $"  Situación: {situacionGroup.Key.SituacionCodigo} - {situacionGroup.Key.Situac
 0312                ws.Range(row, 1, row, TotalCols).Merge();
 0313                ws.Cell(row, 1).Style.Font.Italic = true;
 0314                ws.Cell(row, 1).Style.Fill.BackgroundColor = XLColor.FromHtml("#EEF2F9");
 0315                row++;
 316
 0317                var totalSituacion = new decimal[8];
 318
 0319                foreach (var fila in situacionGroup.OrderBy(f => f.GradoOrden).ThenBy(f => f.NombreCompleto))
 320                {
 0321                    ws.Cell(row, 1).Value = fila.FechaIngreso;
 0322                    ws.Cell(row, 2).Value = fila.GradoDenominacion;
 0323                    ws.Cell(row, 3).Value = fila.Cedula;
 0324                    ws.Cell(row, 4).Value = fila.NombreCompleto;
 0325                    for (int i = 0; i < 6; i++)
 0326                        ws.Cell(row, 5 + i).Value = fila.MesesData[i];
 0327                    ws.Cell(row, 11).Value = fila.TotalSemestral;
 0328                    ws.Cell(row, 12).Value = fila.Duodecimo;
 329
 0330                    if (fila.EsBaja)
 0331                        ws.Range(row, 1, row, TotalCols).Style.Font.Italic = true;
 332
 0333                    for (int i = 0; i < 6; i++) totalSituacion[i] += fila.MesesData[i];
 0334                    totalSituacion[6] += fila.TotalSemestral;
 0335                    totalSituacion[7] += fila.Duodecimo;
 0336                    row++;
 337                }
 338
 0339                EscribirSubtotal(ws, row, TotalCols,
 0340                    $"Subtotal {situacionGroup.Key.SituacionDenominacion}", totalSituacion,
 0341                    XLColor.FromHtml("#EEF2F9"));
 0342                for (int i = 0; i < 8; i++) totalPrograma[i] += totalSituacion[i];
 0343                row++;
 344            }
 345
 0346            EscribirSubtotal(ws, row, TotalCols,
 0347                $"Subtotal Programa {programaGroup.Key.ProgramaCodigo}", totalPrograma,
 0348                XLColor.FromHtml("#D9E1F2"));
 0349            for (int i = 0; i < 8; i++) totalGeneral[i] += totalPrograma[i];
 0350            row++;
 0351            row++;
 352        }
 353
 0354        ws.Cell(row, 4).Value = "TOTAL GENERAL";
 0355        ws.Cell(row, 4).Style.Font.Bold = true;
 0356        ws.Cell(row, 4).Style.Font.FontSize = 12;
 0357        for (int i = 0; i < 6; i++)
 0358            ws.Cell(row, 5 + i).Value = totalGeneral[i];
 0359        ws.Cell(row, 11).Value = totalGeneral[6];
 0360        ws.Cell(row, 12).Value = totalGeneral[7];
 0361        ws.Range(row, 1, row, TotalCols).Style.Font.Bold = true;
 0362        ws.Range(row, 1, row, TotalCols).Style.Fill.BackgroundColor = XLColor.FromHtml("#4472C4");
 0363        ws.Range(row, 1, row, TotalCols).Style.Font.FontColor = XLColor.White;
 364
 0365        ws.Range(headerRow, 5, row, 12).Style.NumberFormat.Format = "#,##0.00";
 0366        ws.Columns().AdjustToContents();
 367
 0368        using var ms = new MemoryStream();
 0369        wb.SaveAs(ms);
 0370        return ms.ToArray();
 0371    }
 372
 373    private static void EscribirSubtotal(IXLWorksheet ws, int row, int totalCols, string etiqueta, decimal[] totales, XL
 374    {
 0375        ws.Cell(row, 4).Value = etiqueta;
 0376        for (int i = 0; i < 6; i++)
 0377            ws.Cell(row, 5 + i).Value = totales[i];
 0378        ws.Cell(row, 11).Value = totales[6];
 0379        ws.Cell(row, 12).Value = totales[7];
 0380        ws.Range(row, 1, row, totalCols).Style.Font.Bold = true;
 0381        ws.Range(row, 1, row, totalCols).Style.Fill.BackgroundColor = bgColor;
 0382    }
 383
 0384    private record FilaControl(
 0385        long PersonaId,
 0386        string Cedula,
 0387        string NombreCompleto,
 0388        string GradoDenominacion,
 0389        short GradoOrden,
 0390        string FechaIngreso,
 0391        string SituacionCodigo,
 0392        string SituacionDenominacion,
 0393        string ProgramaCodigo,
 0394        string ProgramaDenominacion,
 0395        decimal[] MesesData,
 0396        decimal TotalSemestral,
 0397        decimal Duodecimo,
 0398        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()