diff --git a/Areas/OTcalculate/Views/Overtime/OtStatus.cshtml b/Areas/OTcalculate/Views/Overtime/OtStatus.cshtml index bf86063..aa7ab1a 100644 --- a/Areas/OTcalculate/Views/Overtime/OtStatus.cshtml +++ b/Areas/OTcalculate/Views/Overtime/OtStatus.cshtml @@ -341,7 +341,13 @@
{{ entry.approvalRole }} - {{ entry.changes.some(c => c.field === 'Record Deletion') ? 'deleted the record' : 'updated the record' }} + +
{{ formatDateTime(entry.date) }} @@ -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 }); }); diff --git a/Controllers/API/OvertimeAPI.cs b/Controllers/API/OvertimeAPI.cs index 91aab4c..b517034 100644 --- a/Controllers/API/OvertimeAPI.cs +++ b/Controllers/API/OvertimeAPI.cs @@ -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 BeforeEdit { get; set; } public Dictionary AfterEdit { get; set; } - public object DeletedRecord { get; set; } } @@ -2165,35 +2175,48 @@ namespace PSTW_CentralSystem.Controllers.API var beforeChanges = new Dictionary(); var afterChanges = new Dictionary(); + 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 };