diff --git a/Areas/OTcalculate/Services/OvertimePdfService.cs b/Areas/OTcalculate/Services/OvertimePdfService.cs new file mode 100644 index 0000000..d22a78d --- /dev/null +++ b/Areas/OTcalculate/Services/OvertimePdfService.cs @@ -0,0 +1,163 @@ +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") ?? "-"; + } + + } +} diff --git a/Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml b/Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml index cc98900..4c42bf5 100644 --- a/Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml +++ b/Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml @@ -1,5 +1,5 @@ @{ - ViewData["Title"] = "Records Overtime"; + ViewData["Title"] = "Edit Overtime"; Layout = "~/Views/Shared/_Layout.cshtml"; } diff --git a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml index fbd9d94..ca32b29 100644 --- a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml +++ b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml @@ -1,5 +1,5 @@ @{ - ViewData["Title"] = "My Overtime Records"; + ViewData["Title"] = "Overtime Records"; Layout = "~/Views/Shared/_Layout.cshtml"; } @@ -51,6 +51,14 @@ overflow-x: auto; } + td.wrap-text { + white-space: pre-wrap; /* Keep line breaks and wrap text */ + word-wrap: break-word; /* Break long words if necessary */ + max-width: 300px; /* Adjust as needed */ + text-align: left; /* Optional: left-align description */ + } + + @@ -80,7 +88,8 @@ Total OT Hours Break Hours (min) Net OT - Station + Station + Days Description File Action @@ -106,8 +115,9 @@ {{ calcTotalHours(record).toFixed(2) }} {{ calcBreakTotal(record) }} {{ formatHourMinute(calcNetHours(record)) }} - {{ record.stationName || 'N/A' }} - {{ record.otDescription }} + {{ record.stationName || 'N/A' }} + {{ record.otDays}} + {{ record.otDescription }}