diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index b192fba5..2458bc75 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -499,6 +499,9 @@ public class GepafinConstant { public static final String PASSWORD_EXPIRED = "PasswordExpired"; public static final String PASSWORD_EXPIRED_LOGIN_TO_ODESSA = "password.expired.for.login.to.odessa"; + + public static final String APPLICATION="application"; + public static final String APPLICATION_ID="applicationId"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index d49de7fd..bfec7c1d 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -1616,93 +1616,15 @@ public class ApplicationDao { if (value != null && matchMode != null) { Path fieldPath = getFieldPath(root, fieldName); if (fieldPath != null) { - applyStringFilter(fieldPath, criteriaBuilder, predicates, value, matchMode); - applyNumberFilter(fieldPath, criteriaBuilder, predicates, value, matchMode); - applyDateFilter(fieldPath, criteriaBuilder, predicates, value, matchMode,root); + Utils.applyStringFilter(fieldPath, criteriaBuilder, predicates, value, matchMode); + Utils.applyNumberFilter(fieldPath, criteriaBuilder, predicates, value, matchMode); + Utils.applyDateFilter(fieldPath, criteriaBuilder, predicates, value, matchMode,root); } } } } } - private void applyStringFilter(Path fieldPath, CriteriaBuilder criteriaBuilder, List predicates, Object value, MatchModeEnum matchMode) { - if (value instanceof String) { - String valueStr = (String) value; - if (fieldPath.getJavaType().equals(String.class)) { - MatchModeEnum mode = MatchModeEnum.fromObject(matchMode.getValue()); - switch (mode) { - case CONTAINS -> - predicates.add(criteriaBuilder.like(criteriaBuilder.lower(fieldPath.as(String.class)), "%" + valueStr.toLowerCase() + "%")); - case EQUALS -> predicates.add(criteriaBuilder.equal(fieldPath, valueStr)); - case STARTSWITH -> - predicates.add(criteriaBuilder.like(criteriaBuilder.lower(fieldPath.as(String.class)), valueStr.toLowerCase() + "%")); - case ENDSWITH -> - predicates.add(criteriaBuilder.like(criteriaBuilder.lower(fieldPath.as(String.class)), "%" + valueStr.toLowerCase())); - } - } - } - } - - private void applyNumberFilter(Path fieldPath, CriteriaBuilder criteriaBuilder, List predicates, Object value, MatchModeEnum matchMode) { - if (Number.class.isAssignableFrom(fieldPath.getJavaType())) { - Number numberValue = null; - if (value instanceof Number) { - numberValue = (Number) value; - } - MatchModeEnum mode = MatchModeEnum.fromObject(matchMode.getValue()); - switch (mode) { - case EQUALS -> predicates.add(criteriaBuilder.equal(fieldPath, numberValue)); - } - } - } - - - - - private void applyDateFilter(Path fieldPath, CriteriaBuilder criteriaBuilder, List predicates, Object value, MatchModeEnum matchMode, Root root) { - if (fieldPath.getJavaType().equals(LocalDateTime.class)) { - // Convert input string: Replace 'T' with space - String formattedValue = value.toString().replace("T", " "); - - // Handle timezones and UTC (`Z` or `+HH:mm`) - if (formattedValue.contains("Z") || formattedValue.matches(".*[+-]\\d{2}:\\d{2}$")) { - OffsetDateTime offsetDateTime = OffsetDateTime.parse(value.toString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); - formattedValue = offsetDateTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); - } - - // Check if more than 3 decimal places exist - if (formattedValue.contains(".")) { - int dotIndex = formattedValue.indexOf("."); - if (formattedValue.length() > dotIndex + 4) { - formattedValue = formattedValue.substring(0, dotIndex + 4); // Keep only 3 decimals - } - } else { - formattedValue += ".000"; // Ensure 3 decimals - } - - // Define correct date-time format - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); - - // Parse the formatted value into LocalDateTime - LocalDateTime dateTimeValue = LocalDateTime.parse(formattedValue, formatter); - - // Extract only the date portion - LocalDate dateValue = dateTimeValue.toLocalDate(); - - // Convert database field to LocalDate for date-only comparison - Expression dateField = criteriaBuilder.function("DATE", LocalDate.class, fieldPath); - - MatchModeEnum mode = MatchModeEnum.fromObject(matchMode.getValue()); - - switch (mode) { - case DATEIS -> predicates.add(criteriaBuilder.equal(dateField, dateValue)); - case DATEISNOT -> predicates.add(criteriaBuilder.notEqual(dateField, dateValue)); - case BEFORE -> predicates.add(criteriaBuilder.lessThan(fieldPath.as(Timestamp.class), Timestamp.valueOf(dateTimeValue))); - case AFTER -> predicates.add(criteriaBuilder.greaterThan(fieldPath.as(Timestamp.class), Timestamp.valueOf(dateTimeValue))); - } - } - } - diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java index c09c0fb6..95e808a8 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java @@ -1,8 +1,6 @@ package net.gepafin.tendermanagement.dao; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.Predicate; -import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.*; import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; @@ -39,9 +37,7 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Component; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import static net.gepafin.tendermanagement.util.Utils.log; @@ -354,10 +350,22 @@ public class AssignedApplicationsDao { sortBy.setSortDesc(assignedApplicationPageableRequestBean.getGlobalFilters().getSortBy().getSortDesc()); } } + Path sortPath; + Join applicationJoin = root.join(GepafinConstant.APPLICATION, JoinType.LEFT); - query.orderBy(criteriaBuilder.desc(root.get(sortBy.getColumnName()))); + if (GepafinConstant.APPLICATION_ID.equals(sortBy.getColumnName())) { + sortPath = root.join(GepafinConstant.APPLICATION, JoinType.LEFT).get(GepafinConstant.ID); // Join ApplicationEntity and sort by application.id + } + else if (GepafinConstant.PROTOCOL_NUMBER.equals(sortBy.getColumnName())) { + Join protocolJoin = applicationJoin.join(GepafinConstant.PROTOCOL, JoinType.LEFT); + sortPath = protocolJoin.get(GepafinConstant.PROTOCOL_NUMBER); + } + else { + sortPath = root.get(sortBy.getColumnName()); // Sorting by a field in AmendmentEntity + } + query.orderBy(criteriaBuilder.desc(sortPath)); if (Boolean.FALSE.equals(sortBy.getSortDesc())) { - query.orderBy(criteriaBuilder.asc(root.get(sortBy.getColumnName()))); + query.orderBy(criteriaBuilder.asc(sortPath)); } return query.where(criteriaBuilder.and(predicates.toArray(new Predicate[0]))).getRestriction(); }; @@ -369,10 +377,15 @@ public class AssignedApplicationsDao { Integer year = null; String search = null; + Map filters = new HashMap<>(); + if (assignedApplicationPageableRequestBean.getGlobalFilters() != null) { year = assignedApplicationPageableRequestBean.getGlobalFilters().getYear(); search = assignedApplicationPageableRequestBean.getGlobalFilters().getSearch(); } + if (assignedApplicationPageableRequestBean.getFilters() != null) { + filters = assignedApplicationPageableRequestBean.getFilters(); + } List predicates = new ArrayList<>(); Boolean isBeneficiary = validator.checkIsBeneficiary(); @@ -408,6 +421,7 @@ public class AssignedApplicationsDao { } predicates.add(criteriaBuilder.isFalse(root.get(GepafinConstant.IS_DELETED))); + applyFilters(root, criteriaBuilder, predicates, filters); return predicates; @@ -423,4 +437,39 @@ public class AssignedApplicationsDao { AssignedApplicationsEntity updatedAssignment = saveAssignedApplication(assignedApplication, oldAssignedApplicationEntity, VersionActionTypeEnum.UPDATE); return convertEntityToResponse(updatedAssignment); } + private void applyFilters(Root root, CriteriaBuilder criteriaBuilder, List predicates, Map filters) { + if (Boolean.FALSE.equals(filters.isEmpty())) { + for (Map.Entry entry : filters.entrySet()) { + String fieldName = entry.getKey(); + FilterCriteria filterCriteria = entry.getValue(); + Object value = filterCriteria.getValue(); + MatchModeEnum matchMode = filterCriteria.getMatchMode(); + + if (value != null && matchMode != null) { + Path fieldPath = getFieldPath(root, fieldName); + if (fieldPath != null) { + Utils.applyStringFilter(fieldPath, criteriaBuilder, predicates, value, matchMode); + Utils.applyNumberFilter(fieldPath, criteriaBuilder, predicates, value, matchMode); + Utils.applyDateFilter(fieldPath, criteriaBuilder, predicates, value, matchMode,root); + } + } + } + } + } + + + + + private Path getFieldPath(Root root, String fieldName) { + try { + return switch (fieldName) { + case GepafinConstant.APPLICATION_ID -> root.get(GepafinConstant.APPLICATION).get(GepafinConstant.ID); + case GepafinConstant.PROTOCOL_NUMBER-> root.get(GepafinConstant.APPLICATION).get(GepafinConstant.PROTOCOL).get(GepafinConstant.PROTOCOL_NUMBER); + default -> root.get(fieldName); + }; + } catch (IllegalArgumentException e) { + return null; + } + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/AssignedApplicationPageableRequestBean.java b/src/main/java/net/gepafin/tendermanagement/model/request/AssignedApplicationPageableRequestBean.java index 89b13bd4..afcf078f 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/AssignedApplicationPageableRequestBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/AssignedApplicationPageableRequestBean.java @@ -4,6 +4,7 @@ import lombok.Data; import net.gepafin.tendermanagement.enums.AssignedApplicationEnum; import java.util.List; +import java.util.Map; @Data public class AssignedApplicationPageableRequestBean { @@ -12,4 +13,7 @@ public class AssignedApplicationPageableRequestBean { private GlobalFilters globalFilters; private List status; + + private Map filters; + } diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 1838bd57..32615eae 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -5,9 +5,14 @@ import java.lang.reflect.Field; 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.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; @@ -24,9 +29,14 @@ import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.enums.MatchModeEnum; import net.gepafin.tendermanagement.model.request.GlobalFilters; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; @@ -796,4 +806,84 @@ public class Utils { return null; } } + + public static void applyStringFilter(Path fieldPath, CriteriaBuilder criteriaBuilder, List predicates, Object value, MatchModeEnum matchMode) { + if (value instanceof String) { + String valueStr = (String) value; + if (fieldPath.getJavaType().equals(String.class)) { + MatchModeEnum mode = MatchModeEnum.fromObject(matchMode.getValue()); + switch (mode) { + case CONTAINS -> + predicates.add(criteriaBuilder.like(criteriaBuilder.lower(fieldPath.as(String.class)), "%" + valueStr.toLowerCase() + "%")); + case EQUALS -> predicates.add(criteriaBuilder.equal(fieldPath, valueStr)); + case STARTSWITH -> + predicates.add(criteriaBuilder.like(criteriaBuilder.lower(fieldPath.as(String.class)), valueStr.toLowerCase() + "%")); + case ENDSWITH -> + predicates.add(criteriaBuilder.like(criteriaBuilder.lower(fieldPath.as(String.class)), "%" + valueStr.toLowerCase())); + } + } + } + } + + public static void applyNumberFilter(Path fieldPath, CriteriaBuilder criteriaBuilder, List predicates, Object value, MatchModeEnum matchMode) { + if (Number.class.isAssignableFrom(fieldPath.getJavaType())) { + Number numberValue = null; + if (value instanceof Number) { + numberValue = (Number) value; + } + MatchModeEnum mode = MatchModeEnum.fromObject(matchMode.getValue()); + switch (mode) { + case EQUALS -> predicates.add(criteriaBuilder.equal(fieldPath, numberValue)); + } + } + } + + + + + public static void applyDateFilter(Path fieldPath, CriteriaBuilder criteriaBuilder, List predicates, Object value, MatchModeEnum matchMode, Root root) { + if (fieldPath.getJavaType().equals(LocalDateTime.class)) { + // Convert input string: Replace 'T' with space + String formattedValue = value.toString().replace("T", " "); + + // Handle timezones and UTC (`Z` or `+HH:mm`) + if (formattedValue.contains("Z") || formattedValue.matches(".*[+-]\\d{2}:\\d{2}$")) { + OffsetDateTime offsetDateTime = OffsetDateTime.parse(value.toString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); + formattedValue = offsetDateTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); + } + + // Check if more than 3 decimal places exist + if (formattedValue.contains(".")) { + int dotIndex = formattedValue.indexOf("."); + if (formattedValue.length() > dotIndex + 4) { + formattedValue = formattedValue.substring(0, dotIndex + 4); // Keep only 3 decimals + } + } else { + formattedValue += ".000"; // Ensure 3 decimals + } + + // Define correct date-time format + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + // Parse the formatted value into LocalDateTime + LocalDateTime dateTimeValue = LocalDateTime.parse(formattedValue, formatter); + + // Extract only the date portion + LocalDate dateValue = dateTimeValue.toLocalDate(); + + // Convert database field to LocalDate for date-only comparison + jakarta.persistence.criteria.Expression dateField = criteriaBuilder.function("DATE", LocalDate.class, fieldPath); + + MatchModeEnum mode = MatchModeEnum.fromObject(matchMode.getValue()); + + switch (mode) { + case DATEIS -> predicates.add(criteriaBuilder.equal(dateField, dateValue)); + case DATEISNOT -> predicates.add(criteriaBuilder.notEqual(dateField, dateValue)); + case BEFORE -> predicates.add(criteriaBuilder.lessThan(fieldPath.as(Timestamp.class), Timestamp.valueOf(dateTimeValue))); + case AFTER -> predicates.add(criteriaBuilder.greaterThan(fieldPath.as(Timestamp.class), Timestamp.valueOf(dateTimeValue))); + } + } + } + + } \ No newline at end of file