diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java index c8313ffa..ae26a2a1 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java @@ -1266,6 +1266,7 @@ public class ApplicationAmendmentRequestDao { log.info("Updating application amendment with status: {}", id); ApplicationAmendmentRequestEntity existingApplicationAmendment = validateApplicationAmendmentRequest(id); ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity = Utils.getClonedEntityForData(existingApplicationAmendment); + if (Boolean.TRUE.equals(existingApplicationAmendment.getStatus().equals(ApplicationAmendmentRequestEnum.AWAITING.getValue())) && Boolean.TRUE.equals(statusTypeEnum.equals(ApplicationAmendmentRequestEnum.RESPONSE_RECEIVED))) { if (existingApplicationAmendment.getStartDate() == null) { throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.AMENDMENT_MUST_BE_APPROVED_FIRST_MSG)); @@ -1274,11 +1275,10 @@ public class ApplicationAmendmentRequestDao { existingApplicationAmendment.setStatus(ApplicationAmendmentRequestEnum.RESPONSE_RECEIVED.getValue()); existingApplicationAmendment.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); applicationAmendmentRequestRepository.save(existingApplicationAmendment); - - /** This code is responsible for adding a version history log for the "Update Application Amendment" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationAmendmentEntity).newData(existingApplicationAmendment).build()); } - ApplicationAmendmentRequestResponse response = convertEntityToResponse(existingApplicationAmendment,false); + + ApplicationAmendmentRequestResponse response = convertEntityToResponse(existingApplicationAmendment, false); log.info("Amendment status updated successfully: {}", response); return response; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java index 7cfb73cc..60d867bc 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java @@ -427,7 +427,9 @@ public class EmailNotificationDao { if(Boolean.TRUE.equals(emailLogEntity.getEmailType().equals(EmailScenarioTypeEnum.APPLICATION_TECHNICAL_EVALUATION_REJECTED.getValue())) || Boolean.TRUE.equals(emailLogEntity.getEmailType().equals(EmailScenarioTypeEnum.APPLICATION_ADMISSIBLE.getValue())) || Boolean.TRUE.equals(emailLogEntity.getEmailType().equals(EmailScenarioTypeEnum.APPLICATION_REJECTED.getValue())) - || Boolean.TRUE.equals(emailLogEntity.getEmailType().equals(EmailScenarioTypeEnum.APPLICATION_AMENDMENT_REQUESTED.getValue()))) { + || Boolean.TRUE.equals(emailLogEntity.getEmailType().equals(EmailScenarioTypeEnum.APPLICATION_AMENDMENT_REQUESTED.getValue())) + || Boolean.TRUE.equals(emailLogEntity.getEmailType().equals(EmailScenarioTypeEnum.SPECIAL_APPLICATION_AMENDMENT_REQUESTED.getValue())) + || Boolean.TRUE.equals(emailLogEntity.getEmailType().equals(EmailScenarioTypeEnum.SPECIAL_APPLICATION_AMENDMENT_REQUESTED_BLUE_TONGUE.getValue()))) { isSendEmail = Boolean.FALSE; } } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/PecMailDao.java b/src/main/java/net/gepafin/tendermanagement/dao/PecMailDao.java index ea1aef50..d9267ea2 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/PecMailDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/PecMailDao.java @@ -6,6 +6,9 @@ import lombok.extern.log4j.Log4j2; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.*; +import net.gepafin.tendermanagement.enums.ApplicationEvaluationStatusTypeEnum; +import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; +import net.gepafin.tendermanagement.enums.AssignedApplicationEnum; import net.gepafin.tendermanagement.enums.EmailScenarioTypeEnum; import net.gepafin.tendermanagement.enums.EmailServiceTypeEnum; import net.gepafin.tendermanagement.enums.StatusTypeEnum; @@ -13,6 +16,14 @@ import net.gepafin.tendermanagement.model.response.EmailLogResponse; import net.gepafin.tendermanagement.model.response.PecEmailLogResponse; import net.gepafin.tendermanagement.model.response.PecMailResponse; import net.gepafin.tendermanagement.repositories.*; +import net.gepafin.tendermanagement.enums.ApplicationAmendmentRequestEnum; +import net.gepafin.tendermanagement.repositories.ApplicationAmendmentRequestRepository; +import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; +import net.gepafin.tendermanagement.repositories.ApplicationRepository; +import net.gepafin.tendermanagement.repositories.AssignedApplicationsRepository; +import net.gepafin.tendermanagement.repositories.EmailLogRepository; +import net.gepafin.tendermanagement.repositories.UserActionsRepository; +import net.gepafin.tendermanagement.repositories.VersionHistoryRepository; import net.gepafin.tendermanagement.service.ApplicationService; import net.gepafin.tendermanagement.service.CallService; import net.gepafin.tendermanagement.util.DateTimeUtil; @@ -28,6 +39,8 @@ import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Optional; @Component @Log4j2 @@ -112,11 +125,139 @@ public class PecMailDao { } emailLogRepository.saveAll(emailLogs); EmailLogEntity firstLog = emailLogs.get(0); + + rollbackDomainChangesForRejectedEmail(firstLog, userActionId); + ApplicationEntity applicationEntity = applicationService.validateApplication(firstLog.getApplicationId()); String callName = applicationEntity.getCall().getName(); return createPecMailResponse(firstLog.getUserAction().getId(), firstLog, callName); } + /** + * Rolls back domain changes using version history (audit) for the user action that triggered the email. + * Restores application, application_evaluation, and assigned_applications from oldData in version history. + */ + private void rollbackDomainChangesForRejectedEmail(EmailLogEntity firstLog, Long userActionId) { + if (firstLog == null || firstLog.getApplicationId() == null || firstLog.getEmailType() == null || userActionId == null) { + return; + } + + String scenario = firstLog.getEmailType(); + Long applicationId = firstLog.getApplicationId(); + + // Amendment email rejected (normal or special): revert application, then mark amendment REJECTED and soft-deleted + boolean isNormalAmendment = EmailScenarioTypeEnum.APPLICATION_AMENDMENT_REQUESTED.getValue().equals(scenario); + boolean isSpecialAmendment = EmailScenarioTypeEnum.SPECIAL_APPLICATION_AMENDMENT_REQUESTED.getValue().equals(scenario) + || EmailScenarioTypeEnum.SPECIAL_APPLICATION_AMENDMENT_REQUESTED_BLUE_TONGUE.getValue().equals(scenario); + + if (isNormalAmendment || isSpecialAmendment) { + ApplicationEntity applicationEntity = applicationService.validateApplication(applicationId); + if (isNormalAmendment) { + List appHistory = versionHistoryRepository.findVersionHistoryByUserActionIdAndTableName(userActionId, TABLE_APPLICATION); + Optional applicationVersion = appHistory.stream() + .filter(v -> applicationId.equals(v.getRecordId()) && v.getOldData() != null && !v.getOldData().isEmpty()) + .findFirst(); + if (applicationVersion.isPresent()) { + Map oldDataMap = Utils.convertJsonStringToMap(applicationVersion.get().getOldData()); + if (oldDataMap != null) { + String previousStatus = Utils.extractString(oldDataMap, "status"); + if (previousStatus != null) { + applicationEntity.setStatus(previousStatus); + } + applicationRepository.save(applicationEntity); + log.info("Rolled back application id={} from version history after amendment email rejected (userActionId={})", applicationId, userActionId); + } + } else { + log.warn("No APPLICATION version history with oldData found for userActionId={}, applicationId={}; skipping application rollback", userActionId, applicationId); + } + } else { + applicationEntity.setStatus(ApplicationStatusTypeEnum.ADMISSIBLE.getValue()); + applicationEntity.setDateRejected(null); + applicationRepository.save(applicationEntity); + log.info("Set application id={} to ADMISSIBLE after special amendment email rejected (userActionId={})", applicationId, userActionId); + } + + markAmendmentRejectedAndDeleted(firstLog.getAmendmentId(), userActionId); + return; + } + + // Only rollback for the three evaluation-outcome email scenarios + if (!EmailScenarioTypeEnum.APPLICATION_TECHNICAL_EVALUATION_REJECTED.getValue().equals(scenario) + && !EmailScenarioTypeEnum.APPLICATION_REJECTED.getValue().equals(scenario) + && !EmailScenarioTypeEnum.APPLICATION_ADMISSIBLE.getValue().equals(scenario)) { + return; + } + + List appHistory = versionHistoryRepository.findVersionHistoryByUserActionIdAndTableName(userActionId, TABLE_APPLICATION); + Optional applicationVersion = appHistory.stream() + .filter(v -> applicationId.equals(v.getRecordId()) && v.getOldData() != null && !v.getOldData().isEmpty()) + .findFirst(); + + ApplicationEntity applicationEntity = applicationService.validateApplication(applicationId); + if (applicationVersion.isPresent()) { + Map oldDataMap = Utils.convertJsonStringToMap(applicationVersion.get().getOldData()); + if (oldDataMap != null) { + String previousStatus = Utils.extractString(oldDataMap, "status"); + if (previousStatus != null) { + applicationEntity.setStatus(previousStatus); + } + Object dateRejectedObj = oldDataMap.get("dateRejected"); + applicationEntity.setDateRejected(parseLocalDateTimeFromAudit(dateRejectedObj)); + applicationRepository.save(applicationEntity); + log.info("Rolled back application id={} from version history (userActionId={})", applicationId, userActionId); + } + } else { + log.warn("No APPLICATION version history with oldData found for userActionId={}, applicationId={}; skipping application rollback", userActionId, applicationId); + } + + // Only set application_evaluation and assigned_applications to OPEN for REJECTED and TECHNICAL_EVALUATION_REJECTED (not for ADMISSIBLE) + boolean reopenEvaluationAndAssigned = EmailScenarioTypeEnum.APPLICATION_TECHNICAL_EVALUATION_REJECTED.getValue().equals(scenario) + || EmailScenarioTypeEnum.APPLICATION_REJECTED.getValue().equals(scenario); + if (!reopenEvaluationAndAssigned) { + return; + } + + applicationEvaluationRepository.findByApplicationIdAndIsDeletedFalse(applicationId).ifPresent(evaluation -> { + evaluation.setStatus(ApplicationEvaluationStatusTypeEnum.OPEN.getValue()); + evaluation.setClosingDate(null); + evaluation.setActiveDays(null); + applicationEvaluationRepository.save(evaluation); + log.info("Set application_evaluation id={} to OPEN (userActionId={})", evaluation.getId(), userActionId); + }); + + assignedApplicationsRepository.findByApplicationIdAndIsDeletedFalse(applicationId).ifPresent(assigned -> { + assigned.setStatus(AssignedApplicationEnum.OPEN.getValue()); + assignedApplicationsRepository.save(assigned); + log.info("Set assigned_applications id={} to OPEN (userActionId={})", assigned.getId(), userActionId); + }); + } + + private void markAmendmentRejectedAndDeleted(Long amendmentId, Long userActionId) { + if (amendmentId == null) { + return; + } + applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(amendmentId).ifPresent(amendment -> { + amendment.setStatus(ApplicationAmendmentRequestEnum.REJECTED.getValue()); + amendment.setIsDeleted(true); + applicationAmendmentRequestRepository.save(amendment); + log.info("Set amendment id={} to REJECTED and is_deleted=true after amendment email rejected (userActionId={})", amendmentId, userActionId); + }); + } + + private static LocalDateTime parseLocalDateTimeFromAudit(Object value) { + if (value == null) { + return null; + } + if (value instanceof String str && !str.isEmpty()) { + try { + return DateTimeUtil.parseStringToLocalDateTime(str); + } catch (Exception e) { + return null; + } + } + return null; + } + private List getEmailLogEntities(HttpServletRequest request, Long userActionId) { UserActionEntity userActionEntity = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId); if (userActionEntity == null) { diff --git a/src/main/java/net/gepafin/tendermanagement/enums/ApplicationAmendmentRequestEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/ApplicationAmendmentRequestEnum.java index 98881c36..daf5685a 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/ApplicationAmendmentRequestEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/ApplicationAmendmentRequestEnum.java @@ -9,7 +9,9 @@ public enum ApplicationAmendmentRequestEnum { AWAITING("AWAITING"), RESPONSE_RECEIVED("RESPONSE_RECEIVED"), CLOSE("CLOSE"), - EXPIRED("EXPIRED"); + EXPIRED("EXPIRED"), + /** Special amendment PEC email was rejected; amendment status set to REJECTED. */ + REJECTED("REJECTED"); private String value; diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/VersionHistoryRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/VersionHistoryRepository.java index 853bb945..782a2cc6 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/VersionHistoryRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/VersionHistoryRepository.java @@ -13,4 +13,6 @@ public interface VersionHistoryRepository extends JpaRepository findVersionHistoryByUserActionIdAndUserIdNull(Long id); List findVersionHistoryByUserActionId(Long id); + + List findVersionHistoryByUserActionIdAndTableName(Long userActionId, String tableName); }