diff --git a/pom.xml b/pom.xml
index c463be91..a42534bf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -245,6 +245,12 @@
reactor-netty
+
+ net.objecthunter
+ exp4j
+ 0.4.8
+
+
diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java
index d04a6abf..80f30960 100644
--- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java
+++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java
@@ -408,6 +408,7 @@ public class GepafinConstant {
public static final String ASSIGNED_APPLICATION_STATUS_UPDATED_SUCCESSFULLY = "assigned.application.status.updated.successfully";
public static final String REQUIRED_REQUESTED_AMOUNT_MSG = "validation.required.requested.amount";
+ public static final String FORMULA_AMOUNT_NOT_MATCHED="formula.amount.not.matches.requested.amount";
public static final String CRITERIA_TABLE_COLUMNS="criteria_table_columns";
public static final String DOCUMENTATION_INTEGRATION_REQUEST_SVILUPPUMBRIA= "\n" +
diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java
index 794e7796..f43a030f 100644
--- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java
+++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java
@@ -31,6 +31,7 @@ import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundExceptio
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
import org.h2.util.IOUtils;
+import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -49,11 +50,16 @@ import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -94,29 +100,29 @@ public class ApplicationDao {
@Autowired
private FlowDataRepository flowDataRepository;
-
+
@Autowired
private UserCompanyDelegationRepository userCompanyDelegationRepository;
-
+
@Autowired
private Validator validator;
-
+
@Autowired
private CompanyService companyService;
@Autowired
private S3PathConfig s3PathConfig;
-
+
@Autowired
private SystemEmailTemplatesService systemEmailTemplatesService;
@Autowired
private AssignedApplicationsRepository assignedApplicationsRepository;
-
+
@Value("${default_System_Receiver_Email}")
private String defaultSystemReceiverEmail;
-
+
@Value("${rinaldo_email}")
private String rinaldoEmail;
-
+
@Value("${carlo_email}")
private String carloEmail;
@@ -125,37 +131,37 @@ public class ApplicationDao {
@Autowired
private AmazonS3Service amazonS3Service;
-
+
@Autowired
private ApplicationSignedDocumentRepository applicationSignedDocumentRepository;
-
+
// @Value("${aws.s3.url.folder.signed.document}")
// private String signedDocumentS3Folder;
-
+
@Value("${default.hub.uuid}")
private String defaultHubUuid;
-
+
@Autowired
private UserService userService;
@Autowired
private S3PathConfig s3ConfigBean;
-
+
@Autowired
private ProtocolDao protocolDao;
-
+
@Autowired
private HubService hubService;
@Autowired
private EmailNotificationDao emailNotificationDao;
-
+
@Autowired
private FormDao formDao;
@Autowired
private EmailLogDao emailLogDao;
-
+
@Autowired
private UserWithCompanyRepository userWithCompanyRepository;
@@ -183,6 +189,7 @@ public class ApplicationDao {
@Autowired
private ApplicationEvaluationRepository applicationEvaluationRepository;
+
public ApplicationResponseBean createApplication(HttpServletRequest request, ApplicationRequestBean applicationRequestBean, Long formId, Long applicationId) {
FormEntity formEntity = formService.validateForm(formId);
// callService.validatePublishedCall(formEntity.getCall().getId());
@@ -258,7 +265,7 @@ public class ApplicationDao {
// List contentResponseBeans = Utils.convertJsonStringToList(
// applicationFormEntity.getForm().getContent(), ContentResponseBean.class);
-
+
List contentResponseBeans = formDao.convertFormEntityToFormResponseBean(applicationFormEntity.getForm()).getContent();
for (ApplicationFormFieldEntity applicationFormFieldEntity : applicationFormFieldEntities) {
@@ -309,7 +316,7 @@ public class ApplicationDao {
);
}
ApplicationEntity oldApplicationDataEntity = Utils.getClonedEntityForData(applicationEntity);
-
+
validator.validateUserWithCompany(request, applicationEntity.getCompanyId());
applicationEntity.setIsDeleted(true);
applicationEntity = applicationRepository.save(applicationEntity);
@@ -356,9 +363,9 @@ public class ApplicationDao {
//
// return applicationResponses;
// }
-
+
public List getAllApplications(UserEntity userEntity, Long callId, Long companyId,List statusList) {
-
+
log.info("Fetching applications for RoleType: {}", userEntity.getRoleEntity().getRoleType());
Specification spec = search(userEntity, callId, companyId,statusList);
@@ -482,15 +489,18 @@ public class ApplicationDao {
public List createOrUpdateMultipleFormFields(List formFieldResponseBeans,
ApplicationFormEntity applicationFormEntity, FormEntity formEntity) {
+ FieldValidator fieldValidator = FieldValidator.create();
List existingFields = applicationFormFieldRepository.findByApplicationFormId(applicationFormEntity.getId());
- return formFieldResponseBeans.stream().map(requestBean -> createOrUpdateApplicationFormField(requestBean, applicationFormEntity, existingFields, formEntity))
+ List applicationFormFieldEntities=formFieldResponseBeans.stream().map(requestBean -> createOrUpdateApplicationFormField(requestBean, applicationFormEntity, existingFields, formEntity,fieldValidator))
.collect(Collectors.toList());
+ fieldValidator.validate();
+ return applicationFormFieldEntities;
}
public ApplicationFormFieldEntity createOrUpdateApplicationFormField(ApplicationFormFieldRequestBean applicationFormFieldRequestBean,
- ApplicationFormEntity applicationFormEntity, List applicationFormFieldEntities, FormEntity formEntity) {
+ ApplicationFormEntity applicationFormEntity, List applicationFormFieldEntities, FormEntity formEntity,FieldValidator fieldValidator) {
ApplicationFormFieldEntity applicationFormFieldEntity = new ApplicationFormFieldEntity();
@@ -506,7 +516,7 @@ public class ApplicationDao {
.filter(setting -> "isRequestedAmount".equals(setting.getName()) && Boolean.TRUE.equals(setting.getValue()))
.findFirst()
.ifPresent(setting -> {
-
+
Object fieldValue = applicationFormFieldRequestBean.getFieldValue();
if(fieldValue!=null) {
try {
@@ -538,6 +548,7 @@ public class ApplicationDao {
}
}
}
+ calculationProcessForFormula(applicationFormEntity,contentResponseBeans,applicationFormFieldRequestBean,fieldValidator);
Utils.setIfUpdated(applicationFormFieldEntity::getFieldId, applicationFormFieldEntity::setFieldId, applicationFormFieldRequestBean.getFieldId());
if (applicationFormFieldRequestBean.getFieldValue() != null) {
@@ -560,7 +571,6 @@ public class ApplicationDao {
VersionHistoryRequest.builder().request(request).actionType(actionType).oldData(oldApplicationFormFieldData).newData(applicationFormField).build());
log.info("Version history logged for action: {}, Field ID: {}", actionType, applicationFormFieldEntity.getFieldId());
-
return applicationFormField;
}
@@ -818,7 +828,7 @@ public class ApplicationDao {
if(formApplicationResponse.getContent() != null && formApplicationResponse.getFormFields() != null) {
formApplicationResponses.add(formApplicationResponse);
}
-
+
}
public FormApplicationResponse processForm(FormEntity formEntity, ApplicationEntity applicationEntity) {
@@ -954,7 +964,7 @@ public class ApplicationDao {
return (int) Math.round(progress);
}
public void validateFormFields(ApplicationRequestBean request, FormEntity formEntity) {
-
+
// List contentResponseBeans=Utils.convertJsonStringToList(formEntity.getContent(),ContentResponseBean.class);
List contentResponseBeans=formDao.convertFormEntityToFormResponseBean(formEntity).getContent();
@@ -1034,7 +1044,7 @@ public class ApplicationDao {
SystemEmailTemplateResponse systemEmailTemplateResponse = systemEmailTemplatesService
.retrieveTemplateByTypeAndCall(SystemEmailTemplatesEntityTypeEnum.APPLICATION_SUBMISSION_TO_USER_AND_COMPANY,
hub, null);
-
+
// Create the map for subject placeholders
Map subjectPlaceholders = new HashMap<>();
subjectPlaceholders.put("{{call_name}}", call.getName());
@@ -1211,17 +1221,17 @@ public class ApplicationDao {
}
public ApplicationSignedDocumentResponse getSignedDocument(HttpServletRequest request, Long applicationId) {
-
+
ApplicationEntity applicationEntity = validateApplication(applicationId);
// validator.validateUserWithCompany(request, applicationEntity.getCompanyId());
-
+
if (validator.checkIsPreInstructor()) {
ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluationByApplicationId(applicationId);
validator.validatePreInstructor(request, applicationEvaluationEntity.getUserId());
} else {
validator.validateUserId(request, applicationEntity.getUserId());
}
-
+
ApplicationSignedDocumentEntity applicationSignedDocument = applicationSignedDocumentRepository
.findByApplicationIdAndStatus(applicationId, ApplicationSignedDocumentStatusEnum.ACTIVE.getValue());
if(applicationSignedDocument == null) {
@@ -1230,11 +1240,11 @@ public class ApplicationDao {
}
return convertApplicationSignedDocumentToApplicationSignedDocumentResponse(applicationSignedDocument);
}
-
+
public void deleteSignedDocument(HttpServletRequest request, Long applicationId) {
ApplicationEntity applicationEntity = validateApplication(applicationId);
validator.validateUserWithCompany(request, applicationEntity.getCompanyId());
-
+
ApplicationSignedDocumentEntity applicationSignedDocument = applicationSignedDocumentRepository
.findByApplicationIdAndStatus(applicationId, ApplicationSignedDocumentStatusEnum.ACTIVE.getValue());
//cloned entity for old data
@@ -1539,5 +1549,135 @@ public class ApplicationDao {
}
}
+ public void calculationProcessForFormula(ApplicationFormEntity applicationFormEntity, List contentResponseBeans, ApplicationFormFieldRequestBean applicationFormFieldRequestBean,FieldValidator fieldValidator) {
+ List formulaValue = new ArrayList<>();
+ String formulaValueOpt=null;
+ String label=null;
+ for (ContentResponseBean contentResponseBean:contentResponseBeans){
+ if(contentResponseBean.getId().equals(applicationFormFieldRequestBean.getFieldId())){
+ for (SettingResponseBean settingResponseBean:contentResponseBean.getSettings()){
+ if (settingResponseBean.getName().equals("label")){
+ label= String.valueOf(settingResponseBean.getValue());
+ }
+ if(settingResponseBean.getName().equals("formula")){
+ String value= (String) settingResponseBean.getValue();
+ formulaValueOpt=value;
+ formulaValue=Utils.extractValues(value);
+ }
+ }
+ }
+ }
+ Map mappedFormulaValue = new HashMap<>();
+ Object fieldValue = applicationFormFieldRequestBean.getFieldValue();
+ if (formulaValueOpt != null && fieldValue==null) {
+ fieldValue=0;
+ }
+
+ for (ContentResponseBean contentResponseBean : contentResponseBeans) {
+ String contentId = contentResponseBean.getId();
+
+ // Extract variable values once per contentResponseBean to avoid repeated stream operations
+ Set variableValues = contentResponseBean.getSettings().stream()
+ .filter(setting -> "variable".equals(setting.getName()))
+ .flatMap(setting -> {
+ Object value = setting.getValue(); // Get the raw value
+ if (value instanceof String) {
+ return Stream.of((String) value); // Handle single String case
+ } else if (value instanceof List) {
+ return ((List>) value).stream()
+ .filter(item -> item instanceof String) // Ensure it's a String
+ .map(item -> (String) item); // Convert to String
+ } else {
+ return Stream.empty(); // Ignore unexpected types
+ }
+ })
+ .collect(Collectors.toSet()); // Collect into a Set for uniqueness
+
+ for (String formula : formulaValue) {
+ if (variableValues.contains(formula)) { // O(1) lookup instead of O(n)
+ mappedFormulaValue.put(formula, contentId);
+ }
+ }
+ }
+ Map updatedMappedFormulaValue = new HashMap<>();
+
+ for (Map.Entry entry : mappedFormulaValue.entrySet()) {
+ String variable = entry.getKey();
+ String contentId = entry.getValue();
+
+ // Repository call using contentId
+ Optional optionalEntity = applicationFormFieldRepository.findByApplicationFormIdAndFieldId(applicationFormEntity.getId(),contentId);
+ // If entity is found, extract fieldValue and fieldId
+ optionalEntity.ifPresent(entity -> {
+ String entityFieldValue = entity.getFieldValue(); // Assuming getter method exists
+ String fieldId = entity.getFieldId(); // Assuming getter method exists
+ String tableType = contentResponseBeans.stream()
+ .filter(content -> content.getId().equals(fieldId)) // Match Content ID with fieldId
+ .flatMap(content -> content.getSettings().stream()) // Extract settings
+ .filter(setting -> "criteria_table_columns".equals(setting.getName())) // Match name
+ .map(setting -> setting.getName()) // Return the name of the setting
+ .findFirst() // Get the first match
+ .orElse(null); // Default to null if no match
+
+ if(tableType!=null){
+ JSONObject jsonObject = new JSONObject(entityFieldValue);
+
+ // Extract the value of total
+ entityFieldValue = jsonObject.getString("total");
+
+ }
+
+ updatedMappedFormulaValue.put(fieldId, entityFieldValue);
+ });
+ }
+ if(formulaValueOpt==null || formulaValueOpt.isEmpty()){
+ return;
+ }
+ double finalValue = evaluateFormula(formulaValueOpt, mappedFormulaValue, updatedMappedFormulaValue);
+
+ fieldValidator.formulaValidation(fieldValue, finalValue, label);
+ }
+
+
+ public static double evaluateFormula(String formula, Map mappedFormulaValue, Map updatedMappedFormulaValue) {
+ // Step 1: Extract all placeholders (variables) like {rest}, {another_var}, etc.
+ Pattern pattern = Pattern.compile("\\{(.*?)\\}");
+ Matcher matcher = pattern.matcher(formula);
+ List variables = new ArrayList<>();
+
+ while (matcher.find()) {
+ variables.add(matcher.group(1)); // Extract variable names inside the curly braces
+ }
+
+ // Step 2: Replace placeholders with corresponding fieldValue
+ Map variableValues = new HashMap<>();
+
+ for (String variable : variables) {
+ String fieldId = mappedFormulaValue.get(variable);
+ if (fieldId != null && updatedMappedFormulaValue.containsKey(fieldId)) {
+ String fieldValueStr = updatedMappedFormulaValue.get(fieldId);
+ try {
+ double fieldValue = Double.parseDouble(fieldValueStr); // Assuming fieldValue is numeric
+ variableValues.put(variable, fieldValue);
+ } catch (NumberFormatException e) {
+ // Handle invalid number format gracefully (e.g., log an error or default to 0)
+ variableValues.put(variable, 0.0);
+ }
+ }
+ }
+
+ // Step 3: Replace variables in the formula with their corresponding values
+ String expression = formula;
+ for (String variable : variables) {
+ Double value = variableValues.get(variable);
+ if (value != null) {
+ // Replace {variable} with its corresponding value in the formula
+ expression = expression.replace("{" + variable + "}", String.valueOf(value));
+ }
+ }
+
+ // Step 4: Evaluate the mathematical expression
+ return Utils.evaluateExpression(expression);
+ }
}
diff --git a/src/main/java/net/gepafin/tendermanagement/util/FieldValidator.java b/src/main/java/net/gepafin/tendermanagement/util/FieldValidator.java
index ffcdfcac..951a6b3a 100644
--- a/src/main/java/net/gepafin/tendermanagement/util/FieldValidator.java
+++ b/src/main/java/net/gepafin/tendermanagement/util/FieldValidator.java
@@ -2,6 +2,10 @@ package net.gepafin.tendermanagement.util;
import java.text.MessageFormat;
import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -9,12 +13,18 @@ import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.dao.FormDao;
import net.gepafin.tendermanagement.dao.VatCheckDao;
+import net.gepafin.tendermanagement.entities.ApplicationFormEntity;
+import net.gepafin.tendermanagement.entities.ApplicationFormFieldEntity;
+import net.gepafin.tendermanagement.model.request.ApplicationFormFieldRequestBean;
import net.gepafin.tendermanagement.model.request.ContentRequestBean;
import net.gepafin.tendermanagement.model.response.ContentResponseBean;
import net.gepafin.tendermanagement.model.response.SettingResponseBean;
+import net.gepafin.tendermanagement.repositories.ApplicationFormFieldRepository;
+import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
import net.gepafin.tendermanagement.web.rest.api.errors.ValidationException;
import org.apache.commons.lang3.StringUtils;
+import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
@@ -272,5 +282,17 @@ public class FieldValidator {
// Now contentRequestBean is populated with the data from the JSON
return contentRequestBean;
}
-
+ public FieldValidator formulaValidation(Object fieldValue, double finalValue, String label) {
+ if (fieldValue != null) {
+ try {
+ double fieldValueAsDouble = Double.parseDouble(fieldValue.toString()); // Convert fieldValue to double
+ if (Double.compare(finalValue, fieldValueAsDouble) != 0) { // Compare doubles safely
+ errors.add(MessageFormat.format(Translator.toLocale(GepafinConstant.FORMULA_AMOUNT_NOT_MATCHED), label));
+ }
+ } catch (NumberFormatException e) {
+ throw new CustomValidationException(Status.BAD_REQUEST, "Invalid field value: " + fieldValue);
+ }
+ }
+ return this;
+ }
}
diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java
index 0be87340..f444d69d 100644
--- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java
+++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java
@@ -11,6 +11,7 @@ import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -27,6 +28,8 @@ import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.model.request.GlobalFilters;
+import net.objecthunter.exp4j.Expression;
+import net.objecthunter.exp4j.ExpressionBuilder;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,6 +57,9 @@ import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
import static org.apache.commons.lang3.StringUtils.isEmpty;
@@ -753,6 +759,25 @@ public class Utils {
private static Map defaultErrorResponse() {
return Collections.singletonMap("message", Translator.toLocale(GepafinConstant.INVALID_VATNUMBER));
}
+ public static List extractValues(String input) {
+ List extractedValues = new ArrayList<>();
+ Pattern pattern = Pattern.compile("\\{(.*?)\\}"); // Regex to match {value}
+ Matcher matcher = pattern.matcher(input);
+
+ while (matcher.find()) {
+ extractedValues.add(matcher.group(1)); // Extract value inside {}
+ }
+ return extractedValues;
+ }
+ public static double evaluateExpression(String expression) {
+ try {
+ Expression exp = new ExpressionBuilder(expression).build();
+ return exp.evaluate();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return Double.NaN; // Return NaN if the expression is invalid
+ }
+ }
public static boolean isNumeric(String input) {
if (input == null || input.trim().isEmpty()) {
return false;
diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties
index ef54674d..901ab8a6 100644
--- a/src/main/resources/message_en.properties
+++ b/src/main/resources/message_en.properties
@@ -368,5 +368,6 @@ either.applicationId.or.assignedApplicationId.must.be.provided=Either applicatio
assigned.application.status.updated.successfully=Assigned application status updated successfully.
validation.required.requested.amount=The Requested Amount configuration should be mandatory.
+formula.amount.not.matches.requested.amount= The {0} does not matches to calculated amount.
diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties
index e14120cd..5f2ca8b0 100644
--- a/src/main/resources/message_it.properties
+++ b/src/main/resources/message_it.properties
@@ -359,6 +359,5 @@ either.applicationId.or.assignedApplicationId.must.be.provided = "� necessario
assigned.application.status.updated.successfully=Stato dell'applicazione assegnata aggiornato con successo.
validation.required.requested.amount=La configurazione dell'importo richiesto � obbligatoria.
-
-
+formula.amount.not.matches.requested.amount=Il {0} non corrisponde all'importo calcolato.