update user list / update Excel & PDF
This commit is contained in:
parent
9cd3c473cf
commit
ed30316685
@ -36,10 +36,12 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
string? flexiHour = null,
|
||||
byte[]? logoImage = null,
|
||||
bool isSimplifiedExport = false,
|
||||
OtStatusModel? otStatus = null)
|
||||
OtStatusModel? otStatus = null,
|
||||
bool hideSalaryDetails = false)
|
||||
{
|
||||
bool isAdminUser = IsAdmin(user.Id);
|
||||
bool showStationColumn = user.Department?.DepartmentId == 3 || user.Department?.DepartmentId == 2 || isAdminUser;
|
||||
bool hideSalary = hideSalaryDetails || isHoU || isHoD || isManager;
|
||||
|
||||
var stream = new MemoryStream();
|
||||
|
||||
@ -100,7 +102,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isHoU || isHoD || isManager) // If HoU, HoD, or Manager, hide salary details
|
||||
if (hideSalary) // If HoU, HoD, or Manager, hide salary details
|
||||
{
|
||||
if (showStationColumn)
|
||||
{
|
||||
@ -263,7 +265,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
decimal otAmtValParsed = 0;
|
||||
string otAmt = "";
|
||||
|
||||
if (!isHoU && !isHoD && !isManager) // Only calculate and show OT amount if not HoU, HoD, or Manager
|
||||
if (!hideSalary) // Only calculate and show OT amount if not HoU, HoD, or Manager
|
||||
{
|
||||
otAmt = CalculateOtAmount(record, hrp, publicHolidayDates, userSetting?.State?.WeekendId);
|
||||
otAmtValParsed = decimal.TryParse(otAmt, out decimal val) ? val : 0;
|
||||
@ -291,7 +293,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
grandTotalPh += currentRowTotalPh;
|
||||
grandTotalOtAmount += otAmtValParsed; // Only add if it was calculated
|
||||
|
||||
if (!isHoU && !isHoD && !isManager) // Only show if not HoU, HoD, or Manager
|
||||
if (!hideSalary) // Only show if not HoU, HoD, or Manager
|
||||
{
|
||||
// Basic Salary
|
||||
worksheet.Cell(currentRow, col).Value = !hasPrintedSalaryDetails ? basicSalary.ToString("N2") : "";
|
||||
@ -348,7 +350,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
worksheet.Cell(currentRow, col++).Value = currentRowTotalRd > 0 ? currentRowTotalRd.ToString("N2") : "";
|
||||
worksheet.Cell(currentRow, col++).Value = currentRowTotalPh > 0 ? currentRowTotalPh.ToString("N2") : "";
|
||||
|
||||
if (!isHoU && !isHoD && !isManager) // Only show if not HoU, HoD, or Manager
|
||||
if (!hideSalary) // Only show if not HoU, HoD, or Manager
|
||||
{
|
||||
worksheet.Cell(currentRow, col++).Value = otAmt == "0.00" ? "" : otAmt;
|
||||
}
|
||||
@ -415,7 +417,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
int totalLabelStartColumnIndex = 1;
|
||||
int totalLabelEndColumnIndex;
|
||||
|
||||
if (!isHoU && !isHoD && !isManager) // If not HoU, HoD, or Manager, include salary columns in merge
|
||||
if (!hideSalary) // If not HoU, HoD, or Manager, include salary columns in merge
|
||||
{
|
||||
totalLabelEndColumnIndex = 3;
|
||||
}
|
||||
@ -445,7 +447,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
int colTotalPhIndex = 0;
|
||||
int colOtAmtIndex = 0;
|
||||
|
||||
if (!isHoU && !isHoD && !isManager)
|
||||
if (!hideSalary)
|
||||
{
|
||||
colOfficeBreakIndex = 8;
|
||||
colAfterBreakIndex = 11;
|
||||
@ -500,7 +502,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
worksheet.Cell(currentRow, colTotalRdIndex).Value = grandTotalRd.ToString("N2");
|
||||
worksheet.Cell(currentRow, colTotalPhIndex).Value = grandTotalPh.ToString("N2");
|
||||
|
||||
if (!isHoU && !isHoD && !isManager)
|
||||
if (!hideSalary)
|
||||
{
|
||||
worksheet.Cell(currentRow, colOtAmtIndex).Value = Math.Round(grandTotalOtAmount, MidpointRounding.AwayFromZero).ToString("F2");
|
||||
}
|
||||
@ -697,7 +699,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isHoU && !isHoD && !isManager) // If not HoU, HoD, or Manager, include salary columns in width adjustment
|
||||
if (!hideSalary) // If not HoU, HoD, or Manager, include salary columns in width adjustment
|
||||
{
|
||||
worksheet.Column(1).Width = 15; // Basic Salary
|
||||
worksheet.Column(2).Width = 10; // ORP
|
||||
|
||||
@ -68,8 +68,10 @@
|
||||
|
||||
</style>
|
||||
|
||||
<div id="reviewApp" style="max-width: 1300px; margin: auto; font-size: 13px;">
|
||||
<div class="table-container table-responsive">
|
||||
<div id="reviewApp" style="width: 100%; padding: 0 20px; font-size: 13px;">
|
||||
|
||||
<div class="table-container">
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<strong>Employee Name:</strong> {{ userInfo.fullName }}<br />
|
||||
<strong>Department:</strong> {{ userInfo.departmentName || 'N/A' }}<br />
|
||||
@ -83,158 +85,165 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<table class="table table-bordered table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="header-orange" rowspan="2" v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">Basic Salary<br>(RM)</th>
|
||||
<th class="header-orange" rowspan="2" v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">ORP</th>
|
||||
<th class="header-orange" rowspan="2" v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">HRP</th>
|
||||
<th class="header-green text-nowrap" rowspan="2">Date</th>
|
||||
<th class="header-blue" colspan="3">Office Hour</th>
|
||||
<th class="header-blue" colspan="3">After Office Hour</th>
|
||||
<th class="header-green" rowspan="2">OT Hrs<br>(Office Hour)</th>
|
||||
<th class="header-green" rowspan="2">OT Hrs<br>(After Office Hour)</th>
|
||||
<th class="header-blue" colspan="1">Normal Day</th>
|
||||
<th class="header-blue" colspan="3">Off Day</th>
|
||||
<th class="header-blue" colspan="3">Rest Day</th>
|
||||
<th class="header-blue" colspan="2">Public Holiday</th>
|
||||
<th class="header-green" rowspan="2">Total OT Hrs</th>
|
||||
<th class="header-green" rowspan="2">Total OT Hrs<br>ND & OD</th>
|
||||
<th class="header-green" rowspan="2">Total OT Hrs<br>RD</th>
|
||||
<th class="header-green" rowspan="2">Total OT Hrs<br>PH</th>
|
||||
<th class="header-green" rowspan="2" v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">OT Amt (RM)</th>
|
||||
<th class="header-orange" rowspan="2" v-if="showStationColumn">Station</th>
|
||||
<th class="header-blue" rowspan="2">Description</th>
|
||||
<th class="header-green" rowspan="2">Action</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="header-blue">From</th>
|
||||
<th class="header-blue">To</th>
|
||||
<th class="header-blue">Break</th>
|
||||
<th class="header-blue">From</th>
|
||||
<th class="header-blue">To</th>
|
||||
<th class="header-blue">Break</th>
|
||||
<th class="header-blue">ND OT after office hrs</th>
|
||||
<th class="header-blue">OD Within office hrs < 4hrs</th>
|
||||
<th class="header-blue">OD Within office hrs > 4hrs < 8 hrs</th>
|
||||
<th class="header-blue">OD After office hrs</th>
|
||||
<th class="header-blue">RD Within office hrs < 4hrs</th>
|
||||
<th class="header-blue">RD Within office hrs > 4hrs < 8 hrs</th>
|
||||
<th class="header-blue">RD After office hrs</th>
|
||||
<th class="header-blue">PH Within office hrs < 8 hrs</th>
|
||||
<th class="header-blue">PH After office hrs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(record, index) in sortedOtRecords" :key="record.overtimeId">
|
||||
<template v-if="index === 0 && !isApproverRole(['HoU', 'HoD', 'Manager'])">
|
||||
<td :rowspan="sortedOtRecords.length" class="text-nowrap">{{ calculateBasicSalary() }}</td>
|
||||
<td :rowspan="sortedOtRecords.length" class="text-nowrap">{{ calculateOrp() }}</td>
|
||||
<td :rowspan="sortedOtRecords.length" class="text-nowrap">{{ calculateHrp() }}</td>
|
||||
</template>
|
||||
|
||||
<td class="text-nowrap">{{ formatDate(record.otDate) }}</td>
|
||||
<td>{{ formatTime(record.officeFrom) }}</td>
|
||||
<td>{{ formatTime(record.officeTo) }}</td>
|
||||
<td>{{ formatBreakToHourMinute(record.officeBreak, false) }}</td>
|
||||
<td>{{ formatTime(record.afterFrom) }}</td>
|
||||
<td>{{ formatTime(record.afterTo) }}</td>
|
||||
<td>{{ formatBreakToHourMinute(record.afterBreak, false) }}</td>
|
||||
<td>{{ formatTimeFromDecimal(calculateRawDuration(record.officeFrom, record.officeTo, record.officeBreak)) }}</td>
|
||||
<td>{{ formatTimeFromDecimal(calculateRawDuration(record.afterFrom, record.afterTo, record.afterBreak)) }}</td>
|
||||
<div class="table-responsive custom-scrollbar">
|
||||
<table class="table table-bordered table-sm table-striped" style="min-width: 1500px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="header-orange" rowspan="2" v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">Basic Salary<br>(RM)</th>
|
||||
<th class="header-orange" rowspan="2" v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">ORP</th>
|
||||
<th class="header-orange" rowspan="2" v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">HRP</th>
|
||||
<th class="header-green text-nowrap" rowspan="2">Date</th>
|
||||
<th class="header-blue" colspan="3">Office Hour</th>
|
||||
<th class="header-blue" colspan="3">After Office Hour</th>
|
||||
<th class="header-green" rowspan="2">OT Hrs<br>(Office Hour)</th>
|
||||
<th class="header-green" rowspan="2">OT Hrs<br>(After Office Hour)</th>
|
||||
<th class="header-blue" colspan="1">Normal Day</th>
|
||||
<th class="header-blue" colspan="3">Off Day</th>
|
||||
<th class="header-blue" colspan="3">Rest Day</th>
|
||||
<th class="header-blue" colspan="2">Public Holiday</th>
|
||||
<th class="header-green" rowspan="2">Total OT Hrs</th>
|
||||
<th class="header-green" rowspan="2">Total OT Hrs<br>ND & OD</th>
|
||||
<th class="header-green" rowspan="2">Total OT Hrs<br>RD</th>
|
||||
<th class="header-green" rowspan="2">Total OT Hrs<br>PH</th>
|
||||
<th class="header-green" rowspan="2" v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">OT Amt (RM)</th>
|
||||
<th class="header-orange" rowspan="2" v-if="showStationColumn">Station</th>
|
||||
<th class="header-blue" rowspan="2">Description</th>
|
||||
<th class="header-green" rowspan="2">Action</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="header-blue">From</th>
|
||||
<th class="header-blue">To</th>
|
||||
<th class="header-blue">Break</th>
|
||||
<th class="header-blue">From</th>
|
||||
<th class="header-blue">To</th>
|
||||
<th class="header-blue">Break</th>
|
||||
<th class="header-blue">ND OT after office hrs</th>
|
||||
<th class="header-blue">OD Within office hrs < 4hrs</th>
|
||||
<th class="header-blue">OD Within office hrs > 4hrs < 8 hrs</th>
|
||||
<th class="header-blue">OD After office hrs</th>
|
||||
<th class="header-blue">RD Within office hrs < 4hrs</th>
|
||||
<th class="header-blue">RD Within office hrs > 4hrs < 8 hrs</th>
|
||||
<th class="header-blue">RD After office hrs</th>
|
||||
<th class="header-blue">PH Within office hrs < 8 hrs</th>
|
||||
<th class="header-blue">PH After office hrs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(record, index) in sortedOtRecords" :key="record.overtimeId">
|
||||
<template v-if="index === 0 && !isApproverRole(['HoU', 'HoD', 'Manager'])">
|
||||
<td :rowspan="sortedOtRecords.length" class="text-nowrap">{{ calculateBasicSalary() }}</td>
|
||||
<td :rowspan="sortedOtRecords.length" class="text-nowrap">{{ calculateOrp() }}</td>
|
||||
<td :rowspan="sortedOtRecords.length" class="text-nowrap">{{ calculateHrp() }}</td>
|
||||
</template>
|
||||
|
||||
<td>{{ classifyOt(record).ndAfter }}</td>
|
||||
<td class="text-nowrap">{{ formatDate(record.otDate) }}</td>
|
||||
<td>{{ formatTime(record.officeFrom) }}</td>
|
||||
<td>{{ formatTime(record.officeTo) }}</td>
|
||||
<td>{{ formatBreakToHourMinute(record.officeBreak, false) }}</td>
|
||||
<td>{{ formatTime(record.afterFrom) }}</td>
|
||||
<td>{{ formatTime(record.afterTo) }}</td>
|
||||
<td>{{ formatBreakToHourMinute(record.afterBreak, false) }}</td>
|
||||
<td>{{ formatTimeFromDecimal(calculateRawDuration(record.officeFrom, record.officeTo, record.officeBreak)) }}</td>
|
||||
<td>{{ formatTimeFromDecimal(calculateRawDuration(record.afterFrom, record.afterTo, record.afterBreak)) }}</td>
|
||||
|
||||
<td>{{ classifyOt(record).odUnder4 }}</td>
|
||||
<td>{{ classifyOt(record).odBetween4And8 }}</td>
|
||||
<td>{{ classifyOt(record).odAfter }}</td>
|
||||
<td>{{ classifyOt(record).ndAfter }}</td>
|
||||
|
||||
<td>{{ classifyOt(record).rdUnder4 }}</td>
|
||||
<td>{{ classifyOt(record).rdBetween4And8 }}</td>
|
||||
<td>{{ classifyOt(record).rdAfter }}</td>
|
||||
<td>{{ classifyOt(record).odUnder4 }}</td>
|
||||
<td>{{ classifyOt(record).odBetween4And8 }}</td>
|
||||
<td>{{ classifyOt(record).odAfter }}</td>
|
||||
|
||||
<td>{{ classifyOt(record).phUnder8 }}</td>
|
||||
<td>{{ classifyOt(record).phAfter }}</td>
|
||||
<td>{{ classifyOt(record).rdUnder4 }}</td>
|
||||
<td>{{ classifyOt(record).rdBetween4And8 }}</td>
|
||||
<td>{{ classifyOt(record).rdAfter }}</td>
|
||||
|
||||
<td>{{ calculateTotalOtHrs(record) }}</td>
|
||||
<td>{{ classifyOt(record).phUnder8 }}</td>
|
||||
<td>{{ classifyOt(record).phAfter }}</td>
|
||||
|
||||
<td>{{ calculateNdOdTotal(record) }}</td>
|
||||
<td>{{ calculateRdTotal(record) }}</td>
|
||||
<td>{{ calculatePhTotal(record) }}</td>
|
||||
<td>{{ calculateTotalOtHrs(record) }}</td>
|
||||
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">{{ calculateOtAmount(record) }}</td>
|
||||
<td>{{ calculateNdOdTotal(record) }}</td>
|
||||
<td>{{ calculateRdTotal(record) }}</td>
|
||||
<td>{{ calculatePhTotal(record) }}</td>
|
||||
|
||||
<td v-if="showStationColumn">{{ record.stationName || 'N/A' }}</td>
|
||||
<td class="wrap-text">
|
||||
<div class="description-preview" v-on:click="toggleDescription(index)" :class="{ expanded: expandedDescriptions[index] }">
|
||||
{{ record.otDescription }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-light border rounded-circle me-1"
|
||||
v-on:click="editRecord(record.overtimeId)"
|
||||
:disabled="hasApproverActedLocal">
|
||||
<i class="bi bi-pencil-fill text-warning fs-5"></i>
|
||||
</button>
|
||||
<button class="btn btn-light border rounded-circle"
|
||||
v-on:click="deleteRecord(record.overtimeId)"
|
||||
:disabled="hasApproverActedLocal">
|
||||
<i class="bi bi-trash-fill text-danger fs-5"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="otRecords.length === 0">
|
||||
<td :colspan="showStationColumn ? (isApproverRole(['HoU', 'HoD', 'Manager']) ? 27 : 31) : (isApproverRole(['HoU', 'HoD', 'Manager']) ? 26 : 30)">No overtime details found for this submission.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="fw-bold bg-light">
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])" colspan="3">TOTAL</td>
|
||||
<td v-else colspan="1">TOTAL</td>
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">{{ calculateOtAmount(record) }}</td>
|
||||
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])" colspan="2"></td>
|
||||
<td v-else colspan="2"></td>
|
||||
<td><strong>{{ formatBreakToHourMinute(totals.officeBreak) }}</strong></td>
|
||||
<td v-if="showStationColumn">{{ record.stationName || 'N/A' }}</td>
|
||||
<td class="wrap-text">
|
||||
<div class="description-preview" v-on:click="toggleDescription(index)" :class="{ expanded: expandedDescriptions[index] }">
|
||||
{{ record.otDescription }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-light border rounded-circle me-1"
|
||||
v-on:click="editRecord(record.overtimeId)"
|
||||
:disabled="hasApproverActedLocal">
|
||||
<i class="bi bi-pencil-fill text-warning fs-5"></i>
|
||||
</button>
|
||||
<button class="btn btn-light border rounded-circle"
|
||||
v-on:click="deleteRecord(record.overtimeId)"
|
||||
:disabled="hasApproverActedLocal">
|
||||
<i class="bi bi-trash-fill text-danger fs-5"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="otRecords.length === 0">
|
||||
<td :colspan="showStationColumn ? (isApproverRole(['HoU', 'HoD', 'Manager']) ? 27 : 31) : (isApproverRole(['HoU', 'HoD', 'Manager']) ? 26 : 30)">No overtime details found for this submission.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="fw-bold bg-light">
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])" colspan="3">TOTAL</td>
|
||||
<td v-else colspan="1">TOTAL</td>
|
||||
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])" colspan="2"></td>
|
||||
<td v-else colspan="2"></td>
|
||||
<td><strong>{{ formatBreakToHourMinute(totals.afterBreak) }}</strong></td>
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])" colspan="2"></td>
|
||||
<td v-else colspan="2"></td>
|
||||
<td><strong>{{ formatBreakToHourMinute(totals.officeBreak) }}</strong></td>
|
||||
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])" colspan="2"></td>
|
||||
<td v-else colspan="2"></td>
|
||||
<td>{{ totals.ndAfter }}</td>
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])" colspan="2"></td>
|
||||
<td v-else colspan="2"></td>
|
||||
<td><strong>{{ formatBreakToHourMinute(totals.afterBreak) }}</strong></td>
|
||||
|
||||
<td>{{ totals.odUnder4 }}</td>
|
||||
<td>{{ totals.odBetween4And8 }}</td>
|
||||
<td>{{ totals.odAfter }}</td>
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])" colspan="2"></td>
|
||||
<td v-else colspan="2"></td>
|
||||
<td>{{ totals.ndAfter }}</td>
|
||||
|
||||
<td>{{ totals.rdUnder4 }}</td>
|
||||
<td>{{ totals.rdBetween4And8 }}</td>
|
||||
<td>{{ totals.rdAfter }}</td>
|
||||
<td>{{ totals.odUnder4 }}</td>
|
||||
<td>{{ totals.odBetween4And8 }}</td>
|
||||
<td>{{ totals.odAfter }}</td>
|
||||
|
||||
<td>{{ totals.phUnder8 }}</td>
|
||||
<td>{{ totals.phAfter }}</td>
|
||||
<td>{{ totals.rdUnder4 }}</td>
|
||||
<td>{{ totals.rdBetween4And8 }}</td>
|
||||
<td>{{ totals.rdAfter }}</td>
|
||||
|
||||
<td>{{ totals.totalOtHrs }}</td>
|
||||
<td>{{ totals.totalNdOd }}</td>
|
||||
<td>{{ totals.totalRd }}</td>
|
||||
<td>{{ totals.totalPh }}</td>
|
||||
<td>{{ totals.phUnder8 }}</td>
|
||||
<td>{{ totals.phAfter }}</td>
|
||||
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">{{ totals.otAmt }}</td>
|
||||
<td>{{ totals.totalOtHrs }}</td>
|
||||
<td>{{ totals.totalNdOd }}</td>
|
||||
<td>{{ totals.totalRd }}</td>
|
||||
<td>{{ totals.totalPh }}</td>
|
||||
|
||||
<td v-if="showStationColumn"></td>
|
||||
<td v-if="!isApproverRole(['HoU', 'HoD', 'Manager'])">{{ totals.otAmt }}</td>
|
||||
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<td v-if="showStationColumn"></td>
|
||||
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-primary btn-sm" v-on:click="printPdf"><i class="bi bi-printer"></i> Print</button>
|
||||
<button class="btn btn-dark btn-sm" v-on:click="saveAsPdf"><i class="bi bi-file-pdf"></i> Save</button>
|
||||
<button class="btn btn-success btn-sm" v-on:click="exportToExcel"><i class="bi bi-file-earmark-excel"></i> Excel</button>
|
||||
<button class="btn btn-primary btn-sm" v-on:click="printPdf(false)"><i class="bi bi-printer"></i> Print</button>
|
||||
<button class="btn btn-dark btn-sm" v-on:click="saveAsPdf(false)"><i class="bi bi-file-pdf"></i> Save</button>
|
||||
<button class="btn btn-success btn-sm" v-on:click="exportToExcel(false)"><i class="bi bi-file-earmark-excel"></i> Excel</button>
|
||||
</div>
|
||||
<div class="mt-3 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-primary btn-sm" v-on:click="printPdf(true)"><i class="bi bi-printer"></i> Print No Salary</button>
|
||||
<button class="btn btn-dark btn-sm" v-on:click="saveAsPdf(true)"><i class="bi bi-file-pdf"></i> Save No Salary</button>
|
||||
<button class="btn btn-success btn-sm" v-on:click="exportToExcel(true)"><i class="bi bi-file-earmark-excel"></i> Excel No Salary</button>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="editOtModal" tabindex="-1" aria-labelledby="editOtModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
@ -1212,11 +1221,11 @@
|
||||
alert("Error deleting record: " + err.message);
|
||||
});
|
||||
},
|
||||
saveAsPdf() {
|
||||
saveAsPdf(hideSalary = false) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const statusId = params.get("statusId");
|
||||
|
||||
fetch(`/OvertimeAPI/GetOvertimePdfByStatusId/${statusId}`)
|
||||
fetch(`/OvertimeAPI/GetOvertimePdfByStatusId/${statusId}?hideSalary=${hideSalary}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error("Failed to generate PDF");
|
||||
const disposition = response.headers.get("Content-Disposition");
|
||||
@ -1245,11 +1254,11 @@
|
||||
alert("Failed to generate PDF. Please try again later.");
|
||||
});
|
||||
},
|
||||
printPdf() {
|
||||
printPdf(hideSalary = false) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const statusId = params.get("statusId");
|
||||
|
||||
fetch(`/OvertimeAPI/GetOvertimePdfByStatusId/${statusId}`)
|
||||
fetch(`/OvertimeAPI/GetOvertimePdfByStatusId/${statusId}?hideSalary=${hideSalary}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error("Failed to fetch PDF for printing.");
|
||||
return response.blob();
|
||||
@ -1273,11 +1282,11 @@
|
||||
alert("Failed to prepare PDF for printing. Please try again later.");
|
||||
});
|
||||
},
|
||||
exportToExcel() {
|
||||
exportToExcel(hideSalary = false) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const statusId = params.get("statusId");
|
||||
|
||||
fetch(`/OvertimeAPI/GetOvertimeExcelByStatusId/${statusId}`)
|
||||
fetch(`/OvertimeAPI/GetOvertimeExcelByStatusId/${statusId}?hideSalary=${hideSalary}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error("Failed to generate Excel");
|
||||
const disposition = response.headers.get("Content-Disposition");
|
||||
|
||||
@ -154,7 +154,7 @@
|
||||
},
|
||||
async fetchUser() {
|
||||
try {
|
||||
const response = await fetch(`/InvMainAPI/UserList/`, { method: 'POST' });
|
||||
const response = await fetch(`/OvertimeAPI/UserList/`, { method: 'POST' });
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.userList = data.filter(e => e.fullName !== "MAAdmin" && e.fullName !== "SysAdmin");
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" style="max-width: 1300px; margin: auto; font-size: 13px;">
|
||||
<div id="app" style="width: 100%; padding: 0 20px; font-size: 13px;">
|
||||
<div class="mb-3 d-flex flex-wrap">
|
||||
<div class="me-2 mb-2">
|
||||
<label>Month</label>
|
||||
@ -82,79 +82,83 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="print-section" class="table-container table-responsive">
|
||||
<table class="table table-bordered table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="header-green" rowspan="2">Date</th>
|
||||
<th class="header-blue" colspan="3">Office Hour</th>
|
||||
<th class="header-blue" colspan="3">After Office Hour</th>
|
||||
<th class="header-orange" rowspan="2">Total OT Hours</th>
|
||||
<th class="header-orange" rowspan="2">Break (min)</th>
|
||||
<th class="header-orange" rowspan="2">Net OT Hours</th>
|
||||
<th class="header-orange" rowspan="2" v-if="isPSTWAIR">Station</th>
|
||||
<th class="header-green" rowspan="2">Days</th>
|
||||
<th class="header-blue" rowspan="2">Description</th>
|
||||
<th class="header-green" rowspan="2">Action</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="header-blue">From</th>
|
||||
<th class="header-blue">To</th>
|
||||
<th class="header-blue">Break</th>
|
||||
<th class="header-blue">From</th>
|
||||
<th class="header-blue">To</th>
|
||||
<th class="header-blue">Break</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(record, index) in filteredRecords" :key="record.overtimeId">
|
||||
<td>{{ formatDate(record.otDate) }}</td>
|
||||
<td>{{ formatTime(record.officeFrom) }}</td>
|
||||
<td>{{ formatTime(record.officeTo) }}</td>
|
||||
<td>{{ formatMinutesToHourMinute(record.officeBreak) }}</td>
|
||||
<td>{{ formatTime(record.afterFrom) }}</td>
|
||||
<td>{{ formatTime(record.afterTo) }}</td>
|
||||
<td>{{ formatMinutesToHourMinute(record.afterBreak) }}</td>
|
||||
<td>{{ formatHourMinute(calcTotalTime(record)) }}</td>
|
||||
<td>{{ formatMinutesToHourMinute(calcBreakTotal(record)) }}</td>
|
||||
<td>{{ formatHourMinute(calcNetHours(record)) }}</td>
|
||||
<td v-if="isPSTWAIR">{{ record.stationName || 'N/A' }}</td>
|
||||
<td>{{ record.otDays }}</td>
|
||||
<td class="wrap-text">
|
||||
<div class="description-preview" v-on:click="toggleDescription(index)" :class="{ expanded: expandedDescriptions[index] }">
|
||||
{{ record.otDescription }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-light border rounded-circle me-1"
|
||||
title="Edit"
|
||||
:disabled="hasSubmitted"
|
||||
v-on:click="editRecord(index)">
|
||||
<i class="bi bi-pencil-fill text-warning fs-5"></i>
|
||||
</button>
|
||||
<button class="btn btn-light border rounded-circle"
|
||||
title="Delete"
|
||||
:disabled="hasSubmitted"
|
||||
v-on:click="deleteRecord(index)">
|
||||
<i class="bi bi-trash-fill text-danger fs-5"></i>
|
||||
</button>
|
||||
</td>
|
||||
<div id="print-section" class="table-container">
|
||||
|
||||
</tr>
|
||||
<tr v-if="filteredRecords.length === 0">
|
||||
<td :colspan="isPSTWAIR ? 14 : 13">No records found.</td>
|
||||
</tr>
|
||||
<tr class="table-primary fw-bold">
|
||||
<td>TOTAL</td>
|
||||
<td colspan="6"></td>
|
||||
<td>{{ formatHourMinute(totalHours) }}</td>
|
||||
<td>{{ formatHourMinute(totalBreak) }}</td>
|
||||
<td>{{ formatHourMinute(totalNetTime) }}</td>
|
||||
<td v-if="isPSTWAIR"></td>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="table-responsive custom-scrollbar">
|
||||
|
||||
<table class="table table-bordered table-sm table-striped" style="min-width: 1200px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="header-green" rowspan="2">Date</th>
|
||||
<th class="header-blue" colspan="3">Office Hour</th>
|
||||
<th class="header-blue" colspan="3">After Office Hour</th>
|
||||
<th class="header-orange" rowspan="2">Total OT Hours</th>
|
||||
<th class="header-orange" rowspan="2">Break (min)</th>
|
||||
<th class="header-orange" rowspan="2">Net OT Hours</th>
|
||||
<th class="header-orange" rowspan="2" v-if="isPSTWAIR">Station</th>
|
||||
<th class="header-green" rowspan="2">Days</th>
|
||||
<th class="header-blue" rowspan="2">Description</th>
|
||||
<th class="header-green" rowspan="2">Action</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="header-blue">From</th>
|
||||
<th class="header-blue">To</th>
|
||||
<th class="header-blue">Break</th>
|
||||
<th class="header-blue">From</th>
|
||||
<th class="header-blue">To</th>
|
||||
<th class="header-blue">Break</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(record, index) in filteredRecords" :key="record.overtimeId">
|
||||
<td>{{ formatDate(record.otDate) }}</td>
|
||||
<td>{{ formatTime(record.officeFrom) }}</td>
|
||||
<td>{{ formatTime(record.officeTo) }}</td>
|
||||
<td>{{ formatMinutesToHourMinute(record.officeBreak) }}</td>
|
||||
<td>{{ formatTime(record.afterFrom) }}</td>
|
||||
<td>{{ formatTime(record.afterTo) }}</td>
|
||||
<td>{{ formatMinutesToHourMinute(record.afterBreak) }}</td>
|
||||
<td>{{ formatHourMinute(calcTotalTime(record)) }}</td>
|
||||
<td>{{ formatMinutesToHourMinute(calcBreakTotal(record)) }}</td>
|
||||
<td>{{ formatHourMinute(calcNetHours(record)) }}</td>
|
||||
<td v-if="isPSTWAIR">{{ record.stationName || 'N/A' }}</td>
|
||||
<td>{{ record.otDays }}</td>
|
||||
<td class="wrap-text">
|
||||
<div class="description-preview" v-on:click="toggleDescription(index)" :class="{ expanded: expandedDescriptions[index] }">
|
||||
{{ record.otDescription }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-light border rounded-circle me-1"
|
||||
title="Edit"
|
||||
:disabled="hasSubmitted"
|
||||
v-on:click="editRecord(index)">
|
||||
<i class="bi bi-pencil-fill text-warning fs-5"></i>
|
||||
</button>
|
||||
<button class="btn btn-light border rounded-circle"
|
||||
title="Delete"
|
||||
:disabled="hasSubmitted"
|
||||
v-on:click="deleteRecord(index)">
|
||||
<i class="bi bi-trash-fill text-danger fs-5"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr v-if="filteredRecords.length === 0">
|
||||
<td :colspan="isPSTWAIR ? 14 : 13">No records found.</td>
|
||||
</tr>
|
||||
<tr class="table-primary fw-bold">
|
||||
<td>TOTAL</td>
|
||||
<td colspan="6"></td>
|
||||
<td>{{ formatHourMinute(totalHours) }}</td>
|
||||
<td>{{ formatHourMinute(totalBreak) }}</td>
|
||||
<td>{{ formatHourMinute(totalNetTime) }}</td>
|
||||
<td v-if="isPSTWAIR"></td>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 d-flex flex-wrap gap-2">
|
||||
|
||||
@ -51,6 +51,22 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
_env = env;
|
||||
}
|
||||
|
||||
[HttpPost("UserList")]
|
||||
public async Task<IActionResult> UserList()
|
||||
{
|
||||
var userList = await _centralDbContext.Users
|
||||
.Include(i => i.Department)
|
||||
.ToListAsync();
|
||||
|
||||
return Json(userList.Select(i => new
|
||||
{
|
||||
i.Id,
|
||||
i.FullName,
|
||||
i.Department,
|
||||
i.Department?.DepartmentName,
|
||||
}));
|
||||
}
|
||||
|
||||
#region Settings
|
||||
|
||||
[HttpGet("GetUpdateDates")]
|
||||
@ -2478,7 +2494,7 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
|
||||
#region OT Review/ OT Record PDF Excel
|
||||
[HttpGet("GetOvertimePdfByStatusId/{statusId}")]
|
||||
public IActionResult GetOvertimePdfByStatusId(int statusId)
|
||||
public IActionResult GetOvertimePdfByStatusId(int statusId, [FromQuery] bool hideSalary = false)
|
||||
{
|
||||
var otStatus = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == statusId);
|
||||
if (otStatus == null)
|
||||
@ -2519,14 +2535,14 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
.FirstOrDefault(us => us.UserId == user.Id);
|
||||
|
||||
// Determine if the current logged-in user is HoU, HoD, or Manager for the OT user
|
||||
bool hideSalaryDetails = false;
|
||||
bool hideSalaryDetails = hideSalary; // Start with the button's request
|
||||
if (userSetting?.Approvalflow != null)
|
||||
{
|
||||
if (userSetting.Approvalflow.HoU == currentLoggedInUserId ||
|
||||
userSetting.Approvalflow.HoD == currentLoggedInUserId ||
|
||||
userSetting.Approvalflow.Manager == currentLoggedInUserId)
|
||||
{
|
||||
hideSalaryDetails = true;
|
||||
hideSalaryDetails = true; // Override to true if they are a manager
|
||||
}
|
||||
}
|
||||
|
||||
@ -2629,7 +2645,7 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
}
|
||||
|
||||
[HttpGet("GetOvertimeExcelByStatusId/{statusId}")]
|
||||
public IActionResult GetOvertimeExcelByStatusId(int statusId)
|
||||
public IActionResult GetOvertimeExcelByStatusId(int statusId, [FromQuery] bool hideSalary = false)
|
||||
{
|
||||
var otStatus = _centralDbContext.Otstatus.FirstOrDefault(s => s.StatusId == statusId);
|
||||
if (otStatus == null)
|
||||
@ -2700,7 +2716,8 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
flexiHour,
|
||||
logoImage,
|
||||
isSimplifiedExport: false,
|
||||
otStatus
|
||||
otStatus,
|
||||
hideSalaryDetails: hideSalary
|
||||
);
|
||||
|
||||
string fileName = $"OvertimeReport_{user.FullName}_{DateTime.Now:yyyyMMdd}.xlsx";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user