using GestionIntegral.Api.Dtos.Reportes.ViewModels; using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates { public class DistribucionSuscripcionesDocument : IDocument { public DistribucionSuscripcionesViewModel Model { get; } public DistribucionSuscripcionesDocument(DistribucionSuscripcionesViewModel model) { Model = model; } public DocumentMetadata GetMetadata() => DocumentMetadata.Default; public void Compose(IDocumentContainer container) { container.Page(page => { page.Margin(1, Unit.Centimetre); page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(9)); page.Header().Element(ComposeHeader); page.Content().Element(ComposeContent); page.Footer().AlignCenter().Text(x => { x.Span("Página "); x.CurrentPageNumber(); }); }); } void ComposeHeader(IContainer container) { container.Column(column => { column.Item().Row(row => { row.RelativeItem().Column(col => { col.Item().Text("Reporte de Distribución de Suscripciones").SemiBold().FontSize(14); col.Item().Text($"Período: {Model.FechaDesde} al {Model.FechaHasta}").FontSize(11); }); row.ConstantItem(150).AlignRight().Text($"Generado: {Model.FechaGeneracion}"); }); column.Item().PaddingTop(5).BorderBottom(1).BorderColor(Colors.Grey.Lighten2); }); } void ComposeContent(IContainer container) { container.PaddingTop(10).Column(column => { column.Spacing(20); // Espacio entre elementos principales (sección de altas y sección de bajas) // --- Sección 1: Altas y Activas --- column.Item().Column(colAltas => { colAltas.Item().Text("Altas y Suscripciones Activas en el Período").Bold().FontSize(14).Underline(); colAltas.Item().PaddingBottom(10).Text("Listado de suscriptores que deben recibir entregas en el período seleccionado."); if (!Model.DatosAgrupadosAltas.Any()) { colAltas.Item().PaddingTop(10).Text("No se encontraron suscripciones activas para este período.").Italic(); } else { foreach (var empresa in Model.DatosAgrupadosAltas) { colAltas.Item().Element(c => ComposeTablaEmpresa(c, empresa, esBaja: false)); } } }); // --- Sección 2: Bajas --- if (Model.DatosAgrupadosBajas.Any()) { column.Item().PageBreak(); // Salto de página para separar las secciones column.Item().Column(colBajas => { colBajas.Item().Text("Bajas de Suscripciones en el Período").Bold().FontSize(14).Underline().FontColor(Colors.Red.Medium); colBajas.Item().PaddingBottom(10).Text("Listado de suscriptores cuya suscripción finalizó. NO se les debe entregar a partir de su 'Fecha de Baja'."); foreach (var empresa in Model.DatosAgrupadosBajas) { colBajas.Item().Element(c => ComposeTablaEmpresa(c, empresa, esBaja: true)); } }); } }); } void ComposeTablaEmpresa(IContainer container, GrupoEmpresa empresa, bool esBaja) { container.Column(column => { // Cabecera de la EMPRESA (ej. EL DIA) column.Item().Background(Colors.Grey.Lighten2).Padding(5).Text(empresa.NombreEmpresa).Bold().FontSize(12); // Contenedor para las tablas de las publicaciones de esta empresa column.Item().PaddingTop(5).Column(colPub => { colPub.Spacing(10); // Espacio entre cada tabla de publicación foreach (var publicacion in empresa.Publicaciones) { colPub.Item().Element(c => ComposeTablaPublicacion(c, publicacion, esBaja)); } }); }); } void ComposeTablaPublicacion(IContainer container, GrupoPublicacion publicacion, bool esBaja) { // Se envuelve la tabla en una columna para poder ponerle un título simple arriba. container.Column(column => { column.Item().PaddingLeft(2).PaddingBottom(2).Text(publicacion.NombrePublicacion).SemiBold().FontSize(10); column.Item().Table(table => { table.ColumnsDefinition(columns => { columns.RelativeColumn(2.5f); // Nombre columns.RelativeColumn(3); // Dirección columns.RelativeColumn(1.5f); // Teléfono columns.ConstantColumn(65); // Fecha Inicio / Baja columns.RelativeColumn(1.5f); // Días columns.RelativeColumn(2.5f); // Observaciones }); table.Header(header => { header.Cell().BorderBottom(1).Padding(2).Text("Suscriptor").SemiBold(); header.Cell().BorderBottom(1).Padding(2).Text("Dirección").SemiBold(); header.Cell().BorderBottom(1).Padding(2).Text("Teléfono").SemiBold(); header.Cell().BorderBottom(1).Padding(2).Text(esBaja ? "Fecha de Baja" : "Fecha Inicio").SemiBold(); header.Cell().BorderBottom(1).Padding(2).Text("Días Entrega").SemiBold(); header.Cell().BorderBottom(1).Padding(2).Text("Observaciones").SemiBold(); }); foreach (var item in publicacion.Suscripciones) { table.Cell().Padding(2).Text(item.NombreSuscriptor); table.Cell().Padding(2).Text(item.Direccion); table.Cell().Padding(2).Text(item.Telefono ?? "-"); var fecha = esBaja ? item.FechaFin : item.FechaInicio; table.Cell().Padding(2).Text(fecha?.ToString("dd/MM/yyyy") ?? "-"); table.Cell().Padding(2).Text(item.DiasEntrega); table.Cell().Padding(2).Text(item.Observaciones ?? "-"); } }); }); } } }