diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index f223d922..cf39a573 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -637,6 +637,7 @@ public class GepafinConstant { public static final String PEC_EMAIL_REJECTED_SUCCESSFULLY="pec.email.rejected.successfully"; public static final String EMAIL_LOG_FETCHED="email.log.fetched"; public static final String APPLICATION_AMENDMENT_APPROPIATE_STATUS="amendment.appropiate.status"; + public static final String AMENDMENT_MUST_BE_APPROVED_FIRST_MSG = "amendment.must.be.approved.first"; public static final String UPLOAD_COMPANY_DOCUMENT_TO_APPLICATION_MSG="upload.company.document.to.application"; public static final String COMPANY_DOCUMENT_NOT_FOUND_WITH_IDS="company.document.not.found.with.ids"; public static final String REQUIRED_AMOUNT_FIELD_NOT_PROVIDED = "amount.field.not.provided"; diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java index 33e70a17..ae26a2a1 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java @@ -335,12 +335,12 @@ public class ApplicationAmendmentRequestDao { log.warn("Invalid responseDays received: {}", applicationAmendmentRequest.getResponseDays()); throw new CustomValidationException(Status.BAD_REQUEST,Translator.toLocale(GepafinConstant.RESPONSE_DAYS_NOT_NULL)); } - applicationAmendmentRequestEntity.setEndDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now()).plusDays(applicationAmendmentRequest.getResponseDays())); - applicationAmendmentRequestEntity.setIsEmail(applicationAmendmentRequest.getIsSendEmail()); applicationAmendmentRequestEntity.setIsNotification(applicationAmendmentRequest.getIsSendNotification()); - applicationAmendmentRequestEntity.setStartDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); - applicationAmendmentRequestEntity.setStatus(ApplicationAmendmentRequestEnum.AWAITING.getValue()); + // startDate and endDate set to null until director approves (sends PEC); status DRAFT until then + applicationAmendmentRequestEntity.setStartDate(null); + applicationAmendmentRequestEntity.setEndDate(null); + applicationAmendmentRequestEntity.setStatus(ApplicationAmendmentRequestEnum.DRAFT.getValue()); applicationAmendmentRequestEntity.setType(ApplicationAmendmentRequestTypeEnum.REGULAR.getValue()); ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(applicationEvaluationId); //cloned for old data entity @@ -367,20 +367,7 @@ public class ApplicationAmendmentRequestDao { applicationAmendmentRequestEntity.setFormFields(formFieldsJson); } List amendmentRequest = applicationAmendmentRequestRepository.findAllByApplicationEvaluationIdAndIsDeletedFalse(applicationEvaluationEntity.getId()); - // Ensure startDate and initialDays are not null to avoid NullPointerException - if (amendmentRequest !=null && amendmentRequest.isEmpty()) { - if (applicationEvaluationEntity.getStartDate() != null && applicationEvaluationEntity.getInitialDays() != null) { - Long initialDays = applicationEvaluationEntity.getInitialDays(); - LocalDateTime startDate = applicationEvaluationEntity.getStartDate(); - LocalDateTime nowInUTC = DateTimeUtil.DateServerToUTC(LocalDateTime.now()); - // Calculate remaining days - Long remainingDays = initialDays - DAYS.between(startDate, nowInUTC); - // Set remaining days in the entity - applicationEvaluationEntity.setRemainingDays(remainingDays); - //Set stop date time in the entity becuase amendment has started - applicationEvaluationEntity.setStopDateTime(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); - } - } + // remainingDays/stopDateTime are set when director approves (see approveAmendment) boolean noneClosedOrExpired = amendmentRequest.stream() .noneMatch(amendment -> @@ -1221,7 +1208,8 @@ public class ApplicationAmendmentRequestDao { log.info("Extending response days for Application Amendment ID: {}, Additional Days: {}", id, newResponseDays); ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity = validateApplicationAmendmentRequest(id); if(Boolean.TRUE.equals(applicationAmendmentRequestEntity.getStatus().equals(ApplicationAmendmentRequestEnum.CLOSE.getValue())) - || Boolean.TRUE.equals(applicationAmendmentRequestEntity.getStatus().equals(ApplicationAmendmentRequestEnum.AWAITING.getValue()))) { + || Boolean.TRUE.equals(applicationAmendmentRequestEntity.getStatus().equals(ApplicationAmendmentRequestEnum.AWAITING.getValue())) + || Boolean.TRUE.equals(applicationAmendmentRequestEntity.getStatus().equals(ApplicationAmendmentRequestEnum.DRAFT.getValue()))) { throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.APPLICATION_AMENDMENT_APPROPIATE_STATUS)); } log.info("Extending response days for Application Amendment ID: {}, Additional Days: {}", id, newResponseDays); @@ -1280,6 +1268,9 @@ public class ApplicationAmendmentRequestDao { 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)); + } log.info("Updating amendment ID {} status from {} to {}", id, existingApplicationAmendment.getStatus(), statusTypeEnum); existingApplicationAmendment.setStatus(ApplicationAmendmentRequestEnum.RESPONSE_RECEIVED.getValue()); existingApplicationAmendment.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); @@ -1292,12 +1283,63 @@ public class ApplicationAmendmentRequestDao { return response; } + /** + * Sets amendment startDate, endDate and status AWAITING when director sends PEC mail (approval). + * Called from PecMailDao after PEC send succeeds; amendment id comes from email log (user action). + * No-op if amendment not found, not DRAFT (or already has startDate set), or startDate already set. + */ + public void setAmendmentStartAndEndDateOnPecSent(Long amendmentId) { + if (amendmentId == null) { + return; + } + Optional optional = applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(amendmentId); + if (optional.isEmpty()) { + return; + } + ApplicationAmendmentRequestEntity amendment = optional.get(); + boolean isDraft = ApplicationAmendmentRequestEnum.DRAFT.getValue().equals(amendment.getStatus()); + boolean isAwaitingWithNoStart = ApplicationAmendmentRequestEnum.AWAITING.getValue().equals(amendment.getStatus()) && amendment.getStartDate() == null; + if ((!isDraft && !isAwaitingWithNoStart) || amendment.getStartDate() != null) { + return; + } + log.info("Setting amendment ID {} startDate/endDate and status AWAITING after PEC mail sent.", amendmentId); + Long responseDays = amendment.getResponseDays() != null ? amendment.getResponseDays() : 0L; + LocalDateTime nowUtc = DateTimeUtil.DateServerToUTC(LocalDateTime.now()); + ApplicationAmendmentRequestEntity oldEntity = Utils.getClonedEntityForData(amendment); + amendment.setStartDate(nowUtc); + amendment.setEndDate(nowUtc.plusDays(responseDays)); + amendment.setStatus(ApplicationAmendmentRequestEnum.AWAITING.getValue()); + amendment.setUpdatedDate(nowUtc); + + ApplicationEvaluationEntity evaluation = amendment.getApplicationEvaluationEntity(); + List allForEvaluation = applicationAmendmentRequestRepository.findAllByApplicationEvaluationIdAndIsDeletedFalse(evaluation.getId()); + boolean isFirstApprovedAmendment = allForEvaluation.stream() + .filter(a -> !a.getId().equals(amendment.getId())) + .noneMatch(a -> a.getStartDate() != null); + + if (isFirstApprovedAmendment && evaluation.getStartDate() != null && evaluation.getInitialDays() != null) { + Long initialDays = evaluation.getInitialDays(); + LocalDateTime evalStartDate = evaluation.getStartDate(); + Long remainingDays = initialDays - DAYS.between(evalStartDate, nowUtc); + evaluation.setRemainingDays(remainingDays); + evaluation.setStopDateTime(nowUtc); + applicationEvaluationRepository.save(evaluation); + } + + applicationAmendmentRequestRepository.save(amendment); + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldEntity).newData(amendment).build()); + log.info("Amendment ID {} startDate and endDate set after PEC sent.", amendmentId); + } + public EmailReminderResponse sendReminderEmail(Long amendmentId) { log.info("Initiating reminder email process for Amendment ID: {}", amendmentId); ApplicationAmendmentRequestEntity amendment = applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(amendmentId) .orElseThrow(() -> { log.error("Amendment not found with ID: {}", amendmentId); return new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.APPLICATION_AMENDMENT_NOT_FOUND_MSG)); }); + if (!ApplicationAmendmentRequestEnum.AWAITING.getValue().equals(amendment.getStatus())) { + throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.APPLICATION_AMENDMENT_APPROPIATE_STATUS)); + } Optional entityOptional = applicationEvaluationRepository.findByIdAndIsDeletedFalse(amendment.getApplicationEvaluationEntity().getId()); EmailReminderResponse emailReminderResponse = new EmailReminderResponse(); @@ -1875,11 +1917,12 @@ public class ApplicationAmendmentRequestDao { else { applicationAmendmentRequestEntity.setResponseDays(10l); } - applicationAmendmentRequestEntity.setEndDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now()).plusDays(applicationAmendmentRequestEntity.getResponseDays())); + // startDate and endDate set when director approves (see approveAmendment) + applicationAmendmentRequestEntity.setEndDate(null); applicationAmendmentRequestEntity.setIsEmail(Boolean.TRUE); applicationAmendmentRequestEntity.setIsNotification(Boolean.FALSE); - applicationAmendmentRequestEntity.setStartDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); - applicationAmendmentRequestEntity.setStatus(ApplicationAmendmentRequestEnum.AWAITING.getValue()); + applicationAmendmentRequestEntity.setStartDate(null); + applicationAmendmentRequestEntity.setStatus(ApplicationAmendmentRequestEnum.DRAFT.getValue()); // if ( applicationEvaluationEntity.getStartDate() != null && applicationEvaluationEntity.getInitialDays() != null ) { // Long initialDays = applicationEvaluationEntity.getInitialDays(); // LocalDateTime startDate = applicationEvaluationEntity.getStartDate(); @@ -1938,7 +1981,7 @@ public class ApplicationAmendmentRequestDao { ApplicationAmendmentRequestResponse applicationAmendmentRequestResponse = convertEntityToResponse(applicationAmendmentRequestEntity,false); if (!Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())){ - log.info("Sending mail for the special amendment for amendment ID = {}", applicationAmendment.getId()); +// log.info("Sending mail for the special amendment for amendment ID = {}", applicationAmendment.getId()); saveEmailSendResponse(emailSendResponse, applicationAmendmentRequestEntity); applicationAmendmentRequestResponse.setEmailSendResponse(responses); } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/PecMailDao.java b/src/main/java/net/gepafin/tendermanagement/dao/PecMailDao.java index 73a352c6..4bc3abe9 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/PecMailDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/PecMailDao.java @@ -78,6 +78,9 @@ public class PecMailDao { @Autowired private ApplicationAmendmentRequestRepository applicationAmendmentRequestRepository; + @Autowired + private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; + private static final String TABLE_APPLICATION = "APPLICATION"; public List sendPecMail(HttpServletRequest request, List userActionIds) { @@ -95,6 +98,13 @@ public class PecMailDao { recipients, log ); + // When PEC send succeeded and this is an amendment email (regular or special), set amendment startDate/endDate and status AWAITING + boolean isAmendmentEmail = EmailScenarioTypeEnum.APPLICATION_AMENDMENT_REQUESTED.getValue().equals(log.getEmailType()) + || EmailScenarioTypeEnum.SPECIAL_APPLICATION_AMENDMENT_REQUESTED.getValue().equals(log.getEmailType()) + || EmailScenarioTypeEnum.SPECIAL_APPLICATION_AMENDMENT_REQUESTED_BLUE_TONGUE.getValue().equals(log.getEmailType()); + if (isAmendmentEmail && log.getAmendmentId() != null && StatusTypeEnum.SUCCESS.getValue().equals(log.getSendStatus())) { + applicationAmendmentRequestDao.setAmendmentStartAndEndDateOnPecSent(log.getAmendmentId()); + } ApplicationEntity applicationEntity=applicationService.validateApplication(log.getApplicationId()); String callName=applicationEntity.getCall().getName(); PecMailResponse pecMailResponse=createPecMailResponse(log.getUserAction().getId(),log,callName); diff --git a/src/main/java/net/gepafin/tendermanagement/enums/ApplicationAmendmentRequestEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/ApplicationAmendmentRequestEnum.java index 13a6d8c4..daf5685a 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/ApplicationAmendmentRequestEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/ApplicationAmendmentRequestEnum.java @@ -3,6 +3,9 @@ package net.gepafin.tendermanagement.enums; import com.fasterxml.jackson.annotation.JsonValue; public enum ApplicationAmendmentRequestEnum { + /** Created but not yet approved (PEC not sent by director). */ + DRAFT("DRAFT"), + /** Director sent PEC (approved); amendment is active and awaiting beneficiary response. */ AWAITING("AWAITING"), RESPONSE_RECEIVED("RESPONSE_RECEIVED"), CLOSE("CLOSE"), diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index 65b69eff..b223a21d 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -429,6 +429,7 @@ mail.send.successfully=Mail sent succesfully. pec.email.rejected.successfully=Email rejected successfully. email.log.fetched=Email log fetched successfully. amendment.appropiate.status=Application amendment is not in appropiate status for this operation. +amendment.must.be.approved.first=Amendment must be approved by the director before response can be recorded. upload.company.document.to.application=Uploaded company document to application successfully. company.document.not.found.with.ids=Company document not found. Missing IDs: {0}. amount.field.not.provided= Please provide the required amount fields. diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index 2437fa0b..bcff6a8f 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -420,6 +420,7 @@ mail.send.successfully=Email inviata con successo. pec.email.rejected.successfully=Email rifiutata con successo. email.log.fetched=Registro email recuperato correttamente. amendment.appropiate.status=L'emendamento dell'applicazione non � in stato appropriato per questa operazione. +amendment.must.be.approved.first=L'emendamento deve essere approvato dal direttore prima di poter registrare la risposta. upload.company.document.to.application=Documento aziendale caricato correttamente nell'applicazione. company.document.not.found.with.ids=Documento aziendale non trovato. ID mancanti: {0} amount.field.not.provided= Si prega di fornire i campi obbligatori per l'importo.