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

221 lines
10 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
)
{
records = records
.OrderBy(r => r.OtDate)
.ToList();
var stream = new MemoryStream();
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4.Landscape());
page.Margin(30);
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();
col.Item().Text($"Overtime Record: { GetMonthYearString(records)}").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(1.1f); // Date
columns.RelativeColumn(0.8f); // Office From
columns.RelativeColumn(0.8f); // Office To
columns.RelativeColumn(0.8f); // Office Break
columns.RelativeColumn(0.9f); // After From
columns.RelativeColumn(0.9f); // After To
columns.RelativeColumn(0.9f); // After Break
columns.RelativeColumn(); // Total OT
columns.RelativeColumn(); // Break Hours
columns.RelativeColumn(); // Net OT
if (departmentId == 2)
columns.RelativeColumn(); // Station
columns.RelativeColumn(0.9f); // Day Type
columns.RelativeColumn(2.7f); // Description
});
// Header Row
table.Header(header =>
{
void AddHeaderCell(string text, string bgColor)
{
header.Cell().Background(bgColor).Border(0.25f).Padding(5).Text(text).FontSize(9).Bold().AlignCenter();
}
AddHeaderCell("Date", "#d0ead2");
AddHeaderCell("From\n(Office)", "#dceefb");
AddHeaderCell("To\n(Office)", "#dceefb");
AddHeaderCell("Break\n(Office)", "#dceefb");
AddHeaderCell("From\n(After)", "#edf2f7");
AddHeaderCell("To\n(After)", "#edf2f7");
AddHeaderCell("Break\n(After)", "#edf2f7");
AddHeaderCell("Total OT\nHours", "#fdebd0");
AddHeaderCell("Break Hours\n(min)", "#fdebd0");
AddHeaderCell("Net OT Hours", "#fdebd0");
if (departmentId == 2)
AddHeaderCell("Station", "#d0f0ef");
AddHeaderCell("Days", "#e0f7da");
AddHeaderCell("Description", "#e3f2fd");
});
// Data Rows
double totalOTSum = 0;
int totalBreakSum = 0;
TimeSpan totalNetOt = TimeSpan.Zero;
bool alternate = false;
if (!records.Any())
{
uint colspan = (uint)(departmentId == 2 ? 13 : 12);
table.Cell().ColumnSpan(colspan)
.Border(0.5f)
.Padding(10)
.AlignCenter()
.Text("No records found for selected month and year.")
.FontSize(10)
.FontColor(Colors.Grey.Darken2)
.Italic();
}
else
{
foreach (var r in records)
{
var totalOT = CalculateTotalOT(r);
var totalBreak = (r.OfficeBreak ?? 0) + (r.AfterBreak ?? 0);
var netOT = totalOT - TimeSpan.FromMinutes(totalBreak);
totalOTSum += totalOT.TotalHours;
totalBreakSum += totalBreak;
totalNetOt += netOT;
string rowBg = alternate ? "#f9f9f9" : "#ffffff";
alternate = !alternate;
void AddCell(string value, bool alignLeft = false)
{
var text = table.Cell().Background(rowBg).Border(0.25f).Padding(5).Text(value).FontSize(9);
if (alignLeft)
text.AlignLeft();
else
text.AlignCenter();
}
AddCell(r.OtDate.ToString("dd/MM/yyyy"));
AddCell(FormatTime(r.OfficeFrom));
AddCell(FormatTime(r.OfficeTo));
AddCell($"{r.OfficeBreak ?? 0} min");
AddCell(FormatTime(r.AfterFrom));
AddCell(FormatTime(r.AfterTo));
AddCell($"{r.AfterBreak ?? 0} min");
AddCell($"{(int)totalOT.TotalHours} hr {totalOT.Minutes} min");
AddCell($"{totalBreak}");
AddCell($"{netOT.Hours} hr {netOT.Minutes} min");
if (departmentId == 2)
AddCell(r.Stations?.StationName ?? "N/A");
AddCell(r.OtDays);
table.Cell().Background(rowBg).Border(0.25f).Padding(5).Text(r.OtDescription ?? "-").FontSize(9).WrapAnywhere().LineHeight(1.2f);
}
var totalOTTimeSpan = TimeSpan.FromHours(totalOTSum);
var totalBreakTimeSpan = TimeSpan.FromMinutes(totalBreakSum);
int totalCols = departmentId == 2 ? 13 : 12;
int spanCols = departmentId == 2 ? 7 : 6;
for (int i = 0; i < spanCols; i++)
table.Cell().Background("#d8d1f5").Border(0.80f).Padding(5).Text(i == 0 ? "TOTAL" : "").Bold().FontSize(9).AlignCenter();
table.Cell().Background("#d8d1f5").Border(0.80f).Padding(5).Text($"{(int)totalOTTimeSpan.TotalHours} hr {totalOTTimeSpan.Minutes} min").Bold().FontSize(9).AlignCenter();
table.Cell().Background("#d8d1f5").Border(0.80f).Padding(5).Text($"{(int)totalBreakTimeSpan.TotalHours} hr {totalBreakTimeSpan.Minutes} min").Bold().FontSize(9).AlignCenter();
table.Cell().Background("#d8d1f5").Border(0.80f).Padding(5).Text($"{(int)totalNetOt.TotalHours} hr {totalNetOt.Minutes} min").Bold().FontSize(9).AlignCenter();
if (departmentId == 2)
table.Cell().Background("#d8d1f5").Border(0.80f);
else
table.Cell().Background("#d8d1f5").Border(0.80f);
table.Cell().Background("#d8d1f5").Border(0.80f);
table.Cell().Background("#d8d1f5").Border(0.80f);
}
});
});
});
}).GeneratePdf(stream);
stream.Position = 0;
return stream;
}
private TimeSpan CalculateTotalOT(OtRegisterModel r)
{
TimeSpan office = (r.OfficeTo ?? TimeSpan.Zero) - (r.OfficeFrom ?? TimeSpan.Zero);
TimeSpan after = (r.AfterTo ?? TimeSpan.Zero) - (r.AfterFrom ?? TimeSpan.Zero);
if (after < TimeSpan.Zero)
after += TimeSpan.FromHours(24);
return office + after;
}
private string FormatTime(TimeSpan? time)
{
return time?.ToString(@"hh\:mm") ?? "-";
}
private string GetMonthYearString(List<OtRegisterModel> records)
{
if (records == null || !records.Any())
return "No Data";
var firstDate = records.First().OtDate;
return $"{firstDate:MMMM yyyy}";
}
}
}