Handled rejected email rollback cases
This commit is contained in:
@@ -786,6 +786,10 @@ public class ApplicationAmendmentRequestDao {
|
|||||||
log.warn("Permission denied: Beneficiary tried to update amendment ID {} with status RESPONSE_RECEIVED", id);
|
log.warn("Permission denied: Beneficiary tried to update amendment ID {} with status RESPONSE_RECEIVED", id);
|
||||||
throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
|
throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
|
||||||
}
|
}
|
||||||
|
if(Boolean.TRUE.equals(isBeneficiary) && existingApplicationAmendment.getStatus().equals(ApplicationAmendmentRequestEnum.DRAFT.getValue())){
|
||||||
|
log.warn("Permission denied: Beneficiary tried to update amendment ID {} with status DRAFT (only instructor can update)", id);
|
||||||
|
throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
|
||||||
|
}
|
||||||
if(Boolean.FALSE.equals(isBeneficiary) && existingApplicationAmendment.getStatus().equals(ApplicationAmendmentRequestEnum.AWAITING.getValue())){
|
if(Boolean.FALSE.equals(isBeneficiary) && existingApplicationAmendment.getStatus().equals(ApplicationAmendmentRequestEnum.AWAITING.getValue())){
|
||||||
log.warn("Permission denied: Non-beneficiary tried to update amendment ID {} with status AWAITING", id);
|
log.warn("Permission denied: Non-beneficiary tried to update amendment ID {} with status AWAITING", id);
|
||||||
throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
|
throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
|
||||||
@@ -1278,15 +1282,34 @@ public class ApplicationAmendmentRequestDao {
|
|||||||
log.info("Updating application amendment with status: {}", id);
|
log.info("Updating application amendment with status: {}", id);
|
||||||
ApplicationAmendmentRequestEntity existingApplicationAmendment = validateApplicationAmendmentRequest(id);
|
ApplicationAmendmentRequestEntity existingApplicationAmendment = validateApplicationAmendmentRequest(id);
|
||||||
ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity = Utils.getClonedEntityForData(existingApplicationAmendment);
|
ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity = Utils.getClonedEntityForData(existingApplicationAmendment);
|
||||||
if (Boolean.TRUE.equals(existingApplicationAmendment.getStatus().equals(ApplicationAmendmentRequestEnum.AWAITING.getValue())) && Boolean.TRUE.equals(statusTypeEnum.equals(ApplicationAmendmentRequestEnum.RESPONSE_RECEIVED))) {
|
|
||||||
|
if (ApplicationAmendmentRequestEnum.AWAITING.equals(statusTypeEnum)) {
|
||||||
|
// Only instructor can set status to AWAITING (e.g. after finishing edits post email rejection); beneficiary must not be allowed
|
||||||
|
if (Boolean.TRUE.equals(validator.checkIsBeneficiary()) || Boolean.TRUE.equals(validator.checkIsConfidi())) {
|
||||||
|
log.warn("Permission denied: Beneficiary/Confidi tried to set amendment ID {} status to AWAITING", id);
|
||||||
|
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
|
||||||
|
}
|
||||||
|
validator.validatePreInstructor(request, existingApplicationAmendment.getApplicationEvaluationEntity().getUserId());
|
||||||
|
// Allow transition to AWAITING only from DRAFT (e.g. after instructor finished edits post email rejection)
|
||||||
|
if (!ApplicationAmendmentRequestEnum.DRAFT.getValue().equals(existingApplicationAmendment.getStatus())) {
|
||||||
|
log.warn("Invalid status transition: amendment ID {} is not in DRAFT (current: {})", id, existingApplicationAmendment.getStatus());
|
||||||
|
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.APPLICATION_AMENDMENT_APPROPIATE_STATUS));
|
||||||
|
}
|
||||||
|
log.info("Updating amendment ID {} status from DRAFT to AWAITING", id);
|
||||||
|
existingApplicationAmendment.setStatus(ApplicationAmendmentRequestEnum.AWAITING.getValue());
|
||||||
|
existingApplicationAmendment.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now()));
|
||||||
|
applicationAmendmentRequestRepository.save(existingApplicationAmendment);
|
||||||
|
loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationAmendmentEntity).newData(existingApplicationAmendment).build());
|
||||||
|
// Send the same mail as when (normal) amendment was created; only normal amendments can be in DRAFT (special amendment emails are sent at creation, not held)
|
||||||
|
emailNotificationDao.sendMailToNotifyBeneficiaryRegardingNewAmendment(existingApplicationAmendment);
|
||||||
|
} else if (Boolean.TRUE.equals(existingApplicationAmendment.getStatus().equals(ApplicationAmendmentRequestEnum.AWAITING.getValue())) && Boolean.TRUE.equals(statusTypeEnum.equals(ApplicationAmendmentRequestEnum.RESPONSE_RECEIVED))) {
|
||||||
log.info("Updating amendment ID {} status from {} to {}", id, existingApplicationAmendment.getStatus(), statusTypeEnum);
|
log.info("Updating amendment ID {} status from {} to {}", id, existingApplicationAmendment.getStatus(), statusTypeEnum);
|
||||||
existingApplicationAmendment.setStatus(ApplicationAmendmentRequestEnum.RESPONSE_RECEIVED.getValue());
|
existingApplicationAmendment.setStatus(ApplicationAmendmentRequestEnum.RESPONSE_RECEIVED.getValue());
|
||||||
existingApplicationAmendment.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now()));
|
existingApplicationAmendment.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now()));
|
||||||
applicationAmendmentRequestRepository.save(existingApplicationAmendment);
|
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());
|
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);
|
log.info("Amendment status updated successfully: {}", response);
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -6,13 +6,22 @@ import lombok.extern.log4j.Log4j2;
|
|||||||
import net.gepafin.tendermanagement.config.Translator;
|
import net.gepafin.tendermanagement.config.Translator;
|
||||||
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
||||||
import net.gepafin.tendermanagement.entities.*;
|
import net.gepafin.tendermanagement.entities.*;
|
||||||
|
import net.gepafin.tendermanagement.enums.ApplicationEvaluationStatusTypeEnum;
|
||||||
|
import net.gepafin.tendermanagement.enums.AssignedApplicationEnum;
|
||||||
|
import net.gepafin.tendermanagement.enums.EmailScenarioTypeEnum;
|
||||||
import net.gepafin.tendermanagement.enums.EmailServiceTypeEnum;
|
import net.gepafin.tendermanagement.enums.EmailServiceTypeEnum;
|
||||||
import net.gepafin.tendermanagement.enums.StatusTypeEnum;
|
import net.gepafin.tendermanagement.enums.StatusTypeEnum;
|
||||||
import net.gepafin.tendermanagement.model.response.EmailLogResponse;
|
import net.gepafin.tendermanagement.model.response.EmailLogResponse;
|
||||||
import net.gepafin.tendermanagement.model.response.PecEmailLogResponse;
|
import net.gepafin.tendermanagement.model.response.PecEmailLogResponse;
|
||||||
import net.gepafin.tendermanagement.model.response.PecMailResponse;
|
import net.gepafin.tendermanagement.model.response.PecMailResponse;
|
||||||
|
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.EmailLogRepository;
|
||||||
import net.gepafin.tendermanagement.repositories.UserActionsRepository;
|
import net.gepafin.tendermanagement.repositories.UserActionsRepository;
|
||||||
|
import net.gepafin.tendermanagement.repositories.VersionHistoryRepository;
|
||||||
import net.gepafin.tendermanagement.service.ApplicationService;
|
import net.gepafin.tendermanagement.service.ApplicationService;
|
||||||
import net.gepafin.tendermanagement.service.CallService;
|
import net.gepafin.tendermanagement.service.CallService;
|
||||||
import net.gepafin.tendermanagement.util.DateTimeUtil;
|
import net.gepafin.tendermanagement.util.DateTimeUtil;
|
||||||
@@ -28,6 +37,8 @@ import org.springframework.stereotype.Component;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Log4j2
|
@Log4j2
|
||||||
@@ -51,6 +62,22 @@ public class PecMailDao {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationService applicationService;
|
private ApplicationService applicationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationRepository applicationRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationEvaluationRepository applicationEvaluationRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AssignedApplicationsRepository assignedApplicationsRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private VersionHistoryRepository versionHistoryRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationAmendmentRequestRepository applicationAmendmentRequestRepository;
|
||||||
|
|
||||||
|
private static final String TABLE_APPLICATION = "APPLICATION";
|
||||||
|
|
||||||
public List<PecMailResponse> sendPecMail(HttpServletRequest request, List<Long> userActionIds) {
|
public List<PecMailResponse> sendPecMail(HttpServletRequest request, List<Long> userActionIds) {
|
||||||
|
|
||||||
@@ -86,11 +113,104 @@ public class PecMailDao {
|
|||||||
}
|
}
|
||||||
emailLogRepository.saveAll(emailLogs);
|
emailLogRepository.saveAll(emailLogs);
|
||||||
EmailLogEntity firstLog = emailLogs.get(0);
|
EmailLogEntity firstLog = emailLogs.get(0);
|
||||||
|
|
||||||
|
rollbackDomainChangesForRejectedEmail(firstLog, userActionId);
|
||||||
|
|
||||||
ApplicationEntity applicationEntity = applicationService.validateApplication(firstLog.getApplicationId());
|
ApplicationEntity applicationEntity = applicationService.validateApplication(firstLog.getApplicationId());
|
||||||
String callName = applicationEntity.getCall().getName();
|
String callName = applicationEntity.getCall().getName();
|
||||||
return createPecMailResponse(firstLog.getUserAction().getId(), firstLog, callName);
|
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();
|
||||||
|
|
||||||
|
// Only normal amendment emails are rejectable in PEC flow; set amendment to DRAFT so only instructor can update
|
||||||
|
if (EmailScenarioTypeEnum.APPLICATION_AMENDMENT_REQUESTED.getValue().equals(scenario)) {
|
||||||
|
Long amendmentId = firstLog.getAmendmentId();
|
||||||
|
if (amendmentId != null) {
|
||||||
|
applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(amendmentId).ifPresent(amendment -> {
|
||||||
|
amendment.setStatus(ApplicationAmendmentRequestEnum.DRAFT.getValue());
|
||||||
|
applicationAmendmentRequestRepository.save(amendment);
|
||||||
|
log.info("Set amendment id={} to DRAFT after email rejected (userActionId={})", amendmentId, 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<VersionHistoryEntity> appHistory = versionHistoryRepository.findVersionHistoryByUserActionIdAndTableName(userActionId, TABLE_APPLICATION);
|
||||||
|
Optional<VersionHistoryEntity> 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<String, Object> 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 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<EmailLogEntity> getEmailLogEntities(HttpServletRequest request, Long userActionId) {
|
private List<EmailLogEntity> getEmailLogEntities(HttpServletRequest request, Long userActionId) {
|
||||||
UserActionEntity userActionEntity = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId);
|
UserActionEntity userActionEntity = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId);
|
||||||
if (userActionEntity == null) {
|
if (userActionEntity == null) {
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ public enum ApplicationAmendmentRequestEnum {
|
|||||||
AWAITING("AWAITING"),
|
AWAITING("AWAITING"),
|
||||||
RESPONSE_RECEIVED("RESPONSE_RECEIVED"),
|
RESPONSE_RECEIVED("RESPONSE_RECEIVED"),
|
||||||
CLOSE("CLOSE"),
|
CLOSE("CLOSE"),
|
||||||
EXPIRED("EXPIRED");
|
EXPIRED("EXPIRED"),
|
||||||
|
/** Amendment PEC email was rejected; only instructor can update until status is set back to AWAITING. */
|
||||||
|
DRAFT("DRAFT");
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,6 @@ public interface VersionHistoryRepository extends JpaRepository<VersionHistoryEn
|
|||||||
List<VersionHistoryEntity> findVersionHistoryByUserActionIdAndUserIdNull(Long id);
|
List<VersionHistoryEntity> findVersionHistoryByUserActionIdAndUserIdNull(Long id);
|
||||||
|
|
||||||
List<VersionHistoryEntity> findVersionHistoryByUserActionId(Long id);
|
List<VersionHistoryEntity> findVersionHistoryByUserActionId(Long id);
|
||||||
|
|
||||||
|
List<VersionHistoryEntity> findVersionHistoryByUserActionIdAndTableName(Long userActionId, String tableName);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user