< Summary

Information
Class: FAU.Logica.Services.ReciboService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/ReciboService.cs
Line coverage
90%
Covered lines: 173
Uncovered lines: 19
Coverable lines: 192
Total lines: 240
Line coverage: 90.1%
Branch coverage
90%
Covered branches: 67
Total branches: 74
Branch coverage: 90.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ObtenerResumenesAsync()100%210%
ObtenerReciboAsync()100%11100%
GenerarPdfAsync()100%22100%
GenerarLoteZipAsync()0%4260%
GenerarPdfDesdeRecibo(...)98.43%6464100%
AgregarEncabezadoTabla(...)100%11100%
AgregarFilaTabla(...)100%22100%
AgregarFilaSubtotal(...)100%11100%

File(s)

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

#LineLine coverage
 1using System.IO.Compression;
 2using FAU.DataAccess.Repositories;
 3using FAU.Entidades;
 4using QuestPDF.Fluent;
 5using QuestPDF.Helpers;
 6using QuestPDF.Infrastructure;
 7
 8namespace FAU.Logica.Services;
 9
 10public class ReciboService : IReciboService
 11{
 12    private readonly IReciboRepository _repo;
 13
 814    public ReciboService(IReciboRepository repo)
 15    {
 816        _repo = repo;
 817    }
 18
 19    public async Task<List<ReciboResumenDto>> ObtenerResumenesAsync(long periodoId, ReciboFiltroRequest filtro)
 20    {
 021        var version = await _repo.ObtenerVersionMaximaAsync(periodoId);
 022        return await _repo.ObtenerResumenesAsync(periodoId, version, filtro);
 023    }
 24
 25    public async Task<ReciboDto?> ObtenerReciboAsync(long periodoId, long relacionId)
 26    {
 427        var version = await _repo.ObtenerVersionMaximaAsync(periodoId);
 428        return await _repo.ObtenerDatosReciboAsync(periodoId, version, relacionId);
 429    }
 30
 31    public async Task<byte[]> GenerarPdfAsync(long periodoId, long relacionId)
 32    {
 433        var version = await _repo.ObtenerVersionMaximaAsync(periodoId);
 434        var recibo = await _repo.ObtenerDatosReciboAsync(periodoId, version, relacionId)
 435            ?? throw new KeyNotFoundException($"No se encontró liquidación para la relación {relacionId} en el período {
 36
 337        return GenerarPdfDesdeRecibo(recibo);
 338    }
 39
 40    public async Task<Stream> GenerarLoteZipAsync(long periodoId, ReciboFiltroRequest filtro)
 41    {
 042        var version = await _repo.ObtenerVersionMaximaAsync(periodoId);
 043        var resumenes = await _repo.ObtenerResumenesAsync(periodoId, version, filtro);
 44
 045        var memoryStream = new MemoryStream();
 046        using (var zip = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: true))
 47        {
 048            foreach (var resumen in resumenes)
 49            {
 050                var recibo = await _repo.ObtenerDatosReciboAsync(periodoId, version, resumen.RelacionLaboralId);
 051                if (recibo == null) continue;
 52
 053                var pdfBytes = GenerarPdfDesdeRecibo(recibo);
 054                var entry = zip.CreateEntry($"recibo_{resumen.Cedula}.pdf", CompressionLevel.Fastest);
 055                using var entryStream = entry.Open();
 056                await entryStream.WriteAsync(pdfBytes);
 057            }
 058        }
 59
 060        memoryStream.Position = 0;
 061        return memoryStream;
 062    }
 63
 64    private static byte[] GenerarPdfDesdeRecibo(ReciboDto recibo)
 65    {
 366        var nombreMes = System.Globalization.CultureInfo
 367            .GetCultureInfo("es-UY")
 368            .DateTimeFormat.GetMonthName(recibo.Mes);
 69
 370        var nombreCompleto = string.Join(" ",
 371            new[] { recibo.PrimerNombre, recibo.SegundoNombre, recibo.PrimerApellido, recibo.SegundoApellido }
 1572            .Where(s => !string.IsNullOrWhiteSpace(s)));
 73
 374        return Document.Create(container =>
 375        {
 376            container.Page(page =>
 377            {
 378                page.Size(PageSizes.A4);
 379                page.Margin(30);
 680                page.DefaultTextStyle(x => x.FontSize(9));
 381
 382                page.Content().Column(col =>
 383                {
 384                    // Encabezado
 385                    col.Item().BorderBottom(1).PaddingBottom(6).Row(row =>
 386                    {
 387                        row.RelativeItem().Column(c =>
 388                        {
 389                            c.Item().Text("FUERZAS ARMADAS DEL URUGUAY").Bold().FontSize(12);
 390                            c.Item().Text($"RECIBO DE SUELDO — {nombreMes.ToUpper()} {recibo.Anio}").FontSize(10);
 691                        });
 392                        row.ConstantItem(120).AlignRight().Column(c =>
 393                        {
 394                            c.Item().Text($"Período: {recibo.Mes:D2}/{recibo.Anio}").Bold();
 395                            c.Item().Text($"Régimen: {recibo.Regimen}");
 696                        });
 697                    });
 398
 399                    col.Item().PaddingTop(8).PaddingBottom(4).Row(row =>
 3100                    {
 3101                        row.RelativeItem().Column(c =>
 3102                        {
 12103                            c.Item().Text(t => { t.Span("Funcionario: ").Bold(); t.Span(nombreCompleto); });
 12104                            c.Item().Text(t => { t.Span("Cédula: ").Bold(); t.Span(recibo.Cedula); });
 12105                            c.Item().Text(t => { t.Span("Grado: ").Bold(); t.Span(recibo.Grado); });
 12106                            c.Item().Text(t => { t.Span("Escalafón: ").Bold(); t.Span(recibo.Escalafon); });
 6107                        });
 3108                        row.RelativeItem().Column(c =>
 3109                        {
 12110                            c.Item().Text(t => { t.Span("Unidad: ").Bold(); t.Span(recibo.Unidad); });
 12111                            c.Item().Text(t => { t.Span("Programa: ").Bold(); t.Span(recibo.Programa); });
 6112                        });
 6113                    });
 3114
 3115                    // Tabla haberes
 3116                    col.Item().PaddingTop(8).Text("HABERES").Bold().FontSize(10);
 3117                    col.Item().Table(table =>
 3118                    {
 3119                        table.ColumnsDefinition(cols =>
 3120                        {
 3121                            cols.ConstantColumn(60);
 3122                            cols.RelativeColumn();
 3123                            cols.ConstantColumn(80);
 3124                            cols.ConstantColumn(80);
 6125                        });
 3126                        AgregarEncabezadoTabla(table);
 18127                        foreach (var item in recibo.Haberes)
 6128                            AgregarFilaTabla(table, item);
 3129                        AgregarFilaSubtotal(table, "Subtotal Haberes", recibo.TotalHaberes);
 6130                    });
 3131
 3132                    // Tabla descuentos legales
 3133                    col.Item().PaddingTop(8).Text("DESCUENTOS LEGALES").Bold().FontSize(10);
 3134                    col.Item().Table(table =>
 3135                    {
 3136                        table.ColumnsDefinition(cols =>
 3137                        {
 3138                            cols.ConstantColumn(60);
 3139                            cols.RelativeColumn();
 3140                            cols.ConstantColumn(80);
 3141                            cols.ConstantColumn(80);
 6142                        });
 3143                        AgregarEncabezadoTabla(table);
 18144                        foreach (var item in recibo.DescuentosLegales)
 6145                            AgregarFilaTabla(table, item, negativo: true);
 3146                        AgregarFilaSubtotal(table, "Subtotal Desc. Legales", recibo.TotalDescuentosLegales);
 6147                    });
 3148
 3149                    // Tabla descuentos personales
 3150                    if (recibo.DescuentosPersonales.Any())
 3151                    {
 2152                        col.Item().PaddingTop(8).Text("DESCUENTOS PERSONALES").Bold().FontSize(10);
 2153                        col.Item().Table(table =>
 2154                        {
 2155                            table.ColumnsDefinition(cols =>
 2156                            {
 2157                                cols.ConstantColumn(60);
 2158                                cols.RelativeColumn();
 2159                                cols.ConstantColumn(80);
 2160                                cols.ConstantColumn(80);
 4161                            });
 2162                            AgregarEncabezadoTabla(table);
 8163                            foreach (var item in recibo.DescuentosPersonales)
 2164                                AgregarFilaTabla(table, item, negativo: true);
 2165                            AgregarFilaSubtotal(table, "Subtotal Desc. Personales", recibo.TotalDescuentosPersonales);
 4166                        });
 3167                    }
 3168
 3169                    // Resumen final
 3170                    col.Item().PaddingTop(10).BorderTop(1).PaddingTop(6).Row(row =>
 3171                    {
 3172                        row.RelativeItem();
 3173                        row.ConstantItem(200).Column(c =>
 3174                        {
 3175                            c.Item().Row(r =>
 3176                            {
 3177                                r.RelativeItem().Text("Total Haberes").Bold();
 3178                                r.ConstantItem(100).AlignRight().Text($"{recibo.TotalHaberes:N2}").Bold();
 6179                            });
 3180                            c.Item().Row(r =>
 3181                            {
 3182                                r.RelativeItem().Text("Total Descuentos");
 3183                                r.ConstantItem(100).AlignRight().Text($"({recibo.TotalDescuentos:N2})");
 6184                            });
 3185                            c.Item().BorderTop(1).PaddingTop(3).Row(r =>
 3186                            {
 3187                                r.RelativeItem().Text("LÍQUIDO PAGABLE").Bold().FontSize(11);
 3188                                r.ConstantItem(100).AlignRight().Text($"{recibo.LiquidoPagable:N2}").Bold().FontSize(11)
 6189                            });
 6190                        });
 6191                    });
 3192
 3193                    // Datos bancarios
 3194                    col.Item().PaddingTop(10).Row(row =>
 3195                    {
 3196                        row.RelativeItem().Column(c =>
 3197                        {
 3198                            c.Item().Text("DATOS BANCARIOS").Bold();
 3199                            if (recibo.DatosBancarios.Cuenta != null)
 3200                            {
 8201                                c.Item().Text(t => { t.Span("Banco: ").Bold(); t.Span(recibo.DatosBancarios.Banco ?? "-"
 8202                                c.Item().Text(t => { t.Span("Cuenta: ").Bold(); t.Span(recibo.DatosBancarios.Cuenta); })
 3203                            }
 3204                            else
 3205                            {
 1206                                c.Item().Text(recibo.DatosBancarios.Observacion).Italic();
 3207                            }
 4208                        });
 6209                    });
 6210                });
 6211            });
 6212        }).GeneratePdf();
 213    }
 214
 215    private static void AgregarEncabezadoTabla(TableDescriptor table)
 216    {
 8217        table.Header(header =>
 8218        {
 8219            header.Cell().Background(Colors.Grey.Lighten2).Padding(3).Text("Código").Bold();
 8220            header.Cell().Background(Colors.Grey.Lighten2).Padding(3).Text("Concepto").Bold();
 8221            header.Cell().Background(Colors.Grey.Lighten2).Padding(3).AlignRight().Text("Rubro").Bold();
 8222            header.Cell().Background(Colors.Grey.Lighten2).Padding(3).AlignRight().Text("Importe").Bold();
 16223        });
 8224    }
 225
 226    private static void AgregarFilaTabla(TableDescriptor table, ReciboItemDto item, bool negativo = false)
 227    {
 14228        var monto = negativo ? Math.Abs(item.Monto) : item.Monto;
 14229        table.Cell().Padding(3).Text(item.Codigo);
 14230        table.Cell().Padding(3).Text(item.Descripcion);
 14231        table.Cell().Padding(3).AlignRight().Text(item.RubroContable);
 14232        table.Cell().Padding(3).AlignRight().Text($"{monto:N2}");
 14233    }
 234
 235    private static void AgregarFilaSubtotal(TableDescriptor table, string label, decimal total)
 236    {
 8237        table.Cell().ColumnSpan(3).Padding(3).AlignRight().Text(label).Bold();
 8238        table.Cell().Padding(3).AlignRight().Text($"{total:N2}").Bold();
 8239    }
 240}