2195 lines
106 KiB
C#
2195 lines
106 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using ClosedXML.Excel;
|
|
using ClosedXML.Excel.Drawings;
|
|
using Microsoft.AspNetCore.Hosting; // Added for IWebHostEnvironment
|
|
|
|
using PSTW_CentralSystem.Areas.OTcalculate.Models;
|
|
using PSTW_CentralSystem.Models;
|
|
using PSTW_CentralSystem.DBContext;
|
|
|
|
|
|
namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
|
{
|
|
public class OvertimeExcel
|
|
{
|
|
private readonly CentralSystemContext _centralDbContext;
|
|
private readonly IWebHostEnvironment _env; // Added for IWebHostEnvironment
|
|
|
|
public OvertimeExcel(CentralSystemContext centralDbContext, IWebHostEnvironment env) // Modified constructor
|
|
{
|
|
_centralDbContext = centralDbContext;
|
|
_env = env; // Initialize IWebHostEnvironment
|
|
}
|
|
|
|
public MemoryStream GenerateOvertimeExcel(
|
|
List<OtRegisterModel> records,
|
|
UserModel user,
|
|
decimal userRate,
|
|
DateTime? selectedMonth = null,
|
|
bool isHoU = false,
|
|
string? flexiHour = null,
|
|
byte[]? logoImage = null,
|
|
bool isSimplifiedExport = false,
|
|
OtStatusModel? otStatus = null)
|
|
{
|
|
bool isAdminUser = IsAdmin(user.Id);
|
|
bool showStationColumn = user.Department?.DepartmentId == 3 || user.Department?.DepartmentId == 2 || isAdminUser;
|
|
|
|
var stream = new MemoryStream();
|
|
|
|
using (var workbook = new XLWorkbook())
|
|
{
|
|
var worksheet = workbook.Worksheets.Add("Overtime Report");
|
|
worksheet.ShowGridLines = true;
|
|
|
|
int currentRow = 1;
|
|
int logoBottomRow = 0;
|
|
|
|
if (logoImage != null)
|
|
{
|
|
using (var ms = new MemoryStream(logoImage))
|
|
{
|
|
var picture = worksheet.AddPicture(ms)
|
|
.MoveTo(worksheet.Cell(currentRow, 1))
|
|
.WithPlacement(XLPicturePlacement.FreeFloating);
|
|
|
|
picture.Name = "Company Logo";
|
|
picture.Scale(0.3);
|
|
logoBottomRow = currentRow + 2;
|
|
}
|
|
}
|
|
|
|
currentRow = logoBottomRow + 1;
|
|
|
|
DateTime displayMonth = selectedMonth ??
|
|
(records.Any() ? records.First().OtDate : DateTime.Now);
|
|
|
|
var allDatesInMonth = GetAllDatesInMonth(displayMonth);
|
|
records = records.OrderBy(r => r.OtDate).ToList();
|
|
|
|
// Add user information
|
|
worksheet.Cell(currentRow, 1).Value = $"Name: {user.FullName}";
|
|
worksheet.Cell(currentRow, 1).Style.Font.SetBold();
|
|
currentRow++;
|
|
|
|
worksheet.Cell(currentRow, 1).Value = $"Department: {user.Department?.DepartmentName ?? "N/A"}";
|
|
worksheet.Cell(currentRow, 1).Style.Font.SetBold();
|
|
currentRow++;
|
|
|
|
if (!string.IsNullOrEmpty(flexiHour))
|
|
{
|
|
worksheet.Cell(currentRow, 1).Value = $"Flexi Hour: {flexiHour}";
|
|
worksheet.Cell(currentRow, 1).Style.Font.SetBold();
|
|
currentRow++;
|
|
}
|
|
|
|
worksheet.Cell(currentRow, 1).Value = $"Overtime Report: {displayMonth:MMMM yyyy}";
|
|
worksheet.Cell(currentRow, 1).Style.Font.SetBold();
|
|
currentRow++;
|
|
|
|
currentRow++; // Add an empty row for spacing
|
|
|
|
|
|
// --- Conditional Header Generation ---
|
|
if (isSimplifiedExport)
|
|
{
|
|
AddSimplifiedHeaders(worksheet, ref currentRow, showStationColumn);
|
|
}
|
|
else
|
|
{
|
|
// Existing logic for full headers (for approvers or other reports)
|
|
if (isHoU)
|
|
{
|
|
if (showStationColumn)
|
|
{
|
|
AddHoUPSTWAirHeaders(worksheet, ref currentRow);
|
|
}
|
|
else
|
|
{
|
|
AddHoUNonPSTWAirHeaders(worksheet, ref currentRow);
|
|
}
|
|
}
|
|
else // !isHoU path
|
|
{
|
|
if (showStationColumn)
|
|
{
|
|
AddNonHoUPSTWAirHeaders(worksheet, ref currentRow);
|
|
}
|
|
else
|
|
{
|
|
AddNonHoUNonPSTWAirHeaders(worksheet, ref currentRow);
|
|
}
|
|
}
|
|
}
|
|
// --- End Conditional Header Generation ---
|
|
|
|
|
|
var userSetting = _centralDbContext.Hrusersetting
|
|
.Include(us => us.State)
|
|
.FirstOrDefault(us => us.UserId == user.Id);
|
|
|
|
var publicHolidays = _centralDbContext.Holidays
|
|
.Where(h => userSetting != null && h.StateId == userSetting.State.StateId)
|
|
.ToList();
|
|
|
|
var publicHolidayDates = publicHolidays.Select(h => h.HolidayDate.Date).ToList();
|
|
|
|
|
|
var recordsGroupedByDate = records
|
|
.GroupBy(r => r.OtDate.Date)
|
|
.ToDictionary(g => g.Key, g => g.ToList());
|
|
|
|
decimal totalAllBreaksMinutes = 0;
|
|
decimal totalAllOtMinutes = 0;
|
|
|
|
decimal totalOfficeBreak = 0;
|
|
decimal totalAfterBreak = 0;
|
|
decimal totalOtHrsOffice = 0;
|
|
decimal totalOtHrsAfterOffice = 0;
|
|
decimal totalNdOt = 0;
|
|
decimal totalOdUnder4 = 0;
|
|
decimal totalOd4to8 = 0;
|
|
decimal totalOdAfter = 0;
|
|
decimal totalRdUnder4 = 0;
|
|
decimal totalRd4to8 = 0;
|
|
decimal totalRdAfter = 0;
|
|
decimal totalPhUnder8 = 0;
|
|
decimal totalPhAfter = 0;
|
|
decimal grandTotalOt = 0;
|
|
decimal grandTotalNdOd = 0;
|
|
decimal grandTotalRd = 0;
|
|
decimal grandTotalPh = 0;
|
|
decimal grandTotalOtAmount = 0;
|
|
|
|
// UserRate is now interpreted as Basic Salary
|
|
var basicSalary = userRate;
|
|
var orp = CalculateOrp(basicSalary); // ORP = Basic Salary / 26
|
|
var hrp = CalculateHrp(orp); // HRP = ORP / 8
|
|
bool hasPrintedSalaryDetails = false;
|
|
|
|
DateTime? previousDate = null;
|
|
|
|
foreach (var date in allDatesInMonth)
|
|
{
|
|
recordsGroupedByDate.TryGetValue(date, out var dateRecords);
|
|
var recordsToShow = dateRecords ?? new List<OtRegisterModel> { new OtRegisterModel { OtDate = date } };
|
|
recordsToShow = recordsToShow.OrderBy(r => r.OfficeFrom ?? r.AfterFrom).ToList();
|
|
|
|
foreach (var record in recordsToShow)
|
|
{
|
|
int col = 1;
|
|
|
|
if (isSimplifiedExport)
|
|
{
|
|
var dayCell = worksheet.Cell(currentRow, col);
|
|
var dateCell = worksheet.Cell(currentRow, col + 1);
|
|
|
|
if (record.OtDate.Date != previousDate?.Date)
|
|
{
|
|
dayCell.Value = record.OtDate.ToString("ddd");
|
|
dateCell.Value = record.OtDate.ToString("dd-MM-yyyy");
|
|
}
|
|
else
|
|
{
|
|
dayCell.Value = "";
|
|
dateCell.Value = "";
|
|
}
|
|
ApplyDayTypeStyle(dayCell, record.OtDate, userSetting?.State?.WeekendId, publicHolidayDates);
|
|
ApplyDayTypeStyle(dateCell, record.OtDate, userSetting?.State?.WeekendId, publicHolidayDates);
|
|
col += 2;
|
|
|
|
worksheet.Cell(currentRow, col++).Value = record.OfficeFrom?.ToString(@"hh\:mm") ?? "";
|
|
worksheet.Cell(currentRow, col++).Value = record.OfficeTo?.ToString(@"hh\:mm") ?? "";
|
|
worksheet.Cell(currentRow, col++).Value = FormatAsHourMinute(record.OfficeBreak, isMinutes: true);
|
|
|
|
worksheet.Cell(currentRow, col++).Value = record.AfterFrom?.ToString(@"hh\:mm") ?? "";
|
|
worksheet.Cell(currentRow, col++).Value = record.AfterTo?.ToString(@"hh\:mm") ?? "";
|
|
worksheet.Cell(currentRow, col++).Value = FormatAsHourMinute(record.AfterBreak, isMinutes: true);
|
|
|
|
var currentRecordTotalOtMinutes = (CalculateTimeDifferenceInMinutes(record.OfficeFrom, record.OfficeTo) - record.OfficeBreak.GetValueOrDefault(0)) +
|
|
(CalculateTimeDifferenceInMinutes(record.AfterFrom, record.AfterTo) - record.AfterBreak.GetValueOrDefault(0));
|
|
currentRecordTotalOtMinutes = Math.Max(0, currentRecordTotalOtMinutes);
|
|
worksheet.Cell(currentRow, col++).Value = FormatAsHourMinute(currentRecordTotalOtMinutes, isMinutes: true);
|
|
totalAllOtMinutes += currentRecordTotalOtMinutes;
|
|
|
|
var currentRecordTotalBreakMinutes = record.OfficeBreak.GetValueOrDefault(0) + record.AfterBreak.GetValueOrDefault(0);
|
|
worksheet.Cell(currentRow, col++).Value = FormatAsHourMinute(currentRecordTotalBreakMinutes, isMinutes: true);
|
|
totalAllBreaksMinutes += currentRecordTotalBreakMinutes;
|
|
|
|
if (showStationColumn)
|
|
{
|
|
worksheet.Cell(currentRow, col++).Value = record.Stations?.StationName ?? "";
|
|
}
|
|
|
|
var descriptionCell = worksheet.Cell(currentRow, col);
|
|
descriptionCell.Value = record.OtDescription ?? "";
|
|
descriptionCell.Style.Alignment.WrapText = true;
|
|
descriptionCell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
descriptionCell.Style.Alignment.Vertical = XLAlignmentVerticalValues.Top;
|
|
col++;
|
|
|
|
var rowRange = worksheet.Range(currentRow, 1, currentRow, col - 1);
|
|
rowRange.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
rowRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
rowRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
rowRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
|
|
descriptionCell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
|
|
}
|
|
else
|
|
{
|
|
var dayType = GetDayType(date, userSetting?.State?.WeekendId, publicHolidayDates);
|
|
var classified = ClassifyOt(record, hrp, publicHolidayDates, userSetting?.State?.WeekendId);
|
|
|
|
decimal currentNdOt = decimal.TryParse(classified["ndAfter"], out decimal ndOtVal) ? ndOtVal : 0;
|
|
decimal currentOdUnder4 = decimal.TryParse(classified["odUnder4"], out decimal odUnder4Val) ? odUnder4Val : 0;
|
|
decimal currentOd4to8 = decimal.TryParse(classified["odBetween4And8"], out decimal od4to8Val) ? od4to8Val : 0;
|
|
decimal currentOdAfter = decimal.TryParse(classified["odAfter"], out decimal odAfterVal) ? odAfterVal : 0;
|
|
decimal currentRdUnder4 = decimal.TryParse(classified["rdUnder4"], out decimal rdUnder4Val) ? rdUnder4Val : 0;
|
|
decimal currentRd4to8 = decimal.TryParse(classified["rdBetween4And8"], out decimal rd4to8Val) ? rd4to8Val : 0;
|
|
decimal currentRdAfter = decimal.TryParse(classified["rdAfter"], out decimal rdAfterVal) ? rdAfterVal : 0;
|
|
decimal currentPhUnder8 = decimal.TryParse(classified["phUnder8"], out decimal phUnder8Val) ? phUnder8Val : 0;
|
|
decimal currentPhAfter = decimal.TryParse(classified["phAfter"], out decimal phAfterVal) ? phAfterVal : 0;
|
|
|
|
decimal currentRowTotalOt = currentNdOt + currentOdUnder4 + currentOd4to8 + currentOdAfter +
|
|
currentRdUnder4 + currentRd4to8 + currentRdAfter +
|
|
currentPhUnder8 + currentPhAfter;
|
|
|
|
decimal currentRowTotalNdOd = currentNdOt + currentOdUnder4 + currentOd4to8 + currentOdAfter;
|
|
decimal currentRowTotalRd = currentRdUnder4 + currentRd4to8 + currentRdAfter;
|
|
decimal currentRowTotalPh = currentPhUnder8 + currentPhAfter;
|
|
|
|
var otAmt = CalculateOtAmount(record, hrp, publicHolidayDates, userSetting?.State?.WeekendId);
|
|
|
|
totalOfficeBreak += (decimal)record.OfficeBreak.GetValueOrDefault(0);
|
|
totalAfterBreak += (decimal)record.AfterBreak.GetValueOrDefault(0);
|
|
totalOtHrsOffice += ConvertTimeToDecimal(CalculateOfficeOtHours(record));
|
|
totalOtHrsAfterOffice += ConvertTimeToDecimal(CalculateAfterOfficeOtHours(record));
|
|
|
|
totalNdOt += currentNdOt;
|
|
totalOdUnder4 += currentOdUnder4;
|
|
totalOd4to8 += currentOd4to8;
|
|
totalOdAfter += currentOdAfter;
|
|
totalRdUnder4 += currentRdUnder4;
|
|
totalRd4to8 += currentRd4to8;
|
|
totalRdAfter += currentRdAfter;
|
|
totalPhUnder8 += currentPhUnder8;
|
|
totalPhAfter += currentPhAfter;
|
|
|
|
grandTotalOt += currentRowTotalOt;
|
|
grandTotalNdOd += currentRowTotalNdOd;
|
|
grandTotalRd += currentRowTotalRd;
|
|
grandTotalPh += currentRowTotalPh;
|
|
grandTotalOtAmount += decimal.TryParse(otAmt, out decimal otAmtVal) ? otAmtVal : 0;
|
|
|
|
if (!isHoU)
|
|
{
|
|
// Basic Salary
|
|
worksheet.Cell(currentRow, col).Value = !hasPrintedSalaryDetails ? basicSalary.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
// ORP
|
|
worksheet.Cell(currentRow, col).Value = !hasPrintedSalaryDetails ? orp.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
// HRP
|
|
worksheet.Cell(currentRow, col).Value = !hasPrintedSalaryDetails ? hrp.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
}
|
|
hasPrintedSalaryDetails = true;
|
|
|
|
var dayCell = worksheet.Cell(currentRow, col);
|
|
var dateCell = worksheet.Cell(currentRow, col + 1);
|
|
|
|
if (record.OtDate.Date != previousDate?.Date)
|
|
{
|
|
dayCell.Value = record.OtDate.ToString("ddd");
|
|
dateCell.Value = record.OtDate.ToString("dd-MM-yyyy");
|
|
}
|
|
else
|
|
{
|
|
dayCell.Value = "";
|
|
dateCell.Value = "";
|
|
}
|
|
ApplyDayTypeStyle(dayCell, record.OtDate, userSetting?.State?.WeekendId, publicHolidayDates);
|
|
ApplyDayTypeStyle(dateCell, record.OtDate, userSetting?.State?.WeekendId, publicHolidayDates);
|
|
col += 2;
|
|
|
|
worksheet.Cell(currentRow, col++).Value = record.OfficeFrom?.ToString(@"hh\:mm") ?? "";
|
|
worksheet.Cell(currentRow, col++).Value = record.OfficeTo?.ToString(@"hh\:mm") ?? "";
|
|
worksheet.Cell(currentRow, col++).Value = FormatAsHourMinute(record.OfficeBreak, isMinutes: true);
|
|
|
|
worksheet.Cell(currentRow, col++).Value = record.AfterFrom?.ToString(@"hh\:mm") ?? "";
|
|
worksheet.Cell(currentRow, col++).Value = record.AfterTo?.ToString(@"hh\:mm") ?? "";
|
|
worksheet.Cell(currentRow, col++).Value = FormatAsHourMinute(record.AfterBreak, isMinutes: true);
|
|
|
|
worksheet.Cell(currentRow, col++).Value = CalculateOfficeOtHours(record);
|
|
worksheet.Cell(currentRow, col++).Value = CalculateAfterOfficeOtHours(record);
|
|
|
|
worksheet.Cell(currentRow, col++).Value = currentNdOt > 0 ? currentNdOt.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentOdUnder4 > 0 ? currentOdUnder4.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentOd4to8 > 0 ? currentOd4to8.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentOdAfter > 0 ? currentOdAfter.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentRdUnder4 > 0 ? currentRdUnder4.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentRd4to8 > 0 ? currentRd4to8.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentRdAfter > 0 ? currentRdAfter.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentPhUnder8 > 0 ? currentPhUnder8.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentPhAfter > 0 ? currentPhAfter.ToString("N2") : "";
|
|
|
|
worksheet.Cell(currentRow, col++).Value = currentRowTotalOt > 0 ? currentRowTotalOt.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentRowTotalNdOd > 0 ? currentRowTotalNdOd.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentRowTotalRd > 0 ? currentRowTotalRd.ToString("N2") : "";
|
|
worksheet.Cell(currentRow, col++).Value = currentRowTotalPh > 0 ? currentRowTotalPh.ToString("N2") : "";
|
|
|
|
if (!isHoU)
|
|
{
|
|
worksheet.Cell(currentRow, col++).Value = otAmt == "0.00" ? "" : otAmt;
|
|
}
|
|
|
|
if (showStationColumn)
|
|
{
|
|
worksheet.Cell(currentRow, col++).Value = record.Stations?.StationName ?? "";
|
|
}
|
|
|
|
var descriptionCell = worksheet.Cell(currentRow, col);
|
|
descriptionCell.Value = record.OtDescription ?? "";
|
|
descriptionCell.Style.Alignment.WrapText = true;
|
|
descriptionCell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
descriptionCell.Style.Alignment.Vertical = XLAlignmentVerticalValues.Top;
|
|
col++;
|
|
|
|
var rowRange = worksheet.Range(currentRow, 1, currentRow, col - 1);
|
|
rowRange.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
rowRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
rowRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
rowRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
|
|
descriptionCell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
}
|
|
|
|
currentRow++;
|
|
previousDate = record.OtDate.Date;
|
|
}
|
|
}
|
|
|
|
var totalsRowRange = worksheet.Range(currentRow, 1, currentRow, worksheet.LastColumnUsed().ColumnNumber());
|
|
totalsRowRange.Style.Font.SetBold();
|
|
foreach (var cell in totalsRowRange.Cells())
|
|
{
|
|
ApplyHeaderStyle(cell, 5);
|
|
}
|
|
totalsRowRange.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
totalsRowRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
|
|
if (isSimplifiedExport)
|
|
{
|
|
int totalLabelStartColumnIndex = 1;
|
|
int totalLabelEndColumnIndex = 2;
|
|
|
|
worksheet.Range(currentRow, totalLabelStartColumnIndex, currentRow, totalLabelEndColumnIndex).Merge();
|
|
worksheet.Cell(currentRow, totalLabelStartColumnIndex).Value = "TOTAL";
|
|
worksheet.Cell(currentRow, totalLabelStartColumnIndex).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
|
|
int currentTotalCol = totalLabelEndColumnIndex + 1;
|
|
|
|
currentTotalCol += 6;
|
|
|
|
worksheet.Cell(currentRow, currentTotalCol++).Value = FormatAsHourMinute(totalAllOtMinutes, isMinutes: true);
|
|
|
|
worksheet.Cell(currentRow, currentTotalCol++).Value = FormatAsHourMinute(totalAllBreaksMinutes, isMinutes: true);
|
|
|
|
if (showStationColumn)
|
|
{
|
|
currentTotalCol++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int totalLabelStartColumnIndex = 1;
|
|
int totalLabelEndColumnIndex;
|
|
|
|
if (!isHoU)
|
|
{
|
|
totalLabelEndColumnIndex = 3; // Basic Salary, ORP, HRP are new first three columns
|
|
}
|
|
else
|
|
{
|
|
totalLabelEndColumnIndex = 2;
|
|
}
|
|
|
|
worksheet.Range(currentRow, totalLabelStartColumnIndex, currentRow, totalLabelEndColumnIndex).Merge();
|
|
worksheet.Cell(currentRow, totalLabelStartColumnIndex).Value = "TOTAL";
|
|
worksheet.Cell(currentRow, totalLabelStartColumnIndex).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
|
|
int colOfficeBreakIndex = 0;
|
|
int colAfterBreakIndex = 0;
|
|
int colNdOtIndex = 0;
|
|
int colOdUnder4Index = 0;
|
|
int colOd4to8Index = 0;
|
|
int colOdAfterIndex = 0;
|
|
int colRdUnder4Index = 0;
|
|
int colRd4to8Index = 0;
|
|
int colRdAfterIndex = 0;
|
|
int colPhUnder8Index = 0;
|
|
int colPhAfterIndex = 0;
|
|
int colTotalOtIndex = 0;
|
|
int colTotalNdOdIndex = 0;
|
|
int colTotalRdIndex = 0;
|
|
int colTotalPhIndex = 0;
|
|
int colOtAmtIndex = 0;
|
|
|
|
if (!isHoU)
|
|
{
|
|
// Adjusted column indices due to Basic Salary, ORP, HRP being added at the beginning
|
|
colOfficeBreakIndex = 8;
|
|
colAfterBreakIndex = 11;
|
|
colNdOtIndex = 14;
|
|
colOdUnder4Index = 15;
|
|
colOd4to8Index = 16;
|
|
colOdAfterIndex = 17;
|
|
colRdUnder4Index = 18;
|
|
colRd4to8Index = 19;
|
|
colRdAfterIndex = 20;
|
|
colPhUnder8Index = 21;
|
|
colPhAfterIndex = 22;
|
|
colTotalOtIndex = 23;
|
|
colTotalNdOdIndex = 24;
|
|
colTotalRdIndex = 25;
|
|
colTotalPhIndex = 26;
|
|
colOtAmtIndex = 27;
|
|
}
|
|
else
|
|
{
|
|
colOfficeBreakIndex = 5;
|
|
colAfterBreakIndex = 8;
|
|
colNdOtIndex = 11;
|
|
colOdUnder4Index = 12;
|
|
colOd4to8Index = 13;
|
|
colOdAfterIndex = 14;
|
|
colRdUnder4Index = 15;
|
|
colRd4to8Index = 16;
|
|
colRdAfterIndex = 17;
|
|
colPhUnder8Index = 18;
|
|
colPhAfterIndex = 19;
|
|
colTotalOtIndex = 20;
|
|
colTotalNdOdIndex = 21;
|
|
colTotalRdIndex = 22;
|
|
colTotalPhIndex = 23;
|
|
}
|
|
|
|
worksheet.Cell(currentRow, colOfficeBreakIndex).Value = string.IsNullOrEmpty(FormatAsHourMinute(totalOfficeBreak, isMinutes: true)) ? "0:00" : FormatAsHourMinute(totalOfficeBreak, isMinutes: true);
|
|
worksheet.Cell(currentRow, colAfterBreakIndex).Value = string.IsNullOrEmpty(FormatAsHourMinute(totalAfterBreak, isMinutes: true)) ? "0:00" : FormatAsHourMinute(totalAfterBreak, isMinutes: true);
|
|
|
|
worksheet.Cell(currentRow, colNdOtIndex).Value = totalNdOt.ToString("N2");
|
|
worksheet.Cell(currentRow, colOdUnder4Index).Value = totalOdUnder4.ToString("N2");
|
|
worksheet.Cell(currentRow, colOd4to8Index).Value = totalOd4to8.ToString("N2");
|
|
worksheet.Cell(currentRow, colOdAfterIndex).Value = totalOdAfter.ToString("N2");
|
|
worksheet.Cell(currentRow, colRdUnder4Index).Value = totalRdUnder4.ToString("N2");
|
|
worksheet.Cell(currentRow, colRd4to8Index).Value = totalRd4to8.ToString("N2");
|
|
worksheet.Cell(currentRow, colRdAfterIndex).Value = totalRdAfter.ToString("N2");
|
|
worksheet.Cell(currentRow, colPhUnder8Index).Value = totalPhUnder8.ToString("N2");
|
|
worksheet.Cell(currentRow, colPhAfterIndex).Value = totalPhAfter.ToString("N2");
|
|
worksheet.Cell(currentRow, colTotalOtIndex).Value = grandTotalOt.ToString("N2");
|
|
worksheet.Cell(currentRow, colTotalNdOdIndex).Value = grandTotalNdOd.ToString("N2");
|
|
worksheet.Cell(currentRow, colTotalRdIndex).Value = grandTotalRd.ToString("N2");
|
|
worksheet.Cell(currentRow, colTotalPhIndex).Value = grandTotalPh.ToString("N2");
|
|
|
|
if (!isHoU)
|
|
{
|
|
worksheet.Cell(currentRow, colOtAmtIndex).Value = grandTotalOtAmount.ToString("N2");
|
|
}
|
|
}
|
|
|
|
currentRow++;
|
|
|
|
// --- Approval Signatures and Remarks Section (Combined) ---
|
|
if (!isSimplifiedExport && otStatus != null)
|
|
{
|
|
currentRow++; // Add a blank line before approval section
|
|
|
|
// Signature section header (left side)
|
|
worksheet.Cell(currentRow, 1).Value = "Approval Summary:";
|
|
worksheet.Cell(currentRow, 1).Style.Font.SetBold();
|
|
currentRow++;
|
|
|
|
int signatureStartCol = 1;
|
|
int signatureColWidth = 5;
|
|
|
|
// Calculate remarks start column (same as before)
|
|
int remarksStartColumn = worksheet.LastColumnUsed().ColumnNumber() - 2;
|
|
|
|
void AddApproverSignature(int column, string role, string status, DateTime? submitDate, int? approverUserId)
|
|
{
|
|
if (status == "Approved")
|
|
{
|
|
var approverUser = _centralDbContext.Users.FirstOrDefault(u => u.Id == approverUserId);
|
|
if (approverUser != null)
|
|
{
|
|
var approvedByCell = worksheet.Cell(currentRow, column);
|
|
approvedByCell.Value = $"Approved by:";
|
|
approvedByCell.Style.Font.SetBold();
|
|
approvedByCell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
|
|
worksheet.Cell(currentRow + 1, column).Value = approverUser.FullName;
|
|
worksheet.Cell(currentRow + 1, column).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
worksheet.Cell(currentRow + 1, column).Style.Font.SetFontName("Brush Script MT");
|
|
worksheet.Cell(currentRow + 1, column).Style.Font.SetFontSize(18);
|
|
|
|
worksheet.Cell(currentRow + 2, column).Value = approverUser.FullName;
|
|
worksheet.Cell(currentRow + 2, column).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
|
|
worksheet.Cell(currentRow + 3, column).Value = submitDate?.ToString("dd MMMM yyyy") ?? "N/A";
|
|
worksheet.Cell(currentRow + 3, column).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Loop to add all approved signatures
|
|
var approvalFlow = _centralDbContext.Hrusersetting
|
|
.Include(us => us.Approvalflow)
|
|
.ThenInclude(af => af.HeadOfUnit)
|
|
.Include(us => us.Approvalflow)
|
|
.ThenInclude(af => af.HeadOfDepartment)
|
|
.Include(us => us.Approvalflow)
|
|
.ThenInclude(af => af.ManagerUser)
|
|
.Include(us => us.Approvalflow)
|
|
.ThenInclude(af => af.HRUser)
|
|
.Where(us => us.UserId == user.Id)
|
|
.Select(us => us.Approvalflow)
|
|
.FirstOrDefault();
|
|
|
|
if (approvalFlow != null)
|
|
{
|
|
int currentSignatureCol = signatureStartCol;
|
|
bool hasAnyApprovedSignature = false;
|
|
|
|
if (otStatus.HouStatus == "Approved")
|
|
{
|
|
AddApproverSignature(currentSignatureCol, "Head of Unit", otStatus.HouStatus, otStatus.HouSubmitDate, approvalFlow.HoU);
|
|
currentSignatureCol += signatureColWidth;
|
|
hasAnyApprovedSignature = true;
|
|
}
|
|
|
|
if (otStatus.HodStatus == "Approved")
|
|
{
|
|
AddApproverSignature(currentSignatureCol, "Head of Department", otStatus.HodStatus, otStatus.HodSubmitDate, approvalFlow.HoD);
|
|
currentSignatureCol += signatureColWidth;
|
|
hasAnyApprovedSignature = true;
|
|
}
|
|
|
|
if (otStatus.ManagerStatus == "Approved")
|
|
{
|
|
AddApproverSignature(currentSignatureCol, "Manager", otStatus.ManagerStatus, otStatus.ManagerSubmitDate, approvalFlow.Manager);
|
|
currentSignatureCol += signatureColWidth;
|
|
hasAnyApprovedSignature = true;
|
|
}
|
|
|
|
if (otStatus.HrStatus == "Approved")
|
|
{
|
|
AddApproverSignature(currentSignatureCol, "HR", otStatus.HrStatus, otStatus.HrSubmitDate, approvalFlow.HR);
|
|
currentSignatureCol += signatureColWidth;
|
|
hasAnyApprovedSignature = true;
|
|
}
|
|
|
|
// Add public holidays list below the color indicators
|
|
int remarksColorStartRow = currentRow - 1; // Adjust as needed for spacing from signatures
|
|
var phColorCell = worksheet.Cell(remarksColorStartRow, remarksStartColumn);
|
|
phColorCell.Value = " "; // Small cell for color swatch
|
|
phColorCell.Style.Fill.BackgroundColor = XLColor.FromHtml("FFC0CB"); // Pink color
|
|
phColorCell.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
|
|
worksheet.Cell(remarksColorStartRow, remarksStartColumn + 1).Value = " Public Holiday";
|
|
worksheet.Cell(remarksColorStartRow, remarksStartColumn + 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
|
|
// Weekend/Off Day Color (right side) - on next row
|
|
var weekendColorCell = worksheet.Cell(remarksColorStartRow + 1, remarksStartColumn);
|
|
weekendColorCell.Value = " "; // Small cell for color swatch
|
|
weekendColorCell.Style.Fill.BackgroundColor = XLColor.FromHtml("ADD8E6"); // Light Blue color
|
|
weekendColorCell.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
|
|
worksheet.Cell(remarksColorStartRow + 1, remarksStartColumn + 1).Value = " Weekend";
|
|
worksheet.Cell(remarksColorStartRow + 1, remarksStartColumn + 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
|
|
|
|
// Add public holidays list below the color indicators
|
|
int holidayListRow = remarksColorStartRow + 3; // Start below the color indicators
|
|
worksheet.Cell(holidayListRow, remarksStartColumn).Value = $"Public Holidays in {displayMonth:MMMM yyyy}:";
|
|
worksheet.Cell(holidayListRow, remarksStartColumn).Style.Font.SetBold();
|
|
holidayListRow++;
|
|
|
|
if (publicHolidays != null && publicHolidays.Any())
|
|
{
|
|
var monthHolidays = publicHolidays
|
|
.Where(h => h.HolidayDate.Year == displayMonth.Year && h.HolidayDate.Month == displayMonth.Month)
|
|
.OrderBy(h => h.HolidayDate.Day)
|
|
.ToList();
|
|
|
|
if (monthHolidays.Any())
|
|
{
|
|
foreach (var holiday in monthHolidays)
|
|
{
|
|
worksheet.Cell(holidayListRow, remarksStartColumn).Value = $"- {holiday.HolidayDate:dd MMMM}: {holiday.HolidayName}";
|
|
worksheet.Cell(holidayListRow, remarksStartColumn).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
holidayListRow++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
worksheet.Cell(holidayListRow, remarksStartColumn).Value = "No public holidays recorded for this month.";
|
|
worksheet.Cell(holidayListRow, remarksStartColumn).Style.Font.SetItalic();
|
|
holidayListRow++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
worksheet.Cell(holidayListRow, remarksStartColumn).Value = "No public holiday data found.";
|
|
worksheet.Cell(holidayListRow, remarksStartColumn).Style.Font.SetItalic();
|
|
holidayListRow++;
|
|
}
|
|
|
|
// --- START ADDITION ---
|
|
// Display the message only if at least one signature was approved and displayed.
|
|
if (hasAnyApprovedSignature)
|
|
{
|
|
int messageRow = holidayListRow + 2; // 2 rows below the last public holiday detail for spacing
|
|
|
|
// Merge cells for the message to span across the relevant width
|
|
int lastContentColumn = worksheet.LastColumnUsed().ColumnNumber();
|
|
var messageCellRange = worksheet.Range(messageRow, 1, messageRow, lastContentColumn);
|
|
messageCellRange.Merge();
|
|
messageCellRange.Value = "This is an automatically generated document. No signature is required for validation.";
|
|
messageCellRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
messageCellRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
|
|
messageCellRange.Style.Font.SetItalic();
|
|
messageCellRange.Style.Font.SetFontSize(10);
|
|
messageCellRange.Style.Font.FontColor = XLColor.Gray;
|
|
|
|
// Update currentRow to be after the message for subsequent content (if any)
|
|
currentRow = messageRow + 1;
|
|
}
|
|
// --- END ADDITION ---
|
|
|
|
// Update currentRow to the bottom of the entire remarks section
|
|
// This line is now outside the hasAnyApprovedSignature block, and uses the updated currentRow
|
|
currentRow = Math.Max(currentRow, holidayListRow);
|
|
}
|
|
}
|
|
else if (isSimplifiedExport) // This is the new section for simplified export remarks
|
|
{
|
|
// Call the AddRemarksSection here for simplified export
|
|
AddRemarksSection(worksheet, ref currentRow, displayMonth, userSetting?.State?.WeekendId, publicHolidays.Select(h => new CalendarModel { HolidayDate = h.HolidayDate, HolidayName = h.HolidayName }).ToList());
|
|
}
|
|
// --- End Combined Approval Signatures and Remarks Section ---
|
|
|
|
|
|
// --- Column Sizing Logic (Conditional) ---
|
|
worksheet.Columns().AdjustToContents();
|
|
|
|
if (isSimplifiedExport)
|
|
{
|
|
worksheet.Column(1).Width = 10;
|
|
worksheet.Column(2).Width = 12;
|
|
worksheet.Column(3).Width = 9;
|
|
worksheet.Column(4).Width = 9;
|
|
worksheet.Column(5).Width = 9;
|
|
worksheet.Column(6).Width = 9;
|
|
worksheet.Column(7).Width = 9;
|
|
worksheet.Column(8).Width = 9;
|
|
worksheet.Column(9).Width = 15;
|
|
worksheet.Column(10).Width = 10;
|
|
|
|
int descCol = 11;
|
|
if (showStationColumn)
|
|
{
|
|
worksheet.Column(11).Width = 15;
|
|
descCol = 12;
|
|
}
|
|
worksheet.Column(descCol).Width = 60;
|
|
}
|
|
else
|
|
{
|
|
if (!isHoU)
|
|
{
|
|
worksheet.Column(1).Width = 15; // Basic Salary
|
|
worksheet.Column(2).Width = 10; // ORP
|
|
worksheet.Column(3).Width = 10; // HRP
|
|
worksheet.Column(4).Width = 10; // Day
|
|
worksheet.Column(5).Width = 12; // Date
|
|
|
|
worksheet.Column(6).Width = 9; // Office From
|
|
worksheet.Column(7).Width = 9; // Office To
|
|
worksheet.Column(8).Width = 9; // Office Break
|
|
worksheet.Column(9).Width = 9; // After From
|
|
worksheet.Column(10).Width = 9; // After To
|
|
worksheet.Column(11).Width = 9; // After Break
|
|
|
|
worksheet.Column(12).Width = 15; // OT Hrs (Office Hour)
|
|
worksheet.Column(13).Width = 18; // OT Hrs (After Office Hour)
|
|
|
|
for (int i = 14; i <= 27; i++)
|
|
{
|
|
worksheet.Column(i).Width = 12;
|
|
}
|
|
|
|
int descColIndex = showStationColumn ? 29 : 28;
|
|
worksheet.Column(descColIndex).Width = 60;
|
|
}
|
|
else
|
|
{
|
|
worksheet.Column(1).Width = 10;
|
|
worksheet.Column(2).Width = 12;
|
|
|
|
worksheet.Column(3).Width = 9;
|
|
worksheet.Column(4).Width = 9;
|
|
worksheet.Column(5).Width = 9;
|
|
worksheet.Column(6).Width = 9;
|
|
worksheet.Column(7).Width = 9;
|
|
worksheet.Column(8).Width = 9;
|
|
|
|
worksheet.Column(9).Width = 15;
|
|
worksheet.Column(10).Width = 18;
|
|
|
|
for (int i = 11; i <= 23; i++)
|
|
{
|
|
worksheet.Column(i).Width = 12;
|
|
}
|
|
|
|
int descColIndex = showStationColumn ? 25 : 24;
|
|
worksheet.Column(descColIndex).Width = 60;
|
|
}
|
|
}
|
|
// --- End Column Sizing Logic ---
|
|
|
|
workbook.SaveAs(stream);
|
|
}
|
|
|
|
stream.Position = 0;
|
|
return stream;
|
|
}
|
|
|
|
// Add the new simplified header method (remains the same as previous response)
|
|
private void AddSimplifiedHeaders(IXLWorksheet worksheet, ref int rowIndex, bool showStationColumn)
|
|
{
|
|
int startRowIndex = rowIndex;
|
|
int currentCol = 1;
|
|
|
|
// Row 1 Headers
|
|
var cellDay = worksheet.Cell(rowIndex, currentCol);
|
|
cellDay.Value = "Day";
|
|
ApplyHeaderStyle(cellDay, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge();
|
|
currentCol++;
|
|
|
|
var cellDate = worksheet.Cell(rowIndex, currentCol);
|
|
cellDate.Value = "Date";
|
|
ApplyHeaderStyle(cellDate, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge();
|
|
currentCol++;
|
|
|
|
var cellOfficeHour = worksheet.Cell(rowIndex, currentCol);
|
|
cellOfficeHour.Value = "Office Hour";
|
|
ApplyHeaderStyle(cellOfficeHour, 2);
|
|
var mergedRangeOfficeHour = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge();
|
|
mergedRangeOfficeHour.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeOfficeHour.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellAfterOfficeHour = worksheet.Cell(rowIndex, currentCol);
|
|
cellAfterOfficeHour.Value = "After Office Hour";
|
|
ApplyHeaderStyle(cellAfterOfficeHour, 2);
|
|
var mergedRangeAfterOfficeHour = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge();
|
|
mergedRangeAfterOfficeHour.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeAfterOfficeHour.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellTotalOTHrs = worksheet.Cell(rowIndex, currentCol);
|
|
cellTotalOTHrs.Value = "Total OT Hours";
|
|
ApplyHeaderStyle(cellTotalOTHrs, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge();
|
|
currentCol++;
|
|
|
|
var cellBreakMin = worksheet.Cell(rowIndex, currentCol);
|
|
cellBreakMin.Value = "Break (min)";
|
|
ApplyHeaderStyle(cellBreakMin, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge();
|
|
currentCol++;
|
|
|
|
if (showStationColumn)
|
|
{
|
|
var cellStation = worksheet.Cell(rowIndex, currentCol);
|
|
cellStation.Value = "Station";
|
|
ApplyHeaderStyle(cellStation, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge();
|
|
currentCol++;
|
|
}
|
|
|
|
var cellDescription = worksheet.Cell(rowIndex, currentCol);
|
|
cellDescription.Value = "Description";
|
|
ApplyHeaderStyle(cellDescription, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge();
|
|
currentCol++;
|
|
|
|
// Apply borders to the full header range for Row 1
|
|
var headerRow1Range = worksheet.Range(startRowIndex, 1, startRowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow1Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow1Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for sub-headers
|
|
|
|
// Row 2 Sub-Headers
|
|
currentCol = 1;
|
|
currentCol += 2; // Skip Day and Date (vertically merged)
|
|
|
|
var cellOfficeFrom = worksheet.Cell(rowIndex, currentCol++);
|
|
cellOfficeFrom.Value = "From";
|
|
ApplyHeaderStyle(cellOfficeFrom, 3);
|
|
|
|
var cellOfficeTo = worksheet.Cell(rowIndex, currentCol++);
|
|
cellOfficeTo.Value = "To";
|
|
ApplyHeaderStyle(cellOfficeTo, 3);
|
|
|
|
var cellOfficeBreak = worksheet.Cell(rowIndex, currentCol++);
|
|
cellOfficeBreak.Value = "Break";
|
|
ApplyHeaderStyle(cellOfficeBreak, 3);
|
|
|
|
var cellAfterFrom = worksheet.Cell(rowIndex, currentCol++);
|
|
cellAfterFrom.Value = "From";
|
|
ApplyHeaderStyle(cellAfterFrom, 3);
|
|
|
|
var cellAfterTo = worksheet.Cell(rowIndex, currentCol++);
|
|
cellAfterTo.Value = "To";
|
|
ApplyHeaderStyle(cellAfterTo, 3);
|
|
|
|
var cellAfterBreak = worksheet.Cell(rowIndex, currentCol++);
|
|
cellAfterBreak.Value = "Break";
|
|
ApplyHeaderStyle(cellAfterBreak, 3);
|
|
|
|
// Skip Total OT Hours and Break (min) (vertically merged)
|
|
currentCol += 2;
|
|
|
|
if (showStationColumn)
|
|
{
|
|
currentCol++; // Skip Station (vertically merged)
|
|
}
|
|
|
|
// Skip Description (vertically merged)
|
|
currentCol++;
|
|
|
|
|
|
// Apply borders to the full header range for Row 2
|
|
var headerRow2Range = worksheet.Range(rowIndex, 1, rowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow2Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow2Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for data
|
|
}
|
|
|
|
|
|
// --- Helper methods (AddNonHoUPSTWAirHeaders, AddNonHoUNonPSTWAirHeaders, AddHoUPSTWAirHeaders, AddHoUNonPSTWAirHeaders,
|
|
// ApplyHeaderStyle, ApplyDayTypeStyle, GetDayCellColorStyleIndex, IsAdmin, GetAllDatesInMonth, GetDayType, CalculateOrp,
|
|
// FormatAsHourMinute, ConvertTimeToDecimal, CalculateTimeDifferenceInMinutes, CalculateOfficeOtHours, CalculateAfterOfficeOtHours,
|
|
// ClassifyOt, CalculateTotalOtHrs, CalculateNdOdTotal, CalculateRdTotal, CalculatePhTotal, CalculateOtAmount)
|
|
// defined only once below this line.
|
|
|
|
private void ApplyHeaderStyle(IXLCell cell, int styleType)
|
|
{
|
|
cell.Style.Font.SetBold();
|
|
cell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
cell.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
|
|
cell.Style.Alignment.WrapText = true;
|
|
cell.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
cell.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
switch (styleType)
|
|
{
|
|
case 1: // Primary Headers (e.g., "Day", "OT Hrs", "Total OT") - Darker Blue
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#78aafa"); // A standard blue
|
|
cell.Style.Font.FontColor = XLColor.Black; // Changed to Black
|
|
break;
|
|
case 2: // Grouping Headers (e.g., "Office Hour", "After Office Hour", "Off Day", "Rest Day", "Public Holiday") - Light Blue
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#DDEBF7"); // Lighter blue
|
|
cell.Style.Font.FontColor = XLColor.Black;
|
|
break;
|
|
case 3: // Sub-Headers (e.g., "From", "To", "Break", "ND OT", "OD < 4") - Light Green
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#C6E0B4"); // Light green
|
|
cell.Style.Font.FontColor = XLColor.Black;
|
|
break;
|
|
case 4: // Specific headers that need a different color (like "Description" if it needs unique) - Not used in this update, but kept as an option.
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#F8CBAD"); // Light orange
|
|
cell.Style.Font.FontColor = XLColor.Black;
|
|
break;
|
|
case 5: // Totals row background - Purple
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("D8D1F5"); // Original purple for totals
|
|
cell.Style.Font.FontColor = XLColor.Black;
|
|
break;
|
|
case 6: // Pink for Basic Salary, ORP, HRP, OT Amt
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#FF69B4"); // Hot Pink
|
|
cell.Style.Font.FontColor = XLColor.Black; // Changed to Black
|
|
break;
|
|
default: // Fallback
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#A9D08E");
|
|
cell.Style.Font.FontColor = XLColor.Black;
|
|
break;
|
|
}
|
|
}
|
|
private void ApplyDayTypeStyle(IXLCell cell, DateTime date, int? weekendId, List<DateTime> publicHolidays)
|
|
{
|
|
uint styleIndex = GetDayCellColorStyleIndex(date, weekendId, publicHolidays);
|
|
cell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
|
cell.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
|
|
cell.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
cell.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
|
|
switch (styleIndex)
|
|
{
|
|
case 18: // Pink for public holidays (FFC0CB)
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("FFC0CB");
|
|
break;
|
|
case 19: // Light Blue for weekends/off days (ADD8E6)
|
|
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("ADD8E6");
|
|
break;
|
|
default: // Default (no specific background fill)
|
|
cell.Style.Fill.BackgroundColor = XLColor.NoColor;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Method to add headers for Non-HoU, PSTWAIR (includes Basic Salary, ORP, HRP, Station)
|
|
private void AddNonHoUPSTWAirHeaders(IXLWorksheet worksheet, ref int rowIndex)
|
|
{
|
|
int startRowIndex = rowIndex;
|
|
int currentCol = 1;
|
|
|
|
// Row 1 Headers
|
|
currentCol = 1;
|
|
var cellA = worksheet.Cell(rowIndex, currentCol);
|
|
cellA.Value = "Basic Salary (RM)";
|
|
ApplyHeaderStyle(cellA, 6); // Set to pink
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellB = worksheet.Cell(rowIndex, currentCol);
|
|
cellB.Value = "ORP";
|
|
ApplyHeaderStyle(cellB, 6); // Set to pink
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellC = worksheet.Cell(rowIndex, currentCol);
|
|
cellC.Value = "HRP";
|
|
ApplyHeaderStyle(cellC, 6); // Set to pink
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellD = worksheet.Cell(rowIndex, currentCol);
|
|
cellD.Value = "Day";
|
|
ApplyHeaderStyle(cellD, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellE = worksheet.Cell(rowIndex, currentCol);
|
|
cellE.Value = "Date";
|
|
ApplyHeaderStyle(cellE, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellF = worksheet.Cell(rowIndex, currentCol);
|
|
cellF.Value = "Office Hour";
|
|
ApplyHeaderStyle(cellF, 2);
|
|
var mergedRangeF = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge F,G,H for Office Hour
|
|
mergedRangeF.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeF.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellI = worksheet.Cell(rowIndex, currentCol);
|
|
cellI.Value = "After Office Hour";
|
|
ApplyHeaderStyle(cellI, 2);
|
|
var mergedRangeI = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge I,J,K for After Office Hour
|
|
mergedRangeI.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeI.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellL = worksheet.Cell(rowIndex, currentCol);
|
|
cellL.Value = "OT Hrs (Office Hour)";
|
|
ApplyHeaderStyle(cellL, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellM = worksheet.Cell(rowIndex, currentCol);
|
|
cellM.Value = "OT Hrs (After Office Hour)";
|
|
ApplyHeaderStyle(cellM, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellN = worksheet.Cell(rowIndex, currentCol);
|
|
cellN.Value = "Normal Day (After Office)";
|
|
ApplyHeaderStyle(cellN, 2); // Changed to 2 to match 'Off Day' color
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellO = worksheet.Cell(rowIndex, currentCol);
|
|
cellO.Value = "Off Day";
|
|
ApplyHeaderStyle(cellO, 2); // Already using 2
|
|
var mergedRangeO = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge O,P,Q for Off Day
|
|
mergedRangeO.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeO.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellR = worksheet.Cell(rowIndex, currentCol);
|
|
cellR.Value = "Rest Day";
|
|
ApplyHeaderStyle(cellR, 2);
|
|
var mergedRangeR = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge R,S,T for Rest Day
|
|
mergedRangeR.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeR.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellU = worksheet.Cell(rowIndex, currentCol);
|
|
cellU.Value = "Public Holiday";
|
|
ApplyHeaderStyle(cellU, 2);
|
|
var mergedRangeU = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 1).Merge(); // Merge U,V for Public Holiday
|
|
mergedRangeU.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeU.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 2;
|
|
|
|
var cellW = worksheet.Cell(rowIndex, currentCol);
|
|
cellW.Value = "Total OT";
|
|
ApplyHeaderStyle(cellW, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellX = worksheet.Cell(rowIndex, currentCol);
|
|
cellX.Value = "Total ND & OD";
|
|
ApplyHeaderStyle(cellX, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellY = worksheet.Cell(rowIndex, currentCol);
|
|
cellY.Value = "Total RD";
|
|
ApplyHeaderStyle(cellY, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellZ = worksheet.Cell(rowIndex, currentCol);
|
|
cellZ.Value = "Total PH";
|
|
ApplyHeaderStyle(cellZ, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellAA = worksheet.Cell(rowIndex, currentCol);
|
|
cellAA.Value = "OT Amt (RM)";
|
|
ApplyHeaderStyle(cellAA, 6); // Set to pink
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellAB = worksheet.Cell(rowIndex, currentCol);
|
|
cellAB.Value = "Station";
|
|
ApplyHeaderStyle(cellAB, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellAC = worksheet.Cell(rowIndex, currentCol);
|
|
cellAC.Value = "Description";
|
|
ApplyHeaderStyle(cellAC, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var headerRow1Range = worksheet.Range(startRowIndex, 1, startRowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow1Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow1Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
|
|
rowIndex++; // Move to the next row for sub-headers
|
|
|
|
// Row 2 Sub-Headers
|
|
currentCol = 1;
|
|
// These cells are part of vertical merges from Row 1, so they are conceptually empty,
|
|
// but we need to ensure borders are drawn for the entire merged block.
|
|
// We just advance the column counter past the merged cells from Row 1.
|
|
currentCol += 5; // Basic Salary, ORP, HRP, Day, Date
|
|
|
|
var cellF_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellF_sub.Value = "From";
|
|
ApplyHeaderStyle(cellF_sub, 3);
|
|
|
|
var cellG_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellG_sub.Value = "To";
|
|
ApplyHeaderStyle(cellG_sub, 3);
|
|
|
|
var cellH_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellH_sub.Value = "Break";
|
|
ApplyHeaderStyle(cellH_sub, 3);
|
|
|
|
var cellI_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellI_sub.Value = "From";
|
|
ApplyHeaderStyle(cellI_sub, 3);
|
|
|
|
var cellJ_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellJ_sub.Value = "To";
|
|
ApplyHeaderStyle(cellJ_sub, 3);
|
|
|
|
var cellK_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellK_sub.Value = "Break";
|
|
ApplyHeaderStyle(cellK_sub, 3);
|
|
|
|
currentCol += 2; // Skip OT Hrs (Office Hour) and OT Hrs (After Office Hour) (vertically merged)
|
|
|
|
var cellN_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellN_sub.Value = "ND OT";
|
|
ApplyHeaderStyle(cellN_sub, 3);
|
|
|
|
var cellO_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellO_sub.Value = "OD < 4";
|
|
ApplyHeaderStyle(cellO_sub, 3);
|
|
|
|
var cellP_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellP_sub.Value = "OD 4-8";
|
|
ApplyHeaderStyle(cellP_sub, 3);
|
|
|
|
var cellQ_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellQ_sub.Value = "OD After";
|
|
ApplyHeaderStyle(cellQ_sub, 3);
|
|
|
|
var cellR_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellR_sub.Value = "RD < 4";
|
|
ApplyHeaderStyle(cellR_sub, 3);
|
|
|
|
var cellS_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellS_sub.Value = "RD 4-8";
|
|
ApplyHeaderStyle(cellS_sub, 3);
|
|
|
|
var cellT_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellT_sub.Value = "RD After";
|
|
ApplyHeaderStyle(cellT_sub, 3);
|
|
|
|
var cellU_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellU_sub.Value = "PH < 8";
|
|
ApplyHeaderStyle(cellU_sub, 3);
|
|
|
|
var cellV_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellV_sub.Value = "PH After";
|
|
ApplyHeaderStyle(cellV_sub, 3);
|
|
|
|
// Skip Total OT, Total ND & OD, Total RD, Total PH, OT Amt (RM), Station, Description (vertically merged)
|
|
currentCol += 7;
|
|
|
|
// Apply borders to the full header range for Row 2
|
|
var headerRow2Range = worksheet.Range(rowIndex, 1, rowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow2Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow2Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for data
|
|
}
|
|
|
|
// Method to add headers for Non-HoU, Non-PSTWAIR (includes Basic Salary, ORP, HRP, NO Station)
|
|
private void AddNonHoUNonPSTWAirHeaders(IXLWorksheet worksheet, ref int rowIndex)
|
|
{
|
|
int startRowIndex = rowIndex;
|
|
int currentCol;
|
|
|
|
// Row 1 Headers
|
|
currentCol = 1;
|
|
var cellA = worksheet.Cell(rowIndex, currentCol);
|
|
cellA.Value = "Basic Salary (RM)";
|
|
ApplyHeaderStyle(cellA, 6); // Set to pink
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellB = worksheet.Cell(rowIndex, currentCol);
|
|
cellB.Value = "ORP";
|
|
ApplyHeaderStyle(cellB, 6); // Set to pink
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellC = worksheet.Cell(rowIndex, currentCol);
|
|
cellC.Value = "HRP";
|
|
ApplyHeaderStyle(cellC, 6); // Set to pink
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellD = worksheet.Cell(rowIndex, currentCol);
|
|
cellD.Value = "Day";
|
|
ApplyHeaderStyle(cellD, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellE = worksheet.Cell(rowIndex, currentCol);
|
|
cellE.Value = "Date";
|
|
ApplyHeaderStyle(cellE, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellF = worksheet.Cell(rowIndex, currentCol);
|
|
cellF.Value = "Office Hour";
|
|
ApplyHeaderStyle(cellF, 2);
|
|
var mergedRangeF = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge F,G,H
|
|
mergedRangeF.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeF.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellI = worksheet.Cell(rowIndex, currentCol);
|
|
cellI.Value = "After Office Hour";
|
|
ApplyHeaderStyle(cellI, 2);
|
|
var mergedRangeI = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge I,J,K
|
|
mergedRangeI.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeI.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellL = worksheet.Cell(rowIndex, currentCol);
|
|
cellL.Value = "OT Hrs (Office Hour)";
|
|
ApplyHeaderStyle(cellL, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellM = worksheet.Cell(rowIndex, currentCol);
|
|
cellM.Value = "OT Hrs (After Office Hour)";
|
|
ApplyHeaderStyle(cellM, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellN = worksheet.Cell(rowIndex, currentCol);
|
|
cellN.Value = "Normal Day (After Office)";
|
|
ApplyHeaderStyle(cellN, 2); // Changed to 2 to match 'Off Day' color
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellO = worksheet.Cell(rowIndex, currentCol);
|
|
cellO.Value = "Off Day";
|
|
ApplyHeaderStyle(cellO, 2); // Already using 2
|
|
var mergedRangeO = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge O,P,Q
|
|
mergedRangeO.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeO.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellR = worksheet.Cell(rowIndex, currentCol);
|
|
cellR.Value = "Rest Day";
|
|
ApplyHeaderStyle(cellR, 2);
|
|
var mergedRangeR = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge R,S,T
|
|
mergedRangeR.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeR.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellU = worksheet.Cell(rowIndex, currentCol);
|
|
cellU.Value = "Public Holiday";
|
|
ApplyHeaderStyle(cellU, 2);
|
|
var mergedRangeU = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 1).Merge(); // Merge U,V
|
|
mergedRangeU.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeU.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 2;
|
|
|
|
var cellW = worksheet.Cell(rowIndex, currentCol);
|
|
cellW.Value = "Total OT";
|
|
ApplyHeaderStyle(cellW, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellX = worksheet.Cell(rowIndex, currentCol);
|
|
cellX.Value = "Total ND & OD";
|
|
ApplyHeaderStyle(cellX, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellY = worksheet.Cell(rowIndex, currentCol);
|
|
cellY.Value = "Total RD";
|
|
ApplyHeaderStyle(cellY, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellZ = worksheet.Cell(rowIndex, currentCol);
|
|
cellZ.Value = "Total PH";
|
|
ApplyHeaderStyle(cellZ, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellAA = worksheet.Cell(rowIndex, currentCol);
|
|
cellAA.Value = "OT Amt (RM)";
|
|
ApplyHeaderStyle(cellAA, 6); // Set to pink
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellAB = worksheet.Cell(rowIndex, currentCol);
|
|
cellAB.Value = "Description"; // AB (No Station Column)
|
|
ApplyHeaderStyle(cellAB, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var headerRow1Range = worksheet.Range(startRowIndex, 1, startRowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow1Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow1Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for sub-headers
|
|
|
|
// Row 2 Sub-Headers
|
|
currentCol = 1;
|
|
currentCol += 5; // Basic Salary, ORP, HRP, Day, Date
|
|
|
|
var cellF_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellF_sub.Value = "From";
|
|
ApplyHeaderStyle(cellF_sub, 3);
|
|
|
|
var cellG_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellG_sub.Value = "To";
|
|
ApplyHeaderStyle(cellG_sub, 3);
|
|
|
|
var cellH_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellH_sub.Value = "Break";
|
|
ApplyHeaderStyle(cellH_sub, 3);
|
|
|
|
var cellI_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellI_sub.Value = "From";
|
|
ApplyHeaderStyle(cellI_sub, 3);
|
|
|
|
var cellJ_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellJ_sub.Value = "To";
|
|
ApplyHeaderStyle(cellJ_sub, 3);
|
|
|
|
var cellK_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellK_sub.Value = "Break";
|
|
ApplyHeaderStyle(cellK_sub, 3);
|
|
|
|
currentCol += 2; // Skip OT Hrs (Office Hour) and OT Hrs (After Office Hour) (vertically merged)
|
|
|
|
var cellN_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellN_sub.Value = "ND OT";
|
|
ApplyHeaderStyle(cellN_sub, 3);
|
|
|
|
var cellO_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellO_sub.Value = "OD < 4";
|
|
ApplyHeaderStyle(cellO_sub, 3);
|
|
|
|
var cellP_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellP_sub.Value = "OD 4-8";
|
|
ApplyHeaderStyle(cellP_sub, 3);
|
|
|
|
var cellQ_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellQ_sub.Value = "OD After";
|
|
ApplyHeaderStyle(cellQ_sub, 3);
|
|
|
|
var cellR_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellR_sub.Value = "RD < 4";
|
|
ApplyHeaderStyle(cellR_sub, 3);
|
|
|
|
var cellS_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellS_sub.Value = "RD 4-8";
|
|
ApplyHeaderStyle(cellS_sub, 3);
|
|
|
|
var cellT_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellT_sub.Value = "RD After";
|
|
ApplyHeaderStyle(cellT_sub, 3);
|
|
|
|
var cellU_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellU_sub.Value = "PH < 8";
|
|
ApplyHeaderStyle(cellU_sub, 3);
|
|
|
|
var cellV_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellV_sub.Value = "PH After";
|
|
ApplyHeaderStyle(cellV_sub, 3);
|
|
|
|
currentCol += 6; // Skip Total OT, Total ND & OD, Total RD, Total PH, OT Amt (RM), Description (vertically merged)
|
|
|
|
// Apply borders to the full header range for Row 2
|
|
var headerRow2Range = worksheet.Range(rowIndex, 1, rowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow2Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow2Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for data
|
|
}
|
|
|
|
// Method to add headers for HoU, PSTWAIR (NO Basic Salary, ORP, HRP, includes Station)
|
|
private void AddHoUPSTWAirHeaders(IXLWorksheet worksheet, ref int rowIndex)
|
|
{
|
|
int startRowIndex = rowIndex;
|
|
int currentCol = 1;
|
|
|
|
// Row 1 Headers
|
|
currentCol = 1;
|
|
var cellA = worksheet.Cell(rowIndex, currentCol);
|
|
cellA.Value = "Day";
|
|
ApplyHeaderStyle(cellA, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellB = worksheet.Cell(rowIndex, currentCol);
|
|
cellB.Value = "Date";
|
|
ApplyHeaderStyle(cellB, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellC = worksheet.Cell(rowIndex, currentCol);
|
|
cellC.Value = "Office Hour";
|
|
ApplyHeaderStyle(cellC, 2);
|
|
var mergedRangeC = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge C,D,E
|
|
mergedRangeC.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeC.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellF = worksheet.Cell(rowIndex, currentCol);
|
|
cellF.Value = "After Office Hour";
|
|
ApplyHeaderStyle(cellF, 2);
|
|
var mergedRangeF = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge F,G,H
|
|
mergedRangeF.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeF.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellI = worksheet.Cell(rowIndex, currentCol);
|
|
cellI.Value = "OT Hrs (Office Hour)";
|
|
ApplyHeaderStyle(cellI, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellJ = worksheet.Cell(rowIndex, currentCol);
|
|
cellJ.Value = "OT Hrs (After Office Hour)";
|
|
ApplyHeaderStyle(cellJ, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellK = worksheet.Cell(rowIndex, currentCol);
|
|
cellK.Value = "Normal Day (After Office)";
|
|
ApplyHeaderStyle(cellK, 2); // Changed to 2 to match 'Off Day' color
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellL = worksheet.Cell(rowIndex, currentCol);
|
|
cellL.Value = "Off Day";
|
|
ApplyHeaderStyle(cellL, 2); // Already using 2
|
|
var mergedRangeL = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge L,M,N
|
|
mergedRangeL.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeL.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellO = worksheet.Cell(rowIndex, currentCol);
|
|
cellO.Value = "Rest Day";
|
|
ApplyHeaderStyle(cellO, 2);
|
|
var mergedRangeO = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge O,P,Q
|
|
mergedRangeO.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeO.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellR = worksheet.Cell(rowIndex, currentCol);
|
|
cellR.Value = "Public Holiday";
|
|
ApplyHeaderStyle(cellR, 2);
|
|
var mergedRangeR = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 1).Merge(); // Merge R,S
|
|
mergedRangeR.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeR.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 2;
|
|
|
|
var cellT = worksheet.Cell(rowIndex, currentCol);
|
|
cellT.Value = "Total OT";
|
|
ApplyHeaderStyle(cellT, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellU = worksheet.Cell(rowIndex, currentCol);
|
|
cellU.Value = "Total ND & OD";
|
|
ApplyHeaderStyle(cellU, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellV = worksheet.Cell(rowIndex, currentCol);
|
|
cellV.Value = "Total RD";
|
|
ApplyHeaderStyle(cellV, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellW = worksheet.Cell(rowIndex, currentCol);
|
|
cellW.Value = "Total PH";
|
|
ApplyHeaderStyle(cellW, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellX = worksheet.Cell(rowIndex, currentCol);
|
|
cellX.Value = "Station";
|
|
ApplyHeaderStyle(cellX, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellY = worksheet.Cell(rowIndex, currentCol);
|
|
cellY.Value = "Description";
|
|
ApplyHeaderStyle(cellY, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var headerRow1Range = worksheet.Range(startRowIndex, 1, startRowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow1Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow1Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for sub-headers
|
|
|
|
// Row 2 Sub-Headers
|
|
currentCol = 1;
|
|
currentCol += 2; // Day, Date
|
|
|
|
var cellC_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellC_sub.Value = "From";
|
|
ApplyHeaderStyle(cellC_sub, 3);
|
|
|
|
var cellD_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellD_sub.Value = "To";
|
|
ApplyHeaderStyle(cellD_sub, 3);
|
|
|
|
var cellE_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellE_sub.Value = "Break";
|
|
ApplyHeaderStyle(cellE_sub, 3);
|
|
|
|
var cellF_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellF_sub.Value = "From";
|
|
ApplyHeaderStyle(cellF_sub, 3);
|
|
|
|
var cellG_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellG_sub.Value = "To";
|
|
ApplyHeaderStyle(cellG_sub, 3);
|
|
|
|
var cellH_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellH_sub.Value = "Break";
|
|
ApplyHeaderStyle(cellH_sub, 3);
|
|
|
|
currentCol += 2; // Skip OT Hrs (Office Hour) and OT Hrs (After Office Hour) (vertically merged)
|
|
|
|
var cellK_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellK_sub.Value = "ND OT";
|
|
ApplyHeaderStyle(cellK_sub, 3);
|
|
|
|
var cellL_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellL_sub.Value = "OD < 4";
|
|
ApplyHeaderStyle(cellL_sub, 3);
|
|
|
|
var cellM_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellM_sub.Value = "OD 4-8";
|
|
ApplyHeaderStyle(cellM_sub, 3);
|
|
|
|
var cellN_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellN_sub.Value = "OD After";
|
|
ApplyHeaderStyle(cellN_sub, 3);
|
|
|
|
var cellO_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellO_sub.Value = "RD < 4";
|
|
ApplyHeaderStyle(cellO_sub, 3);
|
|
|
|
var cellP_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellP_sub.Value = "RD 4-8";
|
|
ApplyHeaderStyle(cellP_sub, 3);
|
|
|
|
var cellQ_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellQ_sub.Value = "RD After";
|
|
ApplyHeaderStyle(cellQ_sub, 3);
|
|
|
|
var cellR_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellR_sub.Value = "PH < 8";
|
|
ApplyHeaderStyle(cellR_sub, 3);
|
|
|
|
var cellS_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellS_sub.Value = "PH After";
|
|
ApplyHeaderStyle(cellS_sub, 3);
|
|
|
|
currentCol += 5; // Skip Total OT, Total ND & OD, Total RD, Total PH, Station, Description (vertically merged)
|
|
|
|
// Apply borders to the full header range for Row 2
|
|
var headerRow2Range = worksheet.Range(rowIndex, 1, rowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow2Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow2Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for data
|
|
}
|
|
|
|
// Method to add headers for HoU, Non-PSTWAIR (NO Basic Salary, ORP, HRP, NO Station)
|
|
private void AddHoUNonPSTWAirHeaders(IXLWorksheet worksheet, ref int rowIndex)
|
|
{
|
|
int startRowIndex = rowIndex;
|
|
int currentCol;
|
|
|
|
// Row 1 Headers
|
|
currentCol = 1;
|
|
var cellA = worksheet.Cell(rowIndex, currentCol);
|
|
cellA.Value = "Day";
|
|
ApplyHeaderStyle(cellA, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellB = worksheet.Cell(rowIndex, currentCol);
|
|
cellB.Value = "Date";
|
|
ApplyHeaderStyle(cellB, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellC = worksheet.Cell(rowIndex, currentCol);
|
|
cellC.Value = "Office Hour";
|
|
ApplyHeaderStyle(cellC, 2);
|
|
var mergedRangeC = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge C,D,E
|
|
mergedRangeC.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeC.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellF = worksheet.Cell(rowIndex, currentCol);
|
|
cellF.Value = "After Office Hour";
|
|
ApplyHeaderStyle(cellF, 2);
|
|
var mergedRangeF = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge F,G,H
|
|
mergedRangeF.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeF.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellI = worksheet.Cell(rowIndex, currentCol);
|
|
cellI.Value = "OT Hrs (Office Hour)";
|
|
ApplyHeaderStyle(cellI, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellJ = worksheet.Cell(rowIndex, currentCol);
|
|
cellJ.Value = "OT Hrs (After Office Hour)";
|
|
ApplyHeaderStyle(cellJ, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellK = worksheet.Cell(rowIndex, currentCol);
|
|
cellK.Value = "Normal Day (After Office)";
|
|
ApplyHeaderStyle(cellK, 2); // Changed to 2 to match 'Off Day' color
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellL = worksheet.Cell(rowIndex, currentCol);
|
|
cellL.Value = "Off Day";
|
|
ApplyHeaderStyle(cellL, 2); // Already using 2
|
|
var mergedRangeL = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge L,M,N
|
|
mergedRangeL.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeL.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellO = worksheet.Cell(rowIndex, currentCol);
|
|
cellO.Value = "Rest Day";
|
|
ApplyHeaderStyle(cellO, 2);
|
|
var mergedRangeO = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 2).Merge(); // Merge O,P,Q
|
|
mergedRangeO.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeO.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 3;
|
|
|
|
var cellR = worksheet.Cell(rowIndex, currentCol);
|
|
cellR.Value = "Public Holiday";
|
|
ApplyHeaderStyle(cellR, 2);
|
|
var mergedRangeR = worksheet.Range(rowIndex, currentCol, rowIndex, currentCol + 1).Merge(); // Merge R,S
|
|
mergedRangeR.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
mergedRangeR.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
currentCol += 2;
|
|
|
|
var cellT = worksheet.Cell(rowIndex, currentCol);
|
|
cellT.Value = "Total OT";
|
|
ApplyHeaderStyle(cellT, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellU = worksheet.Cell(rowIndex, currentCol);
|
|
cellU.Value = "Total ND & OD";
|
|
ApplyHeaderStyle(cellU, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellV = worksheet.Cell(rowIndex, currentCol);
|
|
cellV.Value = "Total RD";
|
|
ApplyHeaderStyle(cellV, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellW = worksheet.Cell(rowIndex, currentCol);
|
|
cellW.Value = "Total PH";
|
|
ApplyHeaderStyle(cellW, 1); // Changed to style 1 (Darker blue)
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var cellX = worksheet.Cell(rowIndex, currentCol);
|
|
cellX.Value = "Description"; // X (No Station Column)
|
|
ApplyHeaderStyle(cellX, 1);
|
|
worksheet.Range(rowIndex, currentCol, rowIndex + 1, currentCol).Merge(); // Merge vertically
|
|
currentCol++;
|
|
|
|
var headerRow1Range = worksheet.Range(startRowIndex, 1, startRowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow1Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow1Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for sub-headers
|
|
|
|
// Row 2 Sub-Headers
|
|
currentCol = 1;
|
|
currentCol += 2; // Day, Date
|
|
|
|
var cellC_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellC_sub.Value = "From";
|
|
ApplyHeaderStyle(cellC_sub, 3);
|
|
|
|
var cellD_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellD_sub.Value = "To";
|
|
ApplyHeaderStyle(cellD_sub, 3);
|
|
|
|
var cellE_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellE_sub.Value = "Break";
|
|
ApplyHeaderStyle(cellE_sub, 3);
|
|
|
|
var cellF_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellF_sub.Value = "From";
|
|
ApplyHeaderStyle(cellF_sub, 3);
|
|
|
|
var cellG_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellG_sub.Value = "To";
|
|
ApplyHeaderStyle(cellG_sub, 3);
|
|
|
|
var cellH_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellH_sub.Value = "Break";
|
|
ApplyHeaderStyle(cellH_sub, 3);
|
|
|
|
currentCol += 2; // Skip OT Hrs (Office Hour) and OT Hrs (After Office Hour) (vertically merged)
|
|
|
|
var cellK_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellK_sub.Value = "ND OT";
|
|
ApplyHeaderStyle(cellK_sub, 3);
|
|
|
|
var cellL_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellL_sub.Value = "OD < 4";
|
|
ApplyHeaderStyle(cellL_sub, 3);
|
|
|
|
var cellM_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellM_sub.Value = "OD 4-8";
|
|
ApplyHeaderStyle(cellM_sub, 3);
|
|
|
|
var cellN_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellN_sub.Value = "OD After";
|
|
ApplyHeaderStyle(cellN_sub, 3);
|
|
|
|
var cellO_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellO_sub.Value = "RD < 4";
|
|
ApplyHeaderStyle(cellO_sub, 3);
|
|
|
|
var cellP_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellP_sub.Value = "RD 4-8";
|
|
ApplyHeaderStyle(cellP_sub, 3);
|
|
|
|
var cellQ_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellQ_sub.Value = "RD After";
|
|
ApplyHeaderStyle(cellQ_sub, 3);
|
|
|
|
var cellR_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellR_sub.Value = "PH < 8";
|
|
ApplyHeaderStyle(cellR_sub, 3);
|
|
|
|
var cellS_sub = worksheet.Cell(rowIndex, currentCol++);
|
|
cellS_sub.Value = "PH After";
|
|
ApplyHeaderStyle(cellS_sub, 3);
|
|
|
|
currentCol += 4; // Skip Total OT, Total ND & OD, Total RD, Total PH, Description (vertically merged)
|
|
|
|
// Apply borders to the full header range for Row 2
|
|
var headerRow2Range = worksheet.Range(rowIndex, 1, rowIndex, worksheet.LastColumnUsed().ColumnNumber());
|
|
headerRow2Range.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
headerRow2Range.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
|
|
|
rowIndex++; // Move to the next row for data
|
|
}
|
|
|
|
|
|
private uint GetDayCellColorStyleIndex(DateTime date, int? weekendId, List<DateTime> publicHolidays)
|
|
{
|
|
if (publicHolidays.Contains(date.Date))
|
|
return 18; // Style 18: Pink for public holidays
|
|
|
|
DayOfWeek dayOfWeek = date.DayOfWeek;
|
|
if ((weekendId == 1 && (dayOfWeek == DayOfWeek.Friday || dayOfWeek == DayOfWeek.Saturday)) ||
|
|
(weekendId == 2 && (dayOfWeek == DayOfWeek.Saturday || dayOfWeek == DayOfWeek.Sunday)))
|
|
return 19; // Style 19: Light Blue for weekends/off days (Fri/Sat or Sat/Sun)
|
|
|
|
return 11; // Style 11: Default normal text center aligned
|
|
}
|
|
|
|
private bool IsAdmin(int userId)
|
|
{
|
|
var userRoles = _centralDbContext.UserRoles
|
|
.Where(ur => ur.UserId == userId)
|
|
.Join(_centralDbContext.Roles, ur => ur.RoleId, r => r.Id, (ur, r) => r.Name)
|
|
.ToList();
|
|
|
|
return userRoles.Any(role => role.Contains("SuperAdmin") || role.Contains("SystemAdmin"));
|
|
}
|
|
|
|
private List<DateTime> GetAllDatesInMonth(DateTime month)
|
|
{
|
|
return Enumerable.Range(1, DateTime.DaysInMonth(month.Year, month.Month))
|
|
.Select(day => new DateTime(month.Year, month.Month, day))
|
|
.ToList();
|
|
}
|
|
|
|
private string GetDayType(DateTime date, int? weekendId, List<DateTime> publicHolidays)
|
|
{
|
|
if (publicHolidays.Contains(date.Date))
|
|
{
|
|
return "Public Holiday";
|
|
}
|
|
|
|
DayOfWeek dayOfWeek = date.DayOfWeek;
|
|
|
|
if ((weekendId == 1 && (dayOfWeek == DayOfWeek.Friday || dayOfWeek == DayOfWeek.Saturday)) ||
|
|
(weekendId == 2 && (dayOfWeek == DayOfWeek.Saturday || dayOfWeek == DayOfWeek.Sunday)))
|
|
{
|
|
if ((weekendId == 1 && dayOfWeek == DayOfWeek.Friday) ||
|
|
(weekendId == 2 && dayOfWeek == DayOfWeek.Saturday))
|
|
{
|
|
return "Off Day";
|
|
}
|
|
if ((weekendId == 1 && dayOfWeek == DayOfWeek.Saturday) ||
|
|
(weekendId == 2 && dayOfWeek == DayOfWeek.Sunday))
|
|
{
|
|
return "Rest Day";
|
|
}
|
|
}
|
|
|
|
return "Normal Day";
|
|
}
|
|
|
|
// Modified: ORP = Basic Salary / 26
|
|
private decimal CalculateOrp(decimal basicSalary)
|
|
{
|
|
return basicSalary / 26m;
|
|
}
|
|
|
|
// New: HRP = ORP / 8
|
|
private decimal CalculateHrp(decimal orp)
|
|
{
|
|
return orp / 8m;
|
|
}
|
|
|
|
|
|
// Corrected FormatAsHourMinute method
|
|
private string FormatAsHourMinute(decimal? totalMinutes, bool isMinutes = false)
|
|
{
|
|
if (totalMinutes == null || totalMinutes == 0)
|
|
return "";
|
|
|
|
TimeSpan ts;
|
|
if (isMinutes)
|
|
{
|
|
// If isMinutes is true, input is total minutes
|
|
ts = TimeSpan.FromMinutes((double)totalMinutes.Value);
|
|
}
|
|
else
|
|
{
|
|
// If isMinutes is false, input is decimal hours (e.g., 1.5 for 1 hour 30 mins)
|
|
// Convert decimal hours to minutes for TimeSpan.FromMinutes
|
|
ts = TimeSpan.FromMinutes((double)(totalMinutes.Value * 60));
|
|
}
|
|
|
|
// Handle negative times if durations can ever be negative, though usually they are clamped at 0
|
|
if (ts.TotalMinutes < 0)
|
|
{
|
|
return $"-{Math.Abs((int)ts.TotalHours)}:{Math.Abs(ts.Minutes):D2}";
|
|
}
|
|
|
|
int hours = (int)ts.TotalHours;
|
|
int minutes = ts.Minutes; // TimeSpan.Minutes property gives the minute component (0-59)
|
|
|
|
return $"{hours}:{minutes:D2}";
|
|
}
|
|
|
|
|
|
private decimal ConvertTimeToDecimal(string time)
|
|
{
|
|
if (string.IsNullOrEmpty(time)) return 0;
|
|
|
|
var parts = time.Split(':');
|
|
if (parts.Length != 2) return 0;
|
|
|
|
if (int.TryParse(parts[0], out int hours) && int.TryParse(parts[1], out int minutes))
|
|
{
|
|
return hours + (minutes / 60m);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// --- Missing Helper Method: CalculateTimeDifferenceInMinutes ---
|
|
private decimal CalculateTimeDifferenceInMinutes(TimeSpan? from, TimeSpan? to)
|
|
{
|
|
if (!from.HasValue || !to.HasValue) return 0;
|
|
|
|
double fromTotalMinutes = from.Value.TotalMinutes;
|
|
double toTotalMinutes = to.Value.TotalMinutes;
|
|
|
|
double totalMinutes = toTotalMinutes - fromTotalMinutes;
|
|
|
|
// If the 'to' time is earlier than the 'from' time, it means the period crosses midnight.
|
|
// Add 24 hours (1440 minutes) to account for the next day.
|
|
if (totalMinutes < 0)
|
|
{
|
|
totalMinutes += 24 * 60; // Add 24 hours in minutes
|
|
}
|
|
|
|
return (decimal)Math.Max(0, totalMinutes); // Ensure the result is non-negative
|
|
}
|
|
// --- End Missing Helper Method ---
|
|
|
|
|
|
private string CalculateOfficeOtHours(OtRegisterModel record)
|
|
{
|
|
if (!record.OfficeFrom.HasValue || !record.OfficeTo.HasValue) return "";
|
|
|
|
var duration = record.OfficeTo.Value - record.OfficeFrom.Value;
|
|
var totalMinutes = duration.TotalMinutes - (record.OfficeBreak ?? 0);
|
|
totalMinutes = Math.Max(0, totalMinutes);
|
|
|
|
int hours = (int)(totalMinutes / 60);
|
|
int minutes = (int)(totalMinutes % 60);
|
|
|
|
return $"{hours}:{minutes:D2}";
|
|
}
|
|
|
|
private string CalculateAfterOfficeOtHours(OtRegisterModel record)
|
|
{
|
|
if (!record.AfterFrom.HasValue || !record.AfterTo.HasValue) return "";
|
|
|
|
var duration = record.AfterTo.Value - record.AfterFrom.Value;
|
|
var totalMinutes = duration.TotalMinutes - (record.AfterBreak ?? 0);
|
|
totalMinutes = Math.Max(0, totalMinutes);
|
|
|
|
int hours = (int)(totalMinutes / 60);
|
|
int minutes = (int)(totalMinutes % 60);
|
|
|
|
return $"{hours}:{minutes:D2}";
|
|
}
|
|
|
|
private Dictionary<string, string> ClassifyOt(OtRegisterModel record, decimal hrp, List<DateTime> publicHolidays, int? weekendId)
|
|
{
|
|
var officeRaw = CalculateOfficeOtHours(record);
|
|
var afterRaw = CalculateAfterOfficeOtHours(record);
|
|
|
|
var officeHrs = ConvertTimeToDecimal(officeRaw);
|
|
var afterHrs = ConvertTimeToDecimal(afterRaw);
|
|
|
|
var toFixedOrEmpty = (decimal num) => num > 0 ? num.ToString("N2") : "";
|
|
|
|
var dayType = GetDayType(record.OtDate, weekendId, publicHolidays);
|
|
|
|
var result = new Dictionary<string, string>
|
|
{
|
|
["ndAfter"] = "",
|
|
["odUnder4"] = "",
|
|
["odBetween4And8"] = "",
|
|
["odAfter"] = "",
|
|
["rdUnder4"] = "",
|
|
["rdBetween4And8"] = "",
|
|
["rdAfter"] = "",
|
|
["phUnder8"] = "",
|
|
["phAfter"] = ""
|
|
};
|
|
|
|
switch (dayType)
|
|
{
|
|
case "Normal Day":
|
|
result["ndAfter"] = toFixedOrEmpty(afterHrs);
|
|
break;
|
|
case "Off Day":
|
|
if (officeHrs > 0)
|
|
{
|
|
if (officeHrs <= 4) result["odUnder4"] = toFixedOrEmpty(officeHrs);
|
|
else if (officeHrs > 4 && officeHrs <= 8) result["odBetween4And8"] = toFixedOrEmpty(officeHrs);
|
|
}
|
|
result["odAfter"] = toFixedOrEmpty(afterHrs);
|
|
break;
|
|
case "Rest Day":
|
|
if (officeHrs > 0)
|
|
{
|
|
if (officeHrs <= 4) result["rdUnder4"] = toFixedOrEmpty(officeHrs);
|
|
else if (officeHrs > 4 && officeHrs <= 8) result["rdBetween4And8"] = toFixedOrEmpty(officeHrs);
|
|
}
|
|
result["rdAfter"] = toFixedOrEmpty(afterHrs);
|
|
break;
|
|
case "Public Holiday":
|
|
if (officeHrs > 0)
|
|
{
|
|
if (officeHrs <= 8) result["phUnder8"] = toFixedOrEmpty(officeHrs);
|
|
}
|
|
result["phAfter"] = toFixedOrEmpty(afterHrs);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private string CalculateTotalOtHrs(OtRegisterModel record, decimal hrp, List<DateTime> publicHolidays, int? weekendId)
|
|
{
|
|
var classified = ClassifyOt(record, hrp, publicHolidays, weekendId);
|
|
decimal total = 0;
|
|
|
|
foreach (var value in classified.Values)
|
|
{
|
|
if (decimal.TryParse(value, out decimal num))
|
|
{
|
|
total += num;
|
|
}
|
|
}
|
|
|
|
return total > 0 ? total.ToString("N2") : "";
|
|
}
|
|
|
|
private string CalculateNdOdTotal(OtRegisterModel record, decimal hrp, List<DateTime> publicHolidays, int? weekendId)
|
|
{
|
|
var classified = ClassifyOt(record, hrp, publicHolidays, weekendId);
|
|
|
|
var nd = decimal.TryParse(classified["ndAfter"], out decimal ndVal) ? ndVal : 0;
|
|
var od1 = decimal.TryParse(classified["odUnder4"], out decimal od1Val) ? od1Val : 0;
|
|
var od2 = decimal.TryParse(classified["odBetween4And8"], out decimal od2Val) ? od2Val : 0;
|
|
var od3 = decimal.TryParse(classified["odAfter"], out decimal od3Val) ? od3Val : 0;
|
|
|
|
var total = nd + od1 + od2 + od3;
|
|
return total > 0 ? total.ToString("N2") : "";
|
|
}
|
|
|
|
private string CalculateRdTotal(OtRegisterModel record, decimal hrp, List<DateTime> publicHolidays, int? weekendId)
|
|
{
|
|
var classified = ClassifyOt(record, hrp, publicHolidays, weekendId);
|
|
var total =
|
|
(decimal.TryParse(classified["rdUnder4"], out decimal rd1) ? rd1 : 0) +
|
|
(decimal.TryParse(classified["rdBetween4And8"], out decimal rd2) ? rd2 : 0) +
|
|
(decimal.TryParse(classified["rdAfter"], out decimal rd3) ? rd3 : 0);
|
|
|
|
return total > 0 ? total.ToString("N2") : "";
|
|
}
|
|
|
|
private string CalculatePhTotal(OtRegisterModel record, decimal hrp, List<DateTime> publicHolidays, int? weekendId)
|
|
{
|
|
var classified = ClassifyOt(record, hrp, publicHolidays, weekendId);
|
|
var total =
|
|
(decimal.TryParse(classified["phUnder8"], out decimal ph1) ? ph1 : 0) +
|
|
(decimal.TryParse(classified["phAfter"], out decimal ph2) ? ph2 : 0);
|
|
|
|
return total > 0 ? total.ToString("N2") : "";
|
|
}
|
|
|
|
private string CalculateOtAmount(OtRegisterModel record, decimal hrp, List<DateTime> publicHolidays, int? weekendId)
|
|
{
|
|
var orp = CalculateOrp(hrp * 208); // Recalculate ORP based on an assumed 208 hours in a month to get back to basic salary, then divide by 26
|
|
var dayType = GetDayType(record.OtDate, weekendId, publicHolidays);
|
|
|
|
var officeRaw = CalculateOfficeOtHours(record);
|
|
var afterRaw = CalculateAfterOfficeOtHours(record);
|
|
|
|
var officeHours = ConvertTimeToDecimal(officeRaw);
|
|
var afterOfficeHours = ConvertTimeToDecimal(afterRaw);
|
|
|
|
decimal amountOffice = 0;
|
|
decimal amountAfter = 0;
|
|
|
|
if (officeHours > 0)
|
|
{
|
|
if (dayType == "Off Day" || dayType == "Rest Day")
|
|
{
|
|
if (officeHours <= 4)
|
|
{
|
|
amountOffice = 0.5m * orp;
|
|
}
|
|
else if (officeHours > 4 && officeHours <= 8)
|
|
{
|
|
amountOffice = 1 * orp;
|
|
}
|
|
}
|
|
else if (dayType == "Public Holiday")
|
|
{
|
|
amountOffice = 2 * orp;
|
|
}
|
|
}
|
|
|
|
if (afterOfficeHours > 0)
|
|
{
|
|
switch (dayType)
|
|
{
|
|
case "Normal Day":
|
|
case "Off Day":
|
|
amountAfter = 1.5m * hrp * afterOfficeHours;
|
|
break;
|
|
case "Rest Day":
|
|
amountAfter = 2 * hrp * afterOfficeHours;
|
|
break;
|
|
case "Public Holiday":
|
|
amountAfter = 3 * hrp * afterOfficeHours;
|
|
break;
|
|
}
|
|
}
|
|
|
|
var totalAmount = amountOffice + amountAfter;
|
|
return totalAmount.ToString("N2");
|
|
}
|
|
|
|
// Modified AddRemarksSection to start at column 1
|
|
private void AddRemarksSection(IXLWorksheet worksheet, ref int currentRow, DateTime displayMonth, int? weekendId, List<CalendarModel> publicHolidays)
|
|
{
|
|
// Start remarks at column 1 for left alignment
|
|
int remarksStartColumn = 1;
|
|
int remarksRow = currentRow + 1; // Start remarks below the total table
|
|
|
|
// Public Holiday Color
|
|
var phColorCell = worksheet.Cell(remarksRow, remarksStartColumn);
|
|
phColorCell.Value = " "; // Small cell for color swatch
|
|
phColorCell.Style.Fill.BackgroundColor = XLColor.FromHtml("FFC0CB"); // Pink color
|
|
phColorCell.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
|
|
worksheet.Cell(remarksRow, remarksStartColumn + 1).Value = " Public Holiday";
|
|
worksheet.Cell(remarksRow, remarksStartColumn + 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
remarksRow++;
|
|
|
|
// Weekend/Off Day Color
|
|
var weekendColorCell = worksheet.Cell(remarksRow, remarksStartColumn);
|
|
weekendColorCell.Value = " "; // Small cell for color swatch
|
|
weekendColorCell.Style.Fill.BackgroundColor = XLColor.FromHtml("ADD8E6"); // Light Blue color
|
|
weekendColorCell.Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
|
|
|
|
worksheet.Cell(remarksRow, remarksStartColumn + 1).Value = " Weekend";
|
|
worksheet.Cell(remarksRow, remarksStartColumn + 1).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
remarksRow++;
|
|
|
|
remarksRow++; // Blank line for spacing
|
|
|
|
// --- Public Holidays List for the Month ---
|
|
worksheet.Cell(remarksRow, remarksStartColumn).Value = $"Public Holidays in {displayMonth:MMMM yyyy}:";
|
|
worksheet.Cell(remarksRow, remarksStartColumn).Style.Font.SetBold();
|
|
remarksRow++;
|
|
|
|
if (publicHolidays != null && publicHolidays.Any())
|
|
{
|
|
var monthHolidays = publicHolidays
|
|
.Where(h => h.HolidayDate.Year == displayMonth.Year && h.HolidayDate.Month == displayMonth.Month)
|
|
.OrderBy(h => h.HolidayDate.Day)
|
|
.ToList();
|
|
|
|
if (monthHolidays.Any())
|
|
{
|
|
foreach (var holiday in monthHolidays)
|
|
{
|
|
worksheet.Cell(remarksRow, remarksStartColumn).Value = $"- {holiday.HolidayDate:dd MMMM}: {holiday.HolidayName}";
|
|
worksheet.Cell(remarksRow, remarksStartColumn).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Left;
|
|
remarksRow++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
worksheet.Cell(remarksRow, remarksStartColumn).Value = "No public holidays recorded for this month.";
|
|
worksheet.Cell(remarksRow, remarksStartColumn).Style.Font.SetItalic();
|
|
remarksRow++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
worksheet.Cell(remarksRow, remarksStartColumn).Value = "No public holiday data found.";
|
|
worksheet.Cell(remarksRow, remarksStartColumn).Style.Font.SetItalic();
|
|
remarksRow++;
|
|
}
|
|
|
|
// Update currentRow to the bottom of the remarks section
|
|
currentRow = remarksRow;
|
|
}
|
|
|
|
private string GetWeekendDefinition(int? weekendId)
|
|
{
|
|
if (!weekendId.HasValue) return "N/A";
|
|
|
|
switch (weekendId)
|
|
{
|
|
case 1: return "Friday & Saturday";
|
|
case 2: return "Saturday & Sunday";
|
|
default: return "Unknown";
|
|
}
|
|
}
|
|
}
|
|
} |