using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; using System.IO; using System.Collections.Generic; using PSTW_CentralSystem.Areas.OTcalculate.Models; using System; namespace PSTW_CentralSystem.Areas.OTcalculate.Services { public class OvertimePdfService { public MemoryStream GenerateOvertimeTablePdf( List records, int departmentId, string userFullName, string departmentName, byte[]? logoImage = null // Optional logo image ) { var stream = new MemoryStream(); Document.Create(container => { container.Page(page => { page.Size(PageSizes.A4.Landscape()); page.Margin(30); // Header section with logo and user info page.Content().Column(column => { column.Item().Row(row => { row.RelativeItem(2).Column(col => { if (logoImage != null) { col.Item().Container().Height(36).Image(logoImage, ImageScaling.FitArea); col.Spacing(10); } col.Item().Text($"Name: {userFullName}").FontSize(9).SemiBold(); col.Item().Text($"Department: {departmentName}").FontSize(9).Italic(); }); row.RelativeItem(1).AlignRight().Text($"Generated: {DateTime.Now:dd MMM yyyy HH:mm}") .FontSize(9).FontColor(Colors.Grey.Medium); }); column.Item().PaddingVertical(10).LineHorizontal(0.5f).LineColor(Colors.Grey.Lighten2); // Table section column.Item().Table(table => { table.ColumnsDefinition(columns => { columns.RelativeColumn(); // Date columns.RelativeColumn(1); // Office From columns.RelativeColumn(1); // Office To columns.RelativeColumn(); // Office Break columns.RelativeColumn(1); // Outside From columns.RelativeColumn(1); // Outside To columns.RelativeColumn(); // Outside Break columns.RelativeColumn(); // Total OT columns.RelativeColumn(1); // Break Hours columns.RelativeColumn(); // Net OT if (departmentId == 2) columns.RelativeColumn(); // Station columns.RelativeColumn(1); // Day Type columns.RelativeColumn(3); // Description }); // Header Row table.Header(header => { header.Cell().Background("#d0ead2").Padding(5).Text("Date").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#dceefb").Padding(5).Text("From\n(Office)").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#dceefb").Padding(5).Text("To\n(Office)").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#dceefb").Padding(5).Text("Break\n(Office)").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#edf2f7").Padding(5).Text("From\n(Outside)").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#edf2f7").Padding(5).Text("To\n(Outside)").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#edf2f7").Padding(5).Text("Break\n(Outside)").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#fdebd0").Padding(5).Text("Total OT\nHours").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#fdebd0").Padding(5).Text("Break Hours\n(min)").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#fdebd0").Padding(5).Text("Net OT").FontSize(9).Bold().AlignCenter(); if (departmentId == 2) header.Cell().Background("#d0f0ef").Padding(5).Text("Station").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#e0f7da").Padding(5).Text("Days").FontSize(9).Bold().AlignCenter(); header.Cell().Background("#e3f2fd").Padding(5).Text("Description").FontSize(9).Bold().AlignCenter(); }); // Data Rows double totalOTSum = 0; int totalBreakSum = 0; TimeSpan totalNetOt = TimeSpan.Zero; foreach (var r in records) { var totalOT = CalculateTotalOT(r); var totalBreak = (r.OfficeBreak ?? 0) + (r.OutsideBreak ?? 0); var netOT = totalOT - TimeSpan.FromMinutes(totalBreak); totalOTSum += totalOT.TotalHours; totalBreakSum += totalBreak; totalNetOt += netOT; table.Cell().Padding(5).Text(r.OtDate.ToString("dd/MM/yyyy")).FontSize(9); table.Cell().Padding(5).Text(FormatTime(r.OfficeFrom)).FontSize(9); table.Cell().Padding(5).Text(FormatTime(r.OfficeTo)).FontSize(9); table.Cell().Padding(5).Text($"{r.OfficeBreak ?? 0} min").FontSize(9); table.Cell().Padding(5).Text(FormatTime(r.OutsideFrom)).FontSize(9); table.Cell().Padding(5).Text(FormatTime(r.OutsideTo)).FontSize(9); table.Cell().Padding(5).Text($"{r.OutsideBreak ?? 0} min").FontSize(9); table.Cell().Padding(5).Text($"{totalOT.TotalHours:F2}").FontSize(9); table.Cell().Padding(5).Text($"{totalBreak}").FontSize(9); table.Cell().Padding(5).Text($"{netOT.Hours} hr {netOT.Minutes} min").FontSize(9); if (departmentId == 2) table.Cell().Padding(5).Text(r.Stations?.StationName ?? "N/A").FontSize(9); table.Cell().Padding(5).Text(r.OtDays).FontSize(9); table.Cell().Padding(5).Text(r.OtDescription ?? "-").FontSize(9).WrapAnywhere().LineHeight(1.2f); } // Totals Row table.Cell().ColumnSpan((uint)(departmentId == 2 ? 7 : 6)).Background("#d8d1f5").Padding(5).Text("TOTAL").Bold().FontSize(9); table.Cell().Background("#d8d1f5").Padding(5).Text($"{totalOTSum:F2}").Bold().FontSize(9); table.Cell().Background("#d8d1f5").Padding(5).Text($"{totalBreakSum}").Bold().FontSize(9); table.Cell().Background("#d8d1f5").Padding(5).Text($"{(int)totalNetOt.TotalHours} hr {totalNetOt.Minutes} min").Bold().FontSize(9); if (departmentId == 2) table.Cell().Background("#d8d1f5"); table.Cell().Background("#d8d1f5"); table.Cell().Background("#d8d1f5"); }); }); }); }).GeneratePdf(stream); stream.Position = 0; return stream; } private TimeSpan CalculateTotalOT(OtRegisterModel r) { TimeSpan office = (r.OfficeTo ?? TimeSpan.Zero) - (r.OfficeFrom ?? TimeSpan.Zero); TimeSpan outside = (r.OutsideTo ?? TimeSpan.Zero) - (r.OutsideFrom ?? TimeSpan.Zero); if (outside < TimeSpan.Zero) outside += TimeSpan.FromHours(24); return office + outside; } private string FormatTime(TimeSpan? time) { return time?.ToString(@"hh\:mm") ?? "-"; } } }