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">
{{ entry.approvalRole }}
<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>
</h6>
<small class="text-muted"><i class="far fa-clock me-1"></i>{{ formatDateTime(entry.date) }}</small>
@ -702,7 +708,7 @@
getChanges(before, after) {
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;
// Loop through ONLY the keys provided by the clean JSON
@ -721,6 +727,10 @@
formattedBefore = formattedBefore !== "Empty" ? this.formatMinutesToHours(formattedBefore) : "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({
field: key,
@ -773,6 +783,8 @@
approvedBy: logEntry.ApproverRole || 'N/A',
approvalRole: logEntry.ApproverRole || approvalRoles[field],
date: logEntry.UpdateTimestamp || new Date().toISOString(),
recordDate: logEntry.RecordDate,
changeType: logEntry.ChangeType,
changes: changesDetail
});
});

View File

@ -930,16 +930,28 @@ namespace PSTW_CentralSystem.Controllers.API
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 targetYear = request.OtDate.Year;
var isMonthAlreadySubmitted = await _centralDbContext.Otstatus
.AnyAsync(s => s.UserId == request.UserId && s.Month == targetMonth && s.Year == targetYear);
// Check the LATEST submission for this month
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);
@ -2138,11 +2150,9 @@ namespace PSTW_CentralSystem.Controllers.API
public int ApproverUserId { get; set; }
public DateTime UpdateTimestamp { get; set; }
public string ChangeType { get; set; }
// Save the exact fields that changed
public string RecordDate { get; set; }
public Dictionary<string, string> BeforeEdit { get; set; }
public Dictionary<string, string> AfterEdit { get; set; }
public object DeletedRecord { get; set; }
}
@ -2165,35 +2175,48 @@ namespace PSTW_CentralSystem.Controllers.API
var beforeChanges = 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)
{
beforeChanges["OtDate"] = existingRecord.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";
afterChanges["OfficeFrom"] = newOfficeFrom?.ToString(@"hh\:mm") ?? "Empty";
beforeChanges["OfficeFrom"] = oldOfficeFrom;
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";
afterChanges["OfficeTo"] = newOfficeTo?.ToString(@"hh\:mm") ?? "Empty";
beforeChanges["OfficeTo"] = oldOfficeTo;
afterChanges["OfficeTo"] = newOfficeToStr;
}
if ((existingRecord.OfficeBreak ?? 0) != (updatedRecordDto.OfficeBreak ?? 0))
{
beforeChanges["OfficeBreak"] = (existingRecord.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";
afterChanges["AfterFrom"] = newAfterFrom?.ToString(@"hh\:mm") ?? "Empty";
beforeChanges["AfterFrom"] = oldAfterFrom;
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";
afterChanges["AfterTo"] = newAfterTo?.ToString(@"hh\:mm") ?? "Empty";
beforeChanges["AfterTo"] = oldAfterTo;
afterChanges["AfterTo"] = newAfterToStr;
}
if ((existingRecord.AfterBreak ?? 0) != (updatedRecordDto.AfterBreak ?? 0))
{
@ -2252,6 +2275,7 @@ namespace PSTW_CentralSystem.Controllers.API
ApproverUserId = currentLoggedInUserId,
UpdateTimestamp = DateTime.Now,
ChangeType = "Edit",
RecordDate = existingRecord.OtDate.ToString("yyyy-MM-dd"),
BeforeEdit = beforeChanges,
AfterEdit = afterChanges
};
@ -2394,6 +2418,7 @@ namespace PSTW_CentralSystem.Controllers.API
ApproverUserId = currentLoggedInUserId,
UpdateTimestamp = DateTime.Now,
ChangeType = "Delete",
RecordDate = recordToDelete.OtDate.ToString("yyyy-MM-dd"),
DeletedRecord = cleanDeletedRecord
};