diff --git a/src/main/java/net/gepafin/tendermanagement/constants/AppointmentApiConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/AppointmentApiConstant.java index 6e5ed97c..799421fd 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/AppointmentApiConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/AppointmentApiConstant.java @@ -18,7 +18,7 @@ public class AppointmentApiConstant { public static final boolean CREA_ANAGRAFICA = Boolean.TRUE; public static final boolean SALVA_DOCUMENTI = Boolean.TRUE; public static final String VISURA_PROVIDER = "cerved"; - public static final String VISURA_TYPE = "StandardReport"; + public static final String VISURA_TYPE = "FullReport"; public static final String VISURA_MODE = "visure"; public static final String COD_AGENTE = "UtenzaAPIPortal"; public static final boolean IS_FROM_RATING = Boolean.FALSE; @@ -38,4 +38,7 @@ public class AppointmentApiConstant { public static final String COD_OPERAZIONE = "codOperazione"; public static final String MOTIVAZIONE_ID = "id"; public static final String PRODOTTO_CODE = "code"; + + public static final String WS_ANAGRAFICA_URL= "/WSAnagrafica.getList"; + } diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 1fe74e81..71c63a9f 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -568,10 +568,14 @@ public class GepafinConstant { public static final String PROTOCOL_MITTENTE="mittente"; public static final String PROTOCOL_DESTINATARI="destinatari"; public static final String PROTOCOL_EXTERNAL_YEAR="ANNO_PG"; - public static final String PROTOCOL_EXTERNAL_NUMBER=""; - public static final String PROTOCOL_EXTERNAL_DATE=""; - - + public static final String PROTOCOL_EXTERNAL_NUMBER="NUM_PG"; + public static final String PROTOCOL_EXTERNAL_DATE="DATA_PG_DT"; + public static final String PROTOCOL_DOC_SUFFIX="#noauth"; + public static final String CREATE_NDG="CHECK_OR_CREATE_NDG_CODE"; + public static final String NDG_NOT_FOUND="ndg.not.found"; + public static final String EMAIL_PEC_REQUIRED="email.pec.cannot.null"; + public static final String USER_REQUEST_COMPLETED="user.request.completed"; + public static final String END_DATE_GREATER_THAN_NOW="end.date.greater.than.now"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java index 21b4b716..ee165f63 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java @@ -803,7 +803,7 @@ public class ApplicationAmendmentRequestDao { } - private List getApplicationFormFieldList( + public List getApplicationFormFieldList( ApplicationAmendmentRequestEntity applicationAmendment, List fieldIds) { List applicationFormList = applicationFormRepository @@ -1146,6 +1146,7 @@ public class ApplicationAmendmentRequestDao { public ApplicationAmendmentRequestResponse extendResponseDays(Long id, Long newResponseDays) { log.info("Extending response days for Application Amendment ID: {}, Additional Days: {}", id, newResponseDays); ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity = validatApplicationAmendmentRequestByStatus(id,ApplicationAmendmentRequestEnum.EXPIRED.getValue()); + log.info("Extending response days for Application Amendment ID: {}, Additional Days: {}", id, newResponseDays); if (newResponseDays != null && newResponseDays > 0) { ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity = Utils.getClonedEntityForData(applicationAmendmentRequestEntity); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index c45171e8..21522588 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -1,5 +1,6 @@ package net.gepafin.tendermanagement.dao; +import com.amazonaws.services.dynamodbv2.xspec.S; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -16,6 +17,11 @@ import net.gepafin.tendermanagement.entities.*; import net.gepafin.tendermanagement.entities.SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum; import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.*; +import net.gepafin.tendermanagement.model.request.ApplicationFormFieldRequestBean; +import net.gepafin.tendermanagement.model.request.ApplicationRequest; +import net.gepafin.tendermanagement.model.request.ApplicationRequestBean; +import net.gepafin.tendermanagement.model.request.EmailLogRequest; +import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.SortBy; import net.gepafin.tendermanagement.repositories.*; @@ -50,15 +56,14 @@ import org.springframework.web.multipart.MultipartFile; import jakarta.servlet.http.HttpServletRequest; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; +import java.io.*; import java.lang.reflect.Method; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.sql.Timestamp; import java.text.MessageFormat; +import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; @@ -211,6 +216,12 @@ public class ApplicationDao { @Autowired private ApplicationEvaluationDao applicationEvaluationDao; + @Autowired + private EvaluationCriteriaRepository evaluationCriteriaRepository; + + @Autowired + private CallRepository callRepository; + public final Random random = new Random(); public ApplicationResponseBean createApplication(HttpServletRequest request, ApplicationRequestBean applicationRequestBean, Long formId, Long applicationId) { @@ -551,25 +562,68 @@ public class ApplicationDao { VersionActionTypeEnum actionType = VersionActionTypeEnum.INSERT; List contentResponseBeans = formDao.convertFormEntityToFormResponseBean(formEntity).getContent(); - contentResponseBeans.stream() - .filter(content -> "numberinput".equals(content.getName()) && content.getId().toString().equals(applicationFormFieldRequestBean.getFieldId())) - .map(ContentResponseBean::getSettings) - .flatMap(List::stream) - .filter(setting -> "isRequestedAmount".equals(setting.getName()) && Boolean.TRUE.equals(setting.getValue())) - .findFirst() - .ifPresent(setting -> { - Object fieldValue = applicationFormFieldRequestBean.getFieldValue(); - if(fieldValue!=null) { - try { - BigDecimal amountRequested = new BigDecimal(fieldValue.toString()); - applicationFormEntity.getApplication().setAmountRequested(amountRequested); - log.info("Set amountRequested to {} for Application ID: {}", amountRequested, applicationFormEntity.getApplication().getId()); - } catch (NumberFormatException e) { - log.error("Invalid number format for requested amount: {}", fieldValue, e); - throw new IllegalArgumentException("Field value is not a valid number: " + fieldValue, e); - } - } - }); +// contentResponseBeans.stream() +// .filter(content -> "numberinput".equals(content.getName()) && content.getId().toString().equals(applicationFormFieldRequestBean.getFieldId())) +// .map(ContentResponseBean::getSettings) +// .flatMap(List::stream) +// .filter(setting -> "isRequestedAmount".equals(setting.getName()) && Boolean.TRUE.equals(setting.getValue())) +// .findFirst() +// .ifPresent(setting -> { +// Object fieldValue = applicationFormFieldRequestBean.getFieldValue(); +// if(fieldValue!=null) { +// try { +// BigDecimal amountRequested = new BigDecimal(fieldValue.toString()); +// applicationFormEntity.getApplication().setAmountRequested(amountRequested); +// log.info("Set amountRequested to {} for Application ID: {}", amountRequested, applicationFormEntity.getApplication().getId()); +// } catch (NumberFormatException e) { +// log.error("Invalid number format for requested amount: {}", fieldValue, e); +// throw new IllegalArgumentException("Field value is not a valid number: " + fieldValue, e); +// } +// } +// }); + + contentResponseBeans.stream() + .filter(content -> content.getId().toString().equals(applicationFormFieldRequestBean.getFieldId())) + .findFirst() + .ifPresent(content -> { + Object fieldValue = applicationFormFieldRequestBean.getFieldValue(); + if (fieldValue == null) { + return; + } + + // Convert settings list to a map + Map settingMap = content.getSettings().stream() + .collect(Collectors.toMap(SettingResponseBean::getName, SettingResponseBean::getValue, (v1, v2) -> v1)); + + String fieldType = content.getName(); + + // Define handlers for different (fieldType + settingName) combinations + + // Add handler for isRequestedAmount + if ("numberinput".equals(fieldType) && Boolean.TRUE.equals(settingMap.get("isRequestedAmount"))) { + try { + BigDecimal amountRequested = new BigDecimal(fieldValue.toString()); + applicationFormEntity.getApplication().setAmountRequested(amountRequested); + log.info("Set amountRequested to {} for Application ID: {}", amountRequested, applicationFormEntity.getApplication().getId()); + } catch (NumberFormatException e) { + log.error("Invalid number format for requested amount: {}", fieldValue, e); + throw new IllegalArgumentException("Field value is not a valid number: " + fieldValue, e); + } + } + + // Add handler for isPecEmail + if ("textinput".equals(fieldType) && Boolean.TRUE.equals(settingMap.get("isPecEmail"))) { + applicationFormEntity.getApplication().setPecEmail(fieldValue.toString()); + log.info("Set PEC to {} for Application ID: {}", fieldValue, applicationFormEntity.getApplication().getId()); + } + + if ("textinput".equals(fieldType) && Boolean.TRUE.equals(settingMap.get("isPIVA"))) { + applicationFormEntity.getApplication().setVatNumber(fieldValue.toString()); + log.info("Set PEC to {} for Application ID: {}", fieldValue, applicationFormEntity.getApplication().getId()); + } + + // Run all applicable handlers + }); ApplicationFormFieldEntity oldApplicationFormFieldData = null; @@ -938,10 +992,12 @@ public class ApplicationDao { // call = callService.validatePublishedCall(call.getId()); // checkIfApplicationExists(call, userWithCompanyEntity, userEntity); HubEntity hubEntity = hubService.valdateHub(call.getHub().getId()); - if(hubEntity.getUniqueUuid().equals(defaultHubUuid)){ - checkIfApplicationExists(call, userWithCompanyEntity, userEntity); + if(call.getAllowMultipleApplications() == null || Boolean.FALSE.equals(call.getAllowMultipleApplications())){ + if(hubEntity.getUniqueUuid().equals(defaultHubUuid)){ + checkIfApplicationExists(call, userWithCompanyEntity, userEntity); + } } - ApplicationEntity applicationEntity = createApplicationEntity(userEntity, call, userWithCompanyEntity); + ApplicationEntity applicationEntity = createApplicationEntity(userEntity, call, userWithCompanyEntity); applicationEntity.setComments(applicationRequest.getComments()); applicationEntity = saveApplicationEntity(applicationEntity); return getApplicationResponse(applicationEntity); @@ -1153,8 +1209,16 @@ public class ApplicationDao { // Create the map for body placeholders Map bodyPlaceholders = new HashMap<>(); bodyPlaceholders.put("{{call_name}}", call.getName()); - bodyPlaceholders.put("{{protocol_number}}", protocol.getProtocolNumber().toString()); - bodyPlaceholders.put("{{date}}", DateTimeUtil.formatLocalDateTime(protocol.getCreatedDate(), GepafinConstant.DD_MM_YYYY)); + String protocolNumber=applicationEntity.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(applicationEntity.getProtocol().getProtocolNumber()); + } + bodyPlaceholders.put("{{protocol_number}}",protocolNumber); + String protocolDate= DateTimeUtil.formatLocalDateTime(protocol.getCreatedDate(), GepafinConstant.DD_MM_YYYY); + if(protocol.getExternalProtocolDate()!=null){ + protocolDate= DateTimeUtil.formatLocalDateTime(protocol.getExternalProtocolDate(), GepafinConstant.DD_MM_YYYY); + } + bodyPlaceholders.put("{{date}}", protocolDate); bodyPlaceholders.put("{{time}}", DateTimeUtil.parseLocalTimeToString(protocol.getTime(), GepafinConstant.HH_MM_SS)); // Replace placeholders in the subject and body @@ -1164,26 +1228,39 @@ public class ApplicationDao { String email = userEntity.getEmail(); if (userEntity.getBeneficiary() != null) { emailLogRequest.setRecipientType(RecipientTypeEnum.BENEFICIARY); - email = userEntity.getBeneficiary().getEmail(); - emailLogRequest.setRecipientId(userEntity.getBeneficiary().getId()); + + if(Boolean.TRUE.equals(hub.getUniqueUuid().equals(defaultHubUuid))){ + email=applicationEntity.getPecEmail(); + }else { + email = userEntity.getBeneficiary().getEmail(); + } + emailLogRequest.setRecipientId(userEntity.getBeneficiary().getId()); } emailNotificationDao.sendMail(hub.getId(), subject, body, List.of(email),emailLogRequest); List recipientEmails = new ArrayList<>(); // recipientEmails.add(email); String companyEmail = userWithCompany.getEmail(); String contactEmail = userWithCompany.getContactEmail(); + if(Boolean.TRUE.equals(hub.getUniqueUuid().equals(defaultHubUuid))){ + if (company.getPec()!=null) { + recipientEmails.add(company.getPec()); + }else { + recipientEmails.add(userWithCompany.getPec()); + } + } + else { + if (companyEmail != null && !companyEmail.isEmpty()) { + recipientEmails.add(companyEmail); + } - if (companyEmail != null && !companyEmail.isEmpty()) { - recipientEmails.add(companyEmail); - } - - if (contactEmail != null && !contactEmail.isEmpty() && !contactEmail.equals(companyEmail)) { - recipientEmails.add(contactEmail); - } + if (contactEmail != null && !contactEmail.isEmpty() && !contactEmail.equals(companyEmail)) { + recipientEmails.add(contactEmail); + } + } if(Boolean.FALSE.equals(recipientEmails.isEmpty())){ emailLogRequest.setRecipientId(applicationEntity.getCompanyId()); emailLogRequest.setRecipientType(RecipientTypeEnum.COMPANY); - emailLogRequest.setRecipientEmails(companyEmail); + emailLogRequest.setRecipientEmails(String.valueOf(recipientEmails)); } emailNotificationDao.sendMail(hub.getId(), subject, body, recipientEmails,emailLogRequest); } @@ -1204,8 +1281,17 @@ public class ApplicationDao { // Create the map for body placeholders Map bodyPlaceholders = new HashMap<>(); bodyPlaceholders.put("{{call_name}}", call.getName()); - bodyPlaceholders.put("{{protocol_number}}", protocol.getProtocolNumber().toString()); - bodyPlaceholders.put("{{date}}", DateTimeUtil.formatLocalDateTime(protocol.getCreatedDate(), GepafinConstant.DD_MM_YYYY)); + String protocolNumber=applicationEntity.getProtocol().getExternalProtocolNumber(); + + if(protocolNumber==null){ + protocolNumber= String.valueOf(applicationEntity.getProtocol().getProtocolNumber()); + } + String protocolDate= DateTimeUtil.formatLocalDateTime(protocol.getCreatedDate(), GepafinConstant.DD_MM_YYYY); + if(protocol.getExternalProtocolDate()!=null){ + protocolDate= DateTimeUtil.formatLocalDateTime(protocol.getExternalProtocolDate(), GepafinConstant.DD_MM_YYYY); + } + bodyPlaceholders.put("{{protocol_number}}", protocolNumber); + bodyPlaceholders.put("{{date}}",protocolDate); bodyPlaceholders.put("{{time}}", DateTimeUtil.parseLocalTimeToString(protocol.getTime(), GepafinConstant.HH_MM_SS)); // Replace placeholders in the subject and body @@ -1972,7 +2058,7 @@ public class ApplicationDao { } // Wrap it in ="..." to make Excel treat it as a literal string - return "=\"" + stringValue.replace("\"", "\"\"") + "\""; + return stringValue; } catch (Exception e) { return ""; @@ -2323,7 +2409,11 @@ public class ApplicationDao { 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()); + String protocolNumber=applicationEntity.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(applicationEntity.getProtocol().getProtocolNumber()); + } + bodyPlaceholders.put("{{protocol_number}}", protocolNumber); bodyPlaceholders.put("{{user_action_id}}",emailLogRequest.getUserActionId().toString()); String subject = Utils.replacePlaceholders(systemEmailTemplateResponse.getSubject(), subjectPlaceholders); @@ -2335,4 +2425,128 @@ public class ApplicationDao { emailNotificationDao.sendMail(hub.getId(), subject, body, List.of(GepafinConstant.RINALDO_EMAIL),emailLogRequest); } + public byte[] downloadRankingCsv(Long callId,UserEntity userEntity) { + + CallEntity callEntity = validator.validateUserWithCall(userEntity,callId); + + List applications = + applicationRepository.findByCallIdAndIsDeletedFalseAndStatusIn( + callId, + List.of( + ApplicationStatusForEvaluation.APPROVED.getValue(), + ApplicationStatusForEvaluation.ADMISSIBLE.getValue(), + ApplicationStatusForEvaluation.TECHNICAL_EVALUATION.getValue() + )); + + List dynamicLabels = new ArrayList<>(); // Maintain insertion order, allow duplicates only once + Map> appLabelScoresMap = new HashMap<>(); + + Map applicationMap = new HashMap<>(); + Map appTotalScoreMap = new HashMap<>(); + Map appInstructorMap = new HashMap<>(); // New map to store instructor name per app + + for (ApplicationEntity app : applications) { + Long appId = app.getId(); + applicationMap.put(appId, app); + + ApplicationEvaluationEntity evaluation = + applicationEvaluationRepository.findByApplicationId(appId); + + if (evaluation != null && evaluation.getAssignedApplicationsEntity() != null) { + Long userId = evaluation.getAssignedApplicationsEntity().getUserId(); + if (userId != null) { + userRepository.findById(userId).ifPresent(user -> { + String firstName = user.getFirstName() != null ? user.getFirstName() : ""; + String lastName = user.getLastName() != null ? user.getLastName() : ""; + appInstructorMap.put(appId, firstName + " " + lastName); + }); + } + } + + BigDecimal totalScore = applicationEvaluationDao.calculateTotalScore(evaluation.getCriteria()); + appTotalScoreMap.put(appId, Utils.convertToItalianFormatWithOnlyDecimalValue(String.valueOf(totalScore))); + + List criteriaList = + evaluation.getCriteria() != null ? + Utils.convertJsonToList(evaluation.getCriteria(), new TypeReference>() {}) : + List.of(); + + List dbCriteriaList = applicationEvaluationDao.getCriteriaResponse(appId); + + Map scoreByLabel = new HashMap<>(); + + for (CriteriaResponse criteria : criteriaList) { + Optional matchedDb = dbCriteriaList.stream() + .filter(db -> Objects.equals(db.getId(), criteria.getId())) + .findFirst(); + + String label = matchedDb.map(CriteriaResponse::getLabel).orElse(""); + + if (!dynamicLabels.contains(label)) { + dynamicLabels.add(label); + } + String criteriaScore= String.valueOf(criteria.getScore() != null ? criteria.getScore() : BigDecimal.ZERO); + + scoreByLabel.put(label, Utils.convertToItalianFormatWithOnlyDecimalValue(criteriaScore)); + } + + appLabelScoresMap.put(appId, scoreByLabel); + } + + // Build headers dynamically + List headers = new ArrayList<>(List.of( + "ApplicationID","Application VatNumber", "VatNumber", "Company Name", "Protocol", "Requested Amount", "Status","Instructor Name","Application PEC","Company PEC","Total Score" + )); + headers.addAll(dynamicLabels); + + // Prepare data rows + List> rows = new ArrayList<>(); + + for (ApplicationEntity app : applications) { + Long appId = app.getId(); + CompanyEntity company = companyService.validateCompany(app.getCompanyId()); + ProtocolEntity protocolEntity = app.getProtocol(); + + List row = new ArrayList<>(); + row.add(appId); + row.add(app.getVatNumber()); + row.add(company.getVatNumber()); + row.add(company.getCompanyName()); + row.add(protocolEntity != null ? protocolEntity.getProtocolNumber() : 0L); + String formattedAmount=Utils.convertToItalianFormatWithOnlyDecimalValue(String.valueOf(app.getAmountRequested())); + row.add(formattedAmount); + row.add(app.getStatus()); + row.add(appInstructorMap.getOrDefault(appId, "")); + row.add(app.getPecEmail()); + row.add(company.getPec()); + row.add(appTotalScoreMap.get(appId)); + + Map scores = appLabelScoresMap.getOrDefault(appId, Collections.emptyMap()); + + for (String label : dynamicLabels) { + row.add(scores.getOrDefault(label, "")); + } + + rows.add(row); + } + + // Generate CSV + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); + CSVPrinter csvPrinter = new CSVPrinter(writer, + CSVFormat.DEFAULT + .withDelimiter(';') + .withHeader(headers.toArray(new String[0])))) { + + for (List row : rows) { + csvPrinter.printRecord(row); + } + csvPrinter.flush(); + } catch (IOException e) { + throw new RuntimeException("Error while generating CSV", e); + } + + return out.toByteArray(); + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index 2daae6ae..1ccc1664 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -710,7 +710,11 @@ public class ApplicationEvaluationDao { Map placeHolders = new HashMap<>(); placeHolders.put("{{call_name}}", application.getCall().getName()); - placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + String protocolNumber=application.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(application.getProtocol().getProtocolNumber()); + } + placeHolders.put("{{protocol_number}}", protocolNumber); notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.EVALUATION_CREATION); notificationDao.sendNotificationToInstructor(placeHolders,entity,NotificationTypeEnum.EVALUATION_CREATION); @@ -1151,7 +1155,7 @@ public class ApplicationEvaluationDao { applicationEvaluationResponse.setEmailSendResponse(entity.getEmailSendResponse()); return applicationEvaluationResponse; } - private List prepareEvaluationDocumentBeanList(ApplicationEvaluationEntity entity) { + public List prepareEvaluationDocumentBeanList(ApplicationEvaluationEntity entity) { List docRequest = new ArrayList<>(); if (entity != null && entity.getEvaluationDocument() != null) { @@ -1545,13 +1549,13 @@ public class ApplicationEvaluationDao { return callRepository.findCallEntityByApplicationId(applicationId); } - private List getEvaluationCriterias(CallEntity call) { + public List getEvaluationCriterias(CallEntity call) { log.info("Fetching evaluation criterias for callId: {}", call.getId()); return evaluationCriteriaRepository .findByCallIdAndLookupDataTypeAndIsDeletedFalse(call.getId(), LookUpDataEntity.LookUpDataTypeEnum.EVALUATION_CRITERIA.getValue()); } - private CriteriaResponse buildCriteriaResponse(Long applicationId, EvaluationCriteriaEntity criteria) { + public CriteriaResponse buildCriteriaResponse(Long applicationId, EvaluationCriteriaEntity criteria) { CriteriaResponse response = new CriteriaResponse(); response.setId(criteria.getId()); response.setLabel(criteria.getLookupData().getValue()); @@ -1906,7 +1910,8 @@ public class ApplicationEvaluationDao { Optional existingEntityOptional = applicationEvaluationRepository.findByAssignedApplicationsEntity_IdAndIsDeletedFalse( assignedApplicationsEntity.getId()); ApplicationEvaluationEntity entity; - + UserEntity user=userService.validateUser(application.getUserId()); + HubEntity hub=user.getHub(); EmailSendResponse emailSendResponse = new EmailSendResponse(); if (existingEntityOptional.isPresent()) { ApplicationEvaluationEntity existingEntity = existingEntityOptional.get(); @@ -1927,19 +1932,28 @@ public class ApplicationEvaluationDao { } } - if(newStatus.equals(ApplicationStatusForEvaluation.TECHNICAL_EVALUATION) && Boolean.TRUE.equals(application.getStatus().equals(ApplicationStatusTypeEnum.ADMISSIBLE.getValue()))){ + if(newStatus.equals(ApplicationStatusForEvaluation.TECHNICAL_EVALUATION)){ log.info("Processing technical evaluation for applicationId: {}", application.getId()); processTechnicalEvaluation(application.getId(), application, newStatus); } - if((newStatus.equals(ApplicationStatusForEvaluation.APPROVED) || newStatus.equals(ApplicationStatusForEvaluation.REJECTED)) && application.getStatus().equals(ApplicationStatusTypeEnum.EVALUATION.getValue())) { + if((newStatus.equals(ApplicationStatusForEvaluation.APPROVED) || newStatus.equals(ApplicationStatusForEvaluation.REJECTED))) { application.setStatus(newStatus.getValue()); log.info("Application status updated to {} for applicationId: {}", newStatus, application.getId()); } - application = applicationRepository.save(application); - /** This code is responsible for adding a version history log for the "Update Application" operation. **/ - loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationEntity).newData(application).build()); + if(newStatus.equals(ApplicationStatusForEvaluation.TECHNICAL_EVALUATION_REJECTED)) { + application.setStatus(newStatus.getValue()); + log.info("Application status updated to {} for applicationId: {}", newStatus, application.getId()); + emailNotificationDao.sendMailForApplicationTechnicalEvaluationRejected(application,hub,existingEntity); + application.setDateRejected(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + emailSendResponse = emailDao.buildEmailSendResponseFromRequest(request); + responses = List.of(emailSendResponse); + if (!Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())) { + saveEmailSendResponseToEvaluation(emailSendResponse, existingEntity); + } + } + application = applicationRepository.save(application); ApplicationEvaluationEntity oldApplicationEvaluation = Utils.getClonedEntityForData(existingEntity); AssignedApplicationsEntity oldAssignedApplication = Utils.getClonedEntityForData(assignedApplicationsEntity); @@ -1950,7 +1964,7 @@ public class ApplicationEvaluationDao { throw new CustomValidationException(Status.BAD_REQUEST,Translator.toLocale(GepafinConstant.APPLICATION_CANNOT_APPROVED_OR_REJECTED)); } String statusType = application.getStatus(); - if (application.getStatus().equals(ApplicationStatusTypeEnum.APPROVED.getValue()) || application.getStatus().equals(ApplicationStatusTypeEnum.REJECTED.getValue())) { + if (application.getStatus().equals(ApplicationStatusTypeEnum.APPROVED.getValue()) || application.getStatus().equals(ApplicationStatusTypeEnum.REJECTED.getValue()) || application.getStatus().equals(ApplicationStatusTypeEnum.TECHNICAL_EVALUATION_REJECTED.getValue())) { existingEntity.setStatus(ApplicationEvaluationStatusTypeEnum.CLOSE.getValue()); existingEntity.setClosingDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); assignedApplicationsEntity.setStatus(AssignedApplicationEnum.CLOSE.getValue()); @@ -1965,7 +1979,7 @@ public class ApplicationEvaluationDao { entity = applicationEvaluationRepository.save(existingEntity); assignedApplicationsRepository.save(assignedApplicationsEntity); - if (application.getStatus().equals(ApplicationStatusTypeEnum.APPROVED.getValue()) || application.getStatus().equals(ApplicationStatusTypeEnum.REJECTED.getValue())) { + if (application.getStatus().equals(ApplicationStatusTypeEnum.APPROVED.getValue()) || application.getStatus().equals(ApplicationStatusTypeEnum.REJECTED.getValue()) || application.getStatus().equals(ApplicationStatusTypeEnum.TECHNICAL_EVALUATION_REJECTED.getValue())) { /** This code is responsible for adding a version history log for the "Update Application Evaluation" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationEvaluation).newData(entity).build()); @@ -1995,9 +2009,16 @@ public class ApplicationEvaluationDao { notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_RESULT); } + /** This code is responsible for adding a version history log for the "Update Application" operation. **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationEntity).newData(application).build()); + Map placeHolders = new HashMap<>(); placeHolders.put("{{call_name}}", application.getCall().getName()); - placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + String protocolNumber=application.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(application.getProtocol().getProtocolNumber()); + } + placeHolders.put("{{protocol_number}}", protocolNumber); notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.EVALUATION_RESULT); notificationDao.sendNotificationToInstructor(placeHolders,existingEntity,NotificationTypeEnum.EVALUATION_RESULT); @@ -2565,7 +2586,7 @@ public class ApplicationEvaluationDao { } } - private BigDecimal calculateTotalScore(String criteriaJson){ + public BigDecimal calculateTotalScore(String criteriaJson){ try { ObjectMapper objectMapper = new ObjectMapper(); // Convert JSON string to List of Maps @@ -2584,7 +2605,7 @@ public class ApplicationEvaluationDao { return totalScore; } catch (Exception e) { - log.error(" Error parsing criteria JSON: {}", e.getMessage()); + log.error("Error parsing criteria JSON. Input: {}. Exception: {}", criteriaJson, e.getMessage(), e); return BigDecimal.ZERO; } } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java index f82d9906..ef5779fa 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java @@ -1,1209 +1,1485 @@ -package net.gepafin.tendermanagement.dao; - -import com.amazonaws.services.s3.AmazonS3Client; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import feign.FeignException; -import io.jsonwebtoken.Claims; -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -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; -import net.gepafin.tendermanagement.model.request.AppointmentNdgRequest; -import net.gepafin.tendermanagement.model.request.AppointmentVisuraListRequest; -import net.gepafin.tendermanagement.model.request.AppointmentVisuraRequest; -import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; -import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; -import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; -import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; -import net.gepafin.tendermanagement.model.response.AppointmentLoginResponse; -import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; -import net.gepafin.tendermanagement.model.response.NdgResponse; -import net.gepafin.tendermanagement.repositories.ApplicationRepository; -import net.gepafin.tendermanagement.repositories.CompanyRepository; -import net.gepafin.tendermanagement.repositories.DocumentRepository; -import net.gepafin.tendermanagement.repositories.HubRepository; -import net.gepafin.tendermanagement.repositories.UserRepository; -import net.gepafin.tendermanagement.service.AmazonS3Service; -import net.gepafin.tendermanagement.service.ApplicationService; -import net.gepafin.tendermanagement.service.CompanyService; -import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService; -import net.gepafin.tendermanagement.service.impl.ApplicationEvaluationServiceImpl; -import net.gepafin.tendermanagement.util.LoggingUtil; -import net.gepafin.tendermanagement.util.Utils; -import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; -import net.gepafin.tendermanagement.web.rest.api.errors.Status; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -@Slf4j -@Component -public class AppointmentDao { - - @Value("${appointment.portal.user}") - private String user; - - @Value("${appointment.portal.password}") - private String password; - - @Value("${appointment.portal.source}") - private String source; - - @Value("${appointment.portal.context}") - private String context; - - @Value("${default.hub.uuid}") - private String defaultHubUuid; - - @Value("${aws.s3.url}") - private String s3Url; - - @Value("${aws.s3.bucket.name}") - private String OLD_BUCKET; - - @Autowired - private HubRepository hubRepository; - - @Autowired - private AppointmentApiService appointmentApiService; - - @Autowired - private ApplicationService applicationService; - - @Autowired - private CompanyService companyService; - - @Autowired - private ApplicationRepository applicationRepository; - - @Autowired - private CompanyRepository companyRepository; - - @Autowired - private DocumentDao documentDao; - - @Autowired - private AmazonS3Client s3Client; - - @Autowired - private DocumentRepository documentRepository; - - @Autowired - private HttpServletRequest request; - - @Autowired - private LoggingUtil loggingUtil; - - @Autowired - private TokenProvider tokenProvider; - - @Autowired - private NotificationDao notificationDao; - - @Autowired - private UserRepository userRepository; - - @Autowired - private ApplicationEvaluationServiceImpl applicationEvaluationService; - - @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<>(); - - private static final ThreadLocal threadLocalHubId = new ThreadLocal<>(); - - public NdgResponse checkNdgForAppointment(Long applicationId) { - log.info("Starting NDG check for appointment. applicationId: {}", applicationId); - ApplicationEntity application = applicationService.validateApplication(applicationId); - NdgResponse ndgResponse = new NdgResponse(); - if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_IN_PROGRESS)) { - log.warn("NDG generation already in progress. applicationId: {}", applicationId); - throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); - } - - if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_GENERATED) && application.getNdg() != null) { - ndgResponse.setNdg(application.getNdg()); - return ndgResponse; - } - - // Update application status - log.info("Updating NDG status to IN_PROGRESS. applicationId: {}", applicationId); - application.setNdgStatus(GepafinConstant.NDG_IN_PROGRESS); - applicationRepository.save(application); - - // Start async processing - HubEntity hub = hubRepository.findByHubId(application.getHubId()); - loginToOdessa(hub, application); - startAsyncNdgProcessing(applicationId); - log.info("NDG check initiation completed. applicationId: {}", applicationId); - return ndgResponse; - } - - // 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) { - log.info("Starting login to Odessa. HubId: {}, ApplicationId: {}", hub.getId(), application.getId()); - 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 { - 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 to Odessa successful. Parsing response. HubId: {}", hub.getId()); - String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); - AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); - - 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 { - log.error("Login response from Odessa missing tokenId. HubId: {}", hub.getId()); - 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 forbidden error."); - - CheckPasswordExpiredOrErrorInResponse(application, forbiddenException); - } catch (Exception e) { - log.error("Failed to authenticate user on Odessa (Attempt {}): {}", attempt, e.getMessage(), e); - } - } - throw new RuntimeException("Max retries exceeded. Failed to login to Odessa."); - } - private void CheckPasswordExpiredOrErrorInResponse(ApplicationEntity application, FeignException.Forbidden forbiddenException) { - - String responseBody = forbiddenException.contentUTF8(); - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(responseBody); - JsonNode errorsNode = rootNode.path("errors"); - - 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); - } - log.warn("Detected PASSWORD_EXPIRED error during Odessa login. ApplicationId: {}", application.getId()); - 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("Unexpected exception during Odessa login.Error: {}",e.getMessage(), e); - } - } - - private void startAsyncNdgProcessing(Long applicationId) { - // Check if a thread is already running for this application - if (executorMap.containsKey(applicationId)) { - log.warn("Async processing already running for applicationId: {}", applicationId); - return; - } - - // Create a dedicated thread for asynchronous processing - ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { - Thread thread = new Thread(runnable); - thread.setName("AsyncNdgProcessing-" + applicationId); - return thread; - }); - executorMap.put(applicationId, executor); - - executor.submit(() -> { - try { - log.info("Starting async processing for applicationId: {}", applicationId); - processNdgGeneration(applicationId); - } catch (Exception e) { - log.error("Error in async NDG processing for applicationId: {}", applicationId, e); - } finally { - // Cleanup resources - ExecutorService executorToShutdown = executorMap.remove(applicationId); - if (executorToShutdown != null) { - executorToShutdown.shutdown(); - } - log.info("Async processing completed for applicationId: {}", applicationId); - } - }); - } - - private void processNdgGeneration(Long applicationId) { - // Validate application, company, and hub - log.info("Starting NDG generation process for applicationId: {}", applicationId); - ApplicationEntity application = applicationService.validateApplication(applicationId); - CompanyEntity company = companyService.validateCompany(application.getCompanyId()); - HubEntity hub = hubRepository.findByHubId(application.getHubId()); - - if (!hub.getUniqueUuid().equals(defaultHubUuid)) { - log.info("Ndg cannot be created for another Hub, it is default for Gepafin."); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_NDG_FOR_ANOTHER_HUB)); - } - - try { - // Authenticate and fetch token if required - if (hub.getAppointmentAuthTokenId() == null || hub.getAreaCode() == null) { - authenticateAndSaveToken(hub, application); - } - - String authorizationToken = getBearerToken(hub); - - // Try retrieving NDG by VAT number - AppointmentLoginResponse ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application); - if (isNdgValid(ndgResponse.getNdg())) { - saveNdgAndIdVisura(application, company, ndgResponse.getNdg(), null); - log.info("NDG successfully generated for applicationId: {}", applicationId); - } else { - // If NDG isn't immediately available, start polling - handleNdgPolling(application, company, hub, authorizationToken); - } - } catch (Exception e) { - log.error("Exception occurred during NDG generation. ApplicationId: {}, CompanyId: {}, HubId: {}, Error: {}", - applicationId, company.getId(), hub.getId(), e.getMessage(), e); - } - } - - private void handleNdgPolling(ApplicationEntity application, CompanyEntity company, HubEntity hub, String authorizationToken) { - - try { - log.info("Starting NDG polling for applicationId: {}, CompanyId: {}, HubId: {}", application.getId(),company.getId(), hub.getId()); - long startTime = System.currentTimeMillis(); - - while (true) { - if (application.getNdg() != null) { - log.info("NDG retrieved for applicationId: {}", application.getId()); - break; - } - - try { - // Fetch Visura list and attempt to parse NDG - String visuraListJson = getVisuraList(application.getIdVisura(), authorizationToken, application, hub); - log.debug("Parsing NDG from visura list response | ApplicationId: {}", application.getId()); - String ndg = parseNdgFromVisuraListResponse(visuraListJson); - if (isNdgValid(ndg)) { - // CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); - // ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); - - log.info("Valid NDG retrieved: {} | ApplicationId: {}", ndg, application.getId()); - company.setNdg(ndg); - application.setNdg(ndg); - application.setNdgStatus(GepafinConstant.NDG_GENERATED); - application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); - applicationRepository.save(application); - companyRepository.save(company); - ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); -// 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); - log.info("NDG saved successfully for applicationId: {}", application.getId()); - break; - } - - // Check if polling has timed out - if (System.currentTimeMillis() - startTime > TimeUnit.HOURS.toMillis(2)) { - log.warn("NDG polling timed out for applicationId: {}", application.getId()); - application.setNdgStatus(GepafinConstant.NDG_FAILED); - applicationRepository.save(application); - break; - } - - // Wait before the next polling attempt - Thread.sleep(TimeUnit.MINUTES.toMillis(15)); - } catch (InterruptedException e) { - log.warn("NDG polling interrupted for applicationId: {}", application.getId()); - Thread.currentThread().interrupt(); - break; - } catch (Exception e) { - log.error("Error during NDG polling for applicationId: {}", application.getId(), e); - } - } - } finally { - log.info("NDG polling completed for applicationId: {}", application.getId()); - } - } - - private static String getBearerToken(HubEntity hub) { - - return "Bearer " + hub.getAppointmentAuthTokenId(); - } - - private boolean isNdgValid(String ndg) { - - return ndg != null && !ndg.isEmpty(); - } - - private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg, String idVisura) { - - application.setNdg(ndg); - application.setIdVisura(idVisura); - application.setNdgStatus(GepafinConstant.NDG_GENERATED); - application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); - company.setNdg(ndg); - companyRepository.save(company); - applicationRepository.save(application); - ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); -// 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); - log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg()); - } - - private String getVisuraList(String idVisura, String authorizationToken, ApplicationEntity application, HubEntity hub) { - - log.info("Initiating Visura list retrieval | ApplicationId: {}, HubId: {}, IdVisura: {}", application.getId(), hub.getId(), idVisura); - AppointmentVisuraListRequest visuraListRequest = new AppointmentVisuraListRequest(); - AppointmentVisuraListRequest.VisuraFilter filter = new AppointmentVisuraListRequest.VisuraFilter(); - filter.setIdVisura(idVisura); - visuraListRequest.setFilter(filter); - - try { - String requestJson = Utils.convertObjectToJson(visuraListRequest); - ResponseEntity response = appointmentApiService.getVisuraList(requestJson, authorizationToken); - return Utils.convertObjectToJson(response.getBody()); - } catch (FeignException.Forbidden forbiddenException) { - log.warn("403 Forbidden while fetching Visura list. Attempting token regeneration | ApplicationId: {}, HubId: {}", application.getId(), hub.getId()); - // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub, application); - return getVisuraList(idVisura, newAuthorizationToken, application, hub); - } catch (Exception e) { - log.error("Error while fetching Visura list | ApplicationId: {}, HubId: {}, Error: {}", application.getId(), hub.getId(), e.getMessage(), e); - throw new RuntimeException("Error fetching Ndg List", e); - } - } - - private AppointmentLoginResponse retrieveNdgByVatNumber(String vatNumber, String authorizationToken, HubEntity hub, ApplicationEntity application) { - - try { - log.info("Initiating NDG retrieval by VAT number | ApplicationId: {}, HubId: {}, VAT: {}", application.getId(), hub.getId(), vatNumber); - // Prepare the NDG request - AppointmentNdgRequest ndgRequest = getAppointmentNdgRequest(vatNumber); - // Call the API to retrieve NDG - ResponseEntity response = appointmentApiService.getNdgByVatNumber(ndgRequest, authorizationToken); - String responseJson = Utils.convertObjectToJson(response.getBody()); - // Parse and return the NDG response - return parseNdgResponse(responseJson); - } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden during NDG retrieval | ApplicationId: {}, HubId: {}", application.getId(), hub.getId()); - logForbiddenError(); - // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub, application); - return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application); - } catch (Exception e) { - log.error("Error during NDG retrieval | ApplicationId: {}, HubId: {}, Message: {}", application.getId(), hub.getId(), e.getMessage(), e); - throw new RuntimeException("NDG retrieval failed.", e); - } - } - - private String regenerateTokenAndSave(HubEntity hub, ApplicationEntity application) { - - hub = authenticateAndSaveToken(hub, application); - return "Bearer " + hub.getAppointmentAuthTokenId(); - } - - private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) { - - try { - String visuraRequest = getAppointmentVisuraRequest(company, hub.getAreaCode()); - ResponseEntity response = appointmentApiService.createVisura(visuraRequest, authorizationToken); - String responseJson = Utils.convertObjectToJson(response.getBody()); - return parseVisuraResponse(responseJson); - } catch (FeignException.Forbidden forbiddenException) { - logForbiddenError(); - // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub, null); - return createVisura(company, newAuthorizationToken, hub); - } catch (Exception e) { - log.error("Failed to create Visura for Ndg : {}", e.getMessage()); - throw new RuntimeException("Visura creation failed for Ndg.", e); - } - } - - private static void logForbiddenError() { - - log.error("403 Forbidden received while retrieving NDG. Regenerating token..."); - } - - private static AppointmentNdgRequest getAppointmentNdgRequest(String vatNumber) { - - log.info("Creating Appointment NDG Request | VAT Number: {}", vatNumber); - AppointmentNdgRequest request = new AppointmentNdgRequest(); - AppointmentNdgRequest.Filter filter = new AppointmentNdgRequest.Filter(); - filter.setPartitaIva(vatNumber); - - AppointmentNdgRequest.Pagination pagination = new AppointmentNdgRequest.Pagination(); - pagination.setTargetPage(AppointmentApiConstant.TARGET_PAGE_SIZE); - pagination.setRecordsPerPage(AppointmentApiConstant.RECORD_PER_PAGE_SIZE); - - request.setFilter(filter); - request.setPagination(pagination); - return request; - } - - private static String getAppointmentVisuraRequest(CompanyEntity company, String areaCode) { - - AppointmentVisuraRequest visuraRequest = new AppointmentVisuraRequest(); - AppointmentVisuraRequest.VisuraInput input = new AppointmentVisuraRequest.VisuraInput(); - input.setPartitaIva(company.getVatNumber()); - input.setCodiceFiscale(company.getCodiceFiscale()); - input.setCodArea(areaCode); - input.setVisuraMode(AppointmentApiConstant.VISURA_MODE); - input.setVisuraProvider(AppointmentApiConstant.VISURA_PROVIDER); - input.setCodAgente(AppointmentApiConstant.COD_AGENTE); - input.setAnagraficaLegame(AppointmentApiConstant.IS_ANAGRAFICA_LEGAME); - input.setCreaAnagrafica(AppointmentApiConstant.CREA_ANAGRAFICA); - input.setFromRating(AppointmentApiConstant.IS_FROM_RATING); - input.setSalvaDocumenti(AppointmentApiConstant.SALVA_DOCUMENTI); - input.setVisuraType(AppointmentApiConstant.VISURA_TYPE); - visuraRequest.setInput(input); - return Utils.convertObjectToJson(visuraRequest); - } - - private String parseNdgFromVisuraListResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); - - if (dataNode != null && dataNode.isArray() && dataNode.size() > 0) { - JsonNode firstEntry = dataNode.get(0); - JsonNode ndgClienteNode = firstEntry.get("ndgCliente"); - if (ndgClienteNode != null && ndgClienteNode.get("code") != null) { - String code = ndgClienteNode.get("code").asText(); - return normalizeNullValue(code); - } - } - log.warn("NDG not found in Visura List API response."); - return null; - } catch (Exception e) { - log.error("Failed to parse NDG from Visura List API response: {}", e.getMessage(), e); - throw new RuntimeException("Error parsing NDG from Visura List API response", e); - } - } - - public AppointmentLoginResponse parseLoginResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); - - if (dataNode != null) { - AppointmentLoginResponse response = new AppointmentLoginResponse(); - response.setTokenId(dataNode.get("tokenId").asText()); - JsonNode areasNode = dataNode.get("areas"); - if (areasNode != null && areasNode.isArray() && areasNode.size() > 0) { - response.setAreaCode(areasNode.get(0).get("code").asText()); - } - response.setCompanyId(dataNode.get("companyId").asLong()); - return response; - } else { - throw new RuntimeException("Invalid JSON structure: Missing 'data' node."); - } - } catch (Exception e) { - throw new RuntimeException("Failed to parse response from loginApi for odessa: " + e.getMessage(), e); - } - } - - public AppointmentLoginResponse parseVisuraResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); - - if (dataNode != null) { - AppointmentLoginResponse response = new AppointmentLoginResponse(); - response.setIdVisura(normalizeNullValue(dataNode.get(GepafinConstant.ID_VISURA_STRING).asText())); - response.setNdg(normalizeNullValue(dataNode.get(GepafinConstant.NDG_STRING).asText())); - return response; - } else { - throw new RuntimeException("Invalid JSON structure: Missing 'data' node."); - } - } catch (Exception e) { - throw new RuntimeException("Failed to parse response: " + e.getMessage(), e); - } - } - - public AppointmentLoginResponse parseNdgResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode dataArray = rootNode.get(GepafinConstant.DATA_STRING); - if (dataArray == null || !dataArray.isArray() || dataArray.isEmpty()) { - log.info("NDG data is empty or missing in the response."); - AppointmentLoginResponse emptyResponse = new AppointmentLoginResponse(); - emptyResponse.setNdg(null); - return emptyResponse; - } - JsonNode firstDataEntry = dataArray.get(0); - AppointmentLoginResponse response = new AppointmentLoginResponse(); - if (firstDataEntry.has(GepafinConstant.NDG_STRING)) { - response.setNdg(normalizeNullValue(firstDataEntry.get(GepafinConstant.NDG_STRING).asText())); - } - return response; - } catch (Exception e) { - log.error("Failed to parse response: {}", e.getMessage(), e); - throw new RuntimeException("Failed to parse NDG response.", e); - } - } - - private String normalizeNullValue(String value) { - - return (value == null || GepafinConstant.NULL_STRING.equalsIgnoreCase(value.trim())) ? null : value; - } - - public AppointmentCreationResponse createAppointment(Long applicationId, CreateAppointmentRequest createAppointmentRequest) { - // Validate the application - log.info("Starting appointment creation for applicationId: {}", applicationId); - ApplicationEntity application = applicationService.validateApplication(applicationId); - - AppointmentCreationResponse appointmentCreationResponse = new AppointmentCreationResponse(); - - ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); - HubEntity hub = hubRepository.findByHubId(application.getHubId()); - - // Check hub UUID and enforce constraints - if (!hub.getUniqueUuid().equals(defaultHubUuid)) { - log.info("Appointment cannot be created for another Hub; default is required for Gepafin."); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_APPOINTMENT_FOR_ANOTHER_HUB)); - } - - try { - // Pre-check conditions for appointment creation - if (application.getNdg() != null && !Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS) && application.getAppointmentId() != null) { - appointmentCreationResponse.setAppointmentId(application.getAppointmentId()); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_ALREADY_CREATED)); - // return appointmentCreationResponse; - } - - if (application.getNdg() == null && Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS)) { - log.warn("NDG in progress but not available for applicationId: {}", applicationId); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION)); - } - - // Generate authorization token and fetch template data - String authorizationToken = regenerateTokenAndSave(hub, application); - Long appointmentTemplateId = application.getCall().getAppointmentTemplateId(); - if (appointmentTemplateId == null) { - log.error("Missing appointment template ID for applicationId: {}", applicationId); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_CANNOT_BE_CREATED)); - } - ResponseEntity response = appointmentApiService.getAppointmentTemplateForTemplateCreation(authorizationToken, appointmentTemplateId); - - if (response.getStatusCode() != HttpStatus.OK) { - log.error("Failed to retrieve appointment template for appointment creation. Status: {}", response.getStatusCode()); - throw new IllegalStateException("Failed to retrieve appointment template for appointment creation"); - } - - // Parse template data - String responseDataForTemplate = Utils.convertObjectToJson(response.getBody()); - AppointmentCreationRequest templateRichiestaData = parseTemplateResponseData(responseDataForTemplate); - - // Build the appointment request body - AppointmentCreationRequest appointmentCreationRequest = buildAppointmentCreationRequest(applicationId, createAppointmentRequest, appointmentTemplateId, - templateRichiestaData); - log.info("AppointmentCreationRequest : {}", appointmentCreationRequest); - String appointmentRequestBody = Utils.convertObjectToJson(appointmentCreationRequest); - - // Make API call to create the appointment - log.info("Context:{}, Authorization Token : {}, RequestBody : {}", context, authorizationToken, appointmentRequestBody); - ResponseEntity appointmentResponse = appointmentApiService.createAppointment(authorizationToken, context, appointmentRequestBody); - String appointmentId = extractAppointmentIdFromResponse(appointmentResponse); - - if (appointmentId == null) { - log.error("Failed to extract appointment ID from response for applicationId: {}", applicationId); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_NOT_CREATED)); - } - // Update application with the appointment ID - application.setAppointmentId(appointmentId); - application.setStatus(ApplicationStatusTypeEnum.APPOINTMENT.getValue()); - applicationRepository.save(application); - - // Log version history - loggingUtil.addVersionHistory( - VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); - - appointmentCreationResponse.setAppointmentId(appointmentId); - return appointmentCreationResponse; - - } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden received while retrieving template. Attempting to regenerate token and retry. Application ID: {}", applicationId); - regenerateTokenAndSave(hub, application); - return createAppointment(applicationId, createAppointmentRequest); - } - } - - private String extractAppointmentIdFromResponse(ResponseEntity appointmentResponse) { - - if (appointmentResponse.getBody() != null) { - log.info("Appointment API Response : {}", appointmentResponse.getBody()); - 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; - } - - public AppointmentCreationRequest parseTemplateResponseData(String jsonResponse) { - - try { - - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode richiesteClienteArray = rootNode.path(GepafinConstant.DATA_STRING).path(GepafinConstant.RICHIESTE_CLIENTE_STRING); - - // Initialize the result object - AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); - AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); - List richiestaClienteList = new ArrayList<>(); - if (!richiesteClienteArray.isArray()) { - log.warn("richiesteCliente array is missing or not an array."); - return new AppointmentCreationRequest(); - } - for (JsonNode richiestaNode : richiesteClienteArray) { - if (richiestaNode.isNull()) - continue; - - AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente(); - 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)); - - richiestaClienteList.add(richiestaCliente); - } - - input.setRichiestaCliente(richiestaClienteList); - appointmentCreationRequest.setInput(input); - - return appointmentCreationRequest; - - } catch (JsonProcessingException e) { - log.error("JSON processing error: {}", e.getMessage(), e); - throw new IllegalStateException("Invalid JSON structure in template response", e); - } - } - - private String getTextValue(JsonNode node, String fieldName) { - - return node.path(fieldName).isTextual() ? node.path(fieldName).asText() : null; - } - - private int getIntValue(JsonNode node) { - - return node.path(AppointmentApiConstant.MOTIVAZIONE).path(AppointmentApiConstant.MOTIVAZIONE_ID).asInt(); - } - - public AppointmentCreationRequest buildAppointmentCreationRequest(Long applicationId, CreateAppointmentRequest createAppointmentRequest, Long areaCode, - AppointmentCreationRequest templateRichiestaData) { - - ApplicationEntity application = applicationService.validateApplication(applicationId); - CreateAppointmentRequest.Nota nota = createAppointmentRequest.getNota(); - - AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); - AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); - - // Set Input Fields - input.setId(areaCode); - input.setNdg(application.getNdg()); - - // Populate richiestaCliente from template data - List richiestaClienteList = new ArrayList<>(); - for (AppointmentCreationRequest.RichiestaCliente templateRichiesta : templateRichiestaData.getInput().getRichiestaCliente()) { - AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente(); - BeanUtils.copyProperties(templateRichiesta, richiestaCliente); - - // Add specific `nota` - AppointmentCreationRequest.Nota requestNota = new AppointmentCreationRequest.Nota(); - requestNota.setTitolo(nota.getTitolo()); - requestNota.setTesto(nota.getTesto()); - richiestaCliente.setNota(requestNota); - richiestaCliente.setDurataMesiFinanziamento(createAppointmentRequest.getDurataMesiFinanziamento()); - richiestaCliente.setImportoBreveTermine(createAppointmentRequest.getImportoBreveTermine()); - richiestaClienteList.add(richiestaCliente); - } - - input.setRichiestaCliente(richiestaClienteList); - appointmentCreationRequest.setInput(input); - return appointmentCreationRequest; - } - - public DocumentUploadResponse uploadDocumentToExternalSystem(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { - log.info("Initiating upload to external system for documentId: {}", documentId); - // 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, 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(); - response.setDocumentAttachmentId(systemDoc.getDocumentAttachmentId()); - return response; - } - // Check if a thread is already running for this document upload - if (threadForDocumentMap.containsKey(documentId)) { - log.warn("Document upload already running for documentId: {}", documentId); - throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS)); - } - // Start the upload process in the background - ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { - Thread thread = new Thread(runnable); - thread.setName(GepafinConstant.ASYNC_DOCUMENT_UPLOAD_NAME + documentId); - return thread; - }); - 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, finalApplication); - } catch (Exception e) { - log.error("Error in async document upload for documentId: {}", documentId, e); - } finally { - // Cleanup resources - ExecutorService executorToShutdown = threadForDocumentMap.remove(documentId); - if (executorToShutdown != null) { - executorToShutdown.shutdown(); - threadLocalHubId.remove(); - } - log.info("Async document upload completed for documentId: {}", documentId); - } - }); - return null; - } - - private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, ApplicationEntity application) { - log.info("Starting sync document upload for documentId: {}", documentId); - // Synchronous upload logic - DocumentEntity systemDoc = documentDao.validateDocument(documentId); - - Long hubId = threadLocalHubId.get(); - HubEntity hub = hubRepository.findByHubId(hubId); - - if (!hub.getUniqueUuid().equals(defaultHubUuid)) { - log.info("Document cannot be uploaded for another Hub, it is default for Gepafin."); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_DOCUMENT_UPLOAD_FOR_ANOTHER_HUB)); - } - - log.info("Got Document in system: {}", systemDoc); - String oldUrl = systemDoc.getFilePath(); - String authorizationToken = getBearerToken(hub); - - try { - File localFile = downloadFileFromS3(oldUrl); - MultipartFile multipartFile = convertFileToMultipartFile(localFile); - - UploadDocToExternalSystemRequest externalSystemRequest = new UploadDocToExternalSystemRequest(); - externalSystemRequest.setInput(getUploadDocumentInput(docToExternalSystemRequest)); - - String uploadDocRequest = Utils.convertObjectToJson(externalSystemRequest); - ResponseEntity uploadedDocumentData = appointmentApiService.uploadDocumentToExternalSystemForAppointment(authorizationToken, context, uploadDocRequest, - multipartFile); - - String responseData = Utils.convertObjectToJson(uploadedDocumentData.getBody()); - DocumentUploadResponse parsedResponse = parseDocumentUploadResponse(responseData); - - if (parsedResponse == null) { - log.error("Upload failed: parsed response is null for documentId: {}", documentId); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_UPLOADING_DOCUMENT)); - } - - // Save the documentAttachmentId to the database - systemDoc.setDocumentAttachmentId(parsedResponse.getDocumentAttachmentId()); - documentRepository.save(systemDoc); - - log.info("Document uploaded successfully to external system: {}", parsedResponse); - } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden from external system during upload for documentId: {}. Retrying with new token...", documentId); - 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)); - } - } - - private UploadDocToExternalSystemRequest.Input getUploadDocumentInput(UploadDocToExternalSystemRequest docToExternalSystemRequest) { - - UploadDocToExternalSystemRequest.Input input = new UploadDocToExternalSystemRequest.Input(); - input.setIdTipoProtocollo(docToExternalSystemRequest.getInput().getIdTipoProtocollo()); - input.setIdClassificazione(docToExternalSystemRequest.getInput().getIdClassificazione()); - input.setFlagDaFirmare(docToExternalSystemRequest.getInput().getFlagDaFirmare()); - input.setDescrizione(docToExternalSystemRequest.getInput().getDescrizione()); - - UploadDocToExternalSystemRequest.Input.Attributes attributes = new UploadDocToExternalSystemRequest.Input.Attributes(); - attributes.setNdg(docToExternalSystemRequest.getInput().getAttributes().getNdg()); - attributes.setEmail(docToExternalSystemRequest.getInput().getAttributes().getEmail()); - - input.setAttributes(attributes); - return input; - } - - public static MultipartFile convertFileToMultipartFile(File file) throws IOException { - - FileInputStream input = new FileInputStream(file); - return new MockMultipartFile(file.getName(), file.getName(), MediaType.APPLICATION_OCTET_STREAM_VALUE, input); - } - - private File downloadFileFromS3(String fileUrl) throws Exception { - - String key = amazonS3Service.extractS3KeyFromUrl(fileUrl); - String fileName = extractFileName(key); - String folderPath = key.substring(0, key.lastIndexOf("/")); - File localFile = new File(GepafinConstant.TEMP_FILE_PATH + fileName); - - try (InputStream s3Stream = amazonS3Service.getFile(folderPath, key); FileOutputStream outputStream = new FileOutputStream(localFile)) { - s3Stream.transferTo(outputStream); - } - - log.info("Downloaded file from old S3 bucket: {}", key); - return localFile; - } - - private String extractFileName(String filePath) { - - String[] parts = filePath.split("/"); - return parts[parts.length - 1]; - - } - - public DocumentUploadResponse parseDocumentUploadResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - - // Navigate to the "data" node - JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); - if (dataNode != null) { - DocumentUploadResponse response = new DocumentUploadResponse(); - - // Extract "documentAttachmentId" - JsonNode documentAttachmentIdNode = dataNode.get(GepafinConstant.DOCUMENT_ATTACHMENT_ID_STRING); - if (documentAttachmentIdNode != null) { - response.setDocumentAttachmentId(documentAttachmentIdNode.asText()); - } else { - throw new RuntimeException("Invalid JSON structure: Missing 'documentAttachmentId' node."); - } - - return response; - } else { - return null; - } - } catch (Exception e) { - throw new RuntimeException("Failed to parse response: " + e.getMessage(), e); - } - } -} \ No newline at end of file +package net.gepafin.tendermanagement.dao; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.FeignException; +import io.jsonwebtoken.Claims; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +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.*; +import net.gepafin.tendermanagement.enums.*; +import net.gepafin.tendermanagement.model.request.AppointmentCreationRequest; +import net.gepafin.tendermanagement.model.request.AppointmentNdgRequest; +import net.gepafin.tendermanagement.model.request.AppointmentVisuraListRequest; +import net.gepafin.tendermanagement.model.request.AppointmentVisuraRequest; +import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; +import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; +import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; +import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; +import net.gepafin.tendermanagement.model.response.AppointmentLoginResponse; +import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; +import net.gepafin.tendermanagement.model.response.NdgResponse; +import net.gepafin.tendermanagement.repositories.*; +import net.gepafin.tendermanagement.service.AmazonS3Service; +import net.gepafin.tendermanagement.service.ApplicationService; +import net.gepafin.tendermanagement.service.CompanyService; +import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService; +import net.gepafin.tendermanagement.service.impl.ApplicationEvaluationServiceImpl; +import net.gepafin.tendermanagement.util.LoggingUtil; +import net.gepafin.tendermanagement.util.Utils; +import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; +import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; + +@Slf4j +@Component +public class AppointmentDao { + + @Value("${appointment.portal.user}") + private String user; + + @Value("${appointment.portal.password}") + private String password; + + @Value("${appointment.portal.source}") + private String source; + + @Value("${appointment.portal.context}") + private String context; + + @Value("${default.hub.uuid}") + private String defaultHubUuid; + + @Value("${aws.s3.url}") + private String s3Url; + + @Value("${aws.s3.bucket.name}") + private String OLD_BUCKET; + + @Autowired + private HubRepository hubRepository; + + @Autowired + private AppointmentApiService appointmentApiService; + + @Autowired + private ApplicationService applicationService; + + @Autowired + private CompanyService companyService; + + @Autowired + private ApplicationRepository applicationRepository; + + @Autowired + private CompanyRepository companyRepository; + + @Autowired + private DocumentDao documentDao; + + @Autowired + private AmazonS3Client s3Client; + + @Autowired + private DocumentRepository documentRepository; + + @Autowired + private HttpServletRequest request; + + @Autowired + private LoggingUtil loggingUtil; + + @Autowired + private TokenProvider tokenProvider; + + @Autowired + private NotificationDao notificationDao; + + @Autowired + private UserRepository userRepository; + + @Autowired + private ApplicationEvaluationServiceImpl applicationEvaluationService; + + @Autowired + private AmazonS3Service amazonS3Service; + + @Autowired + private ApplicationDao applicationDao; + + @Autowired + private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; + + @Autowired + private ApplicationEvaluationDao applicationEvaluationDao; + + @Autowired + private NdganagRepository ndganagRepository; + + private final Map executorMap = new ConcurrentHashMap<>(); + + + private final ConcurrentHashMap threadForDocumentMap = new ConcurrentHashMap<>(); + + private static final ThreadLocal threadLocalHubId = new ThreadLocal<>(); + + public NdgResponse checkNdgForAppointment(Long applicationId) { + log.info("Starting NDG check for appointment. applicationId: {}", applicationId); + ApplicationEntity application = applicationService.validateApplication(applicationId); + ApplicationEntity oldApplication = Utils.getClonedEntityForData(application); + + NdgResponse ndgResponse = new NdgResponse(); + CompanyEntity company = companyService.validateCompany(application.getCompanyId()); + NdganagEntity ndganagEntity = ndganagRepository.findByVatNumber(company.getVatNumber()); + if (ndganagEntity != null && ndganagEntity.getNdg() != null) { + ndgResponse.setNdg(ndganagEntity.getNdg()); + return ndgResponse; + } + + if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_IN_PROGRESS)) { + log.warn("NDG generation already in progress. applicationId: {}", applicationId); + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); + } + + if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_GENERATED) && application.getNdg() != null) { + ndgResponse.setNdg(application.getNdg()); + return ndgResponse; + } + + // Update application status + log.info("Updating NDG status of applicationId: {}", applicationId); + application.setNdgStatus(NdgStatusEnum.NDG_INITITATED.getValue()); + applicationRepository.save(application); + + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + + + // Start async processing + HubEntity hub = hubRepository.findByHubId(application.getHubId()); + loginToOdessa(hub, application); + startAsyncNdgProcessing(applicationId); + log.info("NDG check initiation completed. applicationId: {}", applicationId); + return ndgResponse; + } + + // 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) { + log.info("Starting login to Odessa. HubId: {}, ApplicationId: {}", hub.getId(), application.getId()); + 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 { + 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 to Odessa successful. Parsing response. HubId: {}", hub.getId()); + String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); + AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); + + 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 { + log.error("Login response from Odessa missing tokenId. HubId: {}", hub.getId()); + 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 forbidden error."); + + CheckPasswordExpiredOrErrorInResponse(application, forbiddenException); + } catch (Exception e) { + log.error("Failed to authenticate user on Odessa (Attempt {}): {}", attempt, e.getMessage(), e); + } + } + throw new RuntimeException("Max retries exceeded. Failed to login to Odessa."); + } + + private void CheckPasswordExpiredOrErrorInResponse(ApplicationEntity application, FeignException.Forbidden forbiddenException) { + + String responseBody = forbiddenException.contentUTF8(); + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(responseBody); + JsonNode errorsNode = rootNode.path("errors"); + + 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); + } + log.warn("Detected PASSWORD_EXPIRED error during Odessa login. ApplicationId: {}", application.getId()); + 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("Unexpected exception during Odessa login.Error: {}", e.getMessage(), e); + } + } + +// private void startAsyncNdgProcessing(Long applicationId) { +// // If already polling for this applicationId, do nothing: +// if (executorMap.containsKey(applicationId)) { +// log.warn("Async processing already running for applicationId: {}", applicationId); +// return; +// } +// ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request); +// +// ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> { +// Thread t = new Thread(runnable); +// t.setName("AsyncNdgProcessing-" + applicationId); +// return t; +// }); +// executorMap.put(applicationId, scheduler); +// +// // Record the start time so we can stop after 2 hours: +// long startTime = System.currentTimeMillis(); +// long twoHoursMs = TimeUnit.HOURS.toMillis(2); +// long fifteenMin = 15; // in MINUTES +// +// // We need a reference to cancel the scheduled task from inside itself when we're done: +// AtomicReference> futureRef = new AtomicReference<>(); +// +// Runnable pollingTask = () -> { +// RequestContextHolder.setRequestAttributes(requestAttributes, true); +// Utils.setHttpServletRequestForThread(request,HttpMethodEnum.POST.getValue(),GepafinConstant.CREATE_NDG, (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID)); +// try { +// // 1) If 2 hours have already passed, mark as FAILED and shut down: +// if (System.currentTimeMillis() - startTime > twoHoursMs) { +// ApplicationEntity app = applicationService.validateApplication(applicationId); +// log.warn("2-hour timeout reached for applicationId {}. Marking NDG_FAILED.", applicationId); +// app.setNdgStatus(GepafinConstant.NDG_FAILED); +// applicationRepository.save(app); +// +// futureRef.get().cancel(false); +// shutdownScheduler(applicationId); +// return; +// } +// +// // 2) Otherwise, call processNdgGeneration once: +// processNdgGeneration(applicationId); +// +// // 3) After return, check if NDG is now set or timed out. If so, cancel & shut down: +// ApplicationEntity updated = applicationService.validateApplication(applicationId); +// if (isNdgValid(updated.getNdg())) { +// log.info("NDG found for applicationId {}. Shutting down scheduler.", applicationId); +// futureRef.get().cancel(false); +// shutdownScheduler(applicationId); +// } else if (updated.getNdgStatus() != null && updated.getNdgStatus().equals(GepafinConstant.NDG_FAILED)) { +// log.info("NDG status is NDG_FAILED for applicationId {}. Shutting down scheduler.", applicationId); +// futureRef.get().cancel(false); +// shutdownScheduler(applicationId); +// } +// // Otherwise: no NDG yet, not timed out → next run happens in 15 minutes automatically. +// } catch (Exception ex) { +// log.error("Unexpected error during scheduled polling for applicationId {}: {}", applicationId, ex.getMessage(), ex); +// try { +// ApplicationEntity checkApp = applicationService.validateApplication(applicationId); +// if (System.currentTimeMillis() - startTime > twoHoursMs) { +// log.warn("After exception, 2-hour window passed for applicationId {}. Marking NDG_FAILED.", applicationId); +// checkApp.setNdgStatus(GepafinConstant.NDG_FAILED); +// applicationRepository.save(checkApp); +// +// futureRef.get().cancel(false); +// shutdownScheduler(applicationId); +// } +// } catch (Exception ignore) { +// futureRef.get().cancel(false); +// shutdownScheduler(applicationId); +// } +// } +// }; +// +// // Schedule pollingTask: run now (delay=0), then every fifteen minutes: +// ScheduledFuture future = scheduler.scheduleWithFixedDelay(pollingTask, 0, // initial delay = 0 min → run immediately +// fifteenMin, // subsequent runs every 15 minutes +// TimeUnit.MINUTES); +// futureRef.set(future); +// } + + private void shutdownScheduler(Long applicationId) { + + ScheduledExecutorService shed = executorMap.remove(applicationId); + if (shed != null) { + shed.shutdownNow(); + } + log.info("Scheduler shut down for applicationId: {}", applicationId); + } + + private void processNdgGeneration(ApplicationEntity application, CompanyEntity company, HubEntity hub) { + // Validate application, company, and hub + Long applicationId = application.getId(); + log.info("Starting NDG generation process for applicationId: {}", applicationId); + + if (!hub.getUniqueUuid().equals(defaultHubUuid)) { + log.info("Ndg cannot be created for another Hub, it is default for Gepafin."); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_NDG_FOR_ANOTHER_HUB)); + } + + try { + // Authenticate and fetch token if required + if (hub.getAppointmentAuthTokenId() == null || hub.getAreaCode() == null) { + authenticateAndSaveToken(hub, application); + } + + String authorizationToken = getBearerToken(hub); + + // Try retrieving NDG by VAT number + AppointmentLoginResponse ndgResponse=new AppointmentLoginResponse(); + ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application); + + if (isNdgValid(ndgResponse.getNdg())) { + saveNdg(application, company, ndgResponse.getNdg()); + log.info("NDG successfully generated for applicationId: {}", applicationId); + } else { + log.info("Polling for NDG for applicationId: {}", applicationId); + handleNdgPolling(application, company, hub, authorizationToken); + } + } catch (Exception e) { + log.error("Exception occurred during NDG generation. ApplicationId: {}, CompanyId: {}, HubId: {}, Error: {}", applicationId, company.getId(), hub.getId(), + e.getMessage(), e); + } + } + + private void handleNdgPolling(ApplicationEntity application, CompanyEntity company, HubEntity hub, String authorizationToken) { + + log.info("Starting single‐shot NDG polling attempt for applicationId: {}, CompanyId: {}, HubId: {}", application.getId(), company.getId(), hub.getId()); + ApplicationEntity oldApplication = Utils.getClonedEntityForData(application); + CompanyEntity oldCompanyEntity = Utils.getClonedEntityForData(company); + long startTime = System.currentTimeMillis(); + long twoHoursMs = TimeUnit.HOURS.toMillis(2); + + try { + // 1) If NDG was already populated (perhaps by another thread), skip polling. + if (application.getNdg() != null) { + log.info("NDG already present for applicationId {}. Exiting single‐shot polling.", application.getId()); + return; + } + + // 2) Attempt to create Visura (this may immediately return a valid NDG) + AppointmentLoginResponse visuraResponse = createVisura(company, authorizationToken, hub); + + // 2a) If createVisura gave us a valid NDG, persist & exit + String fetchedNdg = visuraResponse.getNdg(); + if (isNdgValid(fetchedNdg)) { + log.info("Valid NDG retrieved from createVisura(): {} | applicationId: {}", fetchedNdg, application.getId()); + + company.setNdg(fetchedNdg); + application.setNdg(fetchedNdg); + application.setNdgStatus(NdgStatusEnum.NDG_GENERATED.getValue()); + application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); + application.setIdVisura(visuraResponse.getIdVisura()); + + companyRepository.save(company); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyEntity).newData(company).build()); + + ApplicationEvaluationEntity eval = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); + Map placeholders = new HashMap<>(); + placeholders.put("{{call_name}}", application.getCall().getName()); + placeholders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + notificationDao.sendNotificationToInstructor(placeholders, eval, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application, placeholders, NotificationTypeEnum.NDG_GENERATION); + + log.info("NDG saved successfully for applicationId: {}", application.getId()); + return; + } + + // 2b) If no immediate NDG, fetch the Visura list JSON and parse out NDG + String visuraListJson = getVisuraList(visuraResponse.getIdVisura(), authorizationToken, application, hub); + log.debug("Parsing NDG from VisuraList JSON for applicationId: {}", application.getId()); + String parsedNdg = parseNdgFromVisuraListResponse(visuraListJson); + + if (isNdgValid(parsedNdg)) { + log.info("Valid NDG parsed from VisuraList: {} | applicationId: {}", parsedNdg, application.getId()); + + company.setNdg(parsedNdg); + application.setNdg(parsedNdg); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); + application.setIdVisura(visuraResponse.getIdVisura()); + + companyRepository.save(company); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyEntity).newData(company).build()); + + ApplicationEvaluationEntity eval = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); + Map placeholders = new HashMap<>(); + placeholders.put("{{call_name}}", application.getCall().getName()); + placeholders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + notificationDao.sendNotificationToInstructor(placeholders, eval, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application, placeholders, NotificationTypeEnum.NDG_GENERATION); + + log.info("NDG saved successfully for applicationId: {}", application.getId()); + return; + } + + // 3) Neither direct API nor parsing yielded a valid NDG. Check timeout. + if (System.currentTimeMillis() - startTime > twoHoursMs) { + log.warn("NDG polling timed out after 2 hours for applicationId: {}", application.getId()); + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + return; + } + + // 4) No NDG yet—just return. The scheduler will retry in 15 minutes. + log.info("No valid NDG yet for applicationId {}. Returning to scheduler—next attempt in 15 minutes.", application.getId()); + } catch (Exception e) { + log.error("Exception during NDG polling for applicationId: {}", application.getId(), e); + // If timeout after exception, mark NDG_FAILED + if (System.currentTimeMillis() - startTime > twoHoursMs) { + log.warn("Example: exiting single‐shot polling due to timeout after exception for applicationId: {}", application.getId()); + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + } else { + log.info("Exception occurred but not timed out for applicationId {}. Returning to scheduler for next attempt in 15 minutes.", application.getId()); + } + } + + log.info("NDG polling completed for applicationId: {}", application.getId()); + } + + private static String getBearerToken(HubEntity hub) { + + return "Bearer " + hub.getAppointmentAuthTokenId(); + } + + private boolean isNdgValid(String ndg) { + + return ndg != null && !ndg.isEmpty(); + } + + private void saveNdg(ApplicationEntity application, CompanyEntity company, String ndg) { + + ApplicationEntity oldApplication = Utils.getClonedEntityForData(application); + CompanyEntity oldCompanyEntity = Utils.getClonedEntityForData(company); + application.setNdg(ndg); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); + company.setNdg(ndg); + companyRepository.save(company); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyEntity).newData(company).build()); + + ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); +// 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); + log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg()); + } + + private String getVisuraList(String idVisura, String authorizationToken, ApplicationEntity application, HubEntity hub) { + + log.info("Initiating Visura list retrieval | ApplicationId: {}, HubId: {}, IdVisura: {}", application.getId(), hub.getId(), idVisura); + AppointmentVisuraListRequest visuraListRequest = new AppointmentVisuraListRequest(); + AppointmentVisuraListRequest.VisuraFilter filter = new AppointmentVisuraListRequest.VisuraFilter(); + filter.setIdVisura(idVisura); + visuraListRequest.setFilter(filter); + + try { + String requestJson = Utils.convertObjectToJson(visuraListRequest); + ResponseEntity response = appointmentApiService.getVisuraList(requestJson, authorizationToken); + return Utils.convertObjectToJson(response.getBody()); + } catch (FeignException.Forbidden forbiddenException) { + log.warn("403 Forbidden while fetching Visura list. Attempting token regeneration | ApplicationId: {}, HubId: {}", application.getId(), hub.getId()); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub, application); + return getVisuraList(idVisura, newAuthorizationToken, application, hub); + } catch (Exception e) { + log.error("Error while fetching Visura list | ApplicationId: {}, HubId: {}, Error: {}", application.getId(), hub.getId(), e.getMessage(), e); + throw new RuntimeException("Error fetching Ndg List", e); + } + } + + private AppointmentLoginResponse retrieveNdgByVatNumber(String vatNumber, String authorizationToken, HubEntity hub, ApplicationEntity application) { + + try { + log.info("Initiating NDG retrieval by VAT number | ApplicationId: {}, HubId: {}, VAT: {}", application.getId(), hub.getId(), vatNumber); + // Prepare the NDG request + String responseJson = getNdgFromExternalService(vatNumber, authorizationToken); + // Parse and return the NDG response + return parseNdgResponse(responseJson); + } catch (FeignException.Forbidden forbiddenException) { + log.error("403 Forbidden during NDG retrieval | ApplicationId: {}, HubId: {}", application.getId(), hub.getId()); + logForbiddenError(); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub, application); + return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application); + } catch (Exception e) { + log.error("Error during NDG retrieval | ApplicationId: {}, HubId: {}, Message: {}", application.getId(), hub.getId(), e.getMessage(), e); + throw new RuntimeException("NDG retrieval failed.", e); + } + } + + private String regenerateTokenAndSave(HubEntity hub, ApplicationEntity application) { + + hub = authenticateAndSaveToken(hub, application); + return "Bearer " + hub.getAppointmentAuthTokenId(); + } + + private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) { + + try { + String visuraRequest = getAppointmentVisuraRequest(company, hub.getAreaCode()); + ResponseEntity response = appointmentApiService.createVisura(visuraRequest, authorizationToken); + String responseJson = Utils.convertObjectToJson(response.getBody()); + return parseVisuraResponse(responseJson); + } catch (FeignException.Forbidden forbiddenException) { + logForbiddenError(); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub, null); + return createVisura(company, newAuthorizationToken, hub); + } catch (Exception e) { + log.error("Failed to create Visura for Ndg : {}", e.getMessage()); + throw new RuntimeException("Visura creation failed for Ndg.", e); + } + } + + private static void logForbiddenError() { + + log.error("403 Forbidden received while retrieving NDG. Regenerating token..."); + } + + private static AppointmentNdgRequest getAppointmentNdgRequest(String vatNumber) { + + log.info("Creating Appointment NDG Request | VAT Number: {}", vatNumber); + AppointmentNdgRequest request = new AppointmentNdgRequest(); + AppointmentNdgRequest.Filter filter = new AppointmentNdgRequest.Filter(); + filter.setPartitaIva(vatNumber); + + AppointmentNdgRequest.Pagination pagination = new AppointmentNdgRequest.Pagination(); + pagination.setTargetPage(AppointmentApiConstant.TARGET_PAGE_SIZE); + pagination.setRecordsPerPage(AppointmentApiConstant.RECORD_PER_PAGE_SIZE); + + request.setFilter(filter); + request.setPagination(pagination); + return request; + } + + private static String getAppointmentVisuraRequest(CompanyEntity company, String areaCode) { + + AppointmentVisuraRequest visuraRequest = new AppointmentVisuraRequest(); + AppointmentVisuraRequest.VisuraInput input = new AppointmentVisuraRequest.VisuraInput(); + input.setPartitaIva(company.getVatNumber()); + input.setCodiceFiscale(company.getCodiceFiscale()); + input.setCodArea(areaCode); + input.setVisuraMode(AppointmentApiConstant.VISURA_MODE); + input.setVisuraProvider(AppointmentApiConstant.VISURA_PROVIDER); + input.setCodAgente(AppointmentApiConstant.COD_AGENTE); + input.setAnagraficaLegame(AppointmentApiConstant.IS_ANAGRAFICA_LEGAME); + input.setCreaAnagrafica(AppointmentApiConstant.CREA_ANAGRAFICA); + input.setFromRating(AppointmentApiConstant.IS_FROM_RATING); + input.setSalvaDocumenti(AppointmentApiConstant.SALVA_DOCUMENTI); + input.setVisuraType(AppointmentApiConstant.VISURA_TYPE); + visuraRequest.setInput(input); + return Utils.convertObjectToJson(visuraRequest); + } + + private String parseNdgFromVisuraListResponse(String jsonResponse) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); + + if (dataNode != null && dataNode.isArray() && dataNode.size() > 0) { + JsonNode firstEntry = dataNode.get(0); + JsonNode ndgClienteNode = firstEntry.get("ndgCliente"); + if (ndgClienteNode != null && ndgClienteNode.get("code") != null) { + String code = ndgClienteNode.get("code").asText(); + return normalizeNullValue(code); + } + } + log.warn("NDG not found in Visura List API response."); + return null; + } catch (Exception e) { + log.error("Failed to parse NDG from Visura List API response: {}", e.getMessage(), e); + throw new RuntimeException("Error parsing NDG from Visura List API response", e); + } + } + + public AppointmentLoginResponse parseLoginResponse(String jsonResponse) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); + + if (dataNode != null) { + AppointmentLoginResponse response = new AppointmentLoginResponse(); + response.setTokenId(dataNode.get("tokenId").asText()); + JsonNode areasNode = dataNode.get("areas"); + if (areasNode != null && areasNode.isArray() && areasNode.size() > 0) { + response.setAreaCode(areasNode.get(0).get("code").asText()); + } + response.setCompanyId(dataNode.get("companyId").asLong()); + return response; + } else { + throw new RuntimeException("Invalid JSON structure: Missing 'data' node."); + } + } catch (Exception e) { + throw new RuntimeException("Failed to parse response from loginApi for odessa: " + e.getMessage(), e); + } + } + + public AppointmentLoginResponse parseVisuraResponse(String jsonResponse) { + + try { + // Log full raw JSON for debug purposes + log.info("Raw Visura JSON Response: {}", jsonResponse); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); + + if (dataNode != null && dataNode.isObject()) { + AppointmentLoginResponse response = new AppointmentLoginResponse(); + JsonNode idVisuraNode = dataNode.get(GepafinConstant.ID_VISURA_STRING); + JsonNode ndgNode = dataNode.get(GepafinConstant.NDG_STRING); + if (idVisuraNode == null || ndgNode == null) { + log.error("Missing expected fields in 'data' node. JSON: {}", dataNode); + } + response.setIdVisura(normalizeNullValue(idVisuraNode != null ? idVisuraNode.asText() : null)); + response.setNdg(normalizeNullValue(ndgNode != null ? ndgNode.asText() : null)); + return response; + } else { + System.err.println("Invalid JSON: 'data' node is missing or not an object."); + throw new RuntimeException("Invalid JSON structure: Missing or malformed 'data' node."); + } + } catch (Exception e) { + System.err.println("Exception while parsing Visura response: " + e.getMessage()); + throw new RuntimeException("Failed to parse response: " + e.getMessage(), e); + } + } + + public AppointmentLoginResponse parseNdgResponse(String jsonResponse) { + AppointmentLoginResponse loginResponse = new AppointmentLoginResponse(); + String ndg=extractNdg(jsonResponse); + if (ndg==null){ return null;} + else { + loginResponse.setNdg(ndg); + } + return loginResponse; + } + + private String extractNdg(String jsonResponse) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode dataArray = rootNode.get(GepafinConstant.DATA_STRING); + if (dataArray == null || !dataArray.isArray() || dataArray.isEmpty()) { + log.info("NDG data is empty or missing in the response."); + return null; + } + JsonNode firstDataEntry = dataArray.get(0); + AppointmentLoginResponse response = new AppointmentLoginResponse(); + if (firstDataEntry.has(GepafinConstant.NDG_STRING)) { + String ndg=normalizeNullValue(firstDataEntry.get(GepafinConstant.NDG_STRING).asText()); + return ndg; + } + } catch (Exception e) { + log.error("Failed to parse response: {}", e.getMessage(), e); + throw new RuntimeException("Failed to parse NDG response.", e); + } + return null; + } + + public String normalizeNullValue(String value) { + + return (value == null || GepafinConstant.NULL_STRING.equalsIgnoreCase(value.trim())) ? null : value; + } + + public AppointmentCreationResponse createAppointment(Long applicationId, CreateAppointmentRequest createAppointmentRequest) { + // Validate the application + log.info("Starting appointment creation for applicationId: {}", applicationId); + ApplicationEntity application = applicationService.validateApplication(applicationId); + + AppointmentCreationResponse appointmentCreationResponse = new AppointmentCreationResponse(); + + ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); + HubEntity hub = hubRepository.findByHubId(application.getHubId()); + + // Check hub UUID and enforce constraints + if (!hub.getUniqueUuid().equals(defaultHubUuid)) { + log.info("Appointment cannot be created for another Hub; default is required for Gepafin."); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_APPOINTMENT_FOR_ANOTHER_HUB)); + } + + try { + // Pre-check conditions for appointment creation + if (application.getNdg() != null && !Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS) && application.getAppointmentId() != null) { + appointmentCreationResponse.setAppointmentId(application.getAppointmentId()); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_ALREADY_CREATED)); + // return appointmentCreationResponse; + } + + if (application.getNdg() == null && Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS)) { + log.warn("NDG in progress but not available for applicationId: {}", applicationId); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION)); + } + + // Generate authorization token and fetch template data + String authorizationToken = regenerateTokenAndSave(hub, application); + Long appointmentTemplateId = application.getCall().getAppointmentTemplateId(); + if (appointmentTemplateId == null) { + log.error("Missing appointment template ID for applicationId: {}", applicationId); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_CANNOT_BE_CREATED)); + } + ResponseEntity response = appointmentApiService.getAppointmentTemplateForTemplateCreation(authorizationToken, appointmentTemplateId); + + if (response.getStatusCode() != HttpStatus.OK) { + log.error("Failed to retrieve appointment template for appointment creation. Status: {}", response.getStatusCode()); + throw new IllegalStateException("Failed to retrieve appointment template for appointment creation"); + } + + // Parse template data + String responseDataForTemplate = Utils.convertObjectToJson(response.getBody()); + AppointmentCreationRequest templateRichiestaData = parseTemplateResponseData(responseDataForTemplate); + + // Build the appointment request body + AppointmentCreationRequest appointmentCreationRequest = buildAppointmentCreationRequest(applicationId, createAppointmentRequest, appointmentTemplateId, + templateRichiestaData); + log.info("AppointmentCreationRequest : {}", appointmentCreationRequest); + String appointmentRequestBody = Utils.convertObjectToJson(appointmentCreationRequest); + + // Make API call to create the appointment + log.info("Context:{}, Authorization Token : {}, RequestBody : {}", context, authorizationToken, appointmentRequestBody); + ResponseEntity appointmentResponse = appointmentApiService.createAppointment(authorizationToken, context, appointmentRequestBody); + String appointmentId = extractAppointmentIdFromResponse(appointmentResponse); + + if (appointmentId == null) { + log.error("Failed to extract appointment ID from response for applicationId: {}", applicationId); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_NOT_CREATED)); + } + // Update application with the appointment ID + application.setAppointmentId(appointmentId); + application.setStatus(ApplicationStatusTypeEnum.APPOINTMENT.getValue()); + applicationRepository.save(application); + + // Log version history + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); + + appointmentCreationResponse.setAppointmentId(appointmentId); + return appointmentCreationResponse; + + } catch (FeignException.Forbidden forbiddenException) { + log.error("403 Forbidden received while retrieving template. Attempting to regenerate token and retry. Application ID: {}", applicationId); + regenerateTokenAndSave(hub, application); + return createAppointment(applicationId, createAppointmentRequest); + } + } + + private String extractAppointmentIdFromResponse(ResponseEntity appointmentResponse) { + + if (appointmentResponse.getBody() != null) { + log.info("Appointment API Response : {}", appointmentResponse.getBody()); + 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; + } + + public AppointmentCreationRequest parseTemplateResponseData(String jsonResponse) { + + try { + + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode richiesteClienteArray = rootNode.path(GepafinConstant.DATA_STRING).path(GepafinConstant.RICHIESTE_CLIENTE_STRING); + + // Initialize the result object + AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); + AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); + List richiestaClienteList = new ArrayList<>(); + if (!richiesteClienteArray.isArray()) { + log.warn("richiesteCliente array is missing or not an array."); + return new AppointmentCreationRequest(); + } + for (JsonNode richiestaNode : richiesteClienteArray) { + if (richiestaNode.isNull()) + continue; + + AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente(); + 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)); + + richiestaClienteList.add(richiestaCliente); + } + + input.setRichiestaCliente(richiestaClienteList); + appointmentCreationRequest.setInput(input); + + return appointmentCreationRequest; + + } catch (JsonProcessingException e) { + log.error("JSON processing error: {}", e.getMessage(), e); + throw new IllegalStateException("Invalid JSON structure in template response", e); + } + } + + private String getTextValue(JsonNode node, String fieldName) { + + return node.path(fieldName).isTextual() ? node.path(fieldName).asText() : null; + } + + private int getIntValue(JsonNode node) { + + return node.path(AppointmentApiConstant.MOTIVAZIONE).path(AppointmentApiConstant.MOTIVAZIONE_ID).asInt(); + } + + public AppointmentCreationRequest buildAppointmentCreationRequest(Long applicationId, CreateAppointmentRequest createAppointmentRequest, Long areaCode, + AppointmentCreationRequest templateRichiestaData) { + + ApplicationEntity application = applicationService.validateApplication(applicationId); + CreateAppointmentRequest.Nota nota = createAppointmentRequest.getNota(); + + AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); + AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); + + // Set Input Fields + input.setId(areaCode); + input.setNdg(application.getNdg()); + + // Populate richiestaCliente from template data + List richiestaClienteList = new ArrayList<>(); + for (AppointmentCreationRequest.RichiestaCliente templateRichiesta : templateRichiestaData.getInput().getRichiestaCliente()) { + AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente(); + BeanUtils.copyProperties(templateRichiesta, richiestaCliente); + + // Add specific `nota` + AppointmentCreationRequest.Nota requestNota = new AppointmentCreationRequest.Nota(); + requestNota.setTitolo(nota.getTitolo()); + requestNota.setTesto(nota.getTesto()); + richiestaCliente.setNota(requestNota); + richiestaCliente.setDurataMesiFinanziamento(createAppointmentRequest.getDurataMesiFinanziamento()); + richiestaCliente.setImportoBreveTermine(createAppointmentRequest.getImportoBreveTermine()); + richiestaClienteList.add(richiestaCliente); + } + + input.setRichiestaCliente(richiestaClienteList); + appointmentCreationRequest.setInput(input); + return appointmentCreationRequest; + } + + public DocumentUploadResponse uploadDocumentToExternalSystem(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { + log.info("Initiating upload to external system for documentId: {}", documentId); + // 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, 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(); + response.setDocumentAttachmentId(systemDoc.getDocumentAttachmentId()); + return response; + } + // Check if a thread is already running for this document upload + if (threadForDocumentMap.containsKey(documentId)) { + log.warn("Document upload already running for documentId: {}", documentId); + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS)); + } + // Start the upload process in the background + ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { + Thread thread = new Thread(runnable); + thread.setName(GepafinConstant.ASYNC_DOCUMENT_UPLOAD_NAME + documentId); + return thread; + }); + 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, finalApplication); + } catch (Exception e) { + log.error("Error in async document upload for documentId: {}", documentId, e); + } finally { + // Cleanup resources + ExecutorService executorToShutdown = threadForDocumentMap.remove(documentId); + if (executorToShutdown != null) { + executorToShutdown.shutdown(); + threadLocalHubId.remove(); + } + log.info("Async document upload completed for documentId: {}", documentId); + } + }); + return null; + } + + private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, ApplicationEntity application) { + log.info("Starting sync document upload for documentId: {}", documentId); + // Synchronous upload logic + DocumentEntity systemDoc = documentDao.validateDocument(documentId); + + Long hubId = threadLocalHubId.get(); + HubEntity hub = hubRepository.findByHubId(hubId); + + if (!hub.getUniqueUuid().equals(defaultHubUuid)) { + log.info("Document cannot be uploaded for another Hub, it is default for Gepafin."); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_DOCUMENT_UPLOAD_FOR_ANOTHER_HUB)); + } + + log.info("Got Document in system: {}", systemDoc); + String oldUrl = systemDoc.getFilePath(); + String authorizationToken = getBearerToken(hub); + + try { + File localFile = downloadFileFromS3(oldUrl); + MultipartFile multipartFile = convertFileToMultipartFile(localFile); + + UploadDocToExternalSystemRequest externalSystemRequest = new UploadDocToExternalSystemRequest(); + externalSystemRequest.setInput(getUploadDocumentInput(docToExternalSystemRequest)); + + String uploadDocRequest = Utils.convertObjectToJson(externalSystemRequest); + ResponseEntity uploadedDocumentData = appointmentApiService.uploadDocumentToExternalSystemForAppointment(authorizationToken, context, uploadDocRequest, + multipartFile); + + String responseData = Utils.convertObjectToJson(uploadedDocumentData.getBody()); + DocumentUploadResponse parsedResponse = parseDocumentUploadResponse(responseData); + + if (parsedResponse == null) { + log.error("Upload failed: parsed response is null for documentId: {}", documentId); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_UPLOADING_DOCUMENT)); + } + + // Save the documentAttachmentId to the database + systemDoc.setDocumentAttachmentId(parsedResponse.getDocumentAttachmentId()); + documentRepository.save(systemDoc); + + log.info("Document uploaded successfully to external system: {}", parsedResponse); + } catch (FeignException.Forbidden forbiddenException) { + log.error("403 Forbidden from external system during upload for documentId: {}. Retrying with new token...", documentId); + 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)); + } + } + + private UploadDocToExternalSystemRequest.Input getUploadDocumentInput(UploadDocToExternalSystemRequest docToExternalSystemRequest) { + + UploadDocToExternalSystemRequest.Input input = new UploadDocToExternalSystemRequest.Input(); + input.setIdTipoProtocollo(docToExternalSystemRequest.getInput().getIdTipoProtocollo()); + input.setIdClassificazione(docToExternalSystemRequest.getInput().getIdClassificazione()); + input.setFlagDaFirmare(docToExternalSystemRequest.getInput().getFlagDaFirmare()); + input.setDescrizione(docToExternalSystemRequest.getInput().getDescrizione()); + + UploadDocToExternalSystemRequest.Input.Attributes attributes = new UploadDocToExternalSystemRequest.Input.Attributes(); + attributes.setNdg(docToExternalSystemRequest.getInput().getAttributes().getNdg()); + attributes.setEmail(docToExternalSystemRequest.getInput().getAttributes().getEmail()); + + input.setAttributes(attributes); + return input; + } + + public static MultipartFile convertFileToMultipartFile(File file) throws IOException { + + FileInputStream input = new FileInputStream(file); + return new MockMultipartFile(file.getName(), file.getName(), MediaType.APPLICATION_OCTET_STREAM_VALUE, input); + } + + private File downloadFileFromS3(String fileUrl) throws Exception { + + String key = amazonS3Service.extractS3KeyFromUrl(fileUrl); + String fileName = extractFileName(key); + String folderPath = key.substring(0, key.lastIndexOf("/")); + File localFile = new File(GepafinConstant.TEMP_FILE_PATH + fileName); + + try (InputStream s3Stream = amazonS3Service.getFile(folderPath, key); FileOutputStream outputStream = new FileOutputStream(localFile)) { + s3Stream.transferTo(outputStream); + } + + log.info("Downloaded file from old S3 bucket: {}", key); + return localFile; + } + + private String extractFileName(String filePath) { + + String[] parts = filePath.split("/"); + return parts[parts.length - 1]; + + } + + public DocumentUploadResponse parseDocumentUploadResponse(String jsonResponse) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + + // Navigate to the "data" node + JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); + if (dataNode != null) { + DocumentUploadResponse response = new DocumentUploadResponse(); + + // Extract "documentAttachmentId" + JsonNode documentAttachmentIdNode = dataNode.get(GepafinConstant.DOCUMENT_ATTACHMENT_ID_STRING); + if (documentAttachmentIdNode != null) { + response.setDocumentAttachmentId(documentAttachmentIdNode.asText()); + } else { + throw new RuntimeException("Invalid JSON structure: Missing 'documentAttachmentId' node."); + } + + return response; + } else { + return null; + } + } catch (Exception e) { + throw new RuntimeException("Failed to parse response: " + e.getMessage(), e); + } + } + + private void startAsyncNdgProcessing(Long applicationId) { + if (executorMap.containsKey(applicationId)) { + log.warn("Async processing already running for applicationId: {}", applicationId); + return; + } + ApplicationEntity application = applicationService.validateApplication(applicationId); + CompanyEntity company = companyService.validateCompany(application.getCompanyId()); + ApplicationEntity oldApplication = Utils.getClonedEntityForData(application); + CompanyEntity oldCompanyEntity = Utils.getClonedEntityForData(company); + ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request); + ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r); + t.setName("AsyncNdgPolling-" + applicationId); + return t; + }); + + executorMap.put(applicationId, scheduler); + application.setNdgStatus(NdgStatusEnum.NDG_IN_PROGRESS.getValue()); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + + long startTime = System.currentTimeMillis(); + long twoHoursMs = TimeUnit.HOURS.toMillis(2); + long fifteenMin = 15; + + HubEntity hub = hubRepository.findByHubId(application.getHubId()); + + // 1. Run full NDG generation logic once upfront + processNdgGeneration(application, company, hub); + + AtomicReference> futureRef = new AtomicReference<>(); + + // 2. Now define the polling logic ONLY + Runnable pollingTask = () -> { + RequestContextHolder.setRequestAttributes(requestAttributes, true); + Utils.setHttpServletRequestForThread(request, HttpMethodEnum.POST.getValue(), + GepafinConstant.CREATE_NDG, (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID)); + try { + + // Stop if timeout + if (System.currentTimeMillis() - startTime > twoHoursMs) { + log.warn("Polling timed out for applicationId {}. Marking NDG_FAILED.", applicationId); + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + + futureRef.get().cancel(false); + shutdownScheduler(applicationId); + return; + } + + // If NDG already present or marked failed + if (isNdgValid(application.getNdg()) || GepafinConstant.NDG_FAILED.equals(application.getNdgStatus())) { + log.info("NDG already present or failed for applicationId {}. Stopping polling.", applicationId); + futureRef.get().cancel(false); + shutdownScheduler(applicationId); + return; + } + + // Only Visura polling here: + String visuraListJson = getVisuraList(application.getIdVisura(), hub.getAppointmentAuthTokenId(), application, hub); + String parsedNdg = parseNdgFromVisuraListResponse(visuraListJson); + + if (isNdgValid(parsedNdg)) { + log.info("Valid NDG parsed from VisuraList: {} | applicationId: {}", parsedNdg, application.getId()); + + company.setNdg(parsedNdg); + application.setNdg(parsedNdg); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); +// application.setIdVisura(visuraResponse.getIdVisura()); + + companyRepository.save(company); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyEntity).newData(company).build()); + + ApplicationEvaluationEntity eval = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); + Map placeholders = new HashMap<>(); + placeholders.put("{{call_name}}", application.getCall().getName()); + placeholders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + notificationDao.sendNotificationToInstructor(placeholders, eval, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application, placeholders, NotificationTypeEnum.NDG_GENERATION); + + log.info("NDG saved successfully for applicationId: {}", application.getId()); + return; + } + + } catch (Exception ex) { + log.error("Error during NDG polling: {}", ex.getMessage(), ex); + } + }; + + ScheduledFuture future = scheduler.scheduleWithFixedDelay(pollingTask, fifteenMin, fifteenMin, TimeUnit.MINUTES); + futureRef.set(future); + } + + public NdgResponse getNdgByVatNumber(String vatNumber, UserEntity userEntity) { + HubEntity hub=userEntity.getHub(); + NdganagEntity ndganagEntity = ndganagRepository.findByVatNumber(vatNumber); + NdgResponse ndgResponse=new NdgResponse(); + String jsonResponse=null; + String authorizationToken = hub.getAppointmentAuthTokenId(); + if (ndganagEntity == null || ndganagEntity.getNdg() == null) { + + try { + log.info("Initiating NDG retrieval by VAT number | HubId: {}, VAT: {}", hub.getId(), vatNumber); + // Prepare the NDG request + jsonResponse=getNdgFromExternalService(vatNumber, authorizationToken); + checkAndSaveNdg(jsonResponse, ndgResponse); + // Parse and return the NDG response + } catch (FeignException.Forbidden forbiddenException) { + log.error("403 Forbidden during NDG retrieval | HubId: {}", hub.getId()); + logForbiddenError(); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub, null); + jsonResponse= getNdgFromExternalService(vatNumber,newAuthorizationToken); + if (checkAndSaveNdg(jsonResponse, ndgResponse)) return null; + } catch (Exception e) { + log.error("Error during NDG retrieval |, HubId: {}, Message: {}", hub.getId(), e.getMessage(), e); + throw new RuntimeException("NDG retrieval failed.", e); + } + }else { + ndgResponse.setNdg(ndganagEntity.getNdg()); + } + return ndgResponse; + } + + private boolean checkAndSaveNdg(String jsonResponse, NdgResponse ndgResponse) { + String ndg=extractNdg(jsonResponse); + if (ndg==null){ + return true; + } + else { + ndgResponse.setNdg(ndg); + } + return false; + } + + private String getNdgFromExternalService(String vatNumber, String authorizationToken) { + AppointmentNdgRequest ndgRequest = getAppointmentNdgRequest(vatNumber); + // Call the API to retrieve NDG + ResponseEntity response = appointmentApiService.getNdgByVatNumber(ndgRequest, authorizationToken); + String responseJson = Utils.convertObjectToJson(response.getBody()); + return responseJson; + } +} + diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java index c05b04c2..0df566f5 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java @@ -491,6 +491,7 @@ public class AssignedApplicationsDao { response.setCreatedDate(view.getCreatedDate()); response.setUpdatedDate(view.getUpdatedDate()); response.setEmailSendResponse(view.getEmailSendResponse()); + response.setAssignedUserName(view.getAssignedUserName()); 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 6beca7fc..d0bef93e 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java @@ -224,6 +224,10 @@ public class CallDao { callEntity.setHub(userEntity.getHub()); callEntity.setNumberOfCheck(createCallRequest.getNumberOfCheck()); callEntity.setAppointmentTemplateId(createCallRequest.getAppointmentTemplateId()); + callEntity.setAllowMultipleApplications(false); + if (createCallRequest.getAllowMultipleApplications() != null) { + callEntity.setAllowMultipleApplications(createCallRequest.getAllowMultipleApplications()); + } callEntity = callRepository.save(callEntity); log.info("CallEntity saved with ID: {} for call name: '{}'", callEntity.getId(), callEntity.getName()); @@ -406,6 +410,7 @@ public class CallDao { createCallResponseBean.setDocumentationRequested(callEntity.getDocumentationRequested()); createCallResponseBean.setPriorityArea(callEntity.getPriorityArea()); createCallResponseBean.setConfidi(callEntity.getConfidi()); + createCallResponseBean.setAllowMultipleApplications(callEntity.getAllowMultipleApplications()); createCallResponseBean.setAmountMin(callEntity.getAmountMin()); createCallResponseBean.setPhoneNumber(callEntity.getPhoneNumber()); createCallResponseBean.setEndTime(callEntity.getEndTime()); @@ -623,19 +628,19 @@ public class CallDao { if (dates.size() > 0) { setIfUpdated(callEntity::getStartDate, callEntity::setStartDate, dates.get(0)); } - if (dates.size() > 1) { - LocalDate requestEndDate = dates.get(1).toLocalDate(); // Extract only the date - LocalDate storedEndDate = callEntity.getEndDate().toLocalDate(); // Extract only the date - - if (!requestEndDate.equals(storedEndDate)) { // Check if dates are different - - setIfUpdated(callEntity::getEndDate, callEntity::setEndDate, dates.get(1)); -// callEntity.setStatus(CallStatusEnum.PUBLISH.getValue()); -// callRepository.save(callEntity); - isEndDateUpdated = true; - } - } - } +// if (dates.size() > 1) { +// LocalDate requestEndDate = dates.get(1).toLocalDate(); // Extract only the date +// LocalDate storedEndDate = callEntity.getEndDate().toLocalDate(); // Extract only the date +// +// if (!requestEndDate.equals(storedEndDate)) { // Check if dates are different +// +// setIfUpdated(callEntity::getEndDate, callEntity::setEndDate, dates.get(1)); +//// callEntity.setStatus(CallStatusEnum.PUBLISH.getValue()); +//// callRepository.save(callEntity); +// isEndDateUpdated = true; +// } +// } +// } if (updateCallRequest.getEndTime() != null) { LocalTime requestEndTime = DateTimeUtil.parseTime(updateCallRequest.getEndTime()); @@ -648,6 +653,29 @@ public class CallDao { isEndTimeUpdated = true; } } + if (dates.size() > 1) { + LocalDate requestEndDate = dates.get(1).toLocalDate(); // Extract only the date + LocalDate storedEndDate = callEntity.getEndDate().toLocalDate(); // Extract only the date + + if (!requestEndDate.equals(storedEndDate)) { + // Check if dates are different + + setIfUpdated(callEntity::getEndDate, callEntity::setEndDate, dates.get(1)); + if(callEntity.getStatus().equals(CallStatusEnum.EXPIRED.getValue())) { + LocalDateTime newEndDate = LocalDateTime.of(requestEndDate, callEntity.getEndTime()); + if(newEndDate.isBefore(LocalDateTime.now())){ + throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.END_DATE_GREATER_THAN_NOW)); + } + + if (requestEndDate.isAfter(LocalDate.now()) || requestEndDate.isEqual(LocalDate.now())) { + callEntity.setStatus(CallStatusEnum.PUBLISH.getValue()); + callRepository.save(callEntity); + } + } + isEndDateUpdated = true; + } + } + } if (isEndDateUpdated || isEndTimeUpdated) { callRepository.save(callEntity); loggingUtil.logUserAction(UserActionRequest.builder() @@ -683,6 +711,7 @@ public class CallDao { setIfUpdated(callEntity::getEvaluationVersion, callEntity::setEvaluationVersion, updateCallRequest.getEvaluationVersion().getValue()); setIfUpdated(callEntity::getNumberOfCheck, callEntity::setNumberOfCheck, updateCallRequest.getNumberOfCheck()); setIfUpdated(callEntity::getAppointmentTemplateId, callEntity::setAppointmentTemplateId, updateCallRequest.getAppointmentTemplateId()); + setIfUpdated(callEntity::getAllowMultipleApplications, callEntity::setAllowMultipleApplications, updateCallRequest.getAllowMultipleApplications()); callEntity = callRepository.save(callEntity); /** This code is responsible for adding a version history log for the "update call step 1" operation **/ @@ -792,6 +821,7 @@ public class CallDao { callDetailsResponseBean.setUpdatedDate(callEntity.getUpdatedDate()); callDetailsResponseBean.setNumberOfCheck(callEntity.getNumberOfCheck()); callDetailsResponseBean.setAppointmentTemplateId(callEntity.getAppointmentTemplateId()); + callDetailsResponseBean.setAllowMultipleApplications(callEntity.getAllowMultipleApplications()); return callDetailsResponseBean; } @@ -1040,7 +1070,7 @@ public class CallDao { } public PageableResponseBean> getAllCallsByPagination(HttpServletRequest request,UserEntity user,Long companyId , Boolean onlyPreferredCall, Boolean onlyConfidiCall, CallPageableRequestBean callPageableRequestBean) { - log.info("Fetching paginated calls for userId={}, companyId={}, onlyPreferredCall={}", user.getId(), companyId, onlyPreferredCall); + log.info("Fetching paginated calls for userId={}, companyId={}, onlyPreferredCall={}", user.getId(), companyId, onlyPreferredCall); Integer pageNo = null; Integer pageLimit = null; if (callPageableRequestBean.getGlobalFilters() != null) { diff --git a/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java b/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java index 26ca3f29..8b59eebe 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.dao; import org.springframework.data.domain.Pageable; // Correct package +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -413,7 +414,7 @@ public class CompanyDao { Translator.toLocale(GepafinConstant.INVALID_LIMIT)); } - int successfulUpdates = 0; + int successfulUpdates = 0; int failedUpdates = 0; int invalidVatNumbers = 0; @@ -531,7 +532,11 @@ public class CompanyDao { Map dataMap = Utils.extractMap(companyDataMap, "data"); Object dataObj = companyDataMap.get("data"); - if (dataMap == null) { + if (dataObj instanceof Map singleMap) { + dataMap = (Map) singleMap; + } else if (dataObj instanceof List list && !list.isEmpty() && list.get(0) instanceof Map firstMap) { + dataMap = (Map) firstMap; + } else { log.warn("Company ID {}: 'data' section is missing or invalid in the JSON.", company.getId()); return; } @@ -540,11 +545,11 @@ public class CompanyDao { updateCodiceAtecoField(company); } else { - Object pecEmail = Utils.extractMap(dataMap, "pec"); + Object pecEmail = dataMap.get("pec"); if (pecEmail == null) { log.warn("Company ID {}: 'pec' section is missing or invalid.", company.getId()); - company.setPec((String) pecEmail); } + company.setPec((String) pecEmail); // Extract 'atecoClassification' section Map atecoClassificationMap = Utils.extractMap(dataMap, "atecoClassification"); @@ -577,49 +582,79 @@ public class CompanyDao { } public void getCompanyEntity() { - List companyEntities=companyRepository.findAll(); + List companyEntities=companyRepository.findByJsonIsNotNullAndPecIsNull(); + List companyEntityList=new ArrayList<>(); for (CompanyEntity company:companyEntities){ - if(company.getJson()!=null){ + if(company.getJson()!=null && company.getPec()==null){ if (company == null || company.getJson() == null || company.getJson().isEmpty()) { log.warn("Company is null or JSON data is empty."); - return; + continue; } Map vatCheckResponse = Utils.convertJsonStringToMap(company.getJson()); if (vatCheckResponse == null) { log.warn("Company ID {}: Invalid JSON response.", company.getId()); - return; + continue; } Map companyDataMap = Utils.convertJsonStringToMap(company.getJson()); if (companyDataMap == null) { log.warn("Company ID {}: Failed to parse JSON data.", company.getId()); - return; + continue; } + Object dataObj = vatCheckResponse.get("data"); - if (!(dataObj instanceof Map dataMap)) { - log.warn("Company ID {}: 'data' is missing or not a valid object.", company.getId()); - return; + Map dataMap=null; +// if (!(dataObj instanceof Map dataMap)) { +// log.warn("Company ID {}: 'data' is missing or not a valid object.", company.getId()); +// continue; +// } + + if (dataObj instanceof Map singleMap) { + dataMap = (Map) singleMap; + } else if (dataObj instanceof List list && !list.isEmpty() && list.get(0) instanceof Map firstMap) { + dataMap = (Map) firstMap; + } else { + log.warn("Company ID {}: 'data' section is missing or invalid in the JSON.", company.getId()); + continue; } + + if (dataMap.containsKey("pec")) { + Object pecEmailObj = dataMap.get("pec"); + if (pecEmailObj instanceof String pec && !pec.isEmpty()) { + company.setPec(pec); // Only set if valid string + companyEntityList.add(company); + } else { + log.warn("Company ID {}: 'pec' is missing, empty, or not a string.", company.getId()); + continue; + } + } + + if (!dataMap.containsKey("dettaglio")) { log.warn("Company ID {}: 'dettaglio' not present inside 'data'. Skipping codiceAteco update.", company.getId()); - return; + continue; } + Object dettaglioObj = dataMap.get("dettaglio"); if (!(dettaglioObj instanceof Map dettaglio)) { log.warn("Company ID {}: 'dettaglio' is not a valid object.", company.getId()); - return; + continue; } - Object pecEmailObj = dettaglio.get("pec"); if (pecEmailObj instanceof String pec && !pec.isEmpty()) { - company.setPec(pec); // Only set if valid string + if(pec!=null) { + company.setPec(pec); // Only set if valid string + companyEntityList.add(company); + } } else { log.warn("Company ID {}: 'pec' is missing, empty, or not a string.", company.getId()); + continue; } } } + companyRepository.saveAll(companyEntityList); } } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/DocumentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/DocumentDao.java index 3b5b41f6..013bf9e5 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/DocumentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/DocumentDao.java @@ -1,13 +1,19 @@ package net.gepafin.tendermanagement.dao; import lombok.extern.slf4j.Slf4j; +import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.entities.*; import net.gepafin.tendermanagement.enums.*; +import net.gepafin.tendermanagement.model.request.AmendmentFormField; +import net.gepafin.tendermanagement.model.request.EvaluationDocumentRequest; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; -import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; -import net.gepafin.tendermanagement.repositories.ApplicationRepository; +import net.gepafin.tendermanagement.model.response.ContentResponseBean; +import net.gepafin.tendermanagement.model.response.SettingResponseBean; +import net.gepafin.tendermanagement.repositories.*; import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.util.Utils; import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; @@ -17,13 +23,8 @@ import org.springframework.web.multipart.MultipartFile; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; -import net.gepafin.tendermanagement.entities.ApplicationEntity; -import net.gepafin.tendermanagement.entities.CallEntity; -import net.gepafin.tendermanagement.entities.DocumentEntity; import net.gepafin.tendermanagement.model.response.DocumentResponseBean; import net.gepafin.tendermanagement.model.response.UploadFileOnAmazonS3Response; -import net.gepafin.tendermanagement.repositories.ApplicationAmendmentRequestRepository; -import net.gepafin.tendermanagement.repositories.DocumentRepository; import net.gepafin.tendermanagement.service.AmazonS3Service; import net.gepafin.tendermanagement.service.ApplicationAmendmentRequestService; import net.gepafin.tendermanagement.service.ApplicationService; @@ -31,8 +32,6 @@ import net.gepafin.tendermanagement.service.CallService; import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException; import net.gepafin.tendermanagement.web.rest.api.errors.Status; import org.springframework.beans.factory.annotation.Value; -import java.util.ArrayList; -import java.util.List; @Slf4j @@ -78,6 +77,18 @@ public class DocumentDao { @Autowired private HttpServletRequest request; + @Autowired + private ApplicationFormRepository applicationFormRepository; + + @Autowired + private ApplicationFormFieldRepository applicationFormFieldRepository; + + @Autowired + private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; + + @Autowired + private ApplicationEvaluationDao applicationEvaluationDao; + // @Value("${aws.s3.url.folder}") // private String s3Folder; @@ -224,18 +235,72 @@ public class DocumentDao { } else if (DocumentSourceTypeEnum.APPLICATION.getValue().equalsIgnoreCase(documentEntity.getSource())) { applicationId = documentEntity.getSourceId(); ApplicationEntity applicationEntity = applicationService.validateApplication(applicationId); + + List applicationFormEntity=applicationFormRepository.findByApplicationId(applicationId); + for (ApplicationFormEntity applicationForm:applicationFormEntity){ + FormEntity formEntity=applicationForm.getForm(); + List contentList = Utils.convertJsonStringToList(formEntity.getContent(), ContentResponseBean.class); + List applicationFormFieldEntityList=applicationFormFieldRepository.findByApplicationFormId(applicationForm.getId()); + for (ApplicationFormFieldEntity applicationFormFieldEntity:applicationFormFieldEntityList) { + contentList.forEach(contentResponseBean -> { + if (("fileupload".equals(contentResponseBean.getName()) || GepafinConstant.FILE_SELECT.equals(contentResponseBean.getName())) + && contentResponseBean.getId().equals(applicationFormFieldEntity.getFieldId())) { + String updatedValue = removeDocumentIdFromFieldValue(applicationFormFieldEntity.getFieldValue(), documentId); + applicationFormFieldEntity.setFieldValue(updatedValue); + applicationFormFieldRepository.save(applicationFormFieldEntity); + } + }); + } + } + callId = applicationEntity.getCall().getId(); log.info("Processing document of type APPLICATION. Resolved applicationId={}, callId={}", applicationId, callId); } else if(DocumentSourceTypeEnum.AMENDMENT.getValue().equalsIgnoreCase(documentEntity.getSource())){ amendmentId = documentEntity.getSourceId(); ApplicationEntity applicationEntity = applicationAmendmentRequestRepository.findApplicationByAmendmentId(amendmentId); + Optional applicationAmendmentRequestEntity=applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(amendmentId); + Map amendmentFormFieldMap = Utils + .convertJsonStringToList(applicationAmendmentRequestEntity.get().getFormFields(), AmendmentFormField.class) + .stream().collect(Collectors.toMap(AmendmentFormField::getFieldId, Function.identity())); + for (Map.Entry entry : amendmentFormFieldMap.entrySet()) { + AmendmentFormField amendmentFormField=entry.getValue(); + String updatedValue = removeDocumentIdFromFieldValue(amendmentFormField.getFieldValue(), documentId); + amendmentFormField.setFieldValue(updatedValue); + } + String amendmentDocs=applicationAmendmentRequestEntity.get().getAmendmentDocument(); + Map amendmentDocument=Utils.convertIntoJson(amendmentDocs); + String amendmentDocuments= (String) amendmentDocument.get("amendmentDocuments"); + if(amendmentDocuments!=null){ + String updatedValue = removeDocumentIdFromFieldValue(amendmentDocuments, documentId); + amendmentDocument.put("amendmentDocuments", updatedValue); + + // Step 4: Convert map back to JSON string + String updatedAmendmentDocs = Utils.convertMapIntoJsonString(amendmentDocument); // implement this if not available + + // Step 5: Set it back to entity + applicationAmendmentRequestEntity.get().setAmendmentDocument(updatedAmendmentDocs); + } + applicationAmendmentRequestEntity.get().setFormFields(Utils.convertListToJsonString(amendmentFormFieldMap.values().stream().toList())); + applicationAmendmentRequestRepository.save(applicationAmendmentRequestEntity.get()); + applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); log.info("Processing document of type AMENDMENT. Resolved amendmentId={}, applicationId={}, callId={}", amendmentId, applicationId, callId); } else if(DocumentSourceTypeEnum.EVALUATION.getValue().equalsIgnoreCase(documentEntity.getSource())){ evaluationId = documentEntity.getSourceId(); ApplicationEntity applicationEntity = applicationEvaluationRepository.findApplicationByEvaluationId(evaluationId); + ApplicationEvaluationEntity entity=applicationEvaluationRepository.findByApplicationId(applicationEntity.getId()); + List allDocs = applicationEvaluationDao.prepareEvaluationDocumentBeanList(entity); + List updatedDocs=allDocs; + allDocs = allDocs.stream() + .filter(doc -> doc.getFileValue().equals(removeDocumentIdFromFieldValue(doc.getFileValue(), documentId))) + .collect(Collectors.toList()); + + + String updatedEvaluationDocJson = Utils.convertObjectToJson(allDocs); + entity.setEvaluationDocument(updatedEvaluationDocJson); + applicationEvaluationRepository.save(entity); applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); log.info("Processing document of type EVALUATION. Resolved evaluationId={}, applicationId={}, callId={}", evaluationId, applicationId, callId); @@ -343,4 +408,19 @@ public class DocumentDao { throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.ERROR_MOVING_FILE_TO_DELETED_FOLDER)); } } + + public String removeDocumentIdFromFieldValue(String fieldValue, Long documentId) { + if (fieldValue == null || fieldValue.isBlank()) { + return fieldValue; + } + + List documentIdList = new ArrayList<>(Arrays.asList(fieldValue.split(","))); + documentIdList.replaceAll(String::trim); // Trim spaces for safety + + boolean removed = documentIdList.removeIf(id -> id.equals(String.valueOf(documentId))); + + // Return updated value only if modified, else return original + return removed ? String.join(",", documentIdList) : fieldValue; + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java index 3772ad71..462958cb 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java @@ -67,9 +67,15 @@ public class EmailNotificationDao { @Autowired private ApplicationEvaluationRepository applicationEvaluationRepository; + @Value("${default.hub.uuid}") + private String defaultHubUuid; + @Value("${rinaldo_email}") private String rinaldoEmail; + @Autowired + private SystemEmailTemplatesDao systemEmailTemplatesDao; + private void sendEmail(ApplicationEntity applicationEntity, SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum templateType, Map bodyPlaceholders, List additionalRecipients, Long amendmentId) { @@ -109,20 +115,37 @@ public class EmailNotificationDao { UserWithCompanyEntity userWithCompany=companyService.getUserWithCompany(userEntity.getId(),company.getId()); String companyEmail = userWithCompany.getEmail(); String contactEmail = userWithCompany.getContactEmail(); - - if (companyEmail != null && !companyEmail.isEmpty()) { - EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(systemEmailTemplateResponse.getEmailScenario(), RecipientTypeEnum.COMPANY,company.getId() , - companyEmail, userEntity.getId(), applicationEntity.getId(), amendmentId, applicationEntity.getCall().getId()); - sendMail(applicationEntity.getHubId(), subject, body, List.of(companyEmail), emailLogRequest); - } - if (contactEmail != null && !contactEmail.isEmpty() && !contactEmail.equals(companyEmail)) { - EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(systemEmailTemplateResponse.getEmailScenario(), RecipientTypeEnum.COMPANY,company.getId(), - contactEmail, userEntity.getId(), applicationEntity.getId(), amendmentId, applicationEntity.getCall().getId()); - sendMail(applicationEntity.getHubId(), subject, body, List.of(contactEmail), emailLogRequest); - } + if (Boolean.TRUE.equals(userEntity.getHub().getUniqueUuid().equals(defaultHubUuid))){ + List recipientEmails=new ArrayList<>(); + if (company.getPec()!=null) { + recipientEmails.add(company.getPec()); + }else { + recipientEmails.add(userWithCompany.getPec()); + } + EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(systemEmailTemplateResponse.getEmailScenario(), RecipientTypeEnum.COMPANY,company.getId() , + String.valueOf(recipientEmails), userEntity.getId(), applicationEntity.getId(), amendmentId, applicationEntity.getCall().getId()); + sendMail(applicationEntity.getHubId(), subject, body, recipientEmails, emailLogRequest); + } + else { + if (companyEmail != null && !companyEmail.isEmpty()) { + EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(systemEmailTemplateResponse.getEmailScenario(), RecipientTypeEnum.COMPANY, company.getId(), + companyEmail, userEntity.getId(), applicationEntity.getId(), amendmentId, applicationEntity.getCall().getId()); + sendMail(applicationEntity.getHubId(), subject, body, List.of(companyEmail), emailLogRequest); + } + if (contactEmail != null && !contactEmail.isEmpty() && !contactEmail.equals(companyEmail)) { + EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(systemEmailTemplateResponse.getEmailScenario(), RecipientTypeEnum.COMPANY, company.getId(), + contactEmail, userEntity.getId(), applicationEntity.getId(), amendmentId, applicationEntity.getCall().getId()); + sendMail(applicationEntity.getHubId(), subject, body, List.of(contactEmail), emailLogRequest); + } + } if (userEntity.getBeneficiary().getEmail() != null) { - String beneficiaryEmail = userEntity.getBeneficiary().getEmail(); + String beneficiaryEmail = null; + if (Boolean.TRUE.equals(userEntity.getHub().getUniqueUuid().equals(defaultHubUuid))){ + beneficiaryEmail=applicationEntity.getPecEmail(); + }else { + beneficiaryEmail=userEntity.getBeneficiary().getEmail(); + } EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(systemEmailTemplateResponse.getEmailScenario(), RecipientTypeEnum.BENEFICIARY,userEntity.getBeneficiary().getId() , beneficiaryEmail, userEntity.getId(), applicationEntity.getId(), amendmentId, applicationEntity.getCall().getId()); sendMail(applicationEntity.getHubId(), subject, body, List.of(beneficiaryEmail), emailLogRequest); @@ -174,8 +197,16 @@ public class EmailNotificationDao { public Map prepareEmailPlaceholders(ApplicationEntity applicationEntity, ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity){ Map bodyPlaceholders = new HashMap<>(); bodyPlaceholders.put("{{call_name}}", applicationEntity.getCall().getName()); - bodyPlaceholders.put("{{protocol_number}}", applicationEntity.getProtocol().getProtocolNumber().toString()); - bodyPlaceholders.put("{{protocol_date}}", DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getCreatedDate(), GepafinConstant.DD_MM_YYYY)); + String protocolNumber=applicationEntity.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(applicationEntity.getProtocol().getProtocolNumber()); + } + bodyPlaceholders.put("{{protocol_number}}", protocolNumber); + String protocolDate= DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getCreatedDate(), GepafinConstant.DD_MM_YYYY); + if(applicationEntity.getProtocol().getExternalProtocolDate()!=null){ + protocolDate= DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getExternalProtocolDate(), GepafinConstant.DD_MM_YYYY); + } + bodyPlaceholders.put("{{protocol_date}}", protocolDate); bodyPlaceholders.put("{{protocol_time}}", DateTimeUtil.parseLocalTimeToString(applicationEntity.getProtocol().getTime(), GepafinConstant.HH_MM_SS)); bodyPlaceholders.put("{{response_days}}", applicationAmendmentRequestEntity.getResponseDays().toString()); @@ -259,8 +290,16 @@ public class EmailNotificationDao { public void sendAdmissibilityNotificationEmailForAdmissibleApplication(ApplicationEntity applicationEntity) { Map bodyPlaceholders = new HashMap<>(); bodyPlaceholders.put("{{call_name}}", applicationEntity.getCall().getName()); - bodyPlaceholders.put("{{protocol_number}}", applicationEntity.getProtocol().getProtocolNumber().toString()); - bodyPlaceholders.put("{{protocol_date}}", DateTimeUtil.formatCreatedDate(applicationEntity.getProtocol().getCreatedDate())); + String protocolNumber=applicationEntity.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(applicationEntity.getProtocol().getProtocolNumber()); + } + bodyPlaceholders.put("{{protocol_number}}", protocolNumber); + String protocolDate= DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getCreatedDate(), GepafinConstant.DD_MM_YYYY); + if(applicationEntity.getProtocol().getExternalProtocolDate()!=null){ + protocolDate= DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getExternalProtocolDate(), GepafinConstant.DD_MM_YYYY); + } + bodyPlaceholders.put("{{protocol_date}}", protocolDate); bodyPlaceholders.put("{{protocol_time}}", DateTimeUtil.parseLocalTimeToString(applicationEntity.getProtocol().getTime(), GepafinConstant.HH_MM_SS)); sendEmail(applicationEntity, SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum.ADMISSIBILITY_NOTIFICATION, bodyPlaceholders, null,null); @@ -269,8 +308,16 @@ public class EmailNotificationDao { public void sendInadmissibilityEmailForRejectedApplication(ApplicationEntity applicationEntity,ApplicationEvaluationEntity applicationEvaluationEntity) { Map bodyPlaceholders = new HashMap<>(); bodyPlaceholders.put("{{call_name}}", applicationEntity.getCall().getName()); - bodyPlaceholders.put("{{protocol_number}}", applicationEntity.getProtocol().getProtocolNumber().toString()); - bodyPlaceholders.put("{{protocol_date}}", DateTimeUtil.formatCreatedDate(applicationEntity.getProtocol().getCreatedDate())); + String protocolNumber=applicationEntity.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(applicationEntity.getProtocol().getProtocolNumber()); + } + bodyPlaceholders.put("{{protocol_number}}", protocolNumber); + String protocolDate= DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getCreatedDate(), GepafinConstant.DD_MM_YYYY); + if(applicationEntity.getProtocol().getExternalProtocolDate()!=null){ + protocolDate= DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getExternalProtocolDate(), GepafinConstant.DD_MM_YYYY); + } + bodyPlaceholders.put("{{protocol_date}}", protocolDate); bodyPlaceholders.put("{{protocol_time}}", DateTimeUtil.parseLocalTimeToString(applicationEntity.getProtocol().getTime(), GepafinConstant.HH_MM_SS)); bodyPlaceholders.put("{{form_text}}", applicationEvaluationEntity.getMotivation()); @@ -311,4 +358,30 @@ public class EmailNotificationDao { throw new IllegalArgumentException("Failed to parse email configuration JSON", e); } } -} \ No newline at end of file + public void sendMailForApplicationTechnicalEvaluationRejected(ApplicationEntity applicationEntity,HubEntity hub,ApplicationEvaluationEntity applicationEvaluationEntity) { + + Map bodyPlaceholders = prepareEmailPlaceholdersForTechnicalEvaluationRejected(applicationEntity,hub,applicationEvaluationEntity); + sendEmail(applicationEntity, SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum.INADMISSIBILITY_NOTIFICATION_DUE_TO_TECHNICAL_EVALUATION_FAILURE, bodyPlaceholders, null, + null); + } + public Map prepareEmailPlaceholdersForTechnicalEvaluationRejected(ApplicationEntity applicationEntity,HubEntity hub,ApplicationEvaluationEntity applicationEvaluationEntity) { + Map bodyPlaceholders = new HashMap<>(); + bodyPlaceholders.put("{{call_name}}", applicationEntity.getCall().getName()); + String protocolNumber = applicationEntity.getProtocol().getExternalProtocolNumber(); + if (protocolNumber == null) { + protocolNumber = String.valueOf(applicationEntity.getProtocol().getProtocolNumber()); + } + bodyPlaceholders.put("{{protocol_number}}", protocolNumber); + String protocolDate = DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getCreatedDate(), GepafinConstant.DD_MM_YYYY); + if (applicationEntity.getProtocol().getExternalProtocolDate() != null) { + protocolDate = DateTimeUtil.formatLocalDateTime(applicationEntity.getProtocol().getExternalProtocolDate(), GepafinConstant.DD_MM_YYYY); + } + bodyPlaceholders.put("{{protocol_date}}", protocolDate); + bodyPlaceholders.put("{{protocol_time}}", DateTimeUtil.parseLocalTimeToString(applicationEntity.getProtocol().getTime(), GepafinConstant.HH_MM_SS)); + bodyPlaceholders.put("{{email_signature}}", hub.getEmailSignature()); + bodyPlaceholders.put("{{platform_link}}", hub.getDomainName()); + bodyPlaceholders.put("{{form_text}}", applicationEvaluationEntity.getMotivation()); + + return bodyPlaceholders; + } + } \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/dao/NotificationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/NotificationDao.java index 17b86d6e..55ec6665 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/NotificationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/NotificationDao.java @@ -174,7 +174,11 @@ public class NotificationDao { Map placeHolders = new HashMap<>(); placeHolders.put("{{call_name}}", application.getCall().getName()); - placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + String protocolNumber=application.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(application.getProtocol().getProtocolNumber()); + } + placeHolders.put("{{protocol_number}}", protocolNumber); NotificationReq notificationReq = createNotificationReq(notificationTypeEnum.getValue(), placeHolders, application.getUserId(), application.getUserWithCompany(), listOf(application.getCompanyId())); sendNotification(notificationReq); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ProtocolDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ProtocolDao.java index a9b23cd6..d198c599 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ProtocolDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ProtocolDao.java @@ -89,6 +89,9 @@ public class ProtocolDao { @Value("${tipoCorrispondente}") private String tipoCorrispondente; + @Value("${isSviluppumbriaProtocolEnabled}") + private String isSviluppUmbriaProtocolEnabled; + @Autowired private ApplicationSignedDocumentRepository applicationSignedDocumentRepository; @@ -156,6 +159,11 @@ public class ProtocolDao { } public ProtocolEntity createExternalProtocol(ApplicationEntity application, CompanyEntity company, ProtocolEntity protocol) { + + if (Boolean.FALSE.equals(Boolean.parseBoolean(isSviluppUmbriaProtocolEnabled))) { + return protocol; + } + log.info("Starting createExternalProtocol for application ID: {}", application.getId()); log.debug("Successfully retrieved bearer token"); @@ -170,26 +178,26 @@ public class ProtocolDao { vatNumber=company.getVatNumber(); }else { mittenteValue = tipoCorrispondenteWithoutCompany; - vatNumber = company.getCodiceFiscale(); + vatNumber = company.getCodiceFiscale(); } String companyName = company.getCompanyName() + " " + vatNumber; String callName = application.getCall().getName()+" "+ application.getCall().getId(); String callId = GepafinConstant.PROTOCOL_CALL_NAME+ String.valueOf(application.getCall().getId()); String pecEmail=company.getPec(); - String docUrl=applicationSignedDocumentEntity.getFilePath(); + String docUrl=applicationSignedDocumentEntity.getFilePath()+GepafinConstant.PROTOCOL_DOC_SUFFIX; Map requestBody = new HashMap<>(); requestBody.put(GepafinConstant.PROTOCOL_DOC_URL,docUrl); - requestBody.put(GepafinConstant.PROTOCOL_COMPANY_NAME,companyName); requestBody.put(GepafinConstant.PROTOCOL_DOC_HASH,applicationSignedDocumentEntity.getFileHash()); requestBody.put(GepafinConstant.PROTOCOL_TIPO_PROTOCOLLAZIONE,TIPO_PROTOCOLLAZIONE); + requestBody.put(GepafinConstant.PROTOCOL_COMPANY_NAME_VAT_NUMBER,company.getCompanyName()); Map mittente = new HashMap<>(); - mittente.put(GepafinConstant.PROTOCOL_TIPO_CORRISPONDENTE_COMPANY, mittenteValue); - mittente.put(GepafinConstant.PROTOCOL_COMPANY_NAME_VAT_NUMBER,company.getCompanyName()); + mittente.put(GepafinConstant.PROTOCOL_TIPO_CORRISPONDENTE, mittenteValue); mittente.put(GepafinConstant.PROTOCOL_MEZZO,mezzo); mittente.put(GepafinConstant.PROTOCOL_INDIRIZZO_PEC,pecEmail); mittente.put(GepafinConstant.PROTOCOL_COMPANY_VAT_NUMBER,vatNumber); + mittente.put(GepafinConstant.PROTOCOL_COMPANY_NAME,company.getCompanyName()); List> destinatariObject=new ArrayList<>(); Map destinatari = new HashMap<>(); destinatari.put(GepafinConstant.PROTOCOL_CODICE_UO,codiceUo); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/SystemEmailTemplatesDao.java b/src/main/java/net/gepafin/tendermanagement/dao/SystemEmailTemplatesDao.java index 8808abfd..2362b77e 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/SystemEmailTemplatesDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/SystemEmailTemplatesDao.java @@ -116,7 +116,7 @@ public class SystemEmailTemplatesDao { return htmlContent; } - private String replaceEmailSignature(HubEntity hub, String htmlContent, Map languageMap) { + public String replaceEmailSignature(HubEntity hub, String htmlContent, Map languageMap) { String emailSignature = defaultEmailSignature; if(hub != null && Boolean.FALSE.equals(StringUtils.isEmpty(hub.getEmailSignature()))){ emailSignature = hub.getEmailSignature(); @@ -124,7 +124,7 @@ public class SystemEmailTemplatesDao { return htmlContent.replace("{{email_signature}}", emailSignature); } - private String replacePlatformLinkPlaceholder(HubEntity hub, String htmlContent, Map languageMap) { + public String replacePlatformLinkPlaceholder(HubEntity hub, String htmlContent, Map languageMap) { String platformLink = feBaseUrl; if(hub != null && Boolean.FALSE.equals(StringUtils.isEmpty(hub.getDomainName()))){ platformLink = hub.getDomainName(); diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java index 0d917599..b9af1ca8 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import net.gepafin.tendermanagement.model.response.EmailSendResponse; import java.time.LocalDateTime; @@ -10,6 +11,7 @@ import java.util.List; @Entity @Table(name="application_amendment_request") @Data +@Where(clause = "is_deleted = false") public class ApplicationAmendmentRequestEntity extends BaseEntity { @Column(name = "NOTE") @@ -33,9 +35,6 @@ public class ApplicationAmendmentRequestEntity extends BaseEntity { @Column(name = "FORM_FIELDS") private String formFields; - @Column(name="IS_DELETED") - private Boolean isDeleted=false; - @Column(name = "STATUS") private String status; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java index 50d16557..dacc3541 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.Where; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -9,6 +10,7 @@ import java.time.LocalDateTime; @Entity @Table(name = "APPLICATION") @Data +@Where(clause = "is_deleted = false") public class ApplicationEntity extends BaseEntity { @Column(name = "USER_ID") @@ -29,9 +31,6 @@ public class ApplicationEntity extends BaseEntity { @ManyToOne @JoinColumn(name = "CALL_ID", nullable = false) private CallEntity call; - - @Column(name="IS_DELETED") - private Boolean isDeleted; @OneToOne @JoinColumn(name = "PROTOCOL_NUMBER") @@ -74,4 +73,10 @@ public class ApplicationEntity extends BaseEntity { @Column(name = "APPLICATION_EVALUATION_ID") private Long applicationEvaluationId; + @Column(name = "PEC_EMAIL") + private String pecEmail; + + @Column(name="VAT_NUMBER") + private String vatNumber; + } \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java index b8b00d3b..ae080d48 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import net.gepafin.tendermanagement.model.response.EmailSendResponse; import java.time.LocalDateTime; @@ -10,6 +11,7 @@ import java.util.List; @Data @Entity @Table(name = "application_evaluation") +@Where(clause = "is_deleted = false") public class ApplicationEvaluationEntity extends BaseEntity{ @Column(name = "application_Id") @@ -39,9 +41,6 @@ public class ApplicationEvaluationEntity extends BaseEntity{ @Column(name = "MOTIVATION") private String motivation; - @Column(name="IS_DELETED") - private Boolean isDeleted; - @ManyToOne @JoinColumn(name = "assigned_applications_id", nullable = true) private AssignedApplicationsEntity assignedApplicationsEntity; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormEntity.java index 251fb262..78a881a0 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormEntity.java @@ -2,16 +2,15 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Data @Table(name = "APPLICATION_EVALUATION_FORM") +@Where(clause = "is_deleted = false") public class ApplicationEvaluationFormEntity extends BaseEntity{ private Long applicationId; - @Column(name="IS_DELETED") - private Boolean isDeleted; - @ManyToOne @JoinColumn(name = "EVALUATION_ID") private ApplicationEvaluationEntity applicationEvaluation; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormFieldEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormFieldEntity.java index 913d8831..f4324a2a 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormFieldEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormFieldEntity.java @@ -2,10 +2,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Data @Table(name = "APPLICATION_EVALUATION_FORM_FIELD") +@Where(clause = "is_deleted = false") public class ApplicationEvaluationFormFieldEntity extends BaseEntity { @ManyToOne @@ -18,7 +20,4 @@ public class ApplicationEvaluationFormFieldEntity extends BaseEntity { @Column(name = "FIELD_VALUE") private String fieldValue; - @Column(name="IS_DELETED") - private Boolean isDeleted; - } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormEntity.java index a6cc56aa..cf407cda 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormEntity.java @@ -2,11 +2,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; - +import org.hibernate.annotations.Where; @Entity @Table(name = "APPLICATION_FORM") @Data +@Where(clause = "is_deleted = false") public class ApplicationFormEntity extends BaseEntity { @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormFieldEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormFieldEntity.java index fbab986d..206be527 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormFieldEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormFieldEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -11,6 +12,7 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class ApplicationFormFieldEntity extends BaseEntity { @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormView.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormView.java index b3733afc..e99d43ca 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormView.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormView.java @@ -115,4 +115,12 @@ public class ApplicationFormView { @Column(name = "call_start_time") private LocalTime callStartTime; + @Column(name = "INSTRUCTOR_NAME") + private String instructorName; + + @Column(name = "SUBMISSION_DATE") + private LocalDate submissionDate; + + @Column(name = "SUBMISSION_TIME") + private LocalTime submissionTime; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationSignedDocumentEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationSignedDocumentEntity.java index 9a90af52..3f50c14b 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationSignedDocumentEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationSignedDocumentEntity.java @@ -6,10 +6,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Data @Entity @Table(name = "application_signed_document") +@Where(clause = "is_deleted = false") public class ApplicationSignedDocumentEntity extends BaseEntity { @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsEntity.java index bd26f527..0c80c7af 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsEntity.java @@ -2,13 +2,14 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Data @Table(name = "assigned_applications") - +@Where(clause = "is_deleted = false") public class AssignedApplicationsEntity extends BaseEntity{ @ManyToOne @@ -27,9 +28,7 @@ public class AssignedApplicationsEntity extends BaseEntity{ @Column(name = "NOTE") private String note; - @Column(name="IS_DELETED") - private Boolean isDeleted=false; - @Column(nullable = false) private LocalDateTime assignedAt; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java index 46bf004b..e4224631 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java @@ -67,4 +67,7 @@ public class AssignedApplicationsView{ @Column(name = "HUB_ID") private Long hubId; + + @Column(name="ASSIGNED_USER_NAME") + private String assignedUserName; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/BaseEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/BaseEntity.java index f8427719..16c7e94f 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/BaseEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/BaseEntity.java @@ -21,8 +21,13 @@ public class BaseEntity { @Column(name = "UPDATED_DATE") LocalDateTime updatedDate; - - @PrePersist + + + @Column(name="IS_DELETED") + private Boolean isDeleted = false; + + + @PrePersist public void setCreatedDate() { this.createdDate = DateTimeUtil.DateServerToUTC(LocalDateTime.now()); this.updatedDate = DateTimeUtil.DateServerToUTC(LocalDateTime.now()); diff --git a/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryEntity.java index 2e84dd51..0985c37f 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryEntity.java @@ -7,10 +7,12 @@ import jakarta.persistence.Entity; import jakarta.persistence.Table; import jakarta.validation.constraints.Email; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "BENEFICIARY") @Data +@Where(clause = "is_deleted = false") public class BeneficiaryEntity extends BaseEntity { @Email diff --git a/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryPreferredCallEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryPreferredCallEntity.java index 5bbc316d..22333d18 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryPreferredCallEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryPreferredCallEntity.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -14,6 +15,7 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class BeneficiaryPreferredCallEntity extends BaseEntity{ @Id @@ -35,9 +37,6 @@ public class BeneficiaryPreferredCallEntity extends BaseEntity{ @Column(name = "STATUS", length = 255) private String status; - @Column(name="IS_DELETED") - private Boolean isDeleted; - @ManyToOne @JoinColumn(name = "USER_WITH_COMPANY_ID") private UserWithCompanyEntity userWithCompany; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java index 25388138..dd2655ed 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java @@ -5,6 +5,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.AllArgsConstructor; import lombok.Builder; +import org.hibernate.annotations.Where; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -16,6 +17,7 @@ import java.time.LocalTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class CallEntity extends BaseEntity { @Column(name = "NAME", length = 255) @@ -97,5 +99,8 @@ public class CallEntity extends BaseEntity { @Column(name = "APPOINTMENT_TEMPLATE_ID") private Long appointmentTemplateId; + + @Column(name = "allow_multiple_applications") + private Boolean allowMultipleApplications; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CallTargetAudienceChecklistEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CallTargetAudienceChecklistEntity.java index 6b14df58..465baf67 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CallTargetAudienceChecklistEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CallTargetAudienceChecklistEntity.java @@ -2,10 +2,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "CALL_TARGET_AUDIENCE_CHECKLIST") @Data +@Where(clause = "is_deleted = false") public class CallTargetAudienceChecklistEntity extends BaseEntity{ @ManyToOne @@ -18,9 +20,5 @@ public class CallTargetAudienceChecklistEntity extends BaseEntity{ @Column(name = "IS_VALIDATED") private Boolean isValidated; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; - } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CommunicationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CommunicationEntity.java index 9029649e..a1c3ff45 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CommunicationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CommunicationEntity.java @@ -6,12 +6,14 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Table(name = "communication") @Data +@Where(clause = "is_deleted = false") public class CommunicationEntity extends BaseEntity { @Column(name = "COMMUNICATION_TITLE") @@ -20,9 +22,6 @@ public class CommunicationEntity extends BaseEntity { @Column(name = "COMMUNICATION_COMMENT") private String communicationComment; - @Column(name = "IS_DELETED") - private Boolean isDeleted = false; - @Column(name = "COMMENTED_DATE") private LocalDateTime commentedDate; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CompanyDocumentEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CompanyDocumentEntity.java index 15c3457c..1a08fffc 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CompanyDocumentEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CompanyDocumentEntity.java @@ -2,12 +2,14 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Table(name = "COMPANY_DOCUMENT") @Data +@Where(clause = "is_deleted = false") public class CompanyDocumentEntity extends BaseEntity { @Column(name = "FILE_NAME") @@ -25,9 +27,6 @@ public class CompanyDocumentEntity extends BaseEntity { @Column(name="COMPANY_ID") private Long companyId; - @Column(name ="IS_DELETED") - private Boolean isDeleted = false; - @Column(name="UPLOADED_BY") private Long uploadedBy; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java index 88658077..8a6c94e1 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java @@ -8,10 +8,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "COMPANY") @Data +@Where(clause = "is_deleted = false") public class CompanyEntity extends BaseEntity{ @Column(name = "COMPANY_NAME") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CriteriaFormFieldEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CriteriaFormFieldEntity.java index 1177bbd2..8a65ee5e 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CriteriaFormFieldEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CriteriaFormFieldEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "criteria_form_field") @Data +@Where(clause = "is_deleted = false") public class CriteriaFormFieldEntity extends BaseEntity { private Long callId; @@ -17,8 +19,5 @@ public class CriteriaFormFieldEntity extends BaseEntity { private String formFieldId; private Long evaluationCriteriaId; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/DocumentCategoryEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/DocumentCategoryEntity.java index 25a25d6d..3307e2d0 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/DocumentCategoryEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/DocumentCategoryEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "document_category") @Data +@Where(clause = "is_deleted = false") public class DocumentCategoryEntity extends BaseEntity { @Column(name = "CATEGORY_NAME") @@ -16,7 +18,4 @@ public class DocumentCategoryEntity extends BaseEntity { @Column(name = "DESCRIPTION") private String description; - @Column(name ="IS_DELETED") - private Boolean isDeleted = false; - } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java index 08bfd623..8c658411 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java @@ -6,10 +6,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "DOCUMENT") @Data +@Where(clause = "is_deleted = false") public class DocumentEntity extends BaseEntity{ @Column(name = "FILE_NAME", length = 255) private String fileName; @@ -26,9 +28,6 @@ public class DocumentEntity extends BaseEntity{ @Column(name="SOURCE_ID") private Long sourceId; - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; - @Column(name="DOCUMENT_ATTACHMENT_ID") private String documentAttachmentId; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java index 1a582a21..1f8f3fe5 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java @@ -1,12 +1,14 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Data @Table(name = "email_log") +@Where(clause = "is_deleted = false") public class EmailLogEntity extends BaseEntity{ @Column(name = "email_type", nullable = false, length = 255) @@ -54,9 +56,6 @@ public class EmailLogEntity extends BaseEntity{ @Column(name = "call_id") private Long callId; - @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/EvaluationCriteriaEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/EvaluationCriteriaEntity.java index 8d1c0c69..0322911b 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/EvaluationCriteriaEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/EvaluationCriteriaEntity.java @@ -5,6 +5,9 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; + +import java.math.BigDecimal; import java.math.BigDecimal; @@ -12,6 +15,7 @@ import java.math.BigDecimal; @Entity @Table(name = "EVALUATION_CRITERIA") @Data +@Where(clause = "is_deleted = false") public class EvaluationCriteriaEntity extends BaseEntity { @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/EvaluationFormEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/EvaluationFormEntity.java index 9d3bd3fa..e839814a 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/EvaluationFormEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/EvaluationFormEntity.java @@ -5,12 +5,15 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; + @Entity @Table(name = "EVALUATION_FORM") @Data @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class EvaluationFormEntity extends BaseEntity{ @@ -24,6 +27,4 @@ public class EvaluationFormEntity extends BaseEntity{ @Column(name = "CONTENT") private String content; - @Column(name="IS_DELETED") - private Boolean isDeleted; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java index 83159e3a..0f8336db 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "expiration_config") @Data +@Where(clause = "is_deleted = false") public class ExpirationConfigEntity extends BaseEntity { @Column(name="INTERVAL_DAYS") @@ -15,7 +17,4 @@ public class ExpirationConfigEntity extends BaseEntity { @Column(name="TYPE") private String type; - - @Column(name="IS_DELETED") - private Boolean isDeleted; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FaqEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FaqEntity.java index 46ebff24..5df715c9 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FaqEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FaqEntity.java @@ -9,11 +9,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; - +import org.hibernate.annotations.Where; @Entity @Table(name = "FAQ") @Data +@Where(clause = "is_deleted = false") public class FaqEntity extends BaseEntity { @ManyToOne @@ -38,10 +39,7 @@ public class FaqEntity extends BaseEntity { @Column(name = "RESPONSE_DATE") private LocalDateTime responseDate; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; - + @Column(name ="COMPANY_ID") private Long companyId; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FlowDataEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FlowDataEntity.java index cdffe410..a8c99131 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FlowDataEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FlowDataEntity.java @@ -1,6 +1,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -10,6 +11,7 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FlowDataEntity extends BaseEntity{ @Column(name = "FORM_ID") @@ -23,4 +25,5 @@ public class FlowDataEntity extends BaseEntity{ @Column(name="CALL_ID") private Long callId; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FlowEdgesEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FlowEdgesEntity.java index 119af700..4bcab38d 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FlowEdgesEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FlowEdgesEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -11,6 +12,7 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FlowEdgesEntity extends BaseEntity { @Column(name = "SOURCE_ID") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FormEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FormEntity.java index 588619a5..5861b7a8 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FormEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FormEntity.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Builder; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; @Entity @Table(name = "FORM") @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FormEntity extends BaseEntity{ @Column(name = "LABEL", length = 255) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FormFieldEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FormFieldEntity.java index b75b0c57..66e1485d 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FormFieldEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FormFieldEntity.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Builder; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; @Entity @Table(name = "FORM_FIELD") @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FormFieldEntity extends BaseEntity{ @Column(name = "NAME", length = 255) @@ -31,4 +33,5 @@ public class FormFieldEntity extends BaseEntity{ @Column(name = "SORT_ORDER") private Integer sortOrder; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FormTemplateEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FormTemplateEntity.java index 897a9dfd..93f20e18 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FormTemplateEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FormTemplateEntity.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Builder; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; @Entity @Table(name = "FORM_TEMPLATE") @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FormTemplateEntity extends BaseEntity{ @Column(name = "LABEL", length = 255) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/GlobalConfigEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/GlobalConfigEntity.java index 3f55abdf..21201ba5 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/GlobalConfigEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/GlobalConfigEntity.java @@ -15,7 +15,4 @@ public class GlobalConfigEntity extends BaseEntity{ @Column(name = "TYPE") private String type; - - @Column(name = "IS_DELETED") - private Boolean isDeleted = false; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java index 603b42b4..f59e3d3c 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java @@ -4,12 +4,13 @@ import jakarta.persistence.*; import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; - +import org.hibernate.annotations.Where; @Entity @Table(name="hub") @Setter @Getter +@Where(clause = "is_deleted = false") public class HubEntity extends BaseEntity{ @Column(name = "COMPANY_NAME") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/HubUserEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/HubUserEntity.java index 1bec68ce..ebed1915 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/HubUserEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/HubUserEntity.java @@ -3,11 +3,13 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.Where; @Entity @Table(name = "hub_user") @Getter @Setter +@Where(clause = "is_deleted = false") public class HubUserEntity extends BaseEntity{ @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/LoginAttemptEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/LoginAttemptEntity.java index bc3e445e..6ad5a652 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/LoginAttemptEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/LoginAttemptEntity.java @@ -2,12 +2,14 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Table(name = "LOGIN_ATTEMPT") @Data +@Where(clause = "is_deleted = false") public class LoginAttemptEntity extends BaseEntity{ @Column(name = "USERNAME") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/LookUpDataEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/LookUpDataEntity.java index 1afe87aa..21475d82 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/LookUpDataEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/LookUpDataEntity.java @@ -3,10 +3,12 @@ package net.gepafin.tendermanagement.entities; import com.fasterxml.jackson.annotation.JsonValue; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "LOOKUP_DATA") @Data +@Where(clause = "is_deleted = false") public class LookUpDataEntity extends BaseEntity{ @Column(name = "TITLE", columnDefinition = "TEXT", nullable = true) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/NdganagEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/NdganagEntity.java new file mode 100644 index 00000000..463f31c9 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/entities/NdganagEntity.java @@ -0,0 +1,33 @@ +package net.gepafin.tendermanagement.entities; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.Data; +import org.hibernate.annotations.Where; + +@Entity +@Table(name = "NDGANAG") +@Data +@Where(clause = "is_deleted = false") +public class NdganagEntity extends BaseEntity{ + + @Column(name = "NDG") + private String ndg; + + @Column(name = "COMPANY_NAME") + private String companyName; + + @Column(name = "VAT_NUMBER") + private String vatNumber; + + @Column(name = "CODICE_FISCALE") + private String codiceFiscale; + + @Column(name = "JSON") + private String json; + + @Column(name = "IS_DELETED") + private Boolean isDeleted; + +} diff --git a/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java index 718a7367..9896140b 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java @@ -6,10 +6,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "NOTIFICATION") @Data +@Where(clause = "is_deleted = false") public class NotificationEntity extends BaseEntity { @Column(name = "USER_ID") @@ -24,9 +26,6 @@ public class NotificationEntity extends BaseEntity { @Column(name = "STATUS") private String status; - @Column(name = "IS_DELETED") - private Boolean isDeleted; - @Column(name = "NOTIFICATION_TYPE") private String notificationType; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java index 40d3f220..8a56b9ab 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Data @Table(name = "NOTIFICATION_TYPE") +@Where(clause = "is_deleted = false") public class NotificationTypeEntity extends BaseEntity { @Column(name = "NOTIFICATION_NAME") @@ -18,7 +20,4 @@ public class NotificationTypeEntity extends BaseEntity { @Column(name = "TITLE") private String title; - - @Column(name="IS_DELETED") - private Boolean isDeleted; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java index e807304a..cc46095a 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java @@ -3,6 +3,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; import net.gepafin.tendermanagement.config.LocalTimeAttributeConverter; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; import java.time.LocalTime; @@ -10,6 +11,7 @@ import java.time.LocalTime; @Entity @Table(name = "PROTOCOL") @Data +@Where(clause = "is_deleted = false") public class ProtocolEntity extends BaseEntity { @Column(name = "PROTOCOL_NUMBER", nullable = false) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/RegionEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/RegionEntity.java index 386b9787..d18020bd 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/RegionEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/RegionEntity.java @@ -6,6 +6,7 @@ import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.Where; import java.math.BigDecimal; @@ -13,6 +14,7 @@ import java.math.BigDecimal; @Table(name = "REGION") @Getter @Setter +@Where(clause = "is_deleted = false") public class RegionEntity extends BaseEntity { @Column(name = "REGION_NAME", length = 255, nullable = true) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/RoleActionContextEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/RoleActionContextEntity.java index a3ff676f..88408904 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/RoleActionContextEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/RoleActionContextEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Data @Table(name ="role_action_context") +@Where(clause = "is_deleted = false") public class RoleActionContextEntity extends BaseEntity { @Column(name = "action_context") @@ -16,9 +18,6 @@ public class RoleActionContextEntity extends BaseEntity { @Column(name = "role_id") private Long roleId; - @Column(name="is_deleted") - private Boolean isDeleted; - @Column(name = "is_viewed") private Boolean isViewed; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/RoleEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/RoleEntity.java index c94b36b9..55091136 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/RoleEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/RoleEntity.java @@ -4,11 +4,13 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.Where; @Entity @Table(name = "ROLE") @Getter @Setter +@Where(clause = "is_deleted = false") public class RoleEntity extends BaseEntity { @Column(name = "ROLE_NAME", length = 255, nullable = true) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/S3ConfigEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/S3ConfigEntity.java index 66502bbc..b058aa70 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/S3ConfigEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/S3ConfigEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "s3_path_configuration") @Data +@Where(clause = "is_deleted = false") public class S3ConfigEntity extends BaseEntity { @Column(name = "TYPE") @@ -21,5 +23,6 @@ public class S3ConfigEntity extends BaseEntity { @Column(name = "PARENT_FOLDER") private String parentFolder; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/SamlResponseEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/SamlResponseEntity.java index 05535e97..a3dcd2c8 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/SamlResponseEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/SamlResponseEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "SAML_RESPONSE") @Data +@Where(clause = "is_deleted = false") public class SamlResponseEntity extends BaseEntity{ @Column(name = "AUTHENTICATION_OBJECT") @@ -30,6 +32,5 @@ public class SamlResponseEntity extends BaseEntity{ @Column(name = "TOKEN") private String token; - } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java index 92d97e9f..1ac63bae 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java @@ -8,10 +8,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "system_email_template") @Data +@Where(clause = "is_deleted = false") public class SystemEmailTemplatesEntity extends BaseEntity { @@ -32,9 +34,6 @@ public class SystemEmailTemplatesEntity extends BaseEntity { @Column(name = "SYSTEM") private Boolean system; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; @Column(name = "email_scenario") private String emailScenario; @@ -56,7 +55,8 @@ public class SystemEmailTemplatesEntity extends BaseEntity { USER_ONBOARDING_BANDI("USER_ONBOARDING_BANDI"), PASSWORD_RESET("PASSWORD_RESET"), INADMISSIBILITY_TEMPLATE("INADMISSIBILITY_NOTIFICATION"), - APPLICATION_SUBMISSION_FAILURE_NOTIFICATION("APPLICATION_SUBMISSION_FAILURE_NOTIFICATION"); + APPLICATION_SUBMISSION_FAILURE_NOTIFICATION("APPLICATION_SUBMISSION_FAILURE_NOTIFICATION"), + INADMISSIBILITY_NOTIFICATION_DUE_TO_TECHNICAL_EVALUATION_FAILURE("INADMISSIBILITY_NOTIFICATION_DUE_TO_TECHNICAL_EVALUATION_FAILURE"); private String value; SystemEmailTemplatesEntityTypeEnum(String value) { diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserActionEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserActionEntity.java index 6f85f6ef..f15e173c 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserActionEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserActionEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Data @Entity @Table(name = "user_action") +@Where(clause = "is_deleted = false") public class UserActionEntity extends BaseEntity { @Column(name = "USER_ID") @@ -40,7 +42,4 @@ public class UserActionEntity extends BaseEntity { @Column(name = "RESPONSE") private String response; - @Column(name = "IS_DELETED", nullable = false) - private Boolean isDeleted = false; - } \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserCompanyDelegationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserCompanyDelegationEntity.java index 9def5dc3..8659d16a 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserCompanyDelegationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserCompanyDelegationEntity.java @@ -2,10 +2,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Data @Entity @Table(name = "user_company_delegation") +@Where(clause = "is_deleted = false") public class UserCompanyDelegationEntity extends BaseEntity{ @Column(name="USER_ID") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java index 86147cdf..3dbd21d4 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.Where; import net.gepafin.tendermanagement.model.response.EmailSendResponse; import java.time.LocalDateTime; @@ -16,6 +17,7 @@ import java.util.List; @Table(name = "GEPAFIN_USER") @Getter @Setter +@Where(clause = "is_deleted = false") public class UserEntity extends BaseEntity { @Column(name = "PASSWORD", columnDefinition = "TEXT",nullable = true) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserWithCompanyEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserWithCompanyEntity.java index 65afbe76..9c067b32 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserWithCompanyEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserWithCompanyEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "USER_WITH_COMPANY") @Data +@Where(clause = "is_deleted = false") public class UserWithCompanyEntity extends BaseEntity{ @Column(name = "USER_ID") @@ -34,8 +36,5 @@ public class UserWithCompanyEntity extends BaseEntity{ @Column(name = "EMAIL") private String email; - - @Column(name = "IS_DELETED") - private Boolean isDeleted = false; - + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/VersionHistoryEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/VersionHistoryEntity.java index 2445fb44..cca7dfc2 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/VersionHistoryEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/VersionHistoryEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Data @Entity @Table(name = "version_history") +@Where(clause = "is_deleted = false") public class VersionHistoryEntity extends BaseEntity { @Column(name = "OLD_DATA", columnDefinition = "LONGTEXT") @@ -30,7 +32,4 @@ public class VersionHistoryEntity extends BaseEntity { @Column(name = "USER_ID") private Long userId; - - @Column(name = "IS_DELETED", nullable = false) - private Boolean isDeleted = false; } diff --git a/src/main/java/net/gepafin/tendermanagement/enums/ApplicationStatusForEvaluation.java b/src/main/java/net/gepafin/tendermanagement/enums/ApplicationStatusForEvaluation.java index f0f6acb8..d9c3c45e 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/ApplicationStatusForEvaluation.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/ApplicationStatusForEvaluation.java @@ -6,7 +6,8 @@ public enum ApplicationStatusForEvaluation { APPROVED("APPROVED"), REJECTED("REJECTED"), ADMISSIBLE("ADMISSIBLE"), - TECHNICAL_EVALUATION("TECHNICAL_EVALUATION"); + TECHNICAL_EVALUATION("TECHNICAL_EVALUATION"), + TECHNICAL_EVALUATION_REJECTED("TECHNICAL_EVALUATION_REJECTED"); private String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/ApplicationStatusTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/ApplicationStatusTypeEnum.java index 041ec85a..3afbc3ad 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/ApplicationStatusTypeEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/ApplicationStatusTypeEnum.java @@ -16,7 +16,8 @@ public enum ApplicationStatusTypeEnum { APPOINTMENT("APPOINTMENT"), NDG("NDG"), ADMISSIBLE("ADMISSIBLE"), - TECHNICAL_EVALUATION("TECHNICAL_EVALUATION"); + TECHNICAL_EVALUATION("TECHNICAL_EVALUATION"), + TECHNICAL_EVALUATION_REJECTED("TECHNICAL_EVALUATION_REJECTED"); private String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java index 908e554d..ab25ad25 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java @@ -12,7 +12,8 @@ public enum EmailScenarioTypeEnum { USER_CREATION("USER_CREATION"), PASSWORD_RESET_REQUEST("PASSWORD_RESET_REQUEST"), APPLICATION_REJECTED("APPLICATION_REJECTED"), - APPLICATION_SUBMISSION_FAILURE("APPLICATION_SUBMISSION_FAILURE"); + APPLICATION_SUBMISSION_FAILURE("APPLICATION_SUBMISSION_FAILURE"), + APPLICATION_TECHNICAL_EVALUATION_REJECTED("APPLICATION_TECHNICAL_EVALUATION_REJECTED"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/HttpMethodEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/HttpMethodEnum.java new file mode 100644 index 00000000..f71842fb --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/enums/HttpMethodEnum.java @@ -0,0 +1,18 @@ +package net.gepafin.tendermanagement.enums; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum HttpMethodEnum { + POST("POST"), PUT("PUT"), GET("GET"), DELETE("DELETE"); + + private String value; + + HttpMethodEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } +} \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/enums/NdgStatusEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/NdgStatusEnum.java new file mode 100644 index 00000000..2d16a7bd --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/enums/NdgStatusEnum.java @@ -0,0 +1,22 @@ +package net.gepafin.tendermanagement.enums; + + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum NdgStatusEnum { + + NDG_INITITATED("NDG_INITITATED"), + NDG_GENERATED("NDG_GENERATED"), + NDG_IN_PROGRESS("NDG_IN_PROGRESS"); + + private String value; + + NdgStatusEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java index fe2e4cd1..4200e361 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java @@ -47,6 +47,8 @@ public enum UserActionContextEnum { GET_NEXT_PREVIOUS_FORM("GET_NEXT_PREVIOUS_FORM"), DOWNLOAD_APPLICATION_DOC_ZIP("DOWNLOAD_APPLICATION_DOC_ZIP"), READMIT_APPLICATION("READMIT_APPLICATION"), + DOWNLOAD_CSV_BY_CALL_ID("DOWNLOAD_CSV_BY_CALL_ID"), + DOWNLOAD_CSV_AS_PER_RANKING("DOWNLOAD_CSV_AS_PER_RANKING"), /** FAQ action context **/ CREATE_FAQ("CREATE_FAQ"), @@ -67,6 +69,7 @@ public enum UserActionContextEnum { GET_COMPANY_BY_USER("GET_COMPANY_BY_USER"), REMOVE_COMPANY_FROM_USER("REMOVE_COMPANY_FROM_USER"), UPDATE_COMPANY_JSON("UPDATE_COMPANY_JSON"), + EXTRACT_PEC_FROM_COMPANY("EXTRACT_PEC_FROM_COMPANY"), /** LookUpData action context **/ CREATE_LOOKUP_DATA("CREATE_LOOKUP_DATA"), @@ -180,6 +183,7 @@ public enum UserActionContextEnum { CHECK_OR_CREATE_NDG_CODE("CHECK_OR_CREATE_NDG_CODE"), CREATE_APPOINTMENT("CREATE_APPOINTMENT"), UPLOAD_DOCUMENT_TO_EXTERNAL_SYSTEM("UPLOAD_DOCUMENT_TO_EXTERNAL_SYSTEM"), + GET_NDG_BY_VAT_NUMBER("GET_NDG_BY_VAT_NUMBER"), GET_ALL_NOTIFICATION_BY_PAGINATION("GET_ALL_NOTIFICATION_BY_PAGINATION"), GET_ALL_CALL_BY_PAGINATION("GET_ALL_CALL_BY_PAGINATION"), diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/CreateCallRequestStep1.java b/src/main/java/net/gepafin/tendermanagement/model/request/CreateCallRequestStep1.java index 7d4bf8cf..c364db5a 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/CreateCallRequestStep1.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/CreateCallRequestStep1.java @@ -44,6 +44,8 @@ public class CreateCallRequestStep1 { private Boolean confidi; + private Boolean allowMultipleApplications; + private List faq; private EvaluationVersionEnum evaluationVersion; diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/UpdateCallRequestStep1.java b/src/main/java/net/gepafin/tendermanagement/model/request/UpdateCallRequestStep1.java index 01a0ea39..30530381 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/UpdateCallRequestStep1.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/UpdateCallRequestStep1.java @@ -38,6 +38,8 @@ public class UpdateCallRequestStep1 { private Boolean confidi; + private Boolean allowMultipleApplications; + private List faq; private Long numberOfCheck; 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 e9a1f9f5..22f00b0d 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/AssignedApplicationViewResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/AssignedApplicationViewResponse.java @@ -21,6 +21,7 @@ public class AssignedApplicationViewResponse extends BaseBean { private Long protocolNumber; private String callName; private String companyName; + private String assignedUserName; private List emailSendResponse; diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/CallDetailsResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/CallDetailsResponseBean.java index 7ee0b620..51dbdd4d 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/CallDetailsResponseBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/CallDetailsResponseBean.java @@ -23,6 +23,8 @@ public class CallDetailsResponseBean { private Boolean confidi; + private Boolean allowMultipleApplications; + private CallStatusEnum status; private Long regionId; diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/CallResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/CallResponse.java index 76289b06..13a90715 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/CallResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/CallResponse.java @@ -44,6 +44,8 @@ public class CallResponse { private Boolean confidi; + private Boolean allowMultipleApplications; + private BigDecimal amountMin; private String email; diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java index c760689a..37d2f270 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java @@ -181,4 +181,7 @@ public interface ApplicationRepository extends JpaRepository findByCallIdAndIsDeletedFalseAndStatusIn(Long callId,List status); + + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/CompanyRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/CompanyRepository.java index 350b9b49..39d261ea 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/CompanyRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/CompanyRepository.java @@ -31,5 +31,7 @@ public interface CompanyRepository extends JpaRepository { """) Page findCompaniesWithMissingVatCheck(Pageable pageable); + List findByJsonIsNotNullAndPecIsNull(); + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/EvaluationCriteriaRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/EvaluationCriteriaRepository.java index 1a7f7ccd..8c939956 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/EvaluationCriteriaRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/EvaluationCriteriaRepository.java @@ -18,4 +18,6 @@ public interface EvaluationCriteriaRepository extends JpaRepository findByCallIdAndLookupDataTypeAndIsDeletedFalse(Long callId, String type); // List findByCallId(Long callId); + List findByCallIdAndIsDeletedFalse(Long callId); + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/NdganagRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/NdganagRepository.java new file mode 100644 index 00000000..ad0fe99a --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/repositories/NdganagRepository.java @@ -0,0 +1,14 @@ +package net.gepafin.tendermanagement.repositories; + +import net.gepafin.tendermanagement.entities.NdganagEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + + +@Repository +public interface NdganagRepository extends JpaRepository { + + NdganagEntity findByVatNumber(String vatNumber); + + +} diff --git a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationEvaluationScheduler.java b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationEvaluationScheduler.java index af4cb6be..413efe37 100644 --- a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationEvaluationScheduler.java +++ b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationEvaluationScheduler.java @@ -91,7 +91,11 @@ public class ApplicationEvaluationScheduler { // 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())); + String protocolNumber=application.getProtocol().getExternalProtocolNumber(); + if(protocolNumber==null){ + protocolNumber= String.valueOf(application.getProtocol().getProtocolNumber()); + } + placeHolders.put("{{protocol_number}}", protocolNumber); 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/ApplicationService.java b/src/main/java/net/gepafin/tendermanagement/service/ApplicationService.java index c7af4f88..9cc2ea22 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/ApplicationService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/ApplicationService.java @@ -52,4 +52,7 @@ public interface ApplicationService { public ApplicationResponse readmitApplication(HttpServletRequest request, Long applicationId); - } + public byte[] downloadRankingCsv(HttpServletRequest request, Long callId); + + +} diff --git a/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java b/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java index 842901a4..e324804c 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java @@ -13,4 +13,7 @@ public interface AppointmentService { AppointmentCreationResponse createAppointmentForApplication(HttpServletRequest request, Long applicationId, CreateAppointmentRequest createAppointmentRequest); DocumentUploadResponse uploadDocToExternalSystem(HttpServletRequest request, Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest); + + NdgResponse getNdgByVatNumber(HttpServletRequest request,String vatNumber); + } diff --git a/src/main/java/net/gepafin/tendermanagement/service/CompanyService.java b/src/main/java/net/gepafin/tendermanagement/service/CompanyService.java index 74b6be0e..4c868f1e 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/CompanyService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/CompanyService.java @@ -47,4 +47,5 @@ public interface CompanyService { void updateMissingVatCheckResponses(HttpServletRequest request, LimitRequest limitRequest); + void extractPecFromJson(HttpServletRequest request); } diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationServiceImpl.java index 357661d1..e311340e 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationServiceImpl.java @@ -176,4 +176,10 @@ public class ApplicationServiceImpl implements ApplicationService { UserEntity userEntity = validator.validateUser(request); return applicationDao.readmitApplication(request, applicationId); } + + @Override + public byte[] downloadRankingCsv(HttpServletRequest request, Long callId) { + UserEntity userEntity = validator.validateUser(request); + return applicationDao.downloadRankingCsv(callId,userEntity); + } } diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java index 2e7960df..6c49b214 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java @@ -2,12 +2,14 @@ package net.gepafin.tendermanagement.service.impl; import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.dao.AppointmentDao; +import net.gepafin.tendermanagement.entities.UserEntity; import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; import net.gepafin.tendermanagement.model.response.NdgResponse; import net.gepafin.tendermanagement.service.AppointmentService; +import net.gepafin.tendermanagement.util.Validator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -17,6 +19,9 @@ public class AppointmentServiceImpl implements AppointmentService { @Autowired private AppointmentDao appointmentDao; + @Autowired + private Validator validator; + @Override public NdgResponse checkNdgForAppointment(HttpServletRequest request, Long applicationId) { @@ -34,4 +39,11 @@ public class AppointmentServiceImpl implements AppointmentService { return appointmentDao.uploadDocumentToExternalSystem(documentId, docToExternalSystemRequest); } + + @Override + public NdgResponse getNdgByVatNumber(HttpServletRequest request,String vatNumber) { + UserEntity userEntity = validator.validateUser(request); + NdgResponse ndgResponse= appointmentDao.getNdgByVatNumber(vatNumber,userEntity); + return ndgResponse; + } } diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/CompanyServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/CompanyServiceImpl.java index f1d8f890..f9a8b8c8 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/CompanyServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/CompanyServiceImpl.java @@ -140,6 +140,12 @@ public class CompanyServiceImpl implements CompanyService { @Override public void updateMissingVatCheckResponses(HttpServletRequest request, LimitRequest limitRequest) { UserEntity userEntity =validator.validateUser(request); - companyDao.updateMissingVatCheckResponses(request, limitRequest); + companyDao.updateMissingVatCheckResponses(request,limitRequest); + } + + @Override + public void extractPecFromJson(HttpServletRequest request) { + UserEntity userEntity =validator.validateUser(request); + companyDao.getCompanyEntity(); } } diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 469d93ba..13cf6e87 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -6,9 +6,7 @@ import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.sql.Timestamp; -import java.text.NumberFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.text.*; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; @@ -1052,5 +1050,48 @@ public class Utils { headers.add(org.apache.http.HttpHeaders.USER_AGENT, "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"); return headers; } + + public static void setHttpServletRequestForThread(HttpServletRequest request,String methodType, String remoteUser, Long userActionId) { + MockHttpServletRequest mockRequest = new MockHttpServletRequest(); + mockRequest.setRequestURI(request.getRequestURI()); + mockRequest.setMethod(methodType); + mockRequest.setRemoteUser(remoteUser); // or pass it in if needed + mockRequest.setAttribute(GepafinConstant.USER_ACTION_ID, userActionId); + + ServletRequestAttributes attributes = new ServletRequestAttributes(mockRequest); + RequestContextHolder.setRequestAttributes(attributes, true); + } + public static String convertToItalianFormatWithOnlyDecimalValue(String amount) { + try { + // Step 1: Sanitize and standardize the input + String sanitizedAmount = amount.trim(); + + // Remove thousand separators (either . or , depending on input style) + sanitizedAmount = sanitizedAmount.replace(",", "").replace(" ", ""); + + // Step 2: Ensure it uses '.' as decimal separator for parsing + if (sanitizedAmount.contains(".")) { + // It already uses dot as decimal separator + } else if (sanitizedAmount.contains(",")) { + // If input uses comma as decimal separator (like "1234,56") + sanitizedAmount = sanitizedAmount.replace(",", "."); + } + + // Step 3: Parse to double + double parsedAmount = Double.parseDouble(sanitizedAmount); + + // Step 4: Format without thousand separator and with comma as decimal + DecimalFormatSymbols symbols = new DecimalFormatSymbols(); + symbols.setDecimalSeparator(','); + + DecimalFormat italianDecimalFormat = new DecimalFormat("0.00", symbols); + italianDecimalFormat.setGroupingUsed(false); // no thousand separator + + return italianDecimalFormat.format(parsedAmount); + } catch (NumberFormatException e) { + return "Invalid amount format"; + } + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationApi.java index a18e6852..b64b3951 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationApi.java @@ -252,6 +252,21 @@ public interface ApplicationApi { ResponseEntity> readmitApplication(HttpServletRequest request, @Parameter(description = "The application id", required = true) @PathVariable("applicationId") Long applicationId); + @Operation(summary = "Api to download application data as a CSV file as per ranking", + 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)})) + }) + @GetMapping(value = "/call/{callId}/ranking-csv") + @PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_INSTRUCTOR_MANAGER')") + public ResponseEntity downloadRankingCsv( + HttpServletRequest request, @Parameter(description = "The call id", required = true) @PathVariable(value = "callId", required = true) Long callId); + } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java index 5507492a..64f767b9 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java @@ -56,4 +56,15 @@ public interface AppointmentApi { ResponseEntity> uploadDocumentToExternalSystem(HttpServletRequest request, @Parameter(description = "The document id", required = true) @PathVariable(value = "documentId", required = true) Long documentId, @RequestBody UploadDocToExternalSystemRequest docToExternalSystemRequest); + + @Operation(summary = "API to get ndg by vatNumber", 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) })) }) + @GetMapping(value = "/vatNumber/{vatNumber}", produces = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity> getNdgByVatNumber(HttpServletRequest request,@PathVariable(value = "vatNumber", required = true) @Parameter(description = "vatNumber",required = true)String vatNumber); + } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/CompanyApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/CompanyApi.java index fea29d0d..a30e18be 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/CompanyApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/CompanyApi.java @@ -169,4 +169,17 @@ public interface CompanyApi { @PreAuthorize("hasRole('ROLE_SUPER_ADMIN') ") ResponseEntity> updateMissingVatCheckResponses(HttpServletRequest request, @Parameter(description = "Limit request object ", required = true) @RequestBody LimitRequest limitRequest); + + @Operation(summary = "Api to extract pec from the company json", 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) })) }) + @GetMapping(value = "/pec", produces = { "application/json" }) + @PreAuthorize("hasRole('ROLE_SUPER_ADMIN') ") + ResponseEntity> extractPecFromJson(HttpServletRequest request); + + } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationApiController.java index 840711ae..76946420 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationApiController.java @@ -27,6 +27,8 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.slf4j.Logger; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.List; @@ -236,6 +238,10 @@ public class ApplicationApiController implements ApplicationApi { } @Override public ResponseEntity exportCsv(HttpServletRequest request, Long callId) { + + loggingUtil.logUserAction( + UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.DOWNLOAD).actionContext(UserActionContextEnum.DOWNLOAD_CSV_BY_CALL_ID).build()); + byte[] csvBytes =applicationService.exportCsv(request,callId); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=applications.csv") @@ -254,4 +260,19 @@ public class ApplicationApiController implements ApplicationApi { return ResponseEntity.status(HttpStatus.OK) .body(new Response<>(applicationResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.READMIT_APPLICATION_SUCCESS))); } + + @Override + public ResponseEntity downloadRankingCsv(HttpServletRequest request, Long callId) { + loggingUtil.logUserAction( + UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.DOWNLOAD).actionContext(UserActionContextEnum.DOWNLOAD_CSV_AS_PER_RANKING).build()); + + byte[] csvBytes =applicationService.downloadRankingCsv(request,callId); + String dateString = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + String fileName = "call_" + callId + "_" + dateString + ".csv"; + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(csvBytes); + } } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationEvaluationApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationEvaluationApiController.java index 50eadb36..2f6bbcb3 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationEvaluationApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationEvaluationApiController.java @@ -46,7 +46,7 @@ public class ApplicationEvaluationApiController implements ApplicationEvaluation request, evaluationRequest, assignedApplicationsId); return ResponseEntity.status(HttpStatus.CREATED) - .body(new Response<>(response, Status.SUCCESS, Translator.toLocale(GepafinConstant.EVALUATION_CREATED_SUCCESSFULLY))); + .body(new Response<>(response, Status.SUCCESS, Translator.toLocale(GepafinConstant.USER_REQUEST_COMPLETED))); } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java index 1708b8cd..7212355a 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java @@ -17,6 +17,7 @@ import net.gepafin.tendermanagement.service.AppointmentService; import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.web.rest.api.AppointmentApi; import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.opensaml.xmlsec.signature.G; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -81,4 +82,19 @@ public class AppointmentController implements AppointmentApi { .body(new Response<>(documentUploadResponse, Status.SUCCESS, Translator.toLocale(message))); } + + @Override + public ResponseEntity> getNdgByVatNumber(HttpServletRequest request,String vatNumber) { + loggingUtil.logUserAction( + UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPLOAD).actionContext(UserActionContextEnum.GET_NDG_BY_VAT_NUMBER).build()); + + NdgResponse ndgResponse= appointmentService.getNdgByVatNumber(request,vatNumber); + if(ndgResponse==null){ + return ResponseEntity.status(HttpStatus.CREATED) + .body(new Response<>(ndgResponse, Status.NOT_FOUND, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND))); + } + + return ResponseEntity.status(HttpStatus.CREATED) + .body(new Response<>(ndgResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_FETCH_SUCCESSFULLY))); + } } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CompanyApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CompanyApiController.java index 64dee16b..08d34ccf 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CompanyApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CompanyApiController.java @@ -201,4 +201,17 @@ public class CompanyApiController implements CompanyApi{ return ResponseEntity.status(HttpStatus.OK) .body(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.COMPANY_UPDATED_SUCCESS_MSG))); } + + @Override + public ResponseEntity> extractPecFromJson(HttpServletRequest request) { + + log.info("Api to set pec from json field"); + + companyService.extractPecFromJson(request); + + loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPDATE).actionContext(UserActionContextEnum.EXTRACT_PEC_FROM_COMPANY).build()); + + return ResponseEntity.status(HttpStatus.OK) + .body(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.COMPANY_UPDATED_SUCCESS_MSG))); + } } diff --git a/src/main/resources/application-production.properties b/src/main/resources/application-production.properties index c4ad1fe9..ef5f73f2 100644 --- a/src/main/resources/application-production.properties +++ b/src/main/resources/application-production.properties @@ -37,3 +37,5 @@ spring.rabbitmq.port=61613 spring.rabbitmq.username=guest spring.rabbitmq.password=guest spring.rabbitmq.virtual-host=/ + +isSviluppumbriaProtocolEnabled = false \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index cac5e36b..4cb55816 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -83,7 +83,7 @@ sviluppumbria.username=protocollatoresvilumbria password=8e85ea4265e49497d697168fd3d34916 CLASSIFICA=06.004.3 TIPO_PROTOCOLLAZIONE=E -tipoCorrispondenteCompany=PersonaGiuridica +tipoCorrispondenteCompany=PersonaGiuridica tipoCorrispondenteWithoutCompany=Persona mezzo=Altro indirizzoPec=ufficio01@domain.com @@ -91,3 +91,4 @@ codiceUo=101_10 competente=true tipoCorrispondente=Amministrazione sviluppumbriaUuid=t7jh5wfg9QXylNaTZkPoE +isSviluppumbriaProtocolEnabled = true 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 c5bc6193..48131d63 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 @@ -2737,6 +2737,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/dump/insert_system_email_template_technical_evaluation_rejected.sql b/src/main/resources/db/dump/insert_system_email_template_technical_evaluation_rejected.sql new file mode 100644 index 00000000..7b76a13d --- /dev/null +++ b/src/main/resources/db/dump/insert_system_email_template_technical_evaluation_rejected.sql @@ -0,0 +1,31 @@ +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 Technical Evaluation Rejected Template', + 'INADMISSIBILITY_NOTIFICATION_DUE_TO_TECHNICAL_EVALUATION_FAILURE', + ' + +
+

Buongiorno,

+

Si comunica che, in riferimento alla domanda a valere sul bando “{{call_name}}” di cui al + Protocollo n. {{protocol_number}} del {{protocol_date}} alle {{protocol_time}}, + a stessa è stata sottoposta a valutazione tecnica ed economico finanziaria + con esito negativo

+

Le motivazioni sono le seguenti: {{form_text}}

+

Vi ricordiamo che i Beneficiari, in caso di mancato accoglimento della Domanda di Finanziamento agevolato, entro 10 giorni dalla data di ricevimento della presente potranno formulare ricorso al Gestore tramite + modello disponibile nello sportello online + {{platform_link}}, e sul sito internet home - ND Credit Repair , nella sezione dedicata ai Bandi e Avvisi pubblici.

+

Distinti Saluti,

+

{{email_signature}}

+
+ + ', + 'BANDO – "{{call_name}}" – Esito negativo della valutazione tecnica – {{company_name}}', + null, + true, + false, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + 'APPLICATION_TECHNICAL_EVALUATION_REJECTED' +); diff --git a/src/main/resources/db/dump/update_application_form_view_08_07_2025.sql b/src/main/resources/db/dump/update_application_form_view_08_07_2025.sql new file mode 100644 index 00000000..e68f0b60 --- /dev/null +++ b/src/main/resources/db/dump/update_application_form_view_08_07_2025.sql @@ -0,0 +1,105 @@ + +DROP VIEW IF EXISTS gepafin_schema.application_form_view ; + +CREATE OR REPLACE VIEW gepafin_schema.application_form_view AS +SELECT app_data.id, + app_data.call_id, + app_data.application_form_id, + app_data.form_id, + app_data.application_id, + field_data.value ->> 'id'::text AS field_id, + COALESCE(( SELECT s.value ->> 'value'::text + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'label'::text + LIMIT 1), field_data.value ->> 'label'::text) AS field_label, + ( SELECT (s.value ->> 'value'::text)::boolean AS bool + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'reportEnable'::text + LIMIT 1) AS report_enable, + COALESCE(( SELECT s.value ->> 'value'::text + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'reportHeader'::text + LIMIT 1), field_data.value ->> 'reportHeader'::text) AS report_header, + field_data.value ->> 'name'::text AS field_type, + CASE + WHEN (field_data.value ->> 'name'::text) = 'fileupload'::text THEN to_jsonb(( SELECT string_agg(d.file_name::text, ', '::text) AS string_agg + FROM unnest(string_to_array(app_data.field_value, ','::text)) file_ids(file_id) + JOIN gepafin_schema.document d ON d.id::text = file_ids.file_id + WHERE d.is_deleted = false)) + WHEN (field_data.value ->> 'name'::text) = ANY (ARRAY['checkboxes'::text, 'select'::text, 'radio'::text]) THEN + CASE + WHEN app_data.field_value ~~ '[%'::text THEN to_jsonb(( SELECT string_agg(opt.value ->> 'label'::text, ', '::text) AS string_agg + FROM jsonb_array_elements_text(app_data.field_value::jsonb) selected_id(value) + CROSS JOIN LATERAL ( SELECT s.value + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'options'::text) options_setting, + LATERAL jsonb_array_elements(options_setting.value -> 'value'::text) opt(value) + WHERE (opt.value ->> 'name'::text) = selected_id.value)) + ELSE to_jsonb(( SELECT opt.value ->> 'label'::text + FROM ( SELECT s.value + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'options'::text) options_setting, + LATERAL jsonb_array_elements(options_setting.value -> 'value'::text) opt(value) + WHERE (opt.value ->> 'name'::text) = app_data.field_value + LIMIT 1)) + END + ELSE to_jsonb(app_data.field_value) + END AS field_value, + app_data.status, + app_data.amount_requested, + app_data.amount_accepted, + app_data.is_deleted, + app_data.hub_id, + app_data.user_id, + app_data.evaluation_version, + app_data.company_id, + c.company_name, + c.vat_number AS company_vat_number, + c.codice_ateco, + c.codice_fiscale AS company_codice_fiscale, + p.protocol_number, + b.codice_fiscale AS user_codice_fiscale, + COALESCE(NULLIF(TRIM(BOTH FROM concat(COALESCE(u.first_name, ''::character varying), ' ', COALESCE(u.last_name, ''::character varying))), ''::text), ''::text) AS user_name, + COALESCE( NULLIF(TRIM(BOTH FROM CONCAT(COALESCE(aauser.first_name, ''), ' ', COALESCE(aauser.last_name, ''))),''),'NA') AS instructor_name, + uwc.is_legal_representant AS legal_representative, + cl.name AS call_title, + cl.end_date AS call_end_date, + cl.end_time AS call_end_time, + cl.start_date AS call_start_date, + cl.start_time AS call_start_time + FROM ( SELECT a.id AS application_id, + a.call_id, + a.protocol_number, + af.id AS application_form_id, + af.form_id, + aff.id, + aff.field_value, + a.status, + a.amount_requested, + a.amount_accepted, + a.is_deleted, + a.hub_id, + a.user_id, + a.evaluation_version, + a.created_date, + a.company_id, + aff.field_id, + f.content + FROM gepafin_schema.application a + JOIN gepafin_schema.application_form af ON af.application_id = a.id + JOIN gepafin_schema.application_form_field aff ON aff.application_form_id = af.id + JOIN gepafin_schema.form f ON f.id = af.form_id + WHERE a.is_deleted = false) app_data + CROSS JOIN LATERAL ( SELECT jsonb_array_elements.value + FROM jsonb_array_elements(app_data.content::jsonb) jsonb_array_elements(value) + WHERE (jsonb_array_elements.value ->> 'id'::text) = app_data.field_id::text) field_data(value) + LEFT JOIN gepafin_schema.call cl ON app_data.call_id = cl.id + LEFT JOIN gepafin_schema.company c ON app_data.company_id = c.id + LEFT JOIN gepafin_schema.protocol p ON app_data.protocol_number = p.id + LEFT JOIN gepafin_schema.gepafin_user u ON app_data.user_id = u.id + LEFT JOIN gepafin_schema.user_with_company uwc ON app_data.user_id = uwc.user_id AND app_data.company_id = uwc.company_id AND uwc.is_deleted = false + LEFT JOIN gepafin_schema.beneficiary b ON u.beneficiary_id = b.id + LEFT JOIN gepafin_schema.assigned_applications aa ON app_data.application_id = aa.application_id + LEFT JOIN gepafin_schema.gepafin_user aauser ON aa.user_id = aauser.id + WHERE app_data.id IS NOT NULL AND (app_data.status::text <> ALL (ARRAY['DRAFT'::character varying::text, 'AWAITING'::character varying::text, 'READY'::character varying::text])) + ORDER BY app_data.id, (field_data.value ->> 'id'::text); \ No newline at end of file diff --git a/src/main/resources/db/dump/update_application_form_view_23_07_2025.sql b/src/main/resources/db/dump/update_application_form_view_23_07_2025.sql new file mode 100644 index 00000000..eec055ec --- /dev/null +++ b/src/main/resources/db/dump/update_application_form_view_23_07_2025.sql @@ -0,0 +1,108 @@ + +DROP VIEW IF EXISTS gepafin_schema.application_form_view ; + +CREATE OR REPLACE VIEW gepafin_schema.application_form_view AS +SELECT app_data.id, + app_data.call_id, + app_data.application_form_id, + app_data.form_id, + app_data.application_id, + field_data.value ->> 'id'::text AS field_id, + COALESCE(( SELECT s.value ->> 'value'::text + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'label'::text + LIMIT 1), field_data.value ->> 'label'::text) AS field_label, + ( SELECT (s.value ->> 'value'::text)::boolean AS bool + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'reportEnable'::text + LIMIT 1) AS report_enable, + COALESCE(( SELECT s.value ->> 'value'::text + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'reportHeader'::text + LIMIT 1), field_data.value ->> 'reportHeader'::text) AS report_header, + field_data.value ->> 'name'::text AS field_type, + CASE + WHEN (field_data.value ->> 'name'::text) = 'fileupload'::text THEN to_jsonb(( SELECT string_agg(d.file_name::text, ', '::text) AS string_agg + FROM unnest(string_to_array(app_data.field_value, ','::text)) file_ids(file_id) + JOIN gepafin_schema.document d ON d.id::text = file_ids.file_id + WHERE d.is_deleted = false)) + WHEN (field_data.value ->> 'name'::text) = ANY (ARRAY['checkboxes'::text, 'select'::text, 'radio'::text]) THEN + CASE + WHEN app_data.field_value ~~ '[%'::text THEN to_jsonb(( SELECT string_agg(opt.value ->> 'label'::text, ', '::text) AS string_agg + FROM jsonb_array_elements_text(app_data.field_value::jsonb) selected_id(value) + CROSS JOIN LATERAL ( SELECT s.value + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'options'::text) options_setting, + LATERAL jsonb_array_elements(options_setting.value -> 'value'::text) opt(value) + WHERE (opt.value ->> 'name'::text) = selected_id.value)) + ELSE to_jsonb(( SELECT opt.value ->> 'label'::text + FROM ( SELECT s.value + FROM jsonb_array_elements(field_data.value -> 'settings'::text) s(value) + WHERE (s.value ->> 'name'::text) = 'options'::text) options_setting, + LATERAL jsonb_array_elements(options_setting.value -> 'value'::text) opt(value) + WHERE (opt.value ->> 'name'::text) = app_data.field_value + LIMIT 1)) + END + ELSE to_jsonb(app_data.field_value) + END AS field_value, + app_data.status, + app_data.amount_requested, + app_data.amount_accepted, + app_data.is_deleted, + app_data.hub_id, + app_data.user_id, + app_data.evaluation_version, + app_data.company_id, + c.company_name, + c.vat_number AS company_vat_number, + c.codice_ateco, + c.codice_fiscale AS company_codice_fiscale, + p.protocol_number, + DATE(app_data.submission_date) AS submission_date, + TO_CHAR(app_data.submission_date, 'HH24:MI:SS') AS submission_time, + b.codice_fiscale AS user_codice_fiscale, + COALESCE(NULLIF(TRIM(BOTH FROM concat(COALESCE(u.first_name, ''::character varying), ' ', COALESCE(u.last_name, ''::character varying))), ''::text), ''::text) AS user_name, + COALESCE( NULLIF(TRIM(BOTH FROM CONCAT(COALESCE(aauser.first_name, ''), ' ', COALESCE(aauser.last_name, ''))),''),'NA') AS instructor_name, + uwc.is_legal_representant AS legal_representative, + cl.name AS call_title, + cl.end_date AS call_end_date, + cl.end_time AS call_end_time, + cl.start_date AS call_start_date, + cl.start_time AS call_start_time + FROM ( SELECT a.id AS application_id, + a.call_id, + a.protocol_number, + af.id AS application_form_id, + af.form_id, + aff.id, + aff.field_value, + a.status, + a.amount_requested, + a.amount_accepted, + a.is_deleted, + a.hub_id, + a.user_id, + a.evaluation_version, + a.created_date, + a.company_id, + a.submission_date, + aff.field_id, + f.content + FROM gepafin_schema.application a + JOIN gepafin_schema.application_form af ON af.application_id = a.id + JOIN gepafin_schema.application_form_field aff ON aff.application_form_id = af.id + JOIN gepafin_schema.form f ON f.id = af.form_id + WHERE a.is_deleted = false) app_data + CROSS JOIN LATERAL ( SELECT jsonb_array_elements.value + FROM jsonb_array_elements(app_data.content::jsonb) jsonb_array_elements(value) + WHERE (jsonb_array_elements.value ->> 'id'::text) = app_data.field_id::text) field_data(value) + LEFT JOIN gepafin_schema.call cl ON app_data.call_id = cl.id + LEFT JOIN gepafin_schema.company c ON app_data.company_id = c.id + LEFT JOIN gepafin_schema.protocol p ON app_data.protocol_number = p.id + LEFT JOIN gepafin_schema.gepafin_user u ON app_data.user_id = u.id + LEFT JOIN gepafin_schema.user_with_company uwc ON app_data.user_id = uwc.user_id AND app_data.company_id = uwc.company_id AND uwc.is_deleted = false + LEFT JOIN gepafin_schema.beneficiary b ON u.beneficiary_id = b.id + LEFT JOIN gepafin_schema.assigned_applications aa ON app_data.application_id = aa.application_id + LEFT JOIN gepafin_schema.gepafin_user aauser ON aa.user_id = aauser.id + WHERE app_data.id IS NOT NULL AND (app_data.status::text <> ALL (ARRAY['DRAFT'::character varying::text, 'AWAITING'::character varying::text, 'READY'::character varying::text])) + ORDER BY app_data.id, (field_data.value ->> 'id'::text); \ No newline at end of file diff --git a/src/main/resources/db/dump/update_assigned_application_view_08_07_2025.sql b/src/main/resources/db/dump/update_assigned_application_view_08_07_2025.sql new file mode 100644 index 00000000..354ab8c5 --- /dev/null +++ b/src/main/resources/db/dump/update_assigned_application_view_08_07_2025.sql @@ -0,0 +1,62 @@ + +DROP VIEW IF EXISTS gepafin_schema.assigned_applications_view ; + +CREATE OR REPLACE VIEW gepafin_schema.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.hub_id as hub_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, + COALESCE(NULLIF(TRIM(BOTH FROM concat(COALESCE(u.first_name, ''::character varying), ' ', COALESCE(u.last_name, ''::character varying))), ''::text), ''::text) AS assigned_user_name + + +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 + +LEFT JOIN gepafin_schema.gepafin_user u ON aa.user_id = u.id + + +WHERE aa.is_deleted IS FALSE OR aa.is_deleted IS NULL; diff --git a/src/main/resources/db/dump/updated_form_field_05_06_2025.sql b/src/main/resources/db/dump/updated_form_field_05_06_2025.sql new file mode 100644 index 00000000..42766b94 --- /dev/null +++ b/src/main/resources/db/dump/updated_form_field_05_06_2025.sql @@ -0,0 +1,19 @@ +UPDATE FORM_FIELD +SET + NAME = 'textinput', + LABEL = 'Campo Email', + DESCRIPTION = 'Per l''inserimento di indirizzi email standard (non PEC)', + SETTINGS = '[{"name":"label","value":"Campo Email"},{"name":"placeholder","value":"nome@esempio.it"},{"name":"reportEnable","value":false},{"name":"reportHeader","value":""},{"name": "isPecEmail", "value":false}]', + VALIDATORS = '{"isRequired": false, "custom": "isEmail"}', + UPDATED_DATE = CURRENT_TIMESTAMP +WHERE SORT_ORDER = 15; + +UPDATE FORM_FIELD +SET + NAME = 'textinput', + LABEL = 'Campo PEC', + DESCRIPTION = 'Specifico per l''inserimento di un indirizzo di Posta Elettronica Certificata', + SETTINGS = '[{"name":"label","value":"Campo PEC"},{"name":"placeholder","value":"nome@pec.it"},{"name":"reportEnable","value":false},{"name":"reportHeader","value":""},{"name": "isPecEmail", "value":false}]', + VALIDATORS = '{"isRequired": false, "custom": "isEmailPEC"}', + UPDATED_DATE = CURRENT_TIMESTAMP +WHERE SORT_ORDER = 16; diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index e69af662..ad25a88b 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -408,3 +408,8 @@ 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. +ndg.not.found=NDG not found. +email.pec.cannot.null=Email pec is required. +user.request.completed=User request completed successfully. +end.date.greater.than.now=End date must be greater than the current date and time. + diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index ef23cfac..f98e55b8 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -399,3 +399,8 @@ 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. +ndg.not.found=NDG non trovato. +email.pec.cannot.null=L'indirizzo email pec obbligatorio. +user.request.completed=Richiesta utente completata con successo. +end.date.greater.than.now=La data di fine deve essere successiva alla data e all'ora correnti. +