PSTW_CentralizeSystem/Areas/OTcalculate/Services/OvertimePdfService.cs
2025-04-11 17:21:55 +08:00

164 lines
8.6 KiB
C#

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<OtRegisterModel> 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") ?? "-";
}
}
}