diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 9ccfe7e5..4371d92f 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -510,6 +510,11 @@ public class GepafinConstant { public static final String INVALID_USER_ID="invalid.user"; public static final String COMPANY_NAME="companyName"; + public static final String APPOINTMENT_ID="appointmentId"; + public static final String APPLICATION_STATUS="applicationStatus"; + + + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java index 95e808a8..65035e47 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AssignedApplicationsDao.java @@ -20,6 +20,7 @@ import net.gepafin.tendermanagement.model.util.SortBy; import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; import net.gepafin.tendermanagement.repositories.ApplicationRepository; import net.gepafin.tendermanagement.repositories.AssignedApplicationsRepository; +import net.gepafin.tendermanagement.repositories.AssignedApplicationsViewRepository; import net.gepafin.tendermanagement.service.ApplicationService; import net.gepafin.tendermanagement.service.CompanyService; import net.gepafin.tendermanagement.service.UserService; @@ -77,6 +78,9 @@ public class AssignedApplicationsDao { @Autowired private ApplicationEvaluationRepository applicationEvaluationRepository; + @Autowired + private AssignedApplicationsViewRepository assignedApplicationsViewRepository; + public AssignedApplicationsResponse createAssignedApplications(Long applicationId, Long userId, UserEntity assignedByUser, AssignedApplicationsRequest assignedApplicationsRequest) { log.info("Assigning application to pre-Instructor with details: {}", applicationId, userId); @@ -310,14 +314,13 @@ public class AssignedApplicationsDao { if (pageNo == null || pageNo <= 0) { pageNo = GepafinConstant.DEFAULT_PAGE; } - Specification spec = searchByPagination( assignedApplicationPageableRequestBean, user,userId); - Page entityPage = assignedApplicationsRepository.findAll(spec, PageRequest.of(pageNo - 1, pageLimit)); + Specification spec = searchByPagination( assignedApplicationPageableRequestBean, user,userId); + Page entityPage = assignedApplicationsViewRepository.findAll(spec, PageRequest.of(pageNo - 1, pageLimit)); // Prepare the response - List assignedApplicationsResponses = entityPage.getContent().stream() .map(application -> { - AssignedApplicationsResponse response = convertEntityToResponse(application); + AssignedApplicationsResponse response = getAssignedApplicationResponseByView(application); return response; }) .collect(Collectors.toList()); @@ -334,7 +337,7 @@ public class AssignedApplicationsDao { return pageableResponseBean; } - public Specification searchByPagination(AssignedApplicationPageableRequestBean assignedApplicationPageableRequestBean, UserEntity userEntity,Long userId) { + public Specification searchByPagination(AssignedApplicationPageableRequestBean assignedApplicationPageableRequestBean, UserEntity userEntity,Long userId) { return (root, query, criteriaBuilder) -> { List predicates = getPredicates(assignedApplicationPageableRequestBean, criteriaBuilder, root, userEntity,userId); @@ -350,30 +353,35 @@ public class AssignedApplicationsDao { sortBy.setSortDesc(assignedApplicationPageableRequestBean.getGlobalFilters().getSortBy().getSortDesc()); } } - Path sortPath; - Join applicationJoin = root.join(GepafinConstant.APPLICATION, JoinType.LEFT); +// Path sortPath; +// Join applicationJoin = root.join(GepafinConstant.APPLICATION, JoinType.LEFT); +// +// 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(sortPath)); +// } +// return query.where(criteriaBuilder.and(predicates.toArray(new Predicate[0]))).getRestriction(); + + Path sortPath = root.get(sortBy.getColumnName()); // All fields are accessible directly in the view + query.orderBy(sortBy.getSortDesc() ? criteriaBuilder.desc(sortPath) : criteriaBuilder.asc(sortPath)); - 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(sortPath)); - } return query.where(criteriaBuilder.and(predicates.toArray(new Predicate[0]))).getRestriction(); }; } private List getPredicates(AssignedApplicationPageableRequestBean assignedApplicationPageableRequestBean, - CriteriaBuilder criteriaBuilder, Root root, UserEntity userEntity,Long userId) { + CriteriaBuilder criteriaBuilder, Root root, UserEntity userEntity,Long userId) { Integer year = null; String search = null; @@ -404,12 +412,35 @@ public class AssignedApplicationsDao { } // Search in `title` and `message` (if search term is provided) - if (search != null && !search.isEmpty()) { - Predicate titlePredicate = criteriaBuilder.like( - criteriaBuilder.upper(root.get(GepafinConstant.NOTE)), - "%" + search.toUpperCase() + "%" - ); - predicates.add(criteriaBuilder.or(titlePredicate)); +// if (search != null && !search.isEmpty()) { +// Predicate titlePredicate = criteriaBuilder.like( +// criteriaBuilder.upper(root.get(GepafinConstant.NOTE)), +// "%" + search.toUpperCase() + "%" +// ); +// predicates.add(criteriaBuilder.or(titlePredicate)); +// } + + if (search != null && !search.trim().isEmpty()) { + String pattern = "%" + search.toUpperCase() + "%"; + List searchPredicates = new ArrayList<>(); + + searchPredicates.add(criteriaBuilder.like(criteriaBuilder.upper(root.get(GepafinConstant.CALL_NAME)), pattern)); + searchPredicates.add(criteriaBuilder.like(criteriaBuilder.upper(root.get(GepafinConstant.COMPANY_NAME)), pattern)); + searchPredicates.add(criteriaBuilder.like(criteriaBuilder.upper(root.get(GepafinConstant.STATUS)), pattern)); + searchPredicates.add(criteriaBuilder.like(criteriaBuilder.upper(root.get(GepafinConstant.NDG_STRING)), pattern)); + searchPredicates.add(criteriaBuilder.like(criteriaBuilder.upper(root.get(GepafinConstant.APPOINTMENT_ID)), pattern)); + searchPredicates.add(criteriaBuilder.like(criteriaBuilder.upper(root.get(GepafinConstant.APPLICATION_STATUS)), pattern)); + + // Convert numeric fields to string for search (optional and DB-specific; otherwise exact match) + try { + Long searchLong = Long.parseLong(search); + searchPredicates.add(criteriaBuilder.equal(root.get(GepafinConstant.APPLICATION_ID), searchLong)); + searchPredicates.add(criteriaBuilder.equal(root.get(GepafinConstant.PROTOCOL_NUMBER), searchLong)); + } catch (NumberFormatException ignored) { + // Ignore if search is not a number + } + + predicates.add(criteriaBuilder.or(searchPredicates.toArray(new Predicate[0]))); } // Filter by `status` (if status list is provided) @@ -421,12 +452,31 @@ public class AssignedApplicationsDao { } predicates.add(criteriaBuilder.isFalse(root.get(GepafinConstant.IS_DELETED))); - applyFilters(root, criteriaBuilder, predicates, filters); - + Utils.applyFiltersByPagination(root, criteriaBuilder, predicates, filters); return predicates; } + + private AssignedApplicationsResponse getAssignedApplicationResponseByView(AssignedApplicationsView view) { + AssignedApplicationsResponse response = new AssignedApplicationsResponse(); + response.setId(view.getId()); + response.setUserId(view.getUserId()); + response.setStatus(AssignedApplicationEnum.valueOf(view.getStatus())); + response.setApplicationId(view.getApplicationId()); + response.setApplicationStatus(ApplicationStatusTypeEnum.valueOf(view.getApplicationStatus())); + response.setSubmissionDate(view.getSubmissionDate()); + response.setEvaluationEndDate(view.getEvaluationEndDate()); + response.setNdg(view.getNdg()); + response.setAppointmentId(view.getAppointmentId()); + response.setProtocolNumber(view.getProtocolNumber()); + response.setCallName(view.getCallName()); + response.setCompanyName(view.getCompanyName()); + response.setCreatedDate(view.getCreatedDate()); + response.setUpdatedDate(view.getUpdatedDate()); + return response; + } + public AssignedApplicationsResponse updateAssignedApplicationStatus(HttpServletRequest request, Long assignedApplicationId, AssignedApplicationEnum status) { AssignedApplicationsEntity assignedApplication = validateAssignedApplication(assignedApplicationId); diff --git a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java new file mode 100644 index 00000000..a446daff --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsView.java @@ -0,0 +1,61 @@ +package net.gepafin.tendermanagement.entities; + +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.Immutable; + +import java.time.LocalDateTime; + +@Entity +@Immutable +@Data +@Table(name = "assigned_applications_view") +@IdClass(AssignedApplicationsViewId.class) +public class AssignedApplicationsView{ + + @Id + @Column(name = "ID") + private Long id; + + @Column(name = "APPLICATION_ID") + private Long applicationId; + + @Column(name = "USER_ID") + private Long userId; + + @Column(name = "PROTOCOL_NUMBER") + private Long protocolNumber; + + @Column(name = "CALL_NAME") + private String callName; + + @Column(name = "COMPANY_NAME") + private String companyName; + + @Column(name = "STATUS") + private String status; + + @Column(name = "NDG") + private String ndg; + + @Column(name = "APPOINTMENT_ID") + private String appointmentId; + + @Column(name = "APPLICATION_STATUS") + private String applicationStatus; + + @Column(name = "SUBMISSION_DATE") + private LocalDateTime submissionDate; + + @Column(name = "EVALUATION_END_DATE") + private LocalDateTime evaluationEndDate; + + @Column(name = "CREATED_DATE") + private LocalDateTime createdDate; + + @Column(name = "UPDATED_DATE") + private LocalDateTime updatedDate; + + @Column(name = "IS_DELETED") + private Boolean isDeleted; +} diff --git a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsViewId.java b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsViewId.java new file mode 100644 index 00000000..99c9609c --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsViewId.java @@ -0,0 +1,17 @@ +package net.gepafin.tendermanagement.entities; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class AssignedApplicationsViewId implements Serializable{ + private static final long serialVersionUID = 1L; + private Long id; + + public AssignedApplicationsViewId() {} + + public AssignedApplicationsViewId(Long id) { + this.id = id; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/AssignedApplicationsViewRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/AssignedApplicationsViewRepository.java new file mode 100644 index 00000000..231c7d88 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/repositories/AssignedApplicationsViewRepository.java @@ -0,0 +1,9 @@ +package net.gepafin.tendermanagement.repositories; + +import net.gepafin.tendermanagement.entities.AssignedApplicationsView; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface AssignedApplicationsViewRepository extends JpaRepository , JpaSpecificationExecutor { + +} 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 f4e71555..648d76e8 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 @@ -2722,4 +2722,10 @@ + + + + + diff --git a/src/main/resources/db/dump/create_assigned_application_view.sql b/src/main/resources/db/dump/create_assigned_application_view.sql new file mode 100644 index 00000000..f1900b37 --- /dev/null +++ b/src/main/resources/db/dump/create_assigned_application_view.sql @@ -0,0 +1,52 @@ +CREATE OR REPLACE VIEW 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.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 + +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 + +WHERE aa.is_deleted IS FALSE OR aa.is_deleted IS NULL;