diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 1aaf53ec..888a3ed5 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -575,6 +575,8 @@ public class GepafinConstant { 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 COMPANY_NAME_JSON="denominazione"; + 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 2882dc7f..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 @@ -1152,6 +1152,8 @@ public class ApplicationAmendmentRequestDao { ApplicationAmendmentRequestEntity oldApplicationAmendmentEntity = Utils.getClonedEntityForData(applicationAmendmentRequestEntity); Long currentResponseDays = applicationAmendmentRequestEntity.getResponseDays() != null ? applicationAmendmentRequestEntity.getResponseDays() : 0L; applicationAmendmentRequestEntity.setResponseDays(currentResponseDays + newResponseDays); + applicationAmendmentRequestEntity.setExtendedDays(newResponseDays); + applicationAmendmentRequestEntity.setExtensionDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); applicationAmendmentRequestEntity.setEndDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now().plusDays(newResponseDays))); applicationAmendmentRequestEntity.setStatus(ApplicationAmendmentRequestEnum.AWAITING.getValue()); applicationAmendmentRequestEntity.getApplicationEvaluationEntity().setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue()); @@ -1640,53 +1642,53 @@ public class ApplicationAmendmentRequestDao { return updated; } - public long calculateSuspendedDays(List amendments) { - List> periods = amendments.stream() - .filter(amendmentRequest -> amendmentRequest.getStartDate() != null) - .map(amendmentRequest -> { - LocalDateTime start = amendmentRequest.getStartDate(); - LocalDateTime end; - - String status = amendmentRequest.getStatus(); - if (Boolean.TRUE.equals(ApplicationAmendmentRequestEnum.CLOSE.getValue().equals(status)) && amendmentRequest.getClosingDate() != null) { - end = amendmentRequest.getClosingDate(); - } else if (Boolean.TRUE.equals(ApplicationAmendmentRequestEnum.EXPIRED.getValue().equals(status)) && amendmentRequest.getEndDate() != null) { - end = amendmentRequest.getStartDate().plusDays(amendmentRequest.getResponseDays()); - }else { - end= amendmentRequest.getEndDate(); - } - - return Pair.of(start, end); - }) - .filter(Objects::nonNull) - .sorted(Comparator.comparing(Pair::getLeft)) - .collect(Collectors.toList()); - - long totalDays = 0; - LocalDateTime currentStart = null; - LocalDateTime currentEnd = null; - - for (Pair period : periods) { - if (currentStart == null) { - currentStart = period.getLeft(); - currentEnd = period.getRight(); - } else if (!period.getLeft().isAfter(currentEnd)) { - // Merge overlapping/touching periods - currentEnd = currentEnd.isAfter(period.getRight()) ? currentEnd : period.getRight(); - } else { - // Non-overlapping: count previous period - totalDays += ChronoUnit.DAYS.between(currentStart.toLocalDate(), currentEnd.toLocalDate()); - currentStart = period.getLeft(); - currentEnd = period.getRight(); - } - } - - if (currentStart != null && currentEnd != null) { - totalDays += ChronoUnit.DAYS.between(currentStart.toLocalDate(), currentEnd.toLocalDate()); - } - - return totalDays; - } +// public long calculateSuspendedDays(List amendments) { +// List> periods = amendments.stream() +// .filter(amendmentRequest -> amendmentRequest.getStartDate() != null) +// .map(amendmentRequest -> { +// LocalDateTime start = amendmentRequest.getStartDate(); +// LocalDateTime end; +// +// String status = amendmentRequest.getStatus(); +// if (Boolean.TRUE.equals(ApplicationAmendmentRequestEnum.CLOSE.getValue().equals(status)) && amendmentRequest.getClosingDate() != null) { +// end = amendmentRequest.getClosingDate(); +// } else if (Boolean.TRUE.equals(ApplicationAmendmentRequestEnum.EXPIRED.getValue().equals(status)) && amendmentRequest.getEndDate() != null) { +// end = amendmentRequest.getStartDate().plusDays(amendmentRequest.getResponseDays()); +// }else { +// end= amendmentRequest.getEndDate(); +// } +// +// return Pair.of(start, end); +// }) +// .filter(Objects::nonNull) +// .sorted(Comparator.comparing(Pair::getLeft)) +// .collect(Collectors.toList()); +// +// long totalDays = 0; +// LocalDateTime currentStart = null; +// LocalDateTime currentEnd = null; +// +// for (Pair period : periods) { +// if (currentStart == null) { +// currentStart = period.getLeft(); +// currentEnd = period.getRight(); +// } else if (!period.getLeft().isAfter(currentEnd)) { +// // Merge overlapping/touching periods +// currentEnd = currentEnd.isAfter(period.getRight()) ? currentEnd : period.getRight(); +// } else { +// // Non-overlapping: count previous period +// totalDays += ChronoUnit.DAYS.between(currentStart.toLocalDate(), currentEnd.toLocalDate()); +// currentStart = period.getLeft(); +// currentEnd = period.getRight(); +// } +// } +// +// if (currentStart != null && currentEnd != null) { +// totalDays += ChronoUnit.DAYS.between(currentStart.toLocalDate(), currentEnd.toLocalDate()); +// } +// +// return totalDays; +// } @@ -1712,4 +1714,65 @@ public class ApplicationAmendmentRequestDao { } return applicationAmendmentRequestEntity; } + public long calculateSuspendedDays(List amendments) { + List> periods = amendments.stream() + .filter(amendmentRequest -> amendmentRequest.getStartDate() != null) + .flatMap(amendmentRequest -> { + List> result = new ArrayList<>(); + + LocalDateTime start = amendmentRequest.getStartDate(); + String status = amendmentRequest.getStatus(); + Long responseDays = amendmentRequest.getResponseDays() != null ? amendmentRequest.getResponseDays() : 0L; + LocalDateTime extensionDate = amendmentRequest.getExtensionDate(); + Long extendedDays = amendmentRequest.getExtendedDays() != null ? amendmentRequest.getExtendedDays() : 0L; + + if (ApplicationAmendmentRequestEnum.CLOSE.getValue().equals(status) && amendmentRequest.getClosingDate() != null) { + if (extensionDate != null && amendmentRequest.getClosingDate().isAfter(extensionDate)) { + long overlappingExtensionDays = ChronoUnit.DAYS.between(extensionDate.toLocalDate(), amendmentRequest.getClosingDate().toLocalDate()); + long adjustedInitialPeriod = responseDays - overlappingExtensionDays; + + result.add(Pair.of(start, start.plusDays(adjustedInitialPeriod))); + } else { + result.add(Pair.of(start, amendmentRequest.getClosingDate())); + } + } else if (ApplicationAmendmentRequestEnum.EXPIRED.getValue().equals(status)) { + result.add(Pair.of(start, start.plusDays(responseDays))); + } else { + if (amendmentRequest.getEndDate() != null) { + result.add(Pair.of(start, amendmentRequest.getEndDate())); + } else { + result.add(Pair.of(start, start.plusDays(responseDays))); + } + } + + return result.stream(); + }) + .filter(Objects::nonNull) + .sorted(Comparator.comparing(Pair::getLeft)) + .collect(Collectors.toList()); + + long totalDays = 0; + LocalDateTime currentStart = null; + LocalDateTime currentEnd = null; + + for (Pair period : periods) { + if (currentStart == null) { + currentStart = period.getLeft(); + currentEnd = period.getRight(); + } else if (!period.getLeft().isAfter(currentEnd)) { + currentEnd = currentEnd.isAfter(period.getRight()) ? currentEnd : period.getRight(); + } else { + totalDays += ChronoUnit.DAYS.between(currentStart.toLocalDate(), currentEnd.toLocalDate()); + currentStart = period.getLeft(); + currentEnd = period.getRight(); + } + } + + if (currentStart != null && currentEnd != null) { + totalDays += ChronoUnit.DAYS.between(currentStart.toLocalDate(), currentEnd.toLocalDate()); + } + + return totalDays; + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index a0ae0651..4567d8b2 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -56,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; @@ -217,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) { @@ -593,11 +598,9 @@ public class ApplicationDao { String fieldType = content.getName(); // Define handlers for different (fieldType + settingName) combinations - Map handlers = new HashMap<>(); // Add handler for isRequestedAmount if ("numberinput".equals(fieldType) && Boolean.TRUE.equals(settingMap.get("isRequestedAmount"))) { - handlers.put("isRequestedAmount", () -> { try { BigDecimal amountRequested = new BigDecimal(fieldValue.toString()); applicationFormEntity.getApplication().setAmountRequested(amountRequested); @@ -606,19 +609,20 @@ public class ApplicationDao { 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"))) { - handlers.put("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 - handlers.values().forEach(Runnable::run); }); @@ -641,7 +645,6 @@ public class ApplicationDao { } } } - calculationProcessForFormula(applicationFormEntity,contentResponseBeans,applicationFormFieldRequestBean,fieldValidator); Utils.setIfUpdated(applicationFormFieldEntity::getFieldId, applicationFormFieldEntity::setFieldId, applicationFormFieldRequestBean.getFieldId()); if (applicationFormFieldRequestBean.getFieldValue() != null) { @@ -988,10 +991,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); @@ -1059,9 +1064,9 @@ public class ApplicationDao { ProtocolEntity protocolEntity = protocolDao.createProtocolEntity(applicationEntity, protocolNumber, userEntity.getHub().getId(),true); protocolDao.saveProtocolEntity(protocolEntity); applicationEntity.setProtocol(protocolEntity); - if(Boolean.TRUE.equals(hub.getUniqueUuid().equals(sviluppumbriaUuid))) { - protocolEntity = protocolDao.createExternalProtocol(applicationEntity, company, protocolEntity); - } +// if(Boolean.TRUE.equals(hub.getUniqueUuid().equals(sviluppumbriaUuid))) { +// protocolEntity = protocolDao.createExternalProtocol(applicationEntity, company, protocolEntity); +// } applicationEntity.setStatus(ApplicationStatusTypeEnum.SUBMIT.getValue()); applicationEntity.setSubmissionDate(protocolEntity.getCreatedDate()); applicationEntity = applicationRepository.save(applicationEntity); @@ -1665,12 +1670,12 @@ public class ApplicationDao { // } } - public void calculationProcessForFormula(ApplicationFormEntity applicationFormEntity, List contentResponseBeans, ApplicationFormFieldRequestBean applicationFormFieldRequestBean,FieldValidator fieldValidator) { + public void calculationProcessForFormula(ApplicationFormEntity applicationFormEntity, List contentResponseBeans,String fromFieldId,String formFieldValue,FieldValidator fieldValidator) { List formulaValue = new ArrayList<>(); String formulaValueOpt=null; String label=null; for (ContentResponseBean contentResponseBean:contentResponseBeans){ - if(contentResponseBean.getId().equals(applicationFormFieldRequestBean.getFieldId())){ + if(contentResponseBean.getId().equals(fromFieldId)){ for (SettingResponseBean settingResponseBean:contentResponseBean.getSettings()){ if (settingResponseBean.getName().equals("label")){ label= String.valueOf(settingResponseBean.getValue()); @@ -1684,7 +1689,7 @@ public class ApplicationDao { } } Map mappedFormulaValue = new HashMap<>(); - Object fieldValue = applicationFormFieldRequestBean.getFieldValue(); + Object fieldValue = formFieldValue; if (formulaValueOpt != null && fieldValue==null) { fieldValue=0; } @@ -2422,4 +2427,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 3cb7237a..1ccc1664 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -1155,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) { @@ -1549,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()); @@ -1910,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(); @@ -1931,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); @@ -1954,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()); @@ -1969,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()); @@ -1999,6 +2009,9 @@ 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()); String protocolNumber=application.getProtocol().getExternalProtocolNumber(); @@ -2573,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 diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java index 9889993e..fa7b9ae8 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java @@ -154,6 +154,13 @@ public class AppointmentDao { 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)); @@ -165,7 +172,7 @@ public class AppointmentDao { } // Update application status - log.info("Updating NDG status to IN_PROGRESS. applicationId: {}", applicationId); + log.info("Updating NDG status of applicationId: {}", applicationId); application.setNdgStatus(NdgStatusEnum.NDG_INITITATED.getValue()); applicationRepository.save(application); @@ -538,7 +545,7 @@ public class AppointmentDao { String authorizationToken = getBearerToken(hub); // Try retrieving NDG by VAT number - NdganagEntity ndganagEntity = ndganagRepository.findByVatNumber(company.getVatNumber()); + NdganagEntity ndganagEntity = ndganagRepository.findByVatNumber(company.getVatNumber()); AppointmentLoginResponse ndgResponse=new AppointmentLoginResponse(); if (ndganagEntity != null && ndganagEntity.getNdg() != null) { ndgResponse.setNdg(ndganagEntity.getNdg()); 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 94c6f912..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; } 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 59d6193f..462958cb 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/EmailNotificationDao.java @@ -73,6 +73,9 @@ public class EmailNotificationDao { @Value("${rinaldo_email}") private String rinaldoEmail; + @Autowired + private SystemEmailTemplatesDao systemEmailTemplatesDao; + private void sendEmail(ApplicationEntity applicationEntity, SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum templateType, Map bodyPlaceholders, List additionalRecipients, Long amendmentId) { @@ -355,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/FormDao.java b/src/main/java/net/gepafin/tendermanagement/dao/FormDao.java index 47859a3b..8ce395d0 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/FormDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/FormDao.java @@ -76,6 +76,9 @@ public class FormDao { @Autowired private HttpServletRequest request; + @Autowired + private ApplicationDao applicationDao; + public FormEntity saveFormEntity(FormEntity formEntity){ formEntity=formRepository.save(formEntity); return formEntity; @@ -419,6 +422,8 @@ public class FormDao { .maxLength(value, fieldValidatorBean.getMaxLength(), fieldLabel,fieldValidatorBean.getMax(),contentResponseBean) // Only applies if maxLength is not null .matchesPattern(value, fieldValidatorBean.getPattern(), fieldLabel) // Only applies if pattern is present .validateCustom(value, fieldValidatorBean.getCustom(), fieldLabel,contentResponseBean); // Add the custom validation here + +// applicationDao.calculationProcessForFormula(applicationFormEntity,formResponseBean.getContent(),fieldId,value,validator); if (fieldValidatorBean.getCustom() != null && fieldValidatorBean.getCustom().equals(GepafinConstant.IS_PIVA)) { Long hubId = applicationEntity.getHubId(); String error = validateVatNumber(value, fieldLabel,hubId); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/PdfDao.java b/src/main/java/net/gepafin/tendermanagement/dao/PdfDao.java index 9eda7968..afde01ab 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/PdfDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/PdfDao.java @@ -137,6 +137,7 @@ public class PdfDao { Map stateFieldMap= new HashMap<>(); Paragraph labelParagraph = new Paragraph(label, labelFont); + labelParagraph.setSpacingAfter(-10f); document.add(labelParagraph); float leftMargin = 20f; PdfContentByte canvas = writer.getDirectContent(); @@ -202,7 +203,9 @@ public class PdfDao { valueCell.setPaddingLeft(leftMargin); // Increase left margin for value valueCell.setBorder(Rectangle.NO_BORDER); // Remove border for value cell valueCell.setVerticalAlignment(Element.ALIGN_MIDDLE); - valueCell.setHorizontalAlignment(Element.ALIGN_LEFT); valueCell.setCellEvent(new RoundedCorners()); // Apply rounded corners + valueCell.setHorizontalAlignment(Element.ALIGN_LEFT); + valueCell.setCellEvent(new RoundedCorners()); // Apply rounded corners + valueTable.setSpacingAfter(-15f); // Add the cell to the table valueTable.addCell(valueCell); @@ -285,6 +288,7 @@ public class PdfDao { valueCell.setHorizontalAlignment(Element.ALIGN_LEFT); valueCell.setCellEvent(new RoundedCorners()); // Apply rounded corners valueTable.addCell(valueCell); + valueTable.setSpacingAfter(-15f); document.add(valueTable); } } @@ -388,10 +392,12 @@ public class PdfDao { if (Boolean.TRUE.equals(formulaEnabledMap.get(key)) && Boolean.TRUE.equals(GepafinConstant.NUMERIC.equalsIgnoreCase(fieldTypeMap.get(key)))) { calculateValue(key, fieldValue, formulaTypeMap, columnSums); } - if(Boolean.TRUE.equals(Utils.isNumeric(fieldValue))){ - fieldValue=Utils.convertToItalianFormat(fieldValue); + String fieldLabel = stateFieldMap.getOrDefault(key, ""); + if(Boolean.FALSE.equals(fieldLabel.equalsIgnoreCase("Anno"))) { + if (Boolean.TRUE.equals(Utils.isNumeric(fieldValue))) { + fieldValue = Utils.convertToItalianFormat(fieldValue); + } } - PdfPCell dataCell = PdfUtils.htmlToPdfPCell(fieldValue != null ? fieldValue : "", textFont); dataCell.setBackgroundColor(new BaseColor(239, 243, 248)); // Light blue for the cell dataCell.setMinimumHeight(30f); @@ -541,6 +547,8 @@ public class PdfDao { labelCell.setBorder(Rectangle.NO_BORDER); labelCell.setGrayFill(7); labelCell.setPadding(5); + labelParagraph.setSpacingAfter(-10f); + // Create a PdfPTable with 1 column and add the PdfPCell to it PdfPTable table = new PdfPTable(1); 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 c0112618..b9af1ca8 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java @@ -61,4 +61,11 @@ public class ApplicationAmendmentRequestEntity extends BaseEntity { @Convert(converter = EmailSendResponseConverter.class) @Column(name = "EMAIL_SEND_RESPONSE", columnDefinition = "TEXT") private List emailSendResponse; + + @Column(name = "EXTENDED_DAYS") + private Long extendedDays; + + @Column(name = "EXTENSION_DATE") + private LocalDateTime extensionDate; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java index 8e783c70..dacc3541 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java @@ -76,4 +76,7 @@ public class ApplicationEntity extends BaseEntity { @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/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/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/CallEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java index dc8c05d8..dd2655ed 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java @@ -99,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/SystemEmailTemplatesEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java index a4c40863..1ac63bae 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java @@ -55,7 +55,8 @@ public class SystemEmailTemplatesEntity extends BaseEntity { USER_ONBOARDING_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/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/UserActionContextEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java index 1de6b333..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"), 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/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/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/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/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 19feb0e2..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; @@ -1063,6 +1061,37 @@ public class Utils { 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/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/resources/db/changelog/db.changelog-1.0.0.xml b/src/main/resources/db/changelog/db.changelog-1.0.0.xml index 78c3d4a2..6337ecde 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 @@ -2955,6 +2955,13 @@ + + + + + + + @@ -2986,5 +2993,33 @@ max(id)+1 from gepafin_schema.ndganag), false) + + + + + + + + + + + + + + + + + + + + + + + + + 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/message_en.properties b/src/main/resources/message_en.properties index 8d9aae03..ad25a88b 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -410,3 +410,6 @@ 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 5a94ede6..f98e55b8 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -401,3 +401,6 @@ 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. +