diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 8b3e8b54..f9fbe5d6 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -537,7 +537,20 @@ public class GepafinConstant { public static final String APPLICATION_STATUS="applicationStatus"; public static final String RINALDO_EMAIL = "rinaldo.bonazzo@bflows.net"; + public static final String RESEND_EMAIL_SENT_SUCCESS_MSG = "resend.email.sent.success.msg"; + public static final String RESEND_EMAIL_SENT_FAILED_MSG = "resend.email.sent.failed.msg"; + public static final String REMINDER_EMAIL_FAILED_MSG = "reminder.email.sent.failed.msg"; + public static final String READMIT_APPLICATION_SUCCESS = "application.readmit.success"; + + public static final String NO_EMAIL_LOG_FOUND = "no.email.log.msg"; + public static final String USER_ACTION_ID_NOT_FOUND = "user.action.id.not.found"; + + public static final String CAUSE_STRING = "cause" ; + public static final String ERROR_DESCRIPTION_STRING = "errorDescription" ; + public static final String ERROR_STRING = "errors"; + + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java index b1ac989b..76ce5d3d 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java @@ -1,5 +1,6 @@ package net.gepafin.tendermanagement.dao; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.criteria.CriteriaBuilder; @@ -26,6 +27,7 @@ import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundExceptio import net.gepafin.tendermanagement.web.rest.api.errors.Status; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; @@ -140,6 +142,12 @@ public class ApplicationAmendmentRequestDao { @Autowired private ApplicationDao applicationDao; + @Autowired + private EmailLogRepository emailLogRepository; + + @Autowired + private EmailDao emailDao; + public ApplicationAmendmentRequestResponse getApplicationDataForAmendment(Long applicationEvaluationId) { log.info("Fetching the application data for the Amendment process {}", applicationEvaluationId); ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(applicationEvaluationId); @@ -300,6 +308,15 @@ public class ApplicationAmendmentRequestDao { log.info("Application submitted successfully for amendment", applicationAmendmentRequestResponse); if (Boolean.TRUE.equals(applicationAmendmentRequestResponse.getIsSendEmail())) { emailNotificationDao.sendMailToNotifyBeneficiaryRegardingNewAmendment(applicationAmendmentRequestEntity); + EmailSendResponse emailSendResponse = emailDao.buildEmailSendResponseFromRequest(request); + List responses = List.of(emailSendResponse); + if (!Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())){ + saveEmailSendResponse(emailSendResponse, applicationAmendmentRequestEntity); + applicationAmendmentRequestResponse.setEmailSendResponse(responses); + } + else{ + applicationAmendmentRequestResponse.setEmailSendResponse(Collections.emptyList()); + } } return applicationAmendmentRequestResponse; } @@ -506,6 +523,7 @@ public class ApplicationAmendmentRequestDao { Long hubId = applicationEntity.getHubId(); HubEntity hubEntity = hubService.valdateHub(hubId); + response.setEmailSendResponse(entity.getEmailSendResponse()); response.setId(entity.getId()); response.setApplicationId(entity.getApplicationId()); response.setApplicationEvaluationId(entity.getApplicationEvaluationEntity().getId()); @@ -647,9 +665,9 @@ public class ApplicationAmendmentRequestDao { public ApplicationAmendmentRequestResponse getApplicationAmendmentRequestById(Long id) { log.info("Fetching application amendment with ID: {}", id); ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity = validateApplicationAmendmentRequest(id); - ApplicationAmendmentRequestResponse response = convertEntityToResponse(applicationAmendmentRequestEntity,true); - log.info("Application Amendment fetched successfully by ID: {}", response); - return response; + ApplicationAmendmentRequestResponse response = convertEntityToResponse(applicationAmendmentRequestEntity,true); + response.setEmailSendResponse(applicationAmendmentRequestEntity.getEmailSendResponse()); + return response; } public List getAllApplicationAmendmentRequest(HttpServletRequest request, Long userId) { @@ -1032,7 +1050,7 @@ public class ApplicationAmendmentRequestDao { public ApplicationAmendmentRequestResponse closeAmendmentRequest(Long id, CloseAmendmentRequest closeAmendmentRequest) { log.info("Closing application amendement with ID: {}", id); - ApplicationAmendmentRequestEntity existingApplicationAmendment = validateApplicationAmendmentRequest(id); + ApplicationAmendmentRequestEntity existingApplicationAmendment = validatApplicationAmendmentRequestByStatus(id,ApplicationAmendmentRequestEnum.AWAITING.getValue()); //cloned entity for old data and versioning ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity = Utils.getClonedEntityForData(existingApplicationAmendment); List amendmentRequestList = applicationAmendmentRequestRepository.findAllByApplicationEvaluationIdAndIsDeletedFalse( @@ -1042,17 +1060,18 @@ public class ApplicationAmendmentRequestDao { // Check if this is the last amendment being closed boolean isLastRemaining = amendmentRequestList.stream() .filter(amendment -> !amendment.getId().equals(id)) // Exclude the current amendment - .allMatch(amendment -> amendment.getStatus().equals(ApplicationAmendmentRequestEnum.CLOSE.getValue())); + .allMatch(amendment -> amendment.getStatus().equals(ApplicationAmendmentRequestEnum.CLOSE.getValue()) || amendment.getStatus().equals(ApplicationAmendmentRequestEnum.EXPIRED.getValue())); + - if (isLastRemaining) { - log.info("The current amendment is the last remaining one to be closed."); - applicationAmendmentRequestDao.calculateEndDateAndSuspensionDays(existingApplicationAmendment.getApplicationEvaluationEntity()); - } setIfUpdated(existingApplicationAmendment::getInternalNote, existingApplicationAmendment::setInternalNote, closeAmendmentRequest.getInternalNote()); setIfUpdated(existingApplicationAmendment::getStatus, existingApplicationAmendment::setStatus, ApplicationAmendmentRequestEnum.CLOSE.getValue()); existingApplicationAmendment.setClosingDate(LocalDateTime.now()); ApplicationAmendmentRequestEntity updatedApplicationAmendment = saveApplicationAmendmentRequestEntity(existingApplicationAmendment, oldApplicationAmendmentEntity, VersionActionTypeEnum.UPDATE); + if (isLastRemaining) { + log.info("The current amendment is the last remaining one to be closed."); + applicationAmendmentRequestDao.calculateEndDateAndSuspensionDaysOnExpirationOrClosing(existingApplicationAmendment.getApplicationEvaluationEntity()); + } ApplicationAmendmentRequestResponse response = convertEntityToResponse(updatedApplicationAmendment,false); List amendmentRequests = applicationAmendmentRequestRepository.findAllByApplicationEvaluationIdAndIsDeletedFalse( @@ -1104,15 +1123,17 @@ public class ApplicationAmendmentRequestDao { return response; } - public ApplicationAmendmentRequestResponse extendResponseDays(Long id, Long newResponseDays) { - ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity = validateApplicationAmendmentRequest(id); + ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity = validatApplicationAmendmentRequestByStatus(id,ApplicationAmendmentRequestEnum.EXPIRED.getValue()); if (newResponseDays != null && newResponseDays > 0) { - ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity = Utils.getClonedEntityForData(applicationAmendmentRequestEntity); + ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity = Utils.getClonedEntityForData(applicationAmendmentRequestEntity); Long currentResponseDays = applicationAmendmentRequestEntity.getResponseDays() != null ? applicationAmendmentRequestEntity.getResponseDays() : 0L; applicationAmendmentRequestEntity.setResponseDays(currentResponseDays + newResponseDays); - applicationAmendmentRequestEntity.setEndDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now().plusDays(applicationAmendmentRequestEntity.getResponseDays()))); + applicationAmendmentRequestEntity.setEndDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now().plusDays(newResponseDays))); + applicationAmendmentRequestEntity.setStatus(ApplicationAmendmentRequestEnum.AWAITING.getValue()); + applicationAmendmentRequestEntity.getApplicationEvaluationEntity().setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue()); + applicationAmendmentRequestEntity.getApplicationEvaluationEntity().getAssignedApplicationsEntity().getApplication().setStatus(ApplicationStatusTypeEnum.SOCCORSO.getValue()); applicationAmendmentRequestRepository.save(applicationAmendmentRequestEntity); /** This code is responsible for adding a version history log for the "Update Application Amendment" operation. **/ @@ -1168,12 +1189,13 @@ public class ApplicationAmendmentRequestDao { return response; } - public void sendReminderEmail(Long amendmentId) { + public EmailReminderResponse sendReminderEmail(Long amendmentId) { ApplicationAmendmentRequestEntity amendment = applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(amendmentId) .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.APPLICATION_AMENDMENT_NOT_FOUND_MSG))); Optional entityOptional = applicationEvaluationRepository.findByIdAndIsDeletedFalse(amendment.getApplicationEvaluationEntity().getId()); + EmailReminderResponse emailReminderResponse = new EmailReminderResponse(); if (entityOptional.isPresent()) { ApplicationEntity applicationEntity = applicationService.validateApplication(entityOptional.get().getApplicationId()); UserEntity beneficiaryUser = userService.validateUser(applicationEntity.getUserId()); @@ -1186,10 +1208,20 @@ public class ApplicationAmendmentRequestDao { EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(emailTemplate.getEmailScenario(), RecipientTypeEnum.USER, beneficiaryUser.getId(), email, beneficiaryUser.getId(), applicationEntity.getId(), amendment.getId(), applicationEntity.getCall().getId()); emailNotificationDao.sendMail(hub.getId(), subject, body, List.of(email), emailLogRequest); + EmailSendResponse emailSendResponse = emailDao.buildEmailSendResponseFromRequest(request); + List responses = List.of(emailSendResponse); + if (!Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())){ + emailReminderResponse.setEmailSendResponse(responses); + saveEmailSendResponse(emailSendResponse, amendment); + } + else{ + emailReminderResponse.setEmailSendResponse(Collections.emptyList()); + } } else { throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.BENEFICIARY_EMAIL_NOT_FOUND_MSG)); } } + return emailReminderResponse; } private String prepareSubject(SystemEmailTemplateResponse template, ApplicationAmendmentRequestEntity amendment, UserEntity beneficiary) { @@ -1219,26 +1251,26 @@ public class ApplicationAmendmentRequestDao { return Utils.replacePlaceholders(template.getHtmlContent(), bodyPlaceholders); } - public ApplicationEvaluationEntity calculateEndDateAndSuspensionDays(ApplicationEvaluationEntity applicationEvaluationEntity){ - LocalDateTime currentDate=DateTimeUtil.DateServerToUTC(LocalDateTime.now()); - LocalDateTime endDate=currentDate.plusDays(applicationEvaluationEntity.getRemainingDays()); - Long suspendedDays = ChronoUnit.DAYS.between(applicationEvaluationEntity.getStopDateTime(), currentDate); - - ApplicationEvaluationEntity oldApplicationEvaluationEntity = Utils.getClonedEntityForData(applicationEvaluationEntity); - applicationEvaluationEntity.setEndDate(endDate); - if(applicationEvaluationEntity.getSuspendedDays() == null) { - applicationEvaluationEntity.setSuspendedDays(0L); - } - applicationEvaluationEntity.setSuspendedDays(applicationEvaluationEntity.getSuspendedDays()+suspendedDays); - ApplicationEvaluationEntity applicationEvaluation = applicationEvaluationRepository.save(applicationEvaluationEntity); - - /** 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(oldApplicationEvaluationEntity).newData(applicationEvaluation).build()); - - return applicationEvaluation; - - - } +// public ApplicationEvaluationEntity calculateEndDateAndSuspensionDays(ApplicationEvaluationEntity applicationEvaluationEntity){ +// LocalDateTime currentDate=DateTimeUtil.DateServerToUTC(LocalDateTime.now()); +// LocalDateTime endDate=currentDate.plusDays(applicationEvaluationEntity.getRemainingDays()); +// Long suspendedDays = ChronoUnit.DAYS.between(applicationEvaluationEntity.getStopDateTime(), currentDate); +// +// ApplicationEvaluationEntity oldApplicationEvaluationEntity = Utils.getClonedEntityForData(applicationEvaluationEntity); +// applicationEvaluationEntity.setEndDate(endDate); +// if(applicationEvaluationEntity.getSuspendedDays() == null) { +// applicationEvaluationEntity.setSuspendedDays(0L); +// } +// applicationEvaluationEntity.setSuspendedDays(applicationEvaluationEntity.getSuspendedDays()+suspendedDays); +// ApplicationEvaluationEntity applicationEvaluation = applicationEvaluationRepository.save(applicationEvaluationEntity); +// +// /** 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(oldApplicationEvaluationEntity).newData(applicationEvaluation).build()); +// +// return applicationEvaluation; +// +// +// } private GetAllAmendmentResponseBean initializeGetAllBasicResponse(ApplicationAmendmentRequestEntity entity) { GetAllAmendmentResponseBean response = new GetAllAmendmentResponseBean(); @@ -1539,8 +1571,103 @@ public class ApplicationAmendmentRequestDao { applicationAmendmentRequestViewResponse.setExpirationDate(applicationAmendmentRequestView.getExpirationDate()); applicationAmendmentRequestViewResponse.setAssigendUserName(applicationAmendmentRequestView.getAssigendUserName()); applicationAmendmentRequestViewResponse.setStatus(applicationAmendmentRequestView.getStatus()); + applicationAmendmentRequestViewResponse.setEmailSendResponse(applicationAmendmentRequestView.getEmailSendResponse()); return applicationAmendmentRequestViewResponse; } + public ApplicationEvaluationEntity calculateEndDateAndSuspensionDaysOnExpirationOrClosing + (ApplicationEvaluationEntity applicationEvaluationEntity) { + + // Fetch all linked amendments + List amendments = + applicationAmendmentRequestRepository.findAllByApplicationEvaluationIdAndIsDeletedFalse(applicationEvaluationEntity.getId()); + + // Recalculate suspension + long suspendedDays = calculateSuspendedDays(amendments); + + // Update end date and save + + ApplicationEvaluationEntity oldEntity = Utils.getClonedEntityForData(applicationEvaluationEntity); + HubEntity hub = hubService.valdateHub(applicationEvaluationEntity.getAssignedApplicationsEntity().getApplication().getHubId()); + Long initialDays = (hub != null) ? hub.getEvaluationExpirationDays() : 30L; + LocalDateTime endDate = applicationEvaluationEntity.getStartDate().plusDays(suspendedDays+initialDays); + applicationEvaluationEntity.setEndDate(endDate); + applicationEvaluationEntity.setSuspendedDays(suspendedDays); + ApplicationEvaluationEntity updated = applicationEvaluationRepository.save(applicationEvaluationEntity); + + loggingUtil.addVersionHistory(VersionHistoryRequest.builder() + .request(request) + .actionType(VersionActionTypeEnum.UPDATE) + .oldData(oldEntity) + .newData(updated) + .build()); + + return updated; + } + + public long calculateSuspendedDays(List amendments) { + List> periods = amendments.stream() + .filter(amendmentRequest -> amendmentRequest.getStartDate() != null) + .map(amendmentRequest -> { + LocalDateTime start = amendmentRequest.getStartDate(); + LocalDateTime end; + + String status = amendmentRequest.getStatus(); + if (Boolean.TRUE.equals(ApplicationAmendmentRequestEnum.CLOSE.getValue().equals(status)) && amendmentRequest.getClosingDate() != null) { + end = amendmentRequest.getClosingDate(); + } else if (Boolean.TRUE.equals(ApplicationAmendmentRequestEnum.EXPIRED.getValue().equals(status)) && amendmentRequest.getEndDate() != null) { + end = amendmentRequest.getStartDate().plusDays(amendmentRequest.getResponseDays()); + }else { + end= amendmentRequest.getEndDate(); + } + + return Pair.of(start, end); + }) + .filter(Objects::nonNull) + .sorted(Comparator.comparing(Pair::getLeft)) + .collect(Collectors.toList()); + + long totalDays = 0; + LocalDateTime currentStart = null; + LocalDateTime currentEnd = null; + + for (Pair period : periods) { + if (currentStart == null) { + currentStart = period.getLeft(); + currentEnd = period.getRight(); + } else if (!period.getLeft().isAfter(currentEnd)) { + // Merge overlapping/touching periods + currentEnd = currentEnd.isAfter(period.getRight()) ? currentEnd : period.getRight(); + } else { + // Non-overlapping: count previous period + totalDays += ChronoUnit.DAYS.between(currentStart.toLocalDate(), currentEnd.toLocalDate()); + currentStart = period.getLeft(); + currentEnd = period.getRight(); + } + } + + if (currentStart != null && currentEnd != null) { + totalDays += ChronoUnit.DAYS.between(currentStart.toLocalDate(), currentEnd.toLocalDate()); + } + + return totalDays; + } + + + + public ApplicationAmendmentRequestEntity validatApplicationAmendmentRequestByStatus(Long id,String status) { + ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity = applicationAmendmentRequestRepository.findByIdAndIsDeletedFalseAndStatus(id, status); + if (applicationAmendmentRequestEntity == null) { + throw new ResourceNotFoundException(Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.APPLICATION_AMENDMENT_NOT_FOUND_MSG)); + } + return applicationAmendmentRequestEntity; + } + private void saveEmailSendResponse(EmailSendResponse newResponses, ApplicationAmendmentRequestEntity amendment) { + List mergedResponses = Utils.mergeEmailSendResponses(amendment.getEmailSendResponse(), newResponses); + + amendment.setEmailSendResponse(mergedResponses); + applicationAmendmentRequestRepository.save(amendment); + } } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index 89c8d1e9..f4656197 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -1109,7 +1109,7 @@ public class ApplicationDao { if (userEntity.getBeneficiary() != null) { emailLogRequest.setRecipientType(RecipientTypeEnum.BENEFICIARY); email = userEntity.getBeneficiary().getEmail(); - emailLogRequest.setUserId(userEntity.getBeneficiary().getId()); + emailLogRequest.setRecipientId(userEntity.getBeneficiary().getId()); } emailNotificationDao.sendMail(hub.getId(), subject, body, List.of(email),emailLogRequest); List recipientEmails = new ArrayList<>(); @@ -2211,4 +2211,40 @@ public class ApplicationDao { } + public void sendApplicationSubmissionFailureEmail(EmailLogRequest emailLogRequest){ + + Long callId = emailLogRequest.getCallId(); + CallEntity call = callService.validateCall(callId); + HubEntity hub = call.getHub(); + Long userId = emailLogRequest.getUserId(); + UserEntity user = userService.validateUser(userId); + Long applicationId = emailLogRequest.getApplicatioId(); + ApplicationEntity applicationEntity = validateApplication(applicationId); + CompanyEntity company = companyService.validateCompany(applicationEntity.getCompanyId()); + + + SystemEmailTemplateResponse systemEmailTemplateResponse = systemEmailTemplatesService + .retrieveTemplateByTypeAndCall(SystemEmailTemplatesEntityTypeEnum.APPLICATION_SUBMISSION_FAILURE_NOTIFICATION, + hub, null); + + Map subjectPlaceholders = new HashMap<>(); + subjectPlaceholders.put("{{call_name}}", call.getName()); + + Map bodyPlaceholders = new HashMap<>(); + bodyPlaceholders.put("{{scenario}}",emailLogRequest.getEmailType().getValue()); + bodyPlaceholders.put("{{call_name}}", call.getName()); + bodyPlaceholders.put("{{application_id}}", applicationEntity.getId().toString()); + bodyPlaceholders.put("{{company_name}}", company.getCompanyName()); + bodyPlaceholders.put("{{protocol_number}}", applicationEntity.getProtocol().getProtocolNumber().toString()); + bodyPlaceholders.put("{{user_action_id}}",emailLogRequest.getUserActionId().toString()); + + String subject = Utils.replacePlaceholders(systemEmailTemplateResponse.getSubject(), subjectPlaceholders); + String body = Utils.replacePlaceholders(systemEmailTemplateResponse.getHtmlContent(), bodyPlaceholders); + + emailLogRequest=emailLogDao.createEmailLogRequest(systemEmailTemplateResponse.getEmailScenario(),RecipientTypeEnum.PROPERTIES,null,user.getEmail(),user.getId(),applicationEntity.getId(),null,callId); + + emailLogRequest.setRecipientEmails(GepafinConstant.RINALDO_EMAIL); + emailNotificationDao.sendMail(hub.getId(), subject, body, List.of(GepafinConstant.RINALDO_EMAIL),emailLogRequest); + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index 7052aad0..8d4ce08e 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -143,6 +143,9 @@ public class ApplicationEvaluationDao { @Autowired private ObjectMapper objectMapper; + @Autowired + private EmailDao emailDao; + private ApplicationEvaluationEntity convertToEntity(UserEntity user, ApplicationEvaluationRequest req, Long assignedApplciationId) { @@ -311,7 +314,6 @@ public class ApplicationEvaluationDao { response.setUpdatedDate(entity.getUpdatedDate()); response.setNumberOfCheck(entity.getAssignedApplicationsEntity().getApplication().getCall().getNumberOfCheck()); response.setAppointmentTemplateId(entity.getAssignedApplicationsEntity().getApplication().getCall().getAppointmentTemplateId()); - } @@ -701,7 +703,11 @@ public class ApplicationEvaluationDao { /** This code is responsible for adding a version history log for the "Update Application" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(actionType).oldData(oldApplication).newData(application).build()); - Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_CREATION); +// Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_CREATION); + + Map placeHolders = new HashMap<>(); + placeHolders.put("{{call_name}}", application.getCall().getName()); + placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.EVALUATION_CREATION); notificationDao.sendNotificationToInstructor(placeHolders,entity,NotificationTypeEnum.EVALUATION_CREATION); @@ -1124,10 +1130,14 @@ public class ApplicationEvaluationDao { } else { entityOptional = applicationEvaluationRepository.findFirstByIsDeletedFalseOrderByCreatedDateDesc(); } - return entityOptional.map(this::convertToResponse) - .orElseGet(() -> { - return getEvaluationResponseByApplicationid(user, applicationId, assignedApplicationId); - }); + + if (entityOptional.isEmpty()) { + return null; + } + ApplicationEvaluationEntity entity = entityOptional.get(); + ApplicationEvaluationResponse applicationEvaluationResponse = convertToResponse(entity); + applicationEvaluationResponse.setEmailSendResponse(entity.getEmailSendResponse()); + return applicationEvaluationResponse; } private List prepareEvaluationDocumentBeanList(ApplicationEvaluationEntity entity) { List docRequest = new ArrayList<>(); @@ -1876,7 +1886,7 @@ public class ApplicationEvaluationDao { assignedApplicationsEntity.getId()); ApplicationEvaluationEntity entity; - + EmailSendResponse emailSendResponse = new EmailSendResponse(); if (existingEntityOptional.isPresent()) { ApplicationEvaluationEntity existingEntity = existingEntityOptional.get(); // UserEntity userEntity = userService.validateUser(application.getUserId()); @@ -1884,10 +1894,16 @@ public class ApplicationEvaluationDao { ApplicationEntity oldApplicationEntity = Utils.getClonedEntityForData(application); + List responses = new ArrayList<>(); if(newStatus.equals(ApplicationStatusForEvaluation.ADMISSIBLE) && Boolean.TRUE.equals(application.getStatus().equals(ApplicationStatusTypeEnum.APPOINTMENT.getValue()))){ application.setStatus(newStatus.getValue()); log.info("Status updated to ADMISSIBLE for applicationId: " + application.getId()); emailNotificationDao.sendAdmissibilityNotificationEmailForAdmissibleApplication(application); + emailSendResponse = emailDao.buildEmailSendResponseFromRequest(request); + responses = List.of(emailSendResponse); + if (!Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())) { + saveEmailSendResponseToEvaluation(emailSendResponse, existingEntity); + } } if(newStatus.equals(ApplicationStatusForEvaluation.TECHNICAL_EVALUATION) && Boolean.TRUE.equals(application.getStatus().equals(ApplicationStatusTypeEnum.ADMISSIBLE.getValue()))){ @@ -1934,27 +1950,48 @@ public class ApplicationEvaluationDao { if (Boolean.TRUE.equals(statusType.equals((ApplicationStatusTypeEnum.APPROVED.getValue())))) { - application.setDateAccepted(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); - application.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + application.setDateAccepted(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + application.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); application = applicationRepository.save(application); // emailNotificationDao.sendAdmissibilityNotificationEmailForApprovedApplication(application); + notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_RESULT); } if (Boolean.TRUE.equals(statusType.equals((ApplicationStatusTypeEnum.REJECTED.getValue())))) { application.setDateRejected(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); application.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); application = applicationRepository.save(application); emailNotificationDao.sendInadmissibilityEmailForRejectedApplication(application,existingEntity); + emailSendResponse = emailDao.buildEmailSendResponseFromRequest(request); + responses = List.of(emailSendResponse); + if (!Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())) { + saveEmailSendResponseToEvaluation(emailSendResponse, existingEntity); + } + notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_RESULT); } - Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_RESULT); + Map placeHolders = new HashMap<>(); + placeHolders.put("{{call_name}}", application.getCall().getName()); + placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.EVALUATION_RESULT); notificationDao.sendNotificationToInstructor(placeHolders,existingEntity,NotificationTypeEnum.EVALUATION_RESULT); - return convertToResponse(entity); + ApplicationEvaluationResponse response = convertToResponse(entity); + if (!Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())) { + response.setEmailSendResponse(responses); + } + return response; } return null; } + private void saveEmailSendResponseToEvaluation(EmailSendResponse newResponse, ApplicationEvaluationEntity evaluationEntity) { + List mergedResponses = Utils.mergeEmailSendResponses( + evaluationEntity.getEmailSendResponse(), newResponse + ); + evaluationEntity.setEmailSendResponse(mergedResponses); + applicationEvaluationRepository.save(evaluationEntity); + } + public ApplicationEvaluationEntity validateApplicationEvaluationByApplicationId(Long applicationId) { return applicationEvaluationRepository .findByApplicationIdAndIsDeletedFalse(applicationId) @@ -2006,6 +2043,7 @@ public class ApplicationEvaluationDao { ApplicationEvaluationEntity entity = applicationEvaluationService.validateApplicationEvaluation(evaluationResponse.getId()); + //Handling Application Evaluation form EvaluationFormEntity evaluationFormEntity = evaluationFormService.validateEvaluationForm(evaluationFormId); validateFormFields(applicationEvaluationFormRequestBean,evaluationFormEntity); @@ -2014,7 +2052,9 @@ public class ApplicationEvaluationDao { validateFormFieldCustom(applicationEvaluationFormRequestBean.getFormFields(),entity,evaluationFormEntity); ApplicationEvaluationFormEntity applicationEvaluationFormEntity = getApplicationEvaluationFormOrCreate(evaluationFormEntity,entity); createOrUpdateMultipleFormFields(applicationEvaluationFormRequestBean.getFormFields(), applicationEvaluationFormEntity, evaluationFormEntity); - return processEvaluationForm(entity); + ApplicationEvaluationFormResponse response = processEvaluationForm(entity); + response.setEmailSendResponse(evaluationResponse.getEmailSendResponse()); + return response; } private ApplicationEvaluationFormEntity getApplicationEvaluationFormOrCreate(EvaluationFormEntity evaluationFormEntity, ApplicationEvaluationEntity applicationEvaluationEntity) { @@ -2259,6 +2299,7 @@ public class ApplicationEvaluationDao { } response.setCompanyVatNumber(company.getVatNumber()); response.setCompanyCodiceAteco(company.getCodiceAteco()); + response.setEmailSendResponse(evaluationEntity.getEmailSendResponse()); return response; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java index cb83406e..b1166689 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java @@ -1,7 +1,6 @@ package net.gepafin.tendermanagement.dao; import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.GetObjectRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -13,12 +12,14 @@ import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.config.jwt.TokenProvider; import net.gepafin.tendermanagement.constants.AppointmentApiConstant; import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.entities.ApplicationAmendmentRequestEntity; import net.gepafin.tendermanagement.entities.ApplicationEntity; import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity; import net.gepafin.tendermanagement.entities.CompanyEntity; import net.gepafin.tendermanagement.entities.DocumentEntity; import net.gepafin.tendermanagement.entities.HubEntity; import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; +import net.gepafin.tendermanagement.enums.DocumentSourceTypeEnum; import net.gepafin.tendermanagement.enums.NotificationTypeEnum; import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; import net.gepafin.tendermanagement.model.request.AppointmentCreationRequest; @@ -61,12 +62,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -145,6 +141,15 @@ public class AppointmentDao { @Autowired private AmazonS3Service amazonS3Service; + @Autowired + private ApplicationDao applicationDao; + + @Autowired + private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; + + @Autowired + private ApplicationEvaluationDao applicationEvaluationDao; + private final Map executorMap = new ConcurrentHashMap<>(); private final ConcurrentHashMap threadForDocumentMap = new ConcurrentHashMap<>(); @@ -175,9 +180,182 @@ public class AppointmentDao { return ndgResponse; } - private HubEntity loginToOdessa(HubEntity hub, ApplicationEntity application) { + // private HubEntity loginToOdessa(HubEntity hub, ApplicationEntity application) { + // + // int maxRetries = 3; + // int attempt = 0; + // boolean success = false; + // while (attempt < maxRetries && !success) { + // attempt++; + // try { + // //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call + // String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); + // log.info("Got the auth for login to odessa {}", authJwtToken); + // hub.setAuthToken(authJwtToken); + // hubRepository.save(hub); + // Map body = Collections.emptyMap(); + // ResponseEntity responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body); + // if (responseLogin.getStatusCode() == HttpStatus.OK) { + // log.info("Login successful to odessa. Parsing response."); + // String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); + // AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); + // + // // Validate and save token + // if (parsedResponse.getTokenId() != null) { + // hub.setAppointmentAuthTokenId(parsedResponse.getTokenId()); + // hub.setAreaCode(parsedResponse.getAreaCode()); + // hubRepository.save(hub); + // log.info("Saved new authToken and areaCode for Hub."); + // success = true; + // return hub; + // } else { + // throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again."); + // } + // } + // throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); + // } catch (FeignException.Forbidden forbiddenException) { + // log.error("Failed to login to odessa due to some error"); + // + // // Extract raw response body + // String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response + // + // // Parse JSON to check for "PasswordExpired" + // try { + // ObjectMapper objectMapper = new ObjectMapper(); + // JsonNode rootNode = objectMapper.readTree(responseBody); + // JsonNode errorsNode = rootNode.path("errors"); + // + // if (errorsNode.isArray()) { + // for (JsonNode error : errorsNode) { + // // Check the main errorCode + // if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { + // application.setNdgStatus(GepafinConstant.NDG_FAILED); + // applicationRepository.save(application); + // throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + // } + // + // // Check inside "subErrors" + // JsonNode subErrorsNode = error.path("subErrors"); + // if (subErrorsNode.isArray()) { + // for (JsonNode subError : subErrorsNode) { + // if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) { + // application.setNdgStatus(GepafinConstant.NDG_FAILED); + // applicationRepository.save(application); + // throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + // } + // } + // } + // } + // } + // } catch (IOException e) { + // log.error("Error parsing JSON response: {}", e.getMessage()); + // } + // } catch (Exception e) { + // log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e); + // throw new RuntimeException("Authentication failed on Odessa. try again", e); + // } + // } + // return null; + // } + // + // + // private HubEntity authenticateAndSaveToken(HubEntity hub) { + // + // int maxRetries = 3; + // int attempt = 0; + // boolean success = false; + // while (attempt < maxRetries && !success) { + // attempt++; + // try { + // //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call + // String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); + // log.info("Got the auth for login to odessa {}", authJwtToken); + // hub.setAuthToken(authJwtToken); + // hubRepository.save(hub); + // // Prepare the request body (adjust if necessary for login API) + // Map body = Collections.emptyMap(); + // // Perform login API call + // ResponseEntity responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body); + // + // // Handle successful login + // if (responseLogin.getStatusCode() == HttpStatus.OK) { + // log.info("Login successful to odessa. Parsing response."); + // String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); + // AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); + // + // // Validate and save token + // if (parsedResponse.getTokenId() != null) { + // hub.setAppointmentAuthTokenId(parsedResponse.getTokenId()); + // hub.setAreaCode(parsedResponse.getAreaCode()); + // hubRepository.save(hub); + // + // log.info("Saved new authToken and areaCode for Hub."); + // success = true; + // return hub; + // } else { + // throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again."); + // } + // } + // // Handle non-OK response + // throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); + // } catch (FeignException.Forbidden forbiddenException) { + // log.error("Failed to login to odessa due to some error occurred."); + // + // // Extract raw response body + // String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response + // + // // Parse JSON to check for "PasswordExpired" + // try { + // ObjectMapper objectMapper = new ObjectMapper(); + // JsonNode rootNode = objectMapper.readTree(responseBody); + // JsonNode errorsNode = rootNode.path("errors"); + // + // if (errorsNode.isArray()) { + // for (JsonNode error : errorsNode) { + // // Check the main errorCode + // if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { + // throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + // } + // + // // Check inside "subErrors" + // JsonNode subErrorsNode = error.path("subErrors"); + // if (subErrorsNode.isArray()) { + // for (JsonNode subError : subErrorsNode) { + // if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) { + // throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + // } + // } + // } + // } + // } + // } catch (IOException e) { + // log.error("Error parsing JSON response: {}", e.getMessage()); + // } + // } catch (Exception e) { + // log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e); + // throw new RuntimeException("Authentication failed on Odessa. try again", e); + // } + // } + // return null; + // } + + private void loginToOdessa(HubEntity hub, ApplicationEntity application) { + + performOdessaLogin(hub, application); + } + + private HubEntity authenticateAndSaveToken(HubEntity hub, ApplicationEntity application) { + + return performOdessaLogin(hub, application); + } + + private HubEntity performOdessaLogin(HubEntity hub, ApplicationEntity application) { + + int maxRetries = 3; + int attempt = 0; + while (attempt < maxRetries) { + attempt++; try { - //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); log.info("Got the auth for login to odessa {}", authJwtToken); hub.setAuthToken(authJwtToken); @@ -189,7 +367,6 @@ public class AppointmentDao { String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); - // Validate and save token if (parsedResponse.getTokenId() != null) { hub.setAppointmentAuthTokenId(parsedResponse.getTokenId()); hub.setAreaCode(parsedResponse.getAreaCode()); @@ -201,53 +378,52 @@ public class AppointmentDao { } } throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); + } catch (FeignException.Forbidden forbiddenException) { + log.error("Failed to login to odessa due to forbidden error."); + + CheckPasswordExpiredOrErrorInResponse(application, forbiddenException); + } catch (Exception e) { + log.error("Failed to authenticate user on Odessa (Attempt {}): {}", attempt, e.getMessage(), e); } - catch (FeignException.Forbidden forbiddenException) { - logForbiddenError(); + } + throw new RuntimeException("Max retries exceeded. Failed to login to Odessa."); + } + private void CheckPasswordExpiredOrErrorInResponse(ApplicationEntity application, FeignException.Forbidden forbiddenException) { - // Extract raw response body - String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response + String responseBody = forbiddenException.contentUTF8(); - // Parse JSON to check for "PasswordExpired" - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(responseBody); - JsonNode errorsNode = rootNode.path("errors"); + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(responseBody); + JsonNode errorsNode = rootNode.path("errors"); - if (errorsNode.isArray()) { - for (JsonNode error : errorsNode) { - // Check the main errorCode - if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { - application.setNdgStatus(GepafinConstant.NDG_FAILED); - applicationRepository.save(application); - throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); - } + if (errorsNode.isArray()) { + for (JsonNode error : errorsNode) { + if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { + if (application != null) { + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + } + throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + } - // Check inside "subErrors" - JsonNode subErrorsNode = error.path("subErrors"); - if (subErrorsNode.isArray()) { - for (JsonNode subError : subErrorsNode) { - if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) { - application.setNdgStatus(GepafinConstant.NDG_FAILED); - applicationRepository.save(application); - throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); - } + JsonNode subErrorsNode = error.path("subErrors"); + if (subErrorsNode.isArray()) { + for (JsonNode subError : subErrorsNode) { + if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) { + if (application != null) { + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); } + throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); } } } - } catch (IOException e) { - log.error("Error parsing JSON response: {}", e.getMessage()); } - - // Regenerate the token and retry - loginToOdessa(hub, application); } - catch (Exception e) { - log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e); - throw new RuntimeException("Authentication failed on Odessa. try again", e); - } - return null; + } catch (IOException e) { + log.error("Error parsing JSON response: {}", e.getMessage()); + } } private void startAsyncNdgProcessing(Long applicationId) { @@ -296,7 +472,7 @@ public class AppointmentDao { try { // Authenticate and fetch token if required if (hub.getAppointmentAuthTokenId() == null || hub.getAreaCode() == null) { - authenticateAndSaveToken(hub); + authenticateAndSaveToken(hub, application); } String authorizationToken = getBearerToken(hub); @@ -343,9 +519,13 @@ public class AppointmentDao { applicationRepository.save(application); companyRepository.save(company); ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); - Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); +// Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); + + Map placeHolders = new HashMap<>(); + placeHolders.put("{{call_name}}", application.getCall().getName()); + placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION); - notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application, placeHolders, NotificationTypeEnum.NDG_GENERATION); log.info("NDG saved successfully for applicationId: {}", application.getId()); break; } @@ -393,9 +573,12 @@ public class AppointmentDao { companyRepository.save(company); applicationRepository.save(application); ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); - Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); +// Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); + Map placeHolders = new HashMap<>(); + placeHolders.put("{{call_name}}", application.getCall().getName()); + placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION); - notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application, placeHolders, NotificationTypeEnum.NDG_GENERATION); log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg()); } @@ -413,7 +596,7 @@ public class AppointmentDao { } catch (FeignException.Forbidden forbiddenException) { log.error("403 Forbidden received while getting visuraList for Ndg code. Regenerating token..."); // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub); + String newAuthorizationToken = regenerateTokenAndSave(hub, application); return getVisuraList(idVisura, newAuthorizationToken, application, hub); } catch (Exception e) { log.error("Failed to fetch Ndg code: {}", e.getMessage(), e); @@ -421,82 +604,6 @@ public class AppointmentDao { } } - private HubEntity authenticateAndSaveToken(HubEntity hub) { - - try { - //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call - String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); - log.info("Got the auth for login to odessa {}", authJwtToken); - hub.setAuthToken(authJwtToken); - hubRepository.save(hub); - // Prepare the request body (adjust if necessary for login API) - Map body = Collections.emptyMap(); - // Perform login API call - ResponseEntity responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body); - - // Handle successful login - if (responseLogin.getStatusCode() == HttpStatus.OK) { - log.info("Login successful to odessa. Parsing response."); - String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); - AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); - - // Validate and save token - if (parsedResponse.getTokenId() != null) { - hub.setAppointmentAuthTokenId(parsedResponse.getTokenId()); - hub.setAreaCode(parsedResponse.getAreaCode()); - hubRepository.save(hub); - - log.info("Saved new authToken and areaCode for Hub."); - return hub; - } else { - throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again."); - } - } - // Handle non-OK response - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); - } catch (FeignException.Forbidden forbiddenException) { - logForbiddenError(); - - // Extract raw response body - String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response - - // Parse JSON to check for "PasswordExpired" - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(responseBody); - JsonNode errorsNode = rootNode.path("errors"); - - if (errorsNode.isArray()) { - for (JsonNode error : errorsNode) { - // Check the main errorCode - if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { - throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); - } - - // Check inside "subErrors" - JsonNode subErrorsNode = error.path("subErrors"); - if (subErrorsNode.isArray()) { - for (JsonNode subError : subErrorsNode) { - if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) { - throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); - } - } - } - } - } - } catch (IOException e) { - log.error("Error parsing JSON response: {}", e.getMessage()); - } - - // Regenerate the token and retry - regenerateTokenAndSave(hub); - } catch (Exception e) { - log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e); - throw new RuntimeException("Authentication failed on Odessa. try again", e); - } - return null; - } - private AppointmentLoginResponse retrieveNdgByVatNumber(String vatNumber, String authorizationToken, HubEntity hub, ApplicationEntity application) { try { @@ -510,7 +617,7 @@ public class AppointmentDao { } catch (FeignException.Forbidden forbiddenException) { logForbiddenError(); // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub); + String newAuthorizationToken = regenerateTokenAndSave(hub, application); return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application); } catch (Exception e) { log.error("Failed to retrieve NDG by VAT number: {}", e.getMessage(), e); @@ -518,9 +625,10 @@ public class AppointmentDao { } } - private String regenerateTokenAndSave(HubEntity hub) { - hub = authenticateAndSaveToken(hub); - return "Bearer " + hub.getAppointmentAuthTokenId(); + private String regenerateTokenAndSave(HubEntity hub, ApplicationEntity application) { + + hub = authenticateAndSaveToken(hub, application); + return "Bearer " + hub.getAppointmentAuthTokenId(); } private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) { @@ -533,7 +641,7 @@ public class AppointmentDao { } catch (FeignException.Forbidden forbiddenException) { logForbiddenError(); // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub); + String newAuthorizationToken = regenerateTokenAndSave(hub, null); return createVisura(company, newAuthorizationToken, hub); } catch (Exception e) { log.error("Failed to create Visura for Ndg : {}", e.getMessage()); @@ -703,9 +811,8 @@ public class AppointmentDao { throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION)); } - hub = authenticateAndSaveToken(hub); // Generate authorization token and fetch template data - String authorizationToken = getBearerToken(hub); + String authorizationToken = regenerateTokenAndSave(hub, application); Long appointmentTemplateId = application.getCall().getAppointmentTemplateId(); if (appointmentTemplateId == null) { throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_CANNOT_BE_CREATED)); @@ -749,7 +856,7 @@ public class AppointmentDao { } catch (FeignException.Forbidden forbiddenException) { log.error("403 Forbidden received while retrieving template. Regenerating token..."); - regenerateTokenAndSave(hub); + regenerateTokenAndSave(hub, application); return createAppointment(applicationId, createAppointmentRequest); } } @@ -758,12 +865,31 @@ public class AppointmentDao { if (appointmentResponse.getBody() != null) { log.info("Appointment API Response : {}", appointmentResponse.getBody()); - Map responseBody = (Map) appointmentResponse.getBody(); - if (responseBody.containsKey(GepafinConstant.DATA_STRING)) { - Map data = (Map) responseBody.get(GepafinConstant.DATA_STRING); - if (data != null && data.containsKey(GepafinConstant.ID_STRING)) { - return data.get(GepafinConstant.ID_STRING).toString(); + try { + Map responseBody = (Map) appointmentResponse.getBody(); + // 1. Try to get appointment ID from data.id + if (responseBody.containsKey(GepafinConstant.DATA_STRING)) { + Map data = (Map) responseBody.get(GepafinConstant.DATA_STRING); + if (data != null && data.containsKey(GepafinConstant.ID_STRING)) { + return data.get(GepafinConstant.ID_STRING).toString(); + } } + // 2. If ID not present, check errors[0].cause.errorDescription + if (responseBody.containsKey(GepafinConstant.ERROR_STRING)) { + List> errors = (List>) responseBody.get(GepafinConstant.ERROR_STRING); + if (errors != null && !errors.isEmpty()) { + Map firstError = errors.get(0); + if (firstError.containsKey(GepafinConstant.CAUSE_STRING)) { + Map cause = (Map) firstError.get(GepafinConstant.CAUSE_STRING); + if (cause != null && cause.containsKey(GepafinConstant.ERROR_DESCRIPTION_STRING)) { + String errorDescription = cause.get(GepafinConstant.ERROR_DESCRIPTION_STRING).toString(); + log.warn("Appointment creation failed: {}", errorDescription); + } + } + } + } + } catch (Exception e) { + log.error("Error while extracting appointment ID or parsing error message", e); } } return null; @@ -793,20 +919,20 @@ public class AppointmentDao { JsonNode prodottoNode = richiestaNode.path(AppointmentApiConstant.PRODOTTO); String prodottoCode = prodottoNode.path(AppointmentApiConstant.PRODOTTO_CODE).asText(); - richiestaCliente.setCodProdotto(prodottoCode); - richiestaCliente.setIdMotivazione(getIntValue(richiestaNode)); - richiestaCliente.setCodAbi(getTextValue(richiestaNode, AppointmentApiConstant.COD_ABI)); - richiestaCliente.setCodCab(getTextValue(richiestaNode, AppointmentApiConstant.COD_CAB)); - richiestaCliente.setIdNota(getTextValue(richiestaNode, AppointmentApiConstant.ID_NOTA)); - richiestaCliente.setImportoAgevolato(getTextValue(richiestaNode, AppointmentApiConstant.IMPORTO_AGEVOLATO)); - richiestaCliente.setImportoMedioLungoTermine(getTextValue(richiestaNode, AppointmentApiConstant.IMPORTO_MEDIOLUNGO_TERMINE)); - richiestaCliente.setCodTipoProdotto(getTextValue(richiestaNode, AppointmentApiConstant.COD_TIPO_PRODOTTO)); - richiestaCliente.setCodCategoriaProdotto(getTextValue(richiestaNode, AppointmentApiConstant.COD_CATEGORIA_PRODOTTO)); - richiestaCliente.setCodFormaTecnica(getTextValue(richiestaNode, AppointmentApiConstant.COD_FORMATECNICA)); - richiestaCliente.setCodOperazione(getTextValue(richiestaNode, AppointmentApiConstant.COD_OPERAZIONE)); + richiestaCliente.setCodProdotto(prodottoCode); + richiestaCliente.setIdMotivazione(getIntValue(richiestaNode)); + richiestaCliente.setCodAbi(getTextValue(richiestaNode, AppointmentApiConstant.COD_ABI)); + richiestaCliente.setCodCab(getTextValue(richiestaNode, AppointmentApiConstant.COD_CAB)); + richiestaCliente.setIdNota(getTextValue(richiestaNode, AppointmentApiConstant.ID_NOTA)); + richiestaCliente.setImportoAgevolato(getTextValue(richiestaNode, AppointmentApiConstant.IMPORTO_AGEVOLATO)); + richiestaCliente.setImportoMedioLungoTermine(getTextValue(richiestaNode, AppointmentApiConstant.IMPORTO_MEDIOLUNGO_TERMINE)); + richiestaCliente.setCodTipoProdotto(getTextValue(richiestaNode, AppointmentApiConstant.COD_TIPO_PRODOTTO)); + richiestaCliente.setCodCategoriaProdotto(getTextValue(richiestaNode, AppointmentApiConstant.COD_CATEGORIA_PRODOTTO)); + richiestaCliente.setCodFormaTecnica(getTextValue(richiestaNode, AppointmentApiConstant.COD_FORMATECNICA)); + richiestaCliente.setCodOperazione(getTextValue(richiestaNode, AppointmentApiConstant.COD_OPERAZIONE)); - richiestaClienteList.add(richiestaCliente); - } + richiestaClienteList.add(richiestaCliente); + } input.setRichiestaCliente(richiestaClienteList); appointmentCreationRequest.setInput(input); @@ -867,13 +993,40 @@ public class AppointmentDao { // Check if the document is already being processed DocumentEntity systemDoc = documentDao.validateDocument(documentId); + ApplicationEntity application = null; + + if (systemDoc != null) { + DocumentSourceTypeEnum sourceType = DocumentSourceTypeEnum.valueOf(systemDoc.getSource()); + + switch (sourceType) { + case APPLICATION: + application = applicationDao.validateApplication(systemDoc.getSourceId()); + break; + case AMENDMENT: + ApplicationAmendmentRequestEntity applicationAmendmentEntity = applicationAmendmentRequestDao.validateApplicationAmendmentRequest(systemDoc.getSourceId()); + application = applicationDao.validateApplication(applicationAmendmentEntity.getApplicationId()); + break; + case EVALUATION: + ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationDao.validateApplicationEvaluation(systemDoc.getSourceId()); + application = applicationDao.validateApplication(applicationEvaluationEntity.getApplicationId()); + break; + + case CALL: + break; + + default: + log.warn("Unhandled document source type: {}", sourceType); + break; + } + } + Claims claims = tokenProvider.getClaimsFromToken(tokenProvider.extractTokenFromRequest(request)); Long hubId = Utils.extractHubIdFromPayload(claims.getSubject()); // Authenticate the hub before proceeding HubEntity hub = hubRepository.findByHubId(hubId); - authenticateAndSaveToken(hub); - if (systemDoc.getDocumentAttachmentId() != null) { + authenticateAndSaveToken(hub, application); + if (systemDoc != null && systemDoc.getDocumentAttachmentId() != null) { // If the documentAttachmentId is already set, return the response log.info("Document already uploaded with documentAttachmentId: {}", systemDoc.getDocumentAttachmentId()); DocumentUploadResponse response = new DocumentUploadResponse(); @@ -893,11 +1046,12 @@ public class AppointmentDao { }); threadForDocumentMap.put(documentId, executor); + ApplicationEntity finalApplication = application; executor.submit(() -> { threadLocalHubId.set(hubId); try { log.info("Starting async document upload for documentId: {}", documentId); - uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest); + uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest, finalApplication); } catch (Exception e) { log.error("Error in async document upload for documentId: {}", documentId, e); } finally { @@ -913,7 +1067,7 @@ public class AppointmentDao { return null; } - private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { + private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, ApplicationEntity application) { // Synchronous upload logic DocumentEntity systemDoc = documentDao.validateDocument(documentId); @@ -954,8 +1108,8 @@ public class AppointmentDao { log.info("Document uploaded successfully to external system: {}", parsedResponse); } catch (FeignException.Forbidden forbiddenException) { log.error("403 Forbidden received while uploading document. Regenerating token..."); - regenerateTokenAndSave(hub); - uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest); + regenerateTokenAndSave(hub, application); + uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest, application); } catch (Exception e) { log.error("Exception during document upload: {}", e.getMessage(), e); throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.EXTERNAL_DOCUMENT_UPLOAD_FAILURE_MSG)); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java index 30ebb149..7339a7c5 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java @@ -473,6 +473,7 @@ public class AssignedApplicationsDao { response.setCompanyName(view.getCompanyName()); response.setCreatedDate(view.getCreatedDate()); response.setUpdatedDate(view.getUpdatedDate()); + response.setEmailSendResponse(view.getEmailSendResponse()); return response; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java b/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java index fa02eac1..3cb86a50 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java @@ -986,7 +986,7 @@ public class CallDao { return callEntity; } - public PageableResponseBean> getAllCallsByPagination(HttpServletRequest request,UserEntity user,Long companyId , Boolean onlyPreferredCall, CallPageableRequestBean callPageableRequestBean) { + public PageableResponseBean> getAllCallsByPagination(HttpServletRequest request,UserEntity user,Long companyId , Boolean onlyPreferredCall, Boolean onlyConfidiCall, CallPageableRequestBean callPageableRequestBean) { Integer pageNo = null; Integer pageLimit = null; if (callPageableRequestBean.getGlobalFilters() != null) { @@ -1006,7 +1006,7 @@ public class CallDao { ); } expirePublishedCalls(request); - Specification spec = search(request,user, callPageableRequestBean); + Specification spec = search(request,user, callPageableRequestBean,onlyConfidiCall); Page entityPage; if (Boolean.TRUE.equals(onlyPreferredCall)) { validator.validateUserWithCompany(request, companyId); @@ -1056,10 +1056,10 @@ public class CallDao { return pageableResponseBean; } - public Specification search(HttpServletRequest request,UserEntity userEntity, CallPageableRequestBean callPageableRequestBean) { + public Specification search(HttpServletRequest request,UserEntity userEntity, CallPageableRequestBean callPageableRequestBean,Boolean onlyConfidiCall) { return (root, query, criteriaBuilder) -> { - List predicates = getPredicates(request,callPageableRequestBean, criteriaBuilder, root, userEntity); + List predicates = getPredicates(request,callPageableRequestBean, criteriaBuilder, root, userEntity,onlyConfidiCall); SortBy sortBy = new SortBy(GepafinConstant.CREATED_DATE, true); if (callPageableRequestBean.getGlobalFilters() != null @@ -1083,7 +1083,7 @@ public class CallDao { private List getPredicates(HttpServletRequest request,CallPageableRequestBean callPageableRequestBean, - CriteriaBuilder criteriaBuilder, Root root, UserEntity userEntity) { + CriteriaBuilder criteriaBuilder, Root root, UserEntity userEntity,Boolean onlyConfidiCall) { Integer year = null; String search = null; Map filters = new HashMap<>(); @@ -1137,7 +1137,7 @@ public class CallDao { predicates.add(root.get(GepafinConstant.STATUS).in(statusValues)); } applyFilters(root, criteriaBuilder, predicates, filters); - Boolean isConfidi = callPageableRequestBean.getConfidi(); + Boolean isConfidi =onlyConfidiCall; if (validator.checkIsConfidi()) { diff --git a/src/main/java/net/gepafin/tendermanagement/dao/EmailDao.java b/src/main/java/net/gepafin/tendermanagement/dao/EmailDao.java new file mode 100644 index 00000000..244df6a4 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/dao/EmailDao.java @@ -0,0 +1,212 @@ +package net.gepafin.tendermanagement.dao; + + +import jakarta.servlet.http.HttpServletRequest; +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.EmailScenarioTypeEnum; +import net.gepafin.tendermanagement.enums.EmailServiceTypeEnum; +import net.gepafin.tendermanagement.enums.RecipientTypeEnum; +import net.gepafin.tendermanagement.enums.StatusTypeEnum; +import net.gepafin.tendermanagement.model.request.EmailLogRequest; +import net.gepafin.tendermanagement.model.response.EmailResendResponseBean; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; +import net.gepafin.tendermanagement.repositories.*; +import net.gepafin.tendermanagement.service.CallService; +import net.gepafin.tendermanagement.util.Utils; +import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; +import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException; +import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@Log4j2 +public class EmailDao { + + @Autowired + EmailLogRepository emailLogRepository; + + @Autowired + EmailNotificationDao emailNotificationDao; + + @Autowired + private CallService callService; + + @Autowired + private EmailLogDao emailLogDao; + + @Autowired + private UserActionsRepository userActionsRepository; + + @Autowired + private ApplicationAmendmentRequestRepository applicationAmendmentRequestRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private ApplicationEvaluationRepository applicationEvaluationRepository; + + public EmailResendResponseBean resendEmail(HttpServletRequest request , Long userActionId){ + UserActionEntity userActionEntity = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId); + if(userActionEntity == null){ + throw new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_ACTION_ID_NOT_FOUND)); + } + List emailLogs = emailLogRepository.findByUserActionIdAndEmailServiceTypeAndSendStatus(userActionId,EmailServiceTypeEnum.PEC_SERVICE.getValue(),StatusTypeEnum.FAILED.getValue()); + + if (emailLogs.isEmpty()) { + log.info("No emails found for given userActionId: {}",userActionId); + throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.NO_EMAIL_LOG_FOUND)); + } + EmailResendResponseBean emailResendResponseBean = new EmailResendResponseBean(); + for (EmailLogEntity log : emailLogs){ + EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(EmailScenarioTypeEnum.valueOf(log.getEmailType()), + RecipientTypeEnum.valueOf(log.getRecipientType()), + log.getRecipientId(), + log.getRecipientEmails(), + log.getUserId(), + log.getApplicationId(), + log.getAmendmentId(), + log.getCallId() + ); + + List recipients = Utils.commaSeparatedStringToList(log.getRecipientEmails()); + CallEntity call = callService.validateCall(log.getCallId()); + emailNotificationDao.sendMail( + call.getHub().getId(), + log.getEmailSubject(), + log.getEmailBody(), + recipients, + emailLogRequest + ); + } + EmailSendResponse emailSendResponse = buildEmailSendResponseFromRequest(request); + emailResendResponseBean.setEmailSendResponse(emailSendResponse); + + if (Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())){ + updateEmailSendStatusIfSuccessful(emailSendResponse); + } + return emailResendResponseBean; + } + + private void updateEmailSendStatusIfSuccessful(EmailSendResponse emailSendResponse){ + Long actionId = emailSendResponse.getUserActionId(); + + EmailLogEntity emailLog = emailLogRepository.findTopByUserActionIdAndEmailServiceTypeAndSendStatusOrderByIdDesc( + actionId, + EmailServiceTypeEnum.PEC_SERVICE.getValue(), + StatusTypeEnum.SUCCESS.getValue() + ); + if (emailLog != null) { + switch (emailSendResponse.getEmailScenario()) { + case APPLICATION_AMENDMENT_REQUESTED: + case APPLICATION_AMENDMENT_REMINDER: + updateApplicationAmendmentStatus(emailLog, emailSendResponse.getEmailScenario().getValue()); + break; + + case USER_CREATION: + case PASSWORD_RESET_REQUEST: + updateUserEmailStatus(emailLog, emailSendResponse.getEmailScenario().getValue()); + break; + + case APPLICATION_ADMISSIBLE: + case APPLICATION_REJECTED: + updateApplicationEvaluationStatus(emailLog, emailSendResponse.getEmailScenario().getValue()); + break; + + default: + log.warn("Unhandled email scenario: {}", emailSendResponse.getEmailScenario()); + } + } + } + + private void updateApplicationAmendmentStatus(EmailLogEntity log, String scenario) { + if (log.getAmendmentId() != null) { + applicationAmendmentRequestRepository.findById(log.getAmendmentId()).ifPresent(amendment -> { + if (updateEmailSendResponse(amendment.getEmailSendResponse(), scenario)) { + applicationAmendmentRequestRepository.save(amendment); + } + }); + } + } + + private void updateApplicationEvaluationStatus(EmailLogEntity log, String scenario) { + if (log.getApplicationId() != null) { + ApplicationEvaluationEntity evaluation = applicationEvaluationRepository.findByApplicationId(log.getApplicationId()); + if (evaluation != null && updateEmailSendResponse(evaluation.getEmailSendResponse(), scenario)) { + applicationEvaluationRepository.save(evaluation); + } + } + } + + private void updateUserEmailStatus(EmailLogEntity log, String scenario) { + if (log.getUserId() != null) { + userRepository.findById(log.getUserId()).ifPresent(user -> { + if (updateEmailSendResponse(user.getEmailSendResponse(), scenario)) { + userRepository.save(user); + } + }); + } + } + + + + + private boolean updateEmailSendResponse(List responses, String scenario) { + if (responses == null || responses.isEmpty()) return false; + + for (Iterator iterator = responses.iterator(); iterator.hasNext(); ) { + EmailSendResponse response = iterator.next(); + if (scenario.equals(response.getEmailScenario().getValue())) { + iterator.remove(); // remove only the first match + return true; + } + } + return false; + } + + + + + public EmailSendResponse buildEmailSendResponseFromRequest(HttpServletRequest request) { + Long userActionId = (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID); + List emailLogs = emailLogRepository.findByUserActionIdAndEmailServiceType(userActionId,EmailServiceTypeEnum.PEC_SERVICE.getValue()); + + boolean allSuccess = true; + String emailScenario = null; + + for (EmailLogEntity log : emailLogs) { + + if (emailScenario == null) { + emailScenario = log.getEmailType(); + } + boolean isSuccess = StatusTypeEnum.SUCCESS.getValue().equals(log.getSendStatus()); + if (Boolean.FALSE.equals(isSuccess)) { + allSuccess = false; + break; + } + } + + return buildResponse(userActionId, allSuccess, emailScenario); + } + + private EmailSendResponse buildResponse(Long userActionId, boolean allSuccess, String emailScenario) { + EmailSendResponse response = new EmailSendResponse(); + response.setUserActionId(userActionId); + response.setIsEmailSend(allSuccess); + response.setEmailScenario(emailScenario != null ? EmailScenarioTypeEnum.valueOf(emailScenario) : null); + return response; + } + + + + + + +} diff --git a/src/main/java/net/gepafin/tendermanagement/dao/EmailLogDao.java b/src/main/java/net/gepafin/tendermanagement/dao/EmailLogDao.java index d74a6b3f..d95978fd 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/EmailLogDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/EmailLogDao.java @@ -1,6 +1,7 @@ package net.gepafin.tendermanagement.dao; import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.EmailLogEntity; import net.gepafin.tendermanagement.enums.EmailScenarioTypeEnum; import net.gepafin.tendermanagement.enums.EmailEntityTypeEnum; @@ -9,6 +10,7 @@ import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; import net.gepafin.tendermanagement.model.request.EmailLogRequest; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.repositories.EmailLogRepository; +import net.gepafin.tendermanagement.repositories.UserActionsRepository; import net.gepafin.tendermanagement.util.DateTimeUtil; import net.gepafin.tendermanagement.util.LoggingUtil; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +24,12 @@ public class EmailLogDao { @Autowired private EmailLogRepository emailLogRepository; + @Autowired + private HttpServletRequest request; + + @Autowired + private LoggingUtil loggingUtil; + public EmailLogEntity createEmailLog(EmailLogRequest emailLogRequest) { @@ -42,8 +50,8 @@ public class EmailLogDao { emailLogEntity.setApplicationId(emailLogRequest.getApplicatioId()); emailLogEntity.setAmendmentId(emailLogRequest.getAmendmentId()); emailLogEntity.setCallId(emailLogRequest.getCallId()); + emailLogEntity.setUserAction(loggingUtil.getUserActionLogById(emailLogRequest.getUserActionId())); emailLogEntity = saveEmailLogEntity(emailLogEntity); - return emailLogEntity; } public EmailLogEntity saveEmailLogEntity(EmailLogEntity emailLogEntity){ @@ -52,6 +60,7 @@ public class EmailLogDao { public EmailLogRequest createEmailLogRequest(EmailScenarioTypeEnum emailType, RecipientTypeEnum recipientType, Long recipientId, String recipientEmails, Long userId,Long applicationId,Long amendmentId,Long callId) { EmailLogRequest emailLogRequest = new EmailLogRequest(); + Long userActionId =(Long) request.getAttribute(GepafinConstant.USER_ACTION_ID); emailLogRequest.setEmailType(emailType); emailLogRequest.setRecipientType(recipientType); emailLogRequest.setRecipientId(recipientId); @@ -60,6 +69,7 @@ public class EmailLogDao { emailLogRequest.setApplicatioId(applicationId); emailLogRequest.setAmendmentId(amendmentId); emailLogRequest.setCallId(callId); + emailLogRequest.setUserActionId(userActionId); return emailLogRequest; } } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java index 171ccbfa..3772ad71 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java @@ -139,7 +139,7 @@ public class EmailNotificationDao { } } } - if (rinaldoEmail != null) { + if (GepafinConstant.RINALDO_EMAIL.equals(rinaldoEmail)) { EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(systemEmailTemplateResponse.getEmailScenario(), RecipientTypeEnum.PROPERTIES,null , rinaldoEmail, userEntity.getId(), applicationEntity.getId(), amendmentId, applicationEntity.getCall().getId()); @@ -283,12 +283,11 @@ public class EmailNotificationDao { if (recipientEmails.stream().anyMatch(email -> email.equals(GepafinConstant.RINALDO_EMAIL))) { emailConfig.setEmailServiceType(EmailServiceTypeEnum.SYSTEM_EMAIL_SERVICE.getValue()); EmailService emailService = emailServiceFactory.getEmailService(emailConfig.getEmailServiceType()); - emailService.sendEmail(subject, body, recipientEmails, emailConfig, emailLogRequest); + emailService.sendEmail(subject, body, recipientEmails, emailConfig, emailLogRequest); } else { emailConfig = retrieveEmailConfig(hubId); EmailService emailService = emailServiceFactory.getEmailService(emailConfig.getEmailServiceType()); - emailService.sendEmail(subject, body, recipientEmails, emailConfig, emailLogRequest); - + emailService.sendEmail(subject, body, recipientEmails, emailConfig, emailLogRequest); } } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java index 37ae9b44..0403fde3 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java @@ -15,6 +15,7 @@ import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.JWTToken; import net.gepafin.tendermanagement.model.util.SortBy; import net.gepafin.tendermanagement.repositories.BeneficiaryRepository; +import net.gepafin.tendermanagement.repositories.EmailLogRepository; import net.gepafin.tendermanagement.repositories.UserRepository; import net.gepafin.tendermanagement.service.HubService; import net.gepafin.tendermanagement.service.RoleService; @@ -42,6 +43,7 @@ import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; import static net.gepafin.tendermanagement.util.Utils.setIfUpdated; @@ -109,9 +111,15 @@ public class UserDao { @Autowired private EmailNotificationDao emailNotificationDao; + @Autowired + private EmailLogRepository emailLogRepository; + @Value("${fe.base.url}") private String feBaseUrl; + @Autowired + EmailDao emailDao; + public JWTToken createUser(HttpServletRequest request, String tempToken, UserReq userReq) { if (StringUtils.isEmpty(userReq.getHubUuid())) { @@ -134,7 +142,6 @@ public class UserDao { authenticationService.createSuccessLoginAttempt(loginAttemptEntity); } - JWTToken token = authService.getJWTTokenBean(userEntity, Boolean.TRUE, loginAttemptEntity.getId()); /** This code is responsible for adding a version history log for the "Create beneficiary" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).newData(beneficiary).build()); @@ -142,12 +149,40 @@ public class UserDao { /** This code is responsible for adding a version history log for the "Create user" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).newData(userEntity).build()); + List responses = new ArrayList<>(); if(Boolean.FALSE.equals(roleEntity.getRoleType().equals(RoleStatusEnum.ROLE_BENEFICIARY.getValue()))){ sendEmailToOnboardingUser(userEntity, userReq ); + boolean isEmailSendSuccess = isEmailSentSuccessfully(userEntity.getId()); + EmailSendResponse emailSendResponse = new EmailSendResponse(); + if (Boolean.FALSE.equals(isEmailSendSuccess)){ + emailSendResponse.setIsEmailSend(false); + Long userActionId =(Long)request.getAttribute(GepafinConstant.USER_ACTION_ID); + emailSendResponse.setUserActionId(userActionId); + emailSendResponse.setEmailScenario(EmailScenarioTypeEnum.USER_CREATION); + + saveEmailSendResponseToUser(emailSendResponse,userEntity); + responses = List.of(emailSendResponse); + } + else{ + responses = Collections.emptyList(); + } } + JWTToken token = authService.getJWTTokenBean(userEntity, Boolean.TRUE, loginAttemptEntity.getId(),responses); return token; } - public void sendEmailToOnboardingUser(UserEntity userEntity,UserReq userReq){ + + public boolean isEmailSentSuccessfully(Long userId) { + Optional latestLogOpt = emailLogRepository + .findTopByUserIdAndEmailTypeAndIsDeletedFalseOrderByCreatedDateDesc(userId, EmailScenarioTypeEnum.USER_CREATION.getValue()); + + return latestLogOpt + .map(log -> StatusTypeEnum.SUCCESS.getValue().equals(log.getSendStatus())) + .orElse(false); + } + + + + public void sendEmailToOnboardingUser(UserEntity userEntity,UserReq userReq){ SystemEmailTemplateResponse emailTemplate; RoleStatusEnum roleStatus = RoleStatusEnum.valueOf(userEntity.getRoleEntity().getRoleType()); @@ -382,6 +417,7 @@ public class UserDao { RoleResponseBean roleResponseBean = roleDao.convertRoleEntityToRoleResponse(userEntity.getRoleEntity()); userResponseBean.setRole(roleResponseBean); userResponseBean.setLastLogin(userEntity.getLastLogin()); + userResponseBean.setEmailSendResponse(userEntity.getEmailSendResponse()); List companyResponseBeans = companyDao.getCompanyByUserId(userEntity.getId()); userResponseBean.setCompanies(companyResponseBeans); if (userEntity.getBeneficiary() == null) { @@ -459,7 +495,7 @@ public class UserDao { return user; } - public void initiatePasswordReset(InitiatePasswordResetReq resetReq) { + public InitiatePasswordResetResponse initiatePasswordReset(InitiatePasswordResetReq resetReq) { UserEntity user = userRepository.findUserExcludingRoleType( resetReq.getEmail(), resetReq.getHubUuid(), @@ -478,9 +514,28 @@ public class UserDao { loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldUserEntity).newData(user).build()); log.info("Password reset token generated for user: {}", resetReq.getEmail()); - sendResetPasswordTokenEmail(user, token); + InitiatePasswordResetResponse initiatePasswordResetResponse = new InitiatePasswordResetResponse(); + EmailSendResponse emailSendResponse = emailDao.buildEmailSendResponseFromRequest(request); + List responses = List.of(emailSendResponse); + if (!Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())){ + initiatePasswordResetResponse.setEmailSendResponse(responses); + saveEmailSendResponseToUser(emailSendResponse,user); + } + else{ + initiatePasswordResetResponse.setEmailSendResponse(Collections.emptyList()); + } + return initiatePasswordResetResponse; } + + private void saveEmailSendResponseToUser(EmailSendResponse newResponse, UserEntity user) { + List mergedResponses = Utils.mergeEmailSendResponses( + user.getEmailSendResponse(), newResponse + ); + user.setEmailSendResponse(mergedResponses); + userRepository.save(user); + } + public void sendResetPasswordTokenEmail(UserEntity user, String token) { SystemEmailTemplateResponse emailTemplate = systemEmailTemplatesService.retrieveTemplateByTypeAndCall( diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java index 840ad050..eb71f5c1 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java @@ -2,7 +2,10 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; + import java.time.LocalDateTime; +import java.util.List; @Entity @Table(name="application_amendment_request") @@ -53,7 +56,10 @@ public class ApplicationAmendmentRequestEntity extends BaseEntity { @Column(name = "amendment_document") private String amendmentDocument; - @Column(name = "CLOSING_DATE") private LocalDateTime closingDate; + + @Convert(converter = EmailSendResponseConverter.class) + @Column(name = "EMAIL_SEND_RESPONSE", columnDefinition = "TEXT") + private List emailSendResponse; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestView.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestView.java index b5ee0ecc..c458af08 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestView.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestView.java @@ -3,9 +3,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import net.gepafin.tendermanagement.model.BaseBean; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; import org.hibernate.annotations.Immutable; import java.time.LocalDateTime; +import java.util.List; @Entity @Immutable @@ -13,12 +16,12 @@ import java.time.LocalDateTime; @Getter @Setter @IdClass(ApplicationAmendmentRequestViewId.class) -public class ApplicationAmendmentRequestView { +public class ApplicationAmendmentRequestView extends BaseEntity { - @Id - @Column(name = "ID") - private Long id; +// @Id +// @Column(name = "ID") +// private Long id; @Column(name = "APPLICATION_ID") private Long applicationId; @@ -56,12 +59,17 @@ public class ApplicationAmendmentRequestView { @Column(name = "APPLICATION_USER_ID") private Long applicationUserId; - @Column(name = "CREATED_DATE") - private String createdDate; - - @Column(name = "UPDATED_DATE") - private String updatedDate; +// @Column(name = "CREATED_DATE") +// private String createdDate; +// +// @Column(name = "UPDATED_DATE") +// private String updatedDate; @Column(name = "IS_DELETED") private Boolean isDeleted; + + @Convert(converter = EmailSendResponseConverter.class) + @Column(name = "EMAIL_SEND_RESPONSE", columnDefinition = "TEXT") + private List emailSendResponse; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java index 045a1bdb..b8b00d3b 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java @@ -2,8 +2,10 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; import java.time.LocalDateTime; +import java.util.List; @Data @Entity @@ -71,4 +73,7 @@ public class ApplicationEvaluationEntity extends BaseEntity{ @Column(name = "evaluationVersion") private String evaluationVersion; + @Convert(converter = EmailSendResponseConverter.class) + @Column(name = "EMAIL_SEND_RESPONSE", columnDefinition = "TEXT") + private List emailSendResponse; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java index 75d396a9..46bf004b 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java @@ -2,9 +2,11 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; import org.hibernate.annotations.Immutable; import java.time.LocalDateTime; +import java.util.List; @Entity @Immutable @@ -59,6 +61,10 @@ public class AssignedApplicationsView{ @Column(name = "IS_DELETED") private Boolean isDeleted; + @Convert(converter = EmailSendResponseConverter.class) + @Column(name = "EMAIL_SEND_RESPONSE", columnDefinition = "TEXT") + private List emailSendResponse; + @Column(name = "HUB_ID") private Long hubId; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java index 03ad4c2b..1a582a21 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java @@ -56,5 +56,9 @@ public class EmailLogEntity extends BaseEntity{ @Column(name = "is_deleted") private Boolean isDeleted; + + @ManyToOne + @JoinColumn(name = "user_action_id") + private UserActionEntity userAction; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/EmailSendResponseConverter.java b/src/main/java/net/gepafin/tendermanagement/entities/EmailSendResponseConverter.java new file mode 100644 index 00000000..43409886 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/entities/EmailSendResponseConverter.java @@ -0,0 +1,43 @@ +package net.gepafin.tendermanagement.entities; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; + +import java.io.IOException; +import java.util.List; + +@Converter +public class EmailSendResponseConverter implements AttributeConverter, String> { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public String convertToDatabaseColumn(List attribute) { + try { + if (attribute == null) { + attribute = List.of(); + } + return objectMapper.writeValueAsString(attribute); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Error converting list to JSON", e); + } + } + + @Override + public List convertToEntityAttribute(String dbData) { + if (dbData == null || dbData.isBlank()) { + return List.of(); // or null if you prefer + } + try { + return objectMapper.readValue(dbData, new TypeReference>() {}); + } catch (IOException e) { + throw new IllegalArgumentException("Error reading JSON from database", e); + } + } + +} + diff --git a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java index b2c3cfb7..92d97e9f 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java @@ -55,7 +55,8 @@ public class SystemEmailTemplatesEntity extends BaseEntity { USER_ONBOARDING_CONFIDI("USER_ONBOARDING_CONFIDI"), USER_ONBOARDING_BANDI("USER_ONBOARDING_BANDI"), PASSWORD_RESET("PASSWORD_RESET"), - INADMISSIBILITY_TEMPLATE("INADMISSIBILITY_NOTIFICATION"); + INADMISSIBILITY_TEMPLATE("INADMISSIBILITY_NOTIFICATION"), + APPLICATION_SUBMISSION_FAILURE_NOTIFICATION("APPLICATION_SUBMISSION_FAILURE_NOTIFICATION"); private String value; SystemEmailTemplatesEntityTypeEnum(String value) { diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java index 606f52d9..86147cdf 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java @@ -7,8 +7,10 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; import java.time.LocalDateTime; +import java.util.List; @Entity @Table(name = "GEPAFIN_USER") @@ -69,4 +71,9 @@ public class UserEntity extends BaseEntity { @ManyToOne @JoinColumn(name = "HUB_ID") private HubEntity hub; + + @Convert(converter = EmailSendResponseConverter.class) + @Column(name = "EMAIL_SEND_RESPONSE", columnDefinition = "TEXT") + private List emailSendResponse; + } diff --git a/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java index 479912d7..908e554d 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java @@ -11,7 +11,8 @@ public enum EmailScenarioTypeEnum { APPLICATION_ADMISSIBLE("APPLICATION_ADMISSIBLE"), USER_CREATION("USER_CREATION"), PASSWORD_RESET_REQUEST("PASSWORD_RESET_REQUEST"), - APPLICATION_REJECTED("APPLICATION_REJECTED"); + APPLICATION_REJECTED("APPLICATION_REJECTED"), + APPLICATION_SUBMISSION_FAILURE("APPLICATION_SUBMISSION_FAILURE"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java index 8957c03b..4430c2d6 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java @@ -14,7 +14,8 @@ public enum NotificationTypeEnum { EVALUATION_EXPIRED("EVALUATION_EXPIRED"), AMENDMENT_EXPIRATION_REMINDER("AMENDMENT_EXPIRATION_REMINDER"), EVALUATION_EXPIRATION_REMINDER("EVALUATION_EXPIRATION_REMINDER"), - COMPANY_DOCUMENT_EXPIRATION_REMINDER("COMPANY_DOCUMENT_EXPIRATION_REMINDER"); + COMPANY_DOCUMENT_EXPIRATION_REMINDER("COMPANY_DOCUMENT_EXPIRATION_REMINDER"), + PEC_EMAIL_SENDING_FAILURE("PEC_EMAIL_SENDING_FAILURE"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java index 7ee87e08..fe2e4cd1 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java @@ -216,7 +216,9 @@ public enum UserActionContextEnum { GET_ALL_USER_ACTION_BY_PAGINATION("GET_ALL_USER_ACTION_BY_PAGINATION"), GET_ALL_USER_BY_PAGINATION("GET_ALL_USER_BY_PAGINATION"), UPDATE_CALL_END_DATE_AND_TIME("UPDATE_CALL_END_DATE_AND_TIME"), - UPDATE_EXPIRED_CALL("UPDATE_EXPIRED_CALL") ; + UPDATE_EXPIRED_CALL("UPDATE_EXPIRED_CALL"), + RESEND_EMAIL("RESEND_EMAIL"), + SEND_REMINDER_EMAIL("SEND_REMINDER_EMAIL"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/UserActionLogsEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/UserActionLogsEnum.java index 555d3298..11aecdbc 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/UserActionLogsEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/UserActionLogsEnum.java @@ -12,7 +12,8 @@ public enum UserActionLogsEnum { DOWNLOAD("DOWNLOAD"), UPLOAD("UPLOAD"), SCHEDULER("SCHEDULER"), - SCRIPT("SCRIPT"); + SCRIPT("SCRIPT"), + EMAIL("EMAIL"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/CallPageableRequestBean.java b/src/main/java/net/gepafin/tendermanagement/model/request/CallPageableRequestBean.java index 5a76ad97..260a45e5 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/CallPageableRequestBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/CallPageableRequestBean.java @@ -14,6 +14,4 @@ public class CallPageableRequestBean { private List status; private Map filters; - - private Boolean confidi; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/EmailLogRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/EmailLogRequest.java index b85f0e15..7cae845c 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/EmailLogRequest.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/EmailLogRequest.java @@ -37,4 +37,6 @@ public class EmailLogRequest { private Long callId; + private Long userActionId; + } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponse.java index 8e8aeb83..c7f1a2eb 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponse.java @@ -32,4 +32,5 @@ public class ApplicationAmendmentRequestResponse { private String internalNote; private ApplicationAmendmentRequestEnum status; private String emailTemplate; + private List emailSendResponse; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponseBean.java new file mode 100644 index 00000000..9bbddfeb --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestResponseBean.java @@ -0,0 +1,36 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; +import net.gepafin.tendermanagement.enums.ApplicationAmendmentRequestEnum; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class ApplicationAmendmentRequestResponseBean { + private Long id; + private String callEmail; + private String note; + private Long responseDays; + private LocalDateTime startDate; + private Boolean isSendNotification; + private Boolean isSendEmail; + private Long protocolNumber; + private String callName; + private String beneficiaryName; + private String companyName; + private List formFields; + private List applicationFormFields; + private List amendmentDocuments; + private String amendmentNotes; + private Boolean valid; + private Long applicationId; + private Long applicationEvaluationId; + private LocalDateTime evaluationEndDate; + private LocalDateTime expirationDate; + private List commentsList; + private String internalNote; + private ApplicationAmendmentRequestEnum status; + private String emailTemplate; + private List emailSendResponse; +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestViewResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestViewResponse.java index e4139131..9c73d4d9 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestViewResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationAmendmentRequestViewResponse.java @@ -3,6 +3,7 @@ package net.gepafin.tendermanagement.model.response; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; @Data public class ApplicationAmendmentRequestViewResponse { @@ -24,4 +25,6 @@ public class ApplicationAmendmentRequestViewResponse { private String assigendUserName; private String status; + + private List emailSendResponse; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationFormResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationFormResponse.java index 38530c8a..92f7933a 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationFormResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationFormResponse.java @@ -48,5 +48,6 @@ public class ApplicationEvaluationFormResponse { private Long appointmentTemplateId; private String companyVatNumber; private String companyCodiceAteco; + private List emailSendResponse; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java index 47b018f9..49f07c6f 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java @@ -49,5 +49,6 @@ public class ApplicationEvaluationResponse { private EvaluationVersionEnum evaluationVersion; private String companyVatNumber; private String companyCodiceAteco; + private List emailSendResponse; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponseBean.java new file mode 100644 index 00000000..4cda0fca --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponseBean.java @@ -0,0 +1,52 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; +import net.gepafin.tendermanagement.enums.ApplicationEvaluationStatusTypeEnum; +import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; +import net.gepafin.tendermanagement.enums.EvaluationVersionEnum; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class ApplicationEvaluationResponseBean { + + private Long id; + private Long applicationId; + private ApplicationStatusTypeEnum applicationStatus; + private Long assignedApplicationId; + private String note; + private ApplicationEvaluationStatusTypeEnum status; + private Long minScore; + private List criteria; + private List checklist; + private List files; + private List evaluationDocument; + private List amendmentDetails; + private LocalDateTime createdDate; + private LocalDateTime updatedDate; + private String beneficiary; + private Long assignedUserId; + private String assignedUserName; + private Long protocolNumber; + private String callName; + private String motivation; + private LocalDateTime submissionDate; + private LocalDateTime evaluationEndDate; + private LocalDateTime callEndDate; + private String companyName; + private LocalDateTime assignedAt; + private String ndg; + private String appointmentId; + private BigDecimal amountRequested; + private BigDecimal amountAccepted; + private LocalDateTime dateAccepted; + private LocalDateTime dateRejected; + private Long numberOfCheck; + private Long appointmentTemplateId; + private EvaluationVersionEnum evaluationVersion; + private String companyVatNumber; + private String companyCodiceAteco; + private List emailSendResponse; +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/AssignedApplicationViewResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/AssignedApplicationViewResponse.java index 94c2335a..e9a1f9f5 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/AssignedApplicationViewResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/AssignedApplicationViewResponse.java @@ -6,6 +6,7 @@ import net.gepafin.tendermanagement.enums.AssignedApplicationEnum; import net.gepafin.tendermanagement.model.BaseBean; import java.time.LocalDateTime; +import java.util.List; @Data public class AssignedApplicationViewResponse extends BaseBean { @@ -20,5 +21,7 @@ public class AssignedApplicationViewResponse extends BaseBean { private Long protocolNumber; private String callName; private String companyName; + private List emailSendResponse; + } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/EmailReminderResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/EmailReminderResponse.java new file mode 100644 index 00000000..25b8a3b3 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/EmailReminderResponse.java @@ -0,0 +1,18 @@ +package net.gepafin.tendermanagement.model.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EmailReminderResponse { + + @JsonProperty("emailSendResponse") + private List emailSendResponse; + +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/EmailResendResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/EmailResendResponseBean.java new file mode 100644 index 00000000..88f56e62 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/EmailResendResponseBean.java @@ -0,0 +1,12 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class EmailResendResponseBean { + + private EmailSendResponse emailSendResponse; + +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/EmailSendResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/EmailSendResponse.java new file mode 100644 index 00000000..4cc303f7 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/EmailSendResponse.java @@ -0,0 +1,11 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; +import net.gepafin.tendermanagement.enums.EmailScenarioTypeEnum; + +@Data +public class EmailSendResponse { + private Boolean isEmailSend; + private Long userActionId; + private EmailScenarioTypeEnum emailScenario; +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/InitiatePasswordResetResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/InitiatePasswordResetResponse.java new file mode 100644 index 00000000..129a1744 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/InitiatePasswordResetResponse.java @@ -0,0 +1,15 @@ +package net.gepafin.tendermanagement.model.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class InitiatePasswordResetResponse { + private List emailSendResponse; +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/UserResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/UserResponseBean.java index c8724d43..4b76be85 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/UserResponseBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/UserResponseBean.java @@ -48,4 +48,5 @@ public class UserResponseBean extends BaseBean { private Boolean thirdParty; private String emailPec; + private List emailSendResponse; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/util/JWTToken.java b/src/main/java/net/gepafin/tendermanagement/model/util/JWTToken.java index a57b7d59..fdfb3e3a 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/util/JWTToken.java +++ b/src/main/java/net/gepafin/tendermanagement/model/util/JWTToken.java @@ -3,8 +3,11 @@ package net.gepafin.tendermanagement.model.util; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; import net.gepafin.tendermanagement.model.response.LoginResponse; +import java.util.List; + /** * JWTToken */ @@ -15,10 +18,20 @@ public class JWTToken { @JsonProperty("user") private LoginResponse loginResponse; + + @JsonProperty("emailSendResponse") + private List emailSendResponse; + public JWTToken(String token, LoginResponse loginResponse) { this.token = token; this.loginResponse = loginResponse; } + public JWTToken(String token, LoginResponse loginResponse, List emailSendResponse) { + this.token = token; + this.loginResponse = loginResponse; + this.emailSendResponse = emailSendResponse; + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationAmendmentRequestRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationAmendmentRequestRepository.java index fc492019..4cd6d413 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationAmendmentRequestRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationAmendmentRequestRepository.java @@ -150,4 +150,6 @@ public interface ApplicationAmendmentRequestRepository extends JpaRepository applicationIds, @Param("statuses") List statuses); + + ApplicationAmendmentRequestEntity findByIdAndIsDeletedFalseAndStatus(Long id,String status); } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java index 306ad3a5..87fe8045 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java @@ -80,6 +80,10 @@ public interface ApplicationEvaluationRepository extends JpaRepository { List findByUserIdAndAmendmentIdAndIsDeletedFalse(Long userId,Long amendmentId); + List findByUserActionId(Long userActionId); + List findByUserActionIdAndEmailServiceTypeAndSendStatus( + Long userActionId, String emailServiceType, String sendStatus); + Optional findTopByUserIdAndEmailTypeAndIsDeletedFalseOrderByCreatedDateDesc(Long userId, String emailType); + List findByUserActionIdAndEmailServiceType( + Long userActionId, String emailServiceType); + + EmailLogEntity findTopByUserActionIdAndEmailServiceTypeAndSendStatusOrderByIdDesc( + Long userActionId, + String emailServiceType, + String sendStatus + ); + + + } diff --git a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java index 6a4c7b81..050b4972 100644 --- a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java +++ b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java @@ -122,7 +122,7 @@ public class ApplicationAmendmentScheduler { .findEvaluationsWithoutActiveAmendmentsByIds(applicationEvaluationIds); evaluationsWithoutActiveAmendmentList.forEach(evaluation -> { try { - applicationAmendmentRequestDao.calculateEndDateAndSuspensionDays(evaluation); + applicationAmendmentRequestDao.calculateEndDateAndSuspensionDaysOnExpirationOrClosing(evaluation); updateEvaluationStatus(evaluation); diff --git a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationEvaluationScheduler.java b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationEvaluationScheduler.java index c43aca10..af4cb6be 100644 --- a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationEvaluationScheduler.java +++ b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationEvaluationScheduler.java @@ -24,6 +24,7 @@ import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.LocalTime; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -87,7 +88,10 @@ public class ApplicationEvaluationScheduler { evaluation.setStatus(ApplicationEvaluationStatusTypeEnum.EXPIRED.getValue()); evaluation = applicationEvaluationRepository.save(evaluation); - Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_EXPIRED); +// Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_EXPIRED); + Map placeHolders = new HashMap<>(); + placeHolders.put("{{call_name}}", application.getCall().getName()); + placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.EVALUATION_EXPIRED); notificationDao.sendNotificationToInstructor(placeHolders,evaluation,NotificationTypeEnum.EVALUATION_EXPIRED); notificationDao.sendNotificationToInstructorManager(placeHolders,evaluation,NotificationTypeEnum.EVALUATION_EXPIRED); diff --git a/src/main/java/net/gepafin/tendermanagement/service/ApplicationAmendmentRequestService.java b/src/main/java/net/gepafin/tendermanagement/service/ApplicationAmendmentRequestService.java index d54d49d3..b19f77c4 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/ApplicationAmendmentRequestService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/ApplicationAmendmentRequestService.java @@ -8,10 +8,7 @@ import net.gepafin.tendermanagement.model.request.ApplicationAmendmentPagination import net.gepafin.tendermanagement.model.request.ApplicationAmendmentRequest; import net.gepafin.tendermanagement.model.request.ApplicationAmendmentRequestBean; import net.gepafin.tendermanagement.model.request.CloseAmendmentRequest; -import net.gepafin.tendermanagement.model.response.ApplicationAmendmentRequestResponse; -import net.gepafin.tendermanagement.model.response.ApplicationAmendmentRequestViewResponse; -import net.gepafin.tendermanagement.model.response.PageableResponseBean; -import net.gepafin.tendermanagement.model.response.GetAllAmendmentResponseBean; +import net.gepafin.tendermanagement.model.response.*; import java.util.List; @@ -29,6 +26,6 @@ public interface ApplicationAmendmentRequestService { public List getAmendmentByApplicationId(HttpServletRequest request,Long applicationId,List statuses); public ApplicationAmendmentRequestResponse updateApplicationAmendmentStatus(HttpServletRequest request, Long applicationAmendmentId, ApplicationAmendmentRequestEnum status); - void sendReminderEmail(HttpServletRequest request,Long amendmentId); + EmailReminderResponse sendReminderEmail(HttpServletRequest request, Long amendmentId); PageableResponseBean> getApplicationAmendmentByPaginnation(HttpServletRequest request, Long userId, ApplicationAmendmentPaginationRequestBean amendmentPaginationRequestBean); } diff --git a/src/main/java/net/gepafin/tendermanagement/service/CallService.java b/src/main/java/net/gepafin/tendermanagement/service/CallService.java index 925c71cd..f0130c29 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/CallService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/CallService.java @@ -33,7 +33,7 @@ public interface CallService { byte[] downloadCallDocumentsAsZip(HttpServletRequest request, Long callId); - PageableResponseBean> getAllCallsByPagination(HttpServletRequest request, Long companyId , Boolean onlyPreferredCall,CallPageableRequestBean callPageableRequestBean); + PageableResponseBean> getAllCallsByPagination(HttpServletRequest request, Long companyId , Boolean onlyPreferredCall,Boolean onlyConfidiCall,CallPageableRequestBean callPageableRequestBean); CallResponse createCallStep2EvaluationV2(HttpServletRequest request, Long callId, CreateCallRequestStep2EvaluationV2 createCallRequest); diff --git a/src/main/java/net/gepafin/tendermanagement/service/ResendEmailService.java b/src/main/java/net/gepafin/tendermanagement/service/ResendEmailService.java new file mode 100644 index 00000000..85275365 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/service/ResendEmailService.java @@ -0,0 +1,9 @@ +package net.gepafin.tendermanagement.service; + +import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.model.response.EmailResendResponseBean; + + +public interface ResendEmailService { + EmailResendResponseBean resendEmail(HttpServletRequest request , Long userActionId); +} diff --git a/src/main/java/net/gepafin/tendermanagement/service/UserService.java b/src/main/java/net/gepafin/tendermanagement/service/UserService.java index bbe69e5f..c8224f0c 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/UserService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/UserService.java @@ -8,9 +8,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import net.gepafin.tendermanagement.enums.UserStatusEnum; import net.gepafin.tendermanagement.model.request.*; -import net.gepafin.tendermanagement.model.response.PageableResponseBean; -import net.gepafin.tendermanagement.model.response.UserSamlResponse; -import net.gepafin.tendermanagement.model.response.UserResponseBean; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.JWTToken; import java.util.List; @@ -28,7 +26,7 @@ public interface UserService { UserEntity validateUser(Long userId); - void initiatePasswordReset(InitiatePasswordResetReq resetReq); + InitiatePasswordResetResponse initiatePasswordReset(InitiatePasswordResetReq resetReq); Boolean resetPassword(ResetPasswordReq resetPasswordReq); diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationAmendmentRequestServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationAmendmentRequestServiceImpl.java index 1d7a38a9..a680ec1b 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationAmendmentRequestServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationAmendmentRequestServiceImpl.java @@ -12,10 +12,7 @@ import net.gepafin.tendermanagement.model.request.ApplicationAmendmentPagination import net.gepafin.tendermanagement.model.request.ApplicationAmendmentRequest; import net.gepafin.tendermanagement.model.request.ApplicationAmendmentRequestBean; import net.gepafin.tendermanagement.model.request.CloseAmendmentRequest; -import net.gepafin.tendermanagement.model.response.ApplicationAmendmentRequestResponse; -import net.gepafin.tendermanagement.model.response.ApplicationAmendmentRequestViewResponse; -import net.gepafin.tendermanagement.model.response.PageableResponseBean; -import net.gepafin.tendermanagement.model.response.GetAllAmendmentResponseBean; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.repositories.ApplicationAmendmentRequestRepository; import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; import net.gepafin.tendermanagement.service.ApplicationAmendmentRequestService; @@ -145,17 +142,18 @@ public class ApplicationAmendmentRequestServiceImpl implements ApplicationAmendm } @Override - public void sendReminderEmail(HttpServletRequest request,Long amendmentId) { + public EmailReminderResponse sendReminderEmail(HttpServletRequest request, Long amendmentId) { ApplicationAmendmentRequestEntity amendment = applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(amendmentId) .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.APPLICATION_AMENDMENT_NOT_FOUND_MSG))); + EmailReminderResponse response = new EmailReminderResponse(); Optional entityOptional = applicationEvaluationRepository.findByIdAndIsDeletedFalse(amendment.getApplicationEvaluationEntity().getId()); if (entityOptional.isPresent()) { UserEntity user = validator.validatePreInstructor(request, entityOptional.get().getUserId()); - - applicationAmendmentRequestDao.sendReminderEmail(amendmentId); + response = applicationAmendmentRequestDao.sendReminderEmail(amendmentId); } + return response; } @Override diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationEvaluationServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationEvaluationServiceImpl.java index 044c0902..650acdc1 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationEvaluationServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationEvaluationServiceImpl.java @@ -11,6 +11,7 @@ import net.gepafin.tendermanagement.model.request.ApplicationEvaluationFormReque import net.gepafin.tendermanagement.model.request.ApplicationEvaluationRequest; import net.gepafin.tendermanagement.model.response.ApplicationEvaluationFormResponse; import net.gepafin.tendermanagement.model.response.ApplicationEvaluationResponse; +import net.gepafin.tendermanagement.model.response.ApplicationEvaluationResponseBean; import net.gepafin.tendermanagement.model.response.ApplicationEvaluationVersionResponse; import net.gepafin.tendermanagement.repositories.AssignedApplicationsRepository; import net.gepafin.tendermanagement.service.ApplicationEvaluationService; diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java b/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java index 12ea0123..01ad155b 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java @@ -15,10 +15,7 @@ import net.gepafin.tendermanagement.entities.UserEntity; import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.LoginReq; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; -import net.gepafin.tendermanagement.model.response.CompanyResponse; -import net.gepafin.tendermanagement.model.response.LoginResponse; -import net.gepafin.tendermanagement.model.response.RoleResponseBean; -import net.gepafin.tendermanagement.model.response.UserSamlResponse; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.JWTToken; import net.gepafin.tendermanagement.repositories.LoginAttemptRepository; import net.gepafin.tendermanagement.repositories.SamlResponseRepository; @@ -122,7 +119,7 @@ public class AuthenticationService { createFailedLoginAttempt(loginAttemptEntity, e.getMessage()); throw e; } - return getJWTTokenBean(user, loginReq.getRememberMe(), loginAttemptEntity.getId()); + return getJWTTokenBean(user, loginReq.getRememberMe(), loginAttemptEntity.getId(),null); } public LoginAttemptEntity prepareLoginAttemptEntity(LoginReq loginUserReq, HttpServletRequest request) { @@ -145,7 +142,7 @@ public class AuthenticationService { loginAttemptEntity.setErrorMsg(errorMsg); loginAttemptDao.createLoginAttempt(loginAttemptEntity); } - public JWTToken getJWTTokenBean(UserEntity user, Boolean rememberMe, Long loginAttemptId) { + public JWTToken getJWTTokenBean(UserEntity user, Boolean rememberMe, Long loginAttemptId, List emailSendResponse) { UserEntity oldUserEntity = Utils.getClonedEntityForData(user); user.setLastLogin(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); user = userRepository.save(user); @@ -156,7 +153,7 @@ public class AuthenticationService { LoginResponse loginResponse = getLoginResponse(user, roleResponseBean); - JWTToken jwtToken = new JWTToken(token, loginResponse); + JWTToken jwtToken = new JWTToken(token, loginResponse , emailSendResponse); /** This code is responsible for adding a version history log for the "Create user Or Update user" operation. **/ loggingUtil.addVersionHistoryWithoutToken(VersionHistoryRequest.builder().request(request).oldData(oldUserEntity).newData(user).actionType(VersionActionTypeEnum.UPDATE).build()); @@ -238,7 +235,7 @@ public class AuthenticationService { loginAttemptEntity = prepareLoginAttemptEntity(loginReq, request); loginAttemptEntity.setUserId(userEntity.getId()); LoginAttemptEntity loginAttempt = createSuccessLoginAttempt(loginAttemptEntity); - return getJWTTokenBean(userEntity, Boolean.TRUE, loginAttempt.getId()); + return getJWTTokenBean(userEntity, Boolean.TRUE, loginAttempt.getId(),null); } catch (Exception e) { log.info("Authentication login failed for email: {}",e.getMessage()); loginAttemptEntity.setUserId(userId); diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/CallServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/CallServiceImpl.java index d58a7644..0036cbb7 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/CallServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/CallServiceImpl.java @@ -105,9 +105,9 @@ public class CallServiceImpl implements CallService { @Override @Transactional(rollbackFor = Exception.class) - public PageableResponseBean> getAllCallsByPagination(HttpServletRequest request,Long companyId , Boolean onlyPreferredCall, CallPageableRequestBean callPageableRequestBean) { + public PageableResponseBean> getAllCallsByPagination(HttpServletRequest request,Long companyId , Boolean onlyPreferredCall,Boolean onlyConfidiCall, CallPageableRequestBean callPageableRequestBean) { UserEntity user = validator.validateUser(request); - return callDao.getAllCallsByPagination(request,user,companyId,onlyPreferredCall,callPageableRequestBean); + return callDao.getAllCallsByPagination(request,user,companyId,onlyPreferredCall,onlyConfidiCall,callPageableRequestBean); } @Override @Transactional(rollbackFor = Exception.class) diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/PecEmailService.java b/src/main/java/net/gepafin/tendermanagement/service/impl/PecEmailService.java index 8c44af9d..19742a8c 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/PecEmailService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/PecEmailService.java @@ -1,25 +1,32 @@ package net.gepafin.tendermanagement.service.impl; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; import lombok.extern.slf4j.Slf4j; import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.dao.ApplicationDao; import net.gepafin.tendermanagement.dao.EmailLogDao; -import net.gepafin.tendermanagement.entities.EmailLogEntity; +import net.gepafin.tendermanagement.dao.NotificationDao; +import net.gepafin.tendermanagement.enums.EmailScenarioTypeEnum; import net.gepafin.tendermanagement.enums.EmailServiceTypeEnum; +import net.gepafin.tendermanagement.enums.NotificationTypeEnum; import net.gepafin.tendermanagement.enums.StatusTypeEnum; import net.gepafin.tendermanagement.model.request.EmailConfig; import net.gepafin.tendermanagement.model.request.EmailLogRequest; +import net.gepafin.tendermanagement.model.request.NotificationReq; import net.gepafin.tendermanagement.model.request.PecEmailRequest; import net.gepafin.tendermanagement.util.Utils; import net.gepafin.tendermanagement.util.Validator; -import org.opensaml.xmlsec.signature.G; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Slf4j @Service @@ -30,20 +37,26 @@ public class PecEmailService implements EmailService { @Value("${isPecServiceEnabled}") private String isPecServiceEnabled; - + @Autowired private Validator validator; @Autowired private EmailLogDao emailLogDao; + @Autowired + private NotificationDao notificationDao; + + @Autowired + private ApplicationDao applicationDao; + @Override public void sendEmail(String subject, String body, List recipientEmails, EmailConfig emailConfig, EmailLogRequest emailLogRequest) { if (Boolean.FALSE.equals(Boolean.parseBoolean(isEmailSendingEnabled))) { return; } - + PecEmailRequest emailRequest = new PecEmailRequest(); emailRequest.setSender(emailConfig.getSender()); emailRequest.setSubject(subject); @@ -67,13 +80,27 @@ public class PecEmailService implements EmailService { .header("Content-Type", "application/json") .body(Utils.convertObjectToJson(emailRequest)) // Serialize the emailRequest object to JSON .asString(); + + if (!isSuccessfulPecResponse(response2.getBody())) { + String errorMsg = "PEC sending failed: " + response2.getBody(); + emailLogRequest.setSendStatus(StatusTypeEnum.FAILED.getValue()); + emailLogRequest.setEmailServiceType(EmailServiceTypeEnum.PEC_SERVICE); + emailLogRequest.setErrorMessage(errorMsg); + sendNotificationOnFailure(emailLogRequest.getUserId(),emailLogRequest.getEmailType()); + + if (EmailScenarioTypeEnum.APPLICATION_SUBMITTED.equals(emailLogRequest.getEmailType())) { + applicationDao.sendApplicationSubmissionFailureEmail(emailLogRequest); + } + } } }catch(Exception e) { emailLogRequest.setSendStatus(StatusTypeEnum.FAILED.getValue()); emailLogRequest.setEmailServiceType(EmailServiceTypeEnum.PEC_SERVICE); emailLogRequest.setErrorMessage(e.getMessage()); - emailLogDao.createEmailLog(emailLogRequest); - throw new RuntimeException("Failed to send email via PEC: " + response2.getStatus()); + sendNotificationOnFailure(emailLogRequest.getUserId(),emailLogRequest.getEmailType()); + if (EmailScenarioTypeEnum.APPLICATION_SUBMITTED.equals(emailLogRequest.getEmailType())) { + applicationDao.sendApplicationSubmissionFailureEmail(emailLogRequest); + } } if(response2 != null) { emailLogRequest.setEmailServiceResponse(response2.getBody()); @@ -82,6 +109,57 @@ public class PecEmailService implements EmailService { emailLogRequest.setEmailServiceType(EmailServiceTypeEnum.PEC_SERVICE); emailLogDao.createEmailLog(emailLogRequest); } + + private void sendNotificationOnFailure(Long userId, EmailScenarioTypeEnum emailScenarioTypeEnum) { + if (userId == null) { + log.warn("Cannot send notification: userId is null."); + return; + } + + Map placeholders = new HashMap<>(); + placeholders.put("{{email_scenario}}", emailScenarioTypeEnum.getValue()); + + NotificationReq notificationReq = notificationDao.createNotificationReq( + NotificationTypeEnum.PEC_EMAIL_SENDING_FAILURE.getValue(), + placeholders, + userId, + null, + null + ); + + try { + notificationDao.sendNotification(notificationReq); + log.info("Sent PEC failure notification to user {}", userId); + } catch (Exception e) { + log.error("Failed to send PEC failure notification to user {}: {}", userId, e.getMessage()); + } + } + + private boolean isSuccessfulPecResponse(String responseBody) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + + boolean success = jsonNode.has("success") && jsonNode.get("success").asBoolean(); + boolean hasNoError = !jsonNode.has("error") || jsonNode.get("error").isNull(); + + if (jsonNode.has("success") && !success) { + log.error("PEC response indicates failure: {}", responseBody); + return false; + } + + if (responseBody.contains("403") || responseBody.toLowerCase().contains("")) { + log.error("PEC response is a 403 HTML Forbidden page or invalid format: {}", responseBody); + return false; + } + + return success && hasNoError; + + } catch (Exception e) { + log.error("Invalid PEC response format: {}", responseBody); + return false; + } + } } diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/ResendEmailServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/ResendEmailServiceImpl.java new file mode 100644 index 00000000..bfe199b3 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/ResendEmailServiceImpl.java @@ -0,0 +1,25 @@ +package net.gepafin.tendermanagement.service.impl; + +import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.dao.EmailDao; +import net.gepafin.tendermanagement.model.response.EmailResendResponseBean; +import net.gepafin.tendermanagement.service.ResendEmailService; +import net.gepafin.tendermanagement.util.Validator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ResendEmailServiceImpl implements ResendEmailService { + + @Autowired + Validator validator; + + @Autowired + EmailDao emailDao; + + @Override + public EmailResendResponseBean resendEmail(HttpServletRequest request, Long userActionId) { + validator.validateUser(request); + return emailDao.resendEmail(request,userActionId); + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java index 0b13ef44..d2c2b032 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java @@ -11,9 +11,7 @@ import net.gepafin.tendermanagement.model.request.UpdateUserReq; import net.gepafin.tendermanagement.model.request.UserReq; import net.gepafin.tendermanagement.enums.UserStatusEnum; import net.gepafin.tendermanagement.model.request.*; -import net.gepafin.tendermanagement.model.response.PageableResponseBean; -import net.gepafin.tendermanagement.model.response.UserSamlResponse; -import net.gepafin.tendermanagement.model.response.UserResponseBean; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.JWTToken; import net.gepafin.tendermanagement.service.UserService; import net.gepafin.tendermanagement.util.LoggingUtil; @@ -78,8 +76,8 @@ public class UserServiceImpl implements UserService { } @Override - public void initiatePasswordReset(InitiatePasswordResetReq resetReq) { - userDao.initiatePasswordReset(resetReq); + public InitiatePasswordResetResponse initiatePasswordReset(InitiatePasswordResetReq resetReq) { + return userDao.initiatePasswordReset(resetReq); } @Override diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 6347119a..c14b25d9 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -15,6 +15,7 @@ import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -36,9 +37,11 @@ import jakarta.persistence.criteria.Root; import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.enums.EmailScenarioTypeEnum; import net.gepafin.tendermanagement.enums.MatchModeEnum; import net.gepafin.tendermanagement.model.request.FilterCriteria; import net.gepafin.tendermanagement.model.request.GlobalFilters; +import net.gepafin.tendermanagement.model.response.EmailSendResponse; import net.gepafin.tendermanagement.web.rest.api.errors.*; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; @@ -1014,5 +1017,31 @@ public class Utils { } } + public static List commaSeparatedStringToList(String emails) { + if (emails == null || emails.isEmpty()) { + return new ArrayList<>(); + } + return Arrays.stream(emails.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + } + + + public static List mergeEmailSendResponses(List existingResponses, EmailSendResponse newResponse) { + Map responseMap = Optional.ofNullable(existingResponses) + .orElse(new ArrayList<>()) + .stream() + .collect(Collectors.toMap( + EmailSendResponse::getEmailScenario, + Function.identity(), + (oldVal, newVal) -> newVal, + LinkedHashMap::new + )); + + responseMap.put(newResponse.getEmailScenario(), newResponse); + + return new ArrayList<>(responseMap.values()); + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationAmendmentRequestApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationAmendmentRequestApi.java index eb0c05f0..5d81ce7e 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationAmendmentRequestApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationAmendmentRequestApi.java @@ -183,7 +183,7 @@ public interface ApplicationAmendmentRequestApi { @ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })) }) @PostMapping(value = "/{amendmentId}/reminder", produces = MediaType.APPLICATION_JSON_VALUE) - ResponseEntity> sendReminderEmail(HttpServletRequest request, + ResponseEntity> sendReminderEmail(HttpServletRequest request, @Parameter( required = true) @PathVariable(value = "amendmentId") Long amendmentId); diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/CallApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/CallApi.java index e1893f5a..11c6069c 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/CallApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/CallApi.java @@ -159,7 +159,7 @@ public interface CallApi { @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = @ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE))) }) @PostMapping(value = "/pagination", consumes = "application/json", produces = "application/json") - ResponseEntity>>> getAllCallsByPagination(HttpServletRequest request,@RequestParam(value = "companyId", required = false) Long companyId , @RequestParam(value = "onlyPreferredCall", required = false, defaultValue = "false") Boolean onlyPreferredCall, @RequestBody CallPageableRequestBean callPageableRequestBean); + ResponseEntity>>> getAllCallsByPagination(HttpServletRequest request,@RequestParam(value = "companyId", required = false) Long companyId , @RequestParam(value = "onlyPreferredCall", required = false, defaultValue = "false") Boolean onlyPreferredCall, @RequestParam(value = "onlyConfidiCall", required = false) Boolean onlyConfidiCall, @RequestBody CallPageableRequestBean callPageableRequestBean); @Operation(summary = "Api to update call step 2 (Evaluation V2)", diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/EmailApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/EmailApi.java new file mode 100644 index 00000000..2bbeca34 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/EmailApi.java @@ -0,0 +1,40 @@ +package net.gepafin.tendermanagement.web.rest.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import net.gepafin.tendermanagement.model.request.CreateCallRequestStep1; +import net.gepafin.tendermanagement.model.response.CallResponse; +import net.gepafin.tendermanagement.model.response.EmailResendResponseBean; +import net.gepafin.tendermanagement.model.util.Response; +import net.gepafin.tendermanagement.web.rest.api.errors.ErrorConstants; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@Validated +public interface EmailApi { + @Operation(summary = "Api to resend the email", + responses = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) + }) + @PostMapping(value = "/resend", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> resendEmail( + HttpServletRequest request, + @Parameter(description = "The User Action ID", required = true) + @RequestParam("userActionId") Long userActionId); +} diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java index 6f7aaf8e..3f0d9410 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java @@ -10,10 +10,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import net.gepafin.tendermanagement.enums.UserStatusEnum; import net.gepafin.tendermanagement.model.request.*; -import net.gepafin.tendermanagement.model.response.PageableResponseBean; -import net.gepafin.tendermanagement.model.response.SummaryPageResponseBean; -import net.gepafin.tendermanagement.model.response.UserSamlResponse; -import net.gepafin.tendermanagement.model.response.UserResponseBean; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.JWTToken; import net.gepafin.tendermanagement.model.util.Response; import net.gepafin.tendermanagement.web.rest.api.errors.ErrorConstants; @@ -121,8 +118,8 @@ public interface UserApi { @RequestMapping(value = "/reset-password/initiate", produces = {"application/json"}, method = RequestMethod.POST) - ResponseEntity> initiatePasswordReset(HttpServletRequest request, - @Parameter(description = "Initiate password reset request object", required = true) @Valid @RequestBody InitiatePasswordResetReq initiatePasswordResetReq); + ResponseEntity> initiatePasswordReset(HttpServletRequest request, + @Parameter(description = "Initiate password reset request object", required = true) @Valid @RequestBody InitiatePasswordResetReq initiatePasswordResetReq); @Operation(summary = "Api to reset password", responses = { diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationAmendmentRequestController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationAmendmentRequestController.java index 585a6d1e..0c8ef489 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationAmendmentRequestController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationAmendmentRequestController.java @@ -8,10 +8,7 @@ import net.gepafin.tendermanagement.enums.ApplicationAmendmentRequestEnum; import net.gepafin.tendermanagement.enums.UserActionContextEnum; import net.gepafin.tendermanagement.enums.UserActionLogsEnum; import net.gepafin.tendermanagement.model.request.*; -import net.gepafin.tendermanagement.model.response.ApplicationAmendmentRequestResponse; -import net.gepafin.tendermanagement.model.response.ApplicationAmendmentRequestViewResponse; -import net.gepafin.tendermanagement.model.response.PageableResponseBean; -import net.gepafin.tendermanagement.model.response.GetAllAmendmentResponseBean; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.Response; import net.gepafin.tendermanagement.service.ApplicationAmendmentRequestService; import net.gepafin.tendermanagement.util.LoggingUtil; @@ -176,15 +173,20 @@ public class ApplicationAmendmentRequestController implements ApplicationAmendme .body(new Response<>(applicationResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.APPLICATION_AMENDMENT_UPDATE_SUCCESSFULLY_MSG))); } @Override - public ResponseEntity> sendReminderEmail( + public ResponseEntity> sendReminderEmail( HttpServletRequest request, Long amendmentId) { log.info("Sending reminder email for Amendment ID: {}", amendmentId); - applicationAmendmentRequestService.sendReminderEmail(request,amendmentId); + + /** This code is responsible for "sending reminder email" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.EMAIL) + .actionContext(UserActionContextEnum.SEND_REMINDER_EMAIL).build()); + + EmailReminderResponse emailSendResponse = applicationAmendmentRequestService.sendReminderEmail(request,amendmentId); return ResponseEntity.status(HttpStatus.OK) - .body(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.REMINDER_EMAIL_SENT_SUCCESS_MSG))); + .body(new Response<>(emailSendResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.REMINDER_EMAIL_SENT_SUCCESS_MSG))); } @Override diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CallApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CallApiController.java index 5673bb2b..4eb80c83 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CallApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CallApiController.java @@ -152,12 +152,12 @@ public class CallApiController implements CallApi { } @Override - public ResponseEntity>>> getAllCallsByPagination(HttpServletRequest request, Long companyId , Boolean onlyPreferredCall, CallPageableRequestBean callPageableRequestBean) { + public ResponseEntity>>> getAllCallsByPagination(HttpServletRequest request, Long companyId , Boolean onlyPreferredCall,Boolean onlyConfidiCall, CallPageableRequestBean callPageableRequestBean) { /** This code is responsible for creating user action logs for the "get all call by pagination" operation. **/ loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.VIEW).actionContext(UserActionContextEnum.GET_ALL_CALL_BY_PAGINATION).build()); - PageableResponseBean> callsByPagination=callService.getAllCallsByPagination(request,companyId,onlyPreferredCall,callPageableRequestBean); + PageableResponseBean> callsByPagination=callService.getAllCallsByPagination(request,companyId,onlyPreferredCall,onlyConfidiCall,callPageableRequestBean); return ResponseEntity.status(HttpStatus.OK) .body(new Response<>(callsByPagination, Status.SUCCESS, Translator.toLocale(GepafinConstant.CALL_FETCH_SUCCESS_MSG))); } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/EmailApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/EmailApiController.java new file mode 100644 index 00000000..59d55ac0 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/EmailApiController.java @@ -0,0 +1,52 @@ +package net.gepafin.tendermanagement.web.rest.api.impl; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.log4j.Log4j2; +import net.gepafin.tendermanagement.config.Translator; +import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.enums.UserActionContextEnum; +import net.gepafin.tendermanagement.enums.UserActionLogsEnum; +import net.gepafin.tendermanagement.model.request.UserActionRequest; +import net.gepafin.tendermanagement.model.response.EmailResendResponseBean; +import net.gepafin.tendermanagement.model.util.Response; +import net.gepafin.tendermanagement.service.ResendEmailService; +import net.gepafin.tendermanagement.util.LoggingUtil; +import net.gepafin.tendermanagement.web.rest.api.EmailApi; +import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("${openapi.gepafin.base-path:/v1/email}") +@Log4j2 +public class EmailApiController implements EmailApi { + + @Autowired + LoggingUtil loggingUtil; + + @Autowired + ResendEmailService emailService; + + @Override + public ResponseEntity> resendEmail(HttpServletRequest request, Long userActionId) { + + /** This code is responsible for creating user action logs for the "Resend Email" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.INSERT) + .actionContext(UserActionContextEnum.RESEND_EMAIL).build()); + + EmailResendResponseBean emailResendResponseBean = emailService.resendEmail(request, userActionId); + + if (!emailResendResponseBean.getEmailSendResponse().getIsEmailSend()) { + return ResponseEntity.status(HttpStatus.OK) + .body(new Response<>(emailResendResponseBean, Status.EXCEPTION_ERROR, Translator.toLocale(GepafinConstant.RESEND_EMAIL_SENT_FAILED_MSG))); + } + + + return ResponseEntity.status(HttpStatus.OK) + .body(new Response<>(emailResendResponseBean, Status.SUCCESS, Translator.toLocale(GepafinConstant.RESEND_EMAIL_SENT_SUCCESS_MSG))); + + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java index 14945d65..3e262b77 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java @@ -11,9 +11,7 @@ import net.gepafin.tendermanagement.enums.UserActionContextEnum; import net.gepafin.tendermanagement.enums.UserActionLogsEnum; import net.gepafin.tendermanagement.enums.UserStatusEnum; import net.gepafin.tendermanagement.model.request.*; -import net.gepafin.tendermanagement.model.response.PageableResponseBean; -import net.gepafin.tendermanagement.model.response.UserSamlResponse; -import net.gepafin.tendermanagement.model.response.UserResponseBean; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.JWTToken; import net.gepafin.tendermanagement.model.util.Response; import net.gepafin.tendermanagement.service.UserService; @@ -135,16 +133,16 @@ public class UserApiController implements UserApi { return ResponseEntity.ok(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.SUCCESS_PASSWORD_CHANGED))); } @Override - public ResponseEntity> initiatePasswordReset(HttpServletRequest httpServletRequest,InitiatePasswordResetReq request) { + public ResponseEntity> initiatePasswordReset(HttpServletRequest httpServletRequest, InitiatePasswordResetReq request) { log.info("Initiating password reset for email: {}", request.getEmail()); /** This code is responsible for "Initiating Password Reset Request" operation. **/ loggingUtil.logUserAction(UserActionRequest.builder().request(httpServletRequest).actionType(UserActionLogsEnum.UPDATE) .actionContext(UserActionContextEnum.INITIATE_PASSWORD_RESET_REQUEST).build()); - userService.initiatePasswordReset(request); + InitiatePasswordResetResponse emailSendResponse = userService.initiatePasswordReset(request); log.info("Password reset token generated for email: {}", request.getEmail()); - return ResponseEntity.ok(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.RESET_PASSWORD_INITIATED))); + return ResponseEntity.ok(new Response<>(emailSendResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.RESET_PASSWORD_INITIATED))); } @Override diff --git a/src/main/resources/db/changelog/db.changelog-1.0.0.xml b/src/main/resources/db/changelog/db.changelog-1.0.0.xml index e10adc93..76bfc067 100644 --- a/src/main/resources/db/changelog/db.changelog-1.0.0.xml +++ b/src/main/resources/db/changelog/db.changelog-1.0.0.xml @@ -2653,6 +2653,8 @@ + + @@ -2666,12 +2668,16 @@ + + + + @@ -2747,6 +2753,60 @@ path="db/dump/update_system_email_template_28_04_2025.sql"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/dump/insert_system_email_template_for_application_submission_failure_30_04_2025.sql b/src/main/resources/db/dump/insert_system_email_template_for_application_submission_failure_30_04_2025.sql new file mode 100644 index 00000000..a1e0b030 --- /dev/null +++ b/src/main/resources/db/dump/insert_system_email_template_for_application_submission_failure_30_04_2025.sql @@ -0,0 +1,46 @@ +INSERT INTO gepafin_schema.system_email_template ( + template_name, + "type", + html_content, + subject, + "json", + "system", + is_deleted, + created_date, + updated_date, + email_scenario) + VALUES ( + 'Application submission failure notification', + 'APPLICATION_SUBMISSION_FAILURE_NOTIFICATION', + ' + +
+

Buongiorno,

+ +

+ Si prega di notare che si è verificato un errore durante linvio dell email "{{scenario}}" per la chiamata: {{call_name}}.

+

+ Dettagli:
+ - ID Applicazione: {{application_id}}
+ - Nome Azienda: {{company_name}}
+ - Numero Protocollo: {{protocol_number}}
+ - ID Azione Utente: {{user_action_id}} +

+ +

+ Si prega di effettuare i controlli appropriati e di adottare le misure correttive necessarie. +

+ +

Cordiali saluti,

+

Gepafin S.p.a.

+
+ + ', + 'ERRORE INVIO EMAIL - BANDO {{call_name}}', + NULL, + true, + false, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + 'APPLICATION_SUBMISSION_FAILURE' +); \ No newline at end of file diff --git a/src/main/resources/db/dump/update_application_amendment_request_view.sql b/src/main/resources/db/dump/update_application_amendment_request_view.sql new file mode 100644 index 00000000..9224de74 --- /dev/null +++ b/src/main/resources/db/dump/update_application_amendment_request_view.sql @@ -0,0 +1,27 @@ +CREATE OR REPLACE VIEW application_amendment_request_view AS + +SELECT a.id, + a.application_id, + p.protocol_number, + cl.name AS call_name, + c.company_name, + a.start_date, + a.end_date AS expiration_date, + COALESCE(NULLIF(TRIM(BOTH FROM concat(COALESCE(u.first_name, ''), ' ', COALESCE(u.last_name, ''))), ''), '') AS assigned_user_name, + aa.user_id AS assigned_user_id, + a.status, + a.note, + a.internal_note, + app.user_id AS application_user_id, + a.created_date, + a.updated_date, + a.is_deleted, + a.email_send_response +FROM gepafin_schema.application_amendment_request a +LEFT JOIN gepafin_schema.application app ON a.application_id = app.id AND (app.is_deleted IS FALSE OR app.is_deleted IS NULL) +LEFT JOIN gepafin_schema.call cl ON app.call_id = cl.id +LEFT JOIN gepafin_schema.company c ON app.company_id = c.id +LEFT JOIN gepafin_schema.protocol p ON a.protocol_id = p.id +LEFT JOIN gepafin_schema.assigned_applications aa ON app.id = aa.application_id AND (aa.is_deleted IS FALSE OR aa.is_deleted IS NULL) +LEFT JOIN gepafin_schema.gepafin_user u ON aa.user_id = u.id +WHERE a.is_deleted IS FALSE OR a.is_deleted IS NULL ORDER BY id; \ No newline at end of file diff --git a/src/main/resources/db/dump/update_assigned_application_view.sql b/src/main/resources/db/dump/update_assigned_application_view.sql new file mode 100644 index 00000000..36d8a3ec --- /dev/null +++ b/src/main/resources/db/dump/update_assigned_application_view.sql @@ -0,0 +1,53 @@ +CREATE OR REPLACE VIEW assigned_applications_view AS +SELECT + -- From assigned_applications + aa.id AS id, + aa.user_id AS user_id, + aa.status AS status, + aa.created_date AS created_date, + aa.updated_date AS updated_date, + aa.is_deleted AS is_deleted, + + -- From application + a.id AS application_id, + a.status AS application_status, + a.submission_date AS submission_date, + ae.end_date AS evaluation_end_date, + a.ndg AS ndg, + a.appointment_id AS appointment_id, + + -- From protocol (OneToOne) + p.protocol_number AS protocol_number, + + -- From call (ManyToOne) + cl.name AS call_name, + + -- From company (ManyToOne) + c.company_name AS company_name, + ae.email_send_response AS email_send_response + +FROM gepafin_schema.assigned_applications aa + +-- Join application (ManyToOne from assigned_applications) +LEFT JOIN gepafin_schema.application a + ON aa.application_id = a.id + AND (a.is_deleted IS FALSE OR a.is_deleted IS NULL) + +-- Join application_evaluation (application_id matches + not deleted) +LEFT JOIN gepafin_schema.application_evaluation ae + ON ae.application_id = a.id + AND (ae.is_deleted IS FALSE OR ae.is_deleted IS NULL) + +-- Join protocol (OneToOne from application) +LEFT JOIN gepafin_schema.protocol p + ON a.protocol_number = p.id + +-- Join call (ManyToOne from application) +LEFT JOIN gepafin_schema.call cl + ON a.call_id = cl.id + +-- Join company (ManyToOne from application) +LEFT JOIN gepafin_schema.company c + ON a.company_id = c.id + +WHERE aa.is_deleted IS FALSE OR aa.is_deleted IS NULL; diff --git a/src/main/resources/db/dump/update_json_template_for_notification_22_04_2025.sql b/src/main/resources/db/dump/update_json_template_for_notification_22_04_2025.sql new file mode 100644 index 00000000..9aefaedf --- /dev/null +++ b/src/main/resources/db/dump/update_json_template_for_notification_22_04_2025.sql @@ -0,0 +1,9 @@ +INSERT INTO notification_type (notification_name, title, json_template, created_date, updated_date, is_deleted) +VALUES +('PEC_EMAIL_SENDING_FAILURE', + 'Invio Email Fallito', + 'Non è stato possibile inviare lemail per lo scenario: {{email_scenario}}.', + '2025-01-03T10:16:26.472Z', + '2025-01-03T10:16:26.472Z', + 'false' +); diff --git a/src/main/resources/db/dump/update_system_email_template_for_application_submission_failure_08_05_2025.sql b/src/main/resources/db/dump/update_system_email_template_for_application_submission_failure_08_05_2025.sql new file mode 100644 index 00000000..bc3a43b3 --- /dev/null +++ b/src/main/resources/db/dump/update_system_email_template_for_application_submission_failure_08_05_2025.sql @@ -0,0 +1,29 @@ +UPDATE gepafin_schema.system_email_template +SET html_content = ' + +
+

Buongiorno,

+ +

+

+ Si prega di notare che si è verificato un errore durante linvio dell e-mail nello scenario "{{scenario}}" per la chiamata: {{call_name}}. +

+ +

+ +

+ Dettagli:
+ - ID Applicazione: {{application_id}}
+ - Nome Azienda: {{company_name}}
+ - Numero Protocollo: {{protocol_number}}
+ - ID Azione Utente: {{user_action_id}} +

+ +

Si prega di effettuare i controlli appropriati e di adottare le misure correttive necessarie.

+ +

Cordiali saluti,

+

Gepafin S.p.a.

+
+ +' +WHERE template_name = 'Application submission failure notification'; diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index 237e6e53..e69af662 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -400,4 +400,11 @@ insufficient.score.msg = Insufficient score to pass to the technical and economi password.expired.for.login.to.odessa = Odessa login password has been expired. invalid.user=Invalid user. -application.readmit.success=Application has been readmitted successfully. \ No newline at end of file + +reminder.email.sent.failed.msg = Failed to send reminder email +resend.email.sent.success.msg = Email resend successfully +resend.email.sent.failed.msg = Failed to resend the email. + +application.readmit.success=Application has been readmitted successfully. +no.email.log.msg = No failed emails found for given userActionId. +user.action.id.not.found = User Action id not found. diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index 35247fe3..ef23cfac 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -391,4 +391,11 @@ validation.table.message=I dati per il campo {0} non sono presenti. password.expired.for.login.to.odessa = La password di accesso a Odessa � scaduta invalid.user=Utente non valido. + +reminder.email.sent.failed.msg = Impossibile inviare l'e-mail di promemoria +resend.email.sent.success.msg = Email reinviata con successo +resend.email.sent.failed.msg = Impossibile inviare nuovamente l'e-mail. + application.readmit.success=L'applicazione è stata riammessa con successo. +no.email.log.msg = Nessuna email trovata per userActionId specificato. +user.action.id.not.found = ID azione utente non trovato.