This commit is contained in:
Naz 2026-05-15 15:20:43 +08:00
parent ffac32640a
commit 5d62d2e3b9
2 changed files with 59 additions and 22 deletions

View File

@ -341,7 +341,13 @@
<h6 class="fw-bold mb-0 text-dark"> <h6 class="fw-bold mb-0 text-dark">
{{ entry.approvalRole }} {{ entry.approvalRole }}
<span class="text-muted fw-normal fs-6"> <span class="text-muted fw-normal fs-6">
{{ entry.changes.some(c => c.field === 'Record Deletion') ? 'deleted the record' : 'updated the record' }} <template v-if="entry.changeType === 'Delete'">
deleted the record
</template>
<template v-else>
updated the record for
<strong class="text-primary">{{ formatDate(entry.recordDate) }}</strong>
</template>
</span> </span>
</h6> </h6>
<small class="text-muted"><i class="far fa-clock me-1"></i>{{ formatDateTime(entry.date) }}</small> <small class="text-muted"><i class="far fa-clock me-1"></i>{{ formatDateTime(entry.date) }}</small>
@ -702,7 +708,7 @@
getChanges(before, after) { getChanges(before, after) {
const changes = []; const changes = [];
// Safety check: if there is no before/after data, return empty // If there is no before/after data, return empty
if (!before || !after) return changes; if (!before || !after) return changes;
// Loop through ONLY the keys provided by the clean JSON // Loop through ONLY the keys provided by the clean JSON
@ -721,6 +727,10 @@
formattedBefore = formattedBefore !== "Empty" ? this.formatMinutesToHours(formattedBefore) : "0:00"; formattedBefore = formattedBefore !== "Empty" ? this.formatMinutesToHours(formattedBefore) : "0:00";
formattedAfter = formattedAfter !== "Empty" ? this.formatMinutesToHours(formattedAfter) : "0:00"; formattedAfter = formattedAfter !== "Empty" ? this.formatMinutesToHours(formattedAfter) : "0:00";
} }
else if (key === 'OtDate') {
formattedBefore = formattedBefore !== "Empty" ? this.formatDate(formattedBefore) : "Empty";
formattedAfter = formattedAfter !== "Empty" ? this.formatDate(formattedAfter) : "Empty";
}
changes.push({ changes.push({
field: key, field: key,
@ -773,6 +783,8 @@
approvedBy: logEntry.ApproverRole || 'N/A', approvedBy: logEntry.ApproverRole || 'N/A',
approvalRole: logEntry.ApproverRole || approvalRoles[field], approvalRole: logEntry.ApproverRole || approvalRoles[field],
date: logEntry.UpdateTimestamp || new Date().toISOString(), date: logEntry.UpdateTimestamp || new Date().toISOString(),
recordDate: logEntry.RecordDate,
changeType: logEntry.ChangeType,
changes: changesDetail changes: changesDetail
}); });
}); });

View File

@ -930,16 +930,28 @@ namespace PSTW_CentralSystem.Controllers.API
return Unauthorized("User not found."); return Unauthorized("User not found.");
} }
// Prevent adding records to a month that is already submitted // Prevent adding records to a month that is already submitted AND NOT rejected
var targetMonth = request.OtDate.Month; var targetMonth = request.OtDate.Month;
var targetYear = request.OtDate.Year; var targetYear = request.OtDate.Year;
var isMonthAlreadySubmitted = await _centralDbContext.Otstatus // Check the LATEST submission for this month
.AnyAsync(s => s.UserId == request.UserId && s.Month == targetMonth && s.Year == targetYear); var existingMonthStatus = await _centralDbContext.Otstatus
.Where(s => s.UserId == request.UserId && s.Month == targetMonth && s.Year == targetYear)
.OrderByDescending(s => s.StatusId)
.FirstOrDefaultAsync();
if (isMonthAlreadySubmitted) if (existingMonthStatus != null)
{ {
return BadRequest($"Overtime for {request.OtDate:MMMM yyyy} has already been submitted. You cannot add new records to a locked month."); bool isRejected = existingMonthStatus.HouStatus == "Rejected" ||
existingMonthStatus.HodStatus == "Rejected" ||
existingMonthStatus.ManagerStatus == "Rejected" ||
existingMonthStatus.HrStatus == "Rejected";
if (!isRejected)
{
return BadRequest($"Overtime for {request.OtDate:MMMM yyyy} is currently locked or approved. You can only add new records if the latest submission was rejected.");
}
request.StatusId = existingMonthStatus.StatusId;
} }
var userRoles = await _userManager.GetRolesAsync(user); var userRoles = await _userManager.GetRolesAsync(user);
@ -2138,11 +2150,9 @@ namespace PSTW_CentralSystem.Controllers.API
public int ApproverUserId { get; set; } public int ApproverUserId { get; set; }
public DateTime UpdateTimestamp { get; set; } public DateTime UpdateTimestamp { get; set; }
public string ChangeType { get; set; } public string ChangeType { get; set; }
public string RecordDate { get; set; }
// Save the exact fields that changed
public Dictionary<string, string> BeforeEdit { get; set; } public Dictionary<string, string> BeforeEdit { get; set; }
public Dictionary<string, string> AfterEdit { get; set; } public Dictionary<string, string> AfterEdit { get; set; }
public object DeletedRecord { get; set; } public object DeletedRecord { get; set; }
} }
@ -2165,35 +2175,48 @@ namespace PSTW_CentralSystem.Controllers.API
var beforeChanges = new Dictionary<string, string>(); var beforeChanges = new Dictionary<string, string>();
var afterChanges = new Dictionary<string, string>(); var afterChanges = new Dictionary<string, string>();
bool IsChanged(string oldVal, string newVal) => (oldVal ?? "") != (newVal ?? "");
if (existingRecord.OtDate.Date != updatedRecordDto.OtDate.Date) if (existingRecord.OtDate.Date != updatedRecordDto.OtDate.Date)
{ {
beforeChanges["OtDate"] = existingRecord.OtDate.ToString("yyyy-MM-dd"); beforeChanges["OtDate"] = existingRecord.OtDate.ToString("yyyy-MM-dd");
afterChanges["OtDate"] = updatedRecordDto.OtDate.ToString("yyyy-MM-dd"); afterChanges["OtDate"] = updatedRecordDto.OtDate.ToString("yyyy-MM-dd");
} }
if (existingRecord.OfficeFrom != newOfficeFrom)
string oldOfficeFrom = existingRecord.OfficeFrom?.ToString(@"hh\:mm") ?? "Empty";
string newOfficeFromStr = newOfficeFrom?.ToString(@"hh\:mm") ?? "Empty";
if (IsChanged(oldOfficeFrom, newOfficeFromStr))
{ {
beforeChanges["OfficeFrom"] = existingRecord.OfficeFrom?.ToString(@"hh\:mm") ?? "Empty"; beforeChanges["OfficeFrom"] = oldOfficeFrom;
afterChanges["OfficeFrom"] = newOfficeFrom?.ToString(@"hh\:mm") ?? "Empty"; afterChanges["OfficeFrom"] = newOfficeFromStr;
} }
if (existingRecord.OfficeTo != newOfficeTo)
string oldOfficeTo = existingRecord.OfficeTo?.ToString(@"hh\:mm") ?? "Empty";
string newOfficeToStr = newOfficeTo?.ToString(@"hh\:mm") ?? "Empty";
if (IsChanged(oldOfficeTo, newOfficeToStr))
{ {
beforeChanges["OfficeTo"] = existingRecord.OfficeTo?.ToString(@"hh\:mm") ?? "Empty"; beforeChanges["OfficeTo"] = oldOfficeTo;
afterChanges["OfficeTo"] = newOfficeTo?.ToString(@"hh\:mm") ?? "Empty"; afterChanges["OfficeTo"] = newOfficeToStr;
} }
if ((existingRecord.OfficeBreak ?? 0) != (updatedRecordDto.OfficeBreak ?? 0)) if ((existingRecord.OfficeBreak ?? 0) != (updatedRecordDto.OfficeBreak ?? 0))
{ {
beforeChanges["OfficeBreak"] = (existingRecord.OfficeBreak ?? 0).ToString(); beforeChanges["OfficeBreak"] = (existingRecord.OfficeBreak ?? 0).ToString();
afterChanges["OfficeBreak"] = (updatedRecordDto.OfficeBreak ?? 0).ToString(); afterChanges["OfficeBreak"] = (updatedRecordDto.OfficeBreak ?? 0).ToString();
} }
if (existingRecord.AfterFrom != newAfterFrom) string oldAfterFrom = existingRecord.AfterFrom?.ToString(@"hh\:mm") ?? "Empty";
string newAfterFromStr = newAfterFrom?.ToString(@"hh\:mm") ?? "Empty";
if (IsChanged(oldAfterFrom, newAfterFromStr))
{ {
beforeChanges["AfterFrom"] = existingRecord.AfterFrom?.ToString(@"hh\:mm") ?? "Empty"; beforeChanges["AfterFrom"] = oldAfterFrom;
afterChanges["AfterFrom"] = newAfterFrom?.ToString(@"hh\:mm") ?? "Empty"; afterChanges["AfterFrom"] = newAfterFromStr;
} }
if (existingRecord.AfterTo != newAfterTo)
string oldAfterTo = existingRecord.AfterTo?.ToString(@"hh\:mm") ?? "Empty";
string newAfterToStr = newAfterTo?.ToString(@"hh\:mm") ?? "Empty";
if (IsChanged(oldAfterTo, newAfterToStr))
{ {
beforeChanges["AfterTo"] = existingRecord.AfterTo?.ToString(@"hh\:mm") ?? "Empty"; beforeChanges["AfterTo"] = oldAfterTo;
afterChanges["AfterTo"] = newAfterTo?.ToString(@"hh\:mm") ?? "Empty"; afterChanges["AfterTo"] = newAfterToStr;
} }
if ((existingRecord.AfterBreak ?? 0) != (updatedRecordDto.AfterBreak ?? 0)) if ((existingRecord.AfterBreak ?? 0) != (updatedRecordDto.AfterBreak ?? 0))
{ {
@ -2252,6 +2275,7 @@ namespace PSTW_CentralSystem.Controllers.API
ApproverUserId = currentLoggedInUserId, ApproverUserId = currentLoggedInUserId,
UpdateTimestamp = DateTime.Now, UpdateTimestamp = DateTime.Now,
ChangeType = "Edit", ChangeType = "Edit",
RecordDate = existingRecord.OtDate.ToString("yyyy-MM-dd"),
BeforeEdit = beforeChanges, BeforeEdit = beforeChanges,
AfterEdit = afterChanges AfterEdit = afterChanges
}; };
@ -2394,6 +2418,7 @@ namespace PSTW_CentralSystem.Controllers.API
ApproverUserId = currentLoggedInUserId, ApproverUserId = currentLoggedInUserId,
UpdateTimestamp = DateTime.Now, UpdateTimestamp = DateTime.Now,
ChangeType = "Delete", ChangeType = "Delete",
RecordDate = recordToDelete.OtDate.ToString("yyyy-MM-dd"),
DeletedRecord = cleanDeletedRecord DeletedRecord = cleanDeletedRecord
}; };