diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index f4d5c1e6..39292836 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -487,6 +487,10 @@ public class GepafinConstant { public static final String DATA="data"; public static final String FILE_SELECT = "fileselect"; + public static final String PREFERRED_CALL_ID="preferredCallId"; + + public static final String REGION_ID="regionId"; + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index 0b565c89..9b3466fe 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -424,7 +424,7 @@ public class ApplicationDao { responseBean.setCallTitle(applicationEntity.getCall().getName()); responseBean.setCallEndDate(applicationEntity.getCall().getEndDate()); responseBean.setCallEndTime(applicationEntity.getCall().getEndTime()); - responseBean.setModifiedDate(applicationEntity.getCall().getUpdatedDate()); + responseBean.setModifiedDate(applicationEntity.getUpdatedDate()); responseBean.setCallId(applicationEntity.getCall().getId()); responseBean.setSubmissionDate(applicationEntity.getSubmissionDate()); responseBean.setStatus(applicationEntity.getStatus()); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java b/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java index c34b32aa..bef8c76f 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java @@ -4,17 +4,18 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; +import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -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.entities.*; import net.gepafin.tendermanagement.enums.*; @@ -1058,10 +1059,14 @@ public class CallDao { Integer year = null; String search = null; + Map filters = new HashMap<>(); if (callPageableRequestBean.getGlobalFilters() != null) { year = callPageableRequestBean.getGlobalFilters().getYear(); search = callPageableRequestBean.getGlobalFilters().getSearch(); } + if (callPageableRequestBean.getFilters() != null) { + filters = callPageableRequestBean.getFilters(); + } List predicates = new ArrayList<>(); if (year != null && year > 0) { int filterYear = callPageableRequestBean.getGlobalFilters().getYear(); @@ -1104,7 +1109,7 @@ public class CallDao { .toList(); predicates.add(root.get(GepafinConstant.STATUS).in(statusValues)); } - + applyFilters(root, criteriaBuilder, predicates, filters); predicates.add(criteriaBuilder.equal(root.get(GepafinConstant.HUB).get(GepafinConstant.ID), userEntity.getHub().getId())); @@ -1112,6 +1117,130 @@ public class CallDao { } + 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) { + applyStringFilter(fieldPath, criteriaBuilder, predicates, value, matchMode); + applyNumberFilter(fieldPath, criteriaBuilder, predicates, value, matchMode); + 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))); + } + } + } + + + + + private Path getFieldPath(Root root, String fieldName) { + try { + return switch (fieldName) { + + case GepafinConstant.REGION_ID -> { + // Ensure join is only created if not already present + Join regionJoin = root.getJoins().stream() + .filter(j -> j.getAttribute().getName().equals("region")) + .findFirst() + .map(j -> (Join) j) + .orElseGet(() -> root.join("region", JoinType.LEFT)); + + yield regionJoin.get("id"); + } + + default -> root.get(fieldName); + }; + } catch (IllegalArgumentException e) { + return null; + } + } + + public CallResponse createCallStep2EvaluationV2(CallEntity callEntity, CreateCallRequestStep2EvaluationV2 createCallRequest, UserEntity user) { convertToDocumentEntities(createCallRequest.getDocs(), callEntity.getId(), DocumentTypeEnum.DOCUMENT); diff --git a/src/main/java/net/gepafin/tendermanagement/enums/MatchModeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/MatchModeEnum.java index f3ee252c..878c7f85 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/MatchModeEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/MatchModeEnum.java @@ -4,14 +4,14 @@ import com.fasterxml.jackson.annotation.JsonValue; public enum MatchModeEnum { - STARTSWITH("Starts with"), - ENDSWITH("Ends with"), - CONTAINS("Contains"), - EQUALS("Equals"), - DATEIS("Date is"), - DATEISNOT("Date is not"), - BEFORE("Date is before"), - AFTER("Date is after"); + STARTSWITH("startsWith"), + ENDSWITH("endsWith"), + CONTAINS("contains"), + EQUALS("equals"), + DATEIS("dateIs"), + DATEISNOT("dateIsNot"), + BEFORE("dateBefore"), + AFTER("dateAfter"); private String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/RoleStatusEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/RoleStatusEnum.java index 31d3e9b8..0891dc93 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/RoleStatusEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/RoleStatusEnum.java @@ -8,7 +8,8 @@ public enum RoleStatusEnum { ROLE_SUPER_ADMIN("ROLE_SUPER_ADMIN"), ROLE_PRE_INSTRUCTOR("ROLE_PRE_INSTRUCTOR"), ROLE_GEPAFIN_OPERATOR("ROLE_GEPAFIN_OPERATOR"), - ROLE_INSTRUCTOR_MANAGER("ROLE_INSTRUCTOR_MANAGER"); + ROLE_INSTRUCTOR_MANAGER("ROLE_INSTRUCTOR_MANAGER"), + ROLE_CONFIDI("ROLE_CONFIDI"); private String value; diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/CallPageableRequestBean.java b/src/main/java/net/gepafin/tendermanagement/model/request/CallPageableRequestBean.java index 8b848c85..260a45e5 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/CallPageableRequestBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/CallPageableRequestBean.java @@ -4,6 +4,7 @@ import lombok.Data; import net.gepafin.tendermanagement.enums.CallStatusEnum; import java.util.List; +import java.util.Map; @Data public class CallPageableRequestBean { @@ -11,4 +12,6 @@ public class CallPageableRequestBean { private GlobalFilters globalFilters; private List status; + + private Map filters; } 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 66bf7b3b..33567244 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 @@ -2561,5 +2561,71 @@ - + + select + setval('gepafin_schema.role_id_seq', (select + max(id)+1 + from gepafin_schema.role), false) + + select + setval('gepafin_schema.beneficiary_id_seq', (select + max(id)+1 + from gepafin_schema.beneficiary), false) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + role_type = 'ROLE_CONFIDI' + + +