diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/common/pagination/PaginatedResult.java b/backend/src/main/java/ca/bc/gov/restapi/results/common/pagination/PaginatedResult.java index 477131f8..4e5ac6e9 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/common/pagination/PaginatedResult.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/common/pagination/PaginatedResult.java @@ -20,6 +20,7 @@ public class PaginatedResult { private int pageIndex; private int perPage; private int totalPages; + private long totalItems; private boolean hasNextPage; private List data; } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/common/pagination/PaginationParameters.java b/backend/src/main/java/ca/bc/gov/restapi/results/common/pagination/PaginationParameters.java index 7f21445d..e8ed3597 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/common/pagination/PaginationParameters.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/common/pagination/PaginationParameters.java @@ -4,6 +4,7 @@ import jakarta.validation.constraints.PositiveOrZero; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; /** * Pagination parameters to be used in the processing of HTTP GET requests. @@ -34,4 +35,8 @@ public record PaginationParameters( public Pageable toPageable(int maxPageSize) { return PageRequest.of(page, maxPageSize); } + + public Pageable toPageable(Sort sort) { + return PageRequest.of(page, perPage,sort); + } } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchFiltersDto.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchFiltersDto.java index c4513082..d710a60b 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchFiltersDto.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchFiltersDto.java @@ -1,13 +1,13 @@ package ca.bc.gov.restapi.results.oracle.dto; import ca.bc.gov.restapi.results.oracle.SilvaOracleConstants; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; /** This record contains all possible filters when using the Opening Search API. */ @Slf4j @@ -18,7 +18,7 @@ public class OpeningSearchFiltersDto { private final List category; private final List statusList; private final Boolean myOpenings; - private final Boolean submittedToFrpa; + private final String submittedToFrpa; private final String disturbanceDateStart; private final String disturbanceDateEnd; private final String regenDelayDateStart; @@ -56,25 +56,19 @@ public OpeningSearchFiltersDto( String cutBlockId, String timberMark, String mainSearchTerm) { - this.orgUnit = new ArrayList<>(); - if (!Objects.isNull(orgUnit)) { - this.orgUnit.addAll(orgUnit.stream() - .map(s -> String.format("'%s'", s.toUpperCase().trim())) - .toList()); - } - this.category = new ArrayList<>(); - if (!Objects.isNull(category)) { - this.category.addAll(category.stream() - .map(s -> String.format("'%s'", s.toUpperCase().trim())) - .toList()); - } - this.statusList = new ArrayList<>(); - this.openingIds = new ArrayList<>(); - if (!Objects.isNull(statusList)) { - this.statusList.addAll(statusList.stream().map(s -> String.format("'%s'", s)).toList()); - } + this.orgUnit = !Objects.isNull(orgUnit) ? orgUnit : null; + this.category = !Objects.isNull(category) ? category : null; + this.statusList = !Objects.isNull(statusList) ? statusList : null; + this.openingIds = null; this.myOpenings = myOpenings; - this.submittedToFrpa = submittedToFrpa; + this.submittedToFrpa = + BooleanUtils + .toString( + submittedToFrpa, + "YES", + "NO", + "NO" + ); this.disturbanceDateStart = Objects.isNull(disturbanceDateStart) ? null : disturbanceDateStart.trim(); this.disturbanceDateEnd = Objects.isNull(disturbanceDateEnd) ? null : disturbanceDateEnd.trim(); @@ -95,14 +89,13 @@ public OpeningSearchFiltersDto( } // Create a constructor with only the List openingIds - public OpeningSearchFiltersDto( - List openingIds) { - this.orgUnit = new ArrayList<>(); - this.category = new ArrayList<>(); - this.statusList = new ArrayList<>(); + public OpeningSearchFiltersDto(List openingIds) { + this.orgUnit = null; + this.category = null; + this.statusList = null; this.openingIds = openingIds; this.myOpenings = null; - this.submittedToFrpa = null; + this.submittedToFrpa = "NO"; this.disturbanceDateStart = null; this.disturbanceDateEnd = null; this.regenDelayDateStart = null; @@ -124,10 +117,10 @@ public OpeningSearchFiltersDto( */ public boolean hasValue(String prop) { return switch (prop) { - case SilvaOracleConstants.ORG_UNIT -> !this.orgUnit.isEmpty(); - case SilvaOracleConstants.CATEGORY -> !this.category.isEmpty(); - case SilvaOracleConstants.STATUS_LIST -> !this.statusList.isEmpty(); - case SilvaOracleConstants.OPENING_IDS -> !this.openingIds.isEmpty(); + case SilvaOracleConstants.ORG_UNIT -> !Objects.isNull(this.orgUnit) && !this.orgUnit.isEmpty(); + case SilvaOracleConstants.CATEGORY -> !Objects.isNull(this.category) && !this.category.isEmpty(); + case SilvaOracleConstants.STATUS_LIST -> !Objects.isNull(this.statusList) && !this.statusList.isEmpty(); + case SilvaOracleConstants.OPENING_IDS -> !Objects.isNull(this.openingIds) && !this.openingIds.isEmpty(); case SilvaOracleConstants.MY_OPENINGS -> !Objects.isNull(this.myOpenings); case SilvaOracleConstants.SUBMITTED_TO_FRPA -> !Objects.isNull(this.submittedToFrpa); case SilvaOracleConstants.DISTURBANCE_DATE_START -> diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/entity/SilvicultureSearchProjection.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/entity/SilvicultureSearchProjection.java new file mode 100644 index 00000000..c8d79b1a --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/entity/SilvicultureSearchProjection.java @@ -0,0 +1,27 @@ +package ca.bc.gov.restapi.results.oracle.entity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public interface SilvicultureSearchProjection { + Long getOpeningId(); + String getOpeningNumber(); + String getCategory(); + String getStatus(); + String getCuttingPermitId(); + String getTimberMark(); + String getCutBlockId(); + BigDecimal getOpeningGrossArea(); + LocalDateTime getDisturbanceStartDate(); + String getForestFileId(); + String getOrgUnitCode(); + String getOrgUnitName(); + String getClientNumber(); + String getClientLocation(); + LocalDateTime getRegenDelayDate(); + LocalDateTime getEarlyFreeGrowingDate(); + LocalDateTime getLateFreeGrowingDate(); + LocalDateTime getUpdateTimestamp(); + String getEntryUserId(); + Long getSubmittedToFrpa108(); +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRepository.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRepository.java index 89ba7e72..369519ee 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRepository.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRepository.java @@ -6,7 +6,9 @@ import ca.bc.gov.restapi.results.oracle.dto.DashboardOrgUnitDto; import ca.bc.gov.restapi.results.oracle.dto.DashboardResultsAuditDto; import ca.bc.gov.restapi.results.oracle.dto.DashboardStockingEventDto; +import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchFiltersDto; import ca.bc.gov.restapi.results.oracle.entity.OpeningEntity; +import ca.bc.gov.restapi.results.oracle.entity.SilvicultureSearchProjection; import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -105,4 +107,186 @@ WHERE raac.RESULTS_AUDIT_ACTION_CODE IN (?1) """, nativeQuery = true) List findAllDashboardActionCodes(List codes); + + @Query( + value = """ + SELECT opening_id, opening_number, category, status, cutting_permit_id, timber_mark,\s + cut_block_id, opening_gross_area, disturbance_start_date, forest_file_id,\s + org_unit_code, org_unit_name, client_number, client_location, regen_delay_date,\s + early_free_growing_date, late_free_growing_date, update_timestamp, entry_user_id,\s + submitted_to_frpa108 + FROM ( + SELECT + o.OPENING_ID AS opening_id, + o.OPENING_NUMBER AS opening_number, + o.OPEN_CATEGORY_CODE AS category, + o.OPENING_STATUS_CODE AS status, + cboa.CUTTING_PERMIT_ID AS cutting_permit_id, + cboa.TIMBER_MARK AS timber_mark, + cboa.CUT_BLOCK_ID AS cut_block_id, + cboa.OPENING_GROSS_AREA AS opening_gross_area, + cboa.DISTURBANCE_START_DATE AS disturbance_start_date, + cboa.FOREST_FILE_ID AS forest_file_id, + ou.ORG_UNIT_CODE AS org_unit_code, + ou.ORG_UNIT_NAME AS org_unit_name, + res.CLIENT_NUMBER AS client_number, + res.CLIENT_LOCN_CODE AS client_location, + ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMRG.LATE_OFFSET_YEARS, 0) * 12)) AS regen_delay_date, + ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.EARLY_OFFSET_YEARS, 0) * 12)) AS early_free_growing_date, + ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS, 0) * 12)) AS late_free_growing_date, + o.UPDATE_TIMESTAMP AS update_timestamp, + o.ENTRY_USERID AS entry_user_id, + COALESCE(sra.SILV_RELIEF_APPLICATION_ID, 0) AS submitted_to_frpa108, + ROW_NUMBER() OVER (PARTITION BY o.OPENING_ID ORDER BY o.UPDATE_TIMESTAMP DESC) AS rn + FROM THE.OPENING o + LEFT JOIN THE.CUT_BLOCK_OPEN_ADMIN cboa ON (cboa.OPENING_ID = o.OPENING_ID) + LEFT JOIN THE.ORG_UNIT ou ON (ou.ORG_UNIT_NO = o.ADMIN_DISTRICT_NO) + LEFT JOIN THE.RESULTS_ELECTRONIC_SUBMISSION res ON (res.RESULTS_SUBMISSION_ID = o.RESULTS_SUBMISSION_ID) + LEFT JOIN THE.SILV_RELIEF_APPLICATION sra ON (sra.ACTIVITY_TREATMENT_UNIT_ID = o.OPENING_ID AND sra.SILV_RELIEF_APPL_STATUS_CODE = 'APP') + LEFT JOIN THE.STOCKING_STANDARD_UNIT ssu ON (ssu.OPENING_ID = o.OPENING_ID) + LEFT JOIN THE.STOCKING_MILESTONE smrg ON (smrg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID AND SMRG.SILV_MILESTONE_TYPE_CODE = 'RG') + LEFT JOIN THE.STOCKING_MILESTONE smfg ON (smfg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID AND smfg.SILV_MILESTONE_TYPE_CODE = 'FG') + ) + WHERE rn = 1 AND ( + NVL(:#{#filter.mainSearchTerm},'NOVALUE') = 'NOVALUE' OR ( + REGEXP_LIKE(:#{#filter.mainSearchTerm}, '^\\d+$') + AND OPENING_ID = TO_NUMBER(:#{#filter.mainSearchTerm}) + ) OR ( + OPENING_NUMBER = :#{#filter.mainSearchTerm} OR + TIMBER_MARK = :#{#filter.mainSearchTerm} OR + FOREST_FILE_ID = :#{#filter.mainSearchTerm} + ) + ) + AND ( + NVL(:#{#filter.orgUnit},'NOVALUE') = 'NOVALUE' OR ORG_UNIT_CODE IN (:#{#filter.orgUnit}) + ) + AND ( + NVL(:#{#filter.category},'NOVALUE') = 'NOVALUE' OR CATEGORY IN (:#{#filter.category}) + ) + AND ( + NVL(:#{#filter.statusList},'NOVALUE') = 'NOVALUE' OR STATUS IN (:#{#filter.statusList}) + ) + AND ( + NVL(:#{#filter.requestUserId},'NOVALUE') = 'NOVALUE' OR ENTRY_USER_ID = :#{#filter.requestUserId} + ) + AND ( + NVL(:#{#filter.submittedToFrpa},'NO') = 'NO' OR ( + NVL(:#{#filter.submittedToFrpa},'NO') = 'YES' AND submitted_to_frpa108 > 0 + ) + ) + AND ( + NVL(:#{#filter.disturbanceDateStart},'NOVALUE') = 'NOVALUE' OR DISTURBANCE_START_DATE >= TO_TIMESTAMP(:#{#filter.disturbanceDateStart},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.disturbanceDateEnd},'NOVALUE') = 'NOVALUE' OR DISTURBANCE_START_DATE <= TO_TIMESTAMP(:#{#filter.disturbanceDateEnd},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.regenDelayDateStart},'NOVALUE') = 'NOVALUE' OR regen_delay_date > TO_TIMESTAMP(:#{#filter.regenDelayDateStart},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.regenDelayDateEnd},'NOVALUE') = 'NOVALUE' OR regen_delay_date < TO_TIMESTAMP(:#{#filter.regenDelayDateEnd},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.freeGrowingDateStart},'NOVALUE') = 'NOVALUE' OR early_free_growing_date > TO_TIMESTAMP(:#{#filter.freeGrowingDateStart},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.freeGrowingDateEnd},'NOVALUE') = 'NOVALUE' OR late_free_growing_date < TO_TIMESTAMP(:#{#filter.freeGrowingDateEnd},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.updateDateStart},'NOVALUE') = 'NOVALUE' OR UPDATE_TIMESTAMP >= TO_TIMESTAMP(:#{#filter.updateDateStart}, 'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.updateDateEnd},'NOVALUE') = 'NOVALUE' OR UPDATE_TIMESTAMP <= TO_TIMESTAMP(:#{#filter.updateDateEnd}, 'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.cuttingPermitId},'NOVALUE') = 'NOVALUE' OR CUTTING_PERMIT_ID = :#{#filter.cuttingPermitId} + ) + AND ( + NVL(:#{#filter.cutBlockId},'NOVALUE') = 'NOVALUE' OR CUT_BLOCK_ID = :#{#filter.cutBlockId} + ) + AND ( + NVL(:#{#filter.timberMark},'NOVALUE') = 'NOVALUE' OR TIMBER_MARK = :#{#filter.timberMark} + ) + AND ( + NVL(:#{#filter.openingIds},'NOVALUE') = 'NOVALUE' OR OPENING_ID IN (:#{#filter.openingIds}) + )""", + countQuery = """ + SELECT count(o.OPENING_ID) as total + FROM THE.OPENING o + LEFT JOIN THE.CUT_BLOCK_OPEN_ADMIN cboa ON (cboa.OPENING_ID = o.OPENING_ID) + LEFT JOIN THE.ORG_UNIT ou ON (ou.ORG_UNIT_NO = o.ADMIN_DISTRICT_NO) + LEFT JOIN THE.RESULTS_ELECTRONIC_SUBMISSION res ON (res.RESULTS_SUBMISSION_ID = o.RESULTS_SUBMISSION_ID) + LEFT JOIN THE.SILV_RELIEF_APPLICATION sra ON (sra.ACTIVITY_TREATMENT_UNIT_ID = o.OPENING_ID AND sra.SILV_RELIEF_APPL_STATUS_CODE = 'APP') + LEFT JOIN THE.STOCKING_STANDARD_UNIT ssu ON (ssu.OPENING_ID = o.OPENING_ID) + LEFT JOIN THE.STOCKING_MILESTONE smrg ON (smrg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID AND SMRG.SILV_MILESTONE_TYPE_CODE = 'RG') + LEFT JOIN THE.STOCKING_MILESTONE smfg ON (smfg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID AND smfg.SILV_MILESTONE_TYPE_CODE = 'FG') + WHERE ( + NVL(:#{#filter.mainSearchTerm},'NOVALUE') = 'NOVALUE' OR ( + REGEXP_LIKE(:#{#filter.mainSearchTerm}, '^\\d+$') + AND o.OPENING_ID = TO_NUMBER(:#{#filter.mainSearchTerm}) + ) OR ( + o.OPENING_NUMBER = :#{#filter.mainSearchTerm} OR + cboa.TIMBER_MARK = :#{#filter.mainSearchTerm} OR + cboa.FOREST_FILE_ID = :#{#filter.mainSearchTerm} + ) + ) + AND ( + NVL(:#{#filter.orgUnit},'NOVALUE') = 'NOVALUE' OR ou.ORG_UNIT_CODE IN (:#{#filter.orgUnit}) + ) + AND ( + NVL(:#{#filter.category},'NOVALUE') = 'NOVALUE' OR o.OPEN_CATEGORY_CODE IN (:#{#filter.category}) + ) + AND ( + NVL(:#{#filter.statusList},'NOVALUE') = 'NOVALUE' OR o.OPENING_STATUS_CODE IN (:#{#filter.statusList}) + ) + AND ( + NVL(:#{#filter.requestUserId},'NOVALUE') = 'NOVALUE' OR o.ENTRY_USERID = :#{#filter.requestUserId} + ) + AND ( + NVL(:#{#filter.submittedToFrpa},'NO') = 'NO' OR ( + NVL(:#{#filter.submittedToFrpa},'NO') = 'YES' AND COALESCE(sra.SILV_RELIEF_APPLICATION_ID, 0) > 0 + ) + ) + AND ( + NVL(:#{#filter.disturbanceDateStart},'NOVALUE') = 'NOVALUE' OR cboa.DISTURBANCE_START_DATE >= TO_TIMESTAMP(:#{#filter.disturbanceDateStart},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.disturbanceDateEnd},'NOVALUE') = 'NOVALUE' OR cboa.DISTURBANCE_START_DATE <= TO_TIMESTAMP(:#{#filter.disturbanceDateEnd},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.regenDelayDateStart},'NOVALUE') = 'NOVALUE' OR ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMRG.LATE_OFFSET_YEARS, 0) * 12)) > TO_TIMESTAMP(:#{#filter.regenDelayDateStart},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.regenDelayDateEnd},'NOVALUE') = 'NOVALUE' OR ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMRG.LATE_OFFSET_YEARS, 0) * 12)) < TO_TIMESTAMP(:#{#filter.regenDelayDateEnd},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.freeGrowingDateStart},'NOVALUE') = 'NOVALUE' OR ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.EARLY_OFFSET_YEARS, 0) * 12)) > TO_TIMESTAMP(:#{#filter.freeGrowingDateStart},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.freeGrowingDateEnd},'NOVALUE') = 'NOVALUE' OR ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS, 0) * 12)) < TO_TIMESTAMP(:#{#filter.freeGrowingDateEnd},'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.updateDateStart},'NOVALUE') = 'NOVALUE' OR ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS, 0) * 12)) >= TO_TIMESTAMP(:#{#filter.updateDateStart}, 'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.updateDateEnd},'NOVALUE') = 'NOVALUE' OR ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS, 0) * 12)) <= TO_TIMESTAMP(:#{#filter.updateDateEnd}, 'YYYY-MM-DD') + ) + AND ( + NVL(:#{#filter.cuttingPermitId},'NOVALUE') = 'NOVALUE' OR cboa.CUTTING_PERMIT_ID = :#{#filter.cuttingPermitId} + ) + AND ( + NVL(:#{#filter.cutBlockId},'NOVALUE') = 'NOVALUE' OR cboa.CUT_BLOCK_ID = :#{#filter.cutBlockId} + ) + AND ( + NVL(:#{#filter.timberMark},'NOVALUE') = 'NOVALUE' OR cboa.TIMBER_MARK = :#{#filter.timberMark} + ) + AND ( + NVL(:#{#filter.openingIds},'NOVALUE') = 'NOVALUE' OR o.OPENING_ID IN (:#{#filter.openingIds}) + )""", + nativeQuery = true + ) + Page searchBy( + OpeningSearchFiltersDto filter, + Pageable pageable + ); } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepository.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepository.java deleted file mode 100644 index 050bc59f..00000000 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepository.java +++ /dev/null @@ -1,563 +0,0 @@ -package ca.bc.gov.restapi.results.oracle.repository; - -import ca.bc.gov.restapi.results.common.pagination.PaginatedResult; -import ca.bc.gov.restapi.results.common.pagination.PaginationParameters; -import ca.bc.gov.restapi.results.oracle.SilvaOracleConstants; -import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchFiltersDto; -import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchResponseDto; -import ca.bc.gov.restapi.results.oracle.enums.OpeningCategoryEnum; -import ca.bc.gov.restapi.results.oracle.enums.OpeningStatusEnum; -import ca.bc.gov.restapi.results.oracle.util.PaginationUtil; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.Query; -import java.math.BigDecimal; -import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -/** This class represents the Openings Search repository database access. */ -@Slf4j -@Component -public class OpeningSearchRepository { - - private final EntityManager em; - - public OpeningSearchRepository(@Qualifier("oracleEntityManagerFactory") EntityManagerFactory emf) { - this.em = emf.createEntityManager(); - } - - /** - * Search Opening with filters. - * - * @param filtersDto All possible filters to search openings. - * @param pagination Pagination parameters with pagination settings. - * @return Paginated result with found records, if any. - */ - public PaginatedResult searchOpeningQuery( - OpeningSearchFiltersDto filtersDto, PaginationParameters pagination) { - - final String sqlQuery = createNativeSqlQuery(filtersDto); - final Query query = setQueryParameters(filtersDto, sqlQuery); - - query.setFirstResult(pagination.perPage() * pagination.page()); - query.setMaxResults(pagination.perPage()); - - List result = query.getResultList(); - int lastPage = PaginationUtil.getLastPage(result.size(), pagination.perPage()); - - PaginatedResult paginatedResult = new PaginatedResult<>(); - paginatedResult.setPageIndex(pagination.page()); - paginatedResult.setPerPage(pagination.perPage()); - paginatedResult.setTotalPages(lastPage); - - if (result.isEmpty() || pagination.page() > lastPage) { - log.info("No search openings result for the search given page index and size!"); - paginatedResult.setData(List.of()); - paginatedResult.setTotalPages(result.isEmpty() ? 0 : lastPage); - paginatedResult.setHasNextPage(false); - return paginatedResult; - } - - int startIndex = PaginationUtil.getStartIndex(pagination.page(), pagination.perPage()); - int endIndex = PaginationUtil.getEndIndex(startIndex, pagination.perPage(), result.size()); - - List resultList = - buildResultListDto(result.subList(startIndex, endIndex)); - - paginatedResult.setData(resultList); - paginatedResult.setPerPage(resultList.size()); - paginatedResult.setTotalPages(lastPage); - paginatedResult.setHasNextPage(pagination.page() < lastPage && pagination.page() > 0); - - return paginatedResult; - } - - private List buildResultListDto(List result) { - List resultList = new ArrayList<>(); - - for (Object obj : result) { - int index = 0; - if (obj.getClass().isArray()) { - Object[] row = (Object[]) obj; - OpeningSearchResponseDto searchOpeningDto = new OpeningSearchResponseDto(); - if (row.length > index) { - searchOpeningDto.setOpeningId(getValue(Integer.class, row[index++], "openingId")); - } - - if (row.length > index) { - String openingNumber = getValue(String.class, row[index++], "openingNumber"); - if (!Objects.isNull(openingNumber)) { - searchOpeningDto.setOpeningNumber(openingNumber.trim()); - } - } - - if (row.length > index) { - String category = getValue(String.class, row[index++], "category"); - searchOpeningDto.setCategory(OpeningCategoryEnum.of(category)); - } - - if (row.length > index) { - String status = getValue(String.class, row[index++], "status"); - searchOpeningDto.setStatus(OpeningStatusEnum.of(status)); - } - - if (row.length > index) { - String cuttingPermitId = getValue(String.class, row[index++], "cuttingPermitId"); - searchOpeningDto.setCuttingPermitId(cuttingPermitId); - } - - if (row.length > index) { - String timberMark = getValue(String.class, row[index++], "timberMark"); - searchOpeningDto.setTimberMark(timberMark); - } - - if (row.length > index) { - String cutBlockId = getValue(String.class, row[index++], "cutBlockId"); - searchOpeningDto.setCutBlockId(cutBlockId); - } - - if (row.length > index) { - BigDecimal openingGrossAreaHa = - getValue(BigDecimal.class, row[index++], "openingGrossAreaHa"); - searchOpeningDto.setOpeningGrossAreaHa(openingGrossAreaHa); - } - - if (row.length > index) { - Timestamp startDate = getValue(Timestamp.class, row[index++], "disturbanceStartDate"); - if (!Objects.isNull(startDate)) { - searchOpeningDto.setDisturbanceStartDate(startDate.toLocalDateTime()); - } - } - - if (row.length > index) { - String forestFileId = getValue(String.class, row[index++], "forestFileId"); - searchOpeningDto.setForestFileId(forestFileId); - } - - if (row.length > index) { - String orgUnitCode = getValue(String.class, row[index++], "orgUnitCode"); - searchOpeningDto.setOrgUnitCode(orgUnitCode); - } - - if (row.length > index) { - String orgUnitName = getValue(String.class, row[index++], "orgUnitName"); - searchOpeningDto.setOrgUnitName(orgUnitName); - } - - if (row.length > index) { - String clientNumber = getValue(String.class, row[index++], "clientNumber"); - searchOpeningDto.setClientNumber(clientNumber); - } - - if (row.length > index) { - String clientLocation = getValue(String.class, row[index++], "clientLocation"); - searchOpeningDto.setClientLocation(clientLocation); - } - - if (row.length > index) { - Timestamp regenDelayDate = getValue(Timestamp.class, row[index++], "regenDelayDate"); - if (!Objects.isNull(regenDelayDate)) { - searchOpeningDto.setRegenDelayDate(regenDelayDate.toLocalDateTime()); - } - } - - if (row.length > index) { - Timestamp earlyDate = getValue(Timestamp.class, row[index++], "earlyFreeGrowingDate"); - if (!Objects.isNull(earlyDate)) { - searchOpeningDto.setEarlyFreeGrowingDate(earlyDate.toLocalDateTime()); - } - } - - if (row.length > index) { - Timestamp dateDate = getValue(Timestamp.class, row[index++], "lateFreeGrowingDate"); - if (!Objects.isNull(dateDate)) { - searchOpeningDto.setLateFreeGrowingDate(dateDate.toLocalDateTime()); - } - } - - if (row.length > index) { - Timestamp updateTimestamp = getValue(Timestamp.class, row[index++], "updateTimestamp"); - searchOpeningDto.setUpdateTimestamp(updateTimestamp.toLocalDateTime()); - } - - if (row.length > index) { - String entryUserId = getValue(String.class, row[index++], "entryUserId"); - searchOpeningDto.setEntryUserId(entryUserId); - } - - if (row.length > index) { - BigDecimal silvaReliefAppId = - getValue(BigDecimal.class, row[index++], "submittedToFrpa108"); - boolean submittedApp = silvaReliefAppId.compareTo(BigDecimal.ZERO) > 0; - searchOpeningDto.setSubmittedToFrpa(submittedApp); - if (submittedApp) { - searchOpeningDto.setSilvaReliefAppId(silvaReliefAppId.longValue()); - } - } - - resultList.add(searchOpeningDto); - } - } - - return resultList; - } - - private T getValue(Class clazz, Object obj, String name) { - if (Objects.isNull(obj)) { - log.debug("{} is null", name); - return null; - } - if (clazz.equals(Integer.class) && obj instanceof Integer intVal) { - log.debug("Integer {}={}", name, intVal); - return clazz.cast(obj); - } - if (clazz.equals(String.class) && obj instanceof String strVal) { - log.debug("String {}={}", name, strVal); - return clazz.cast(obj); - } - if (clazz.equals(LocalDateTime.class) && obj instanceof LocalDateTime localDateTime) { - log.debug("LocalDateTime {}={}", name, localDateTime); - return clazz.cast(obj); - } - if (clazz.equals(BigDecimal.class) && obj instanceof BigDecimal bigDecValue) { - log.debug("BigDecimal {}={}", name, bigDecValue); - return clazz.cast(obj); - } - if (clazz.equals(Timestamp.class) && obj instanceof Timestamp timestamp) { - log.debug("Timestamp {}={}", name, timestamp); - return clazz.cast(obj); - } - log.info("Unhandled class {} for {}", obj.getClass().getName(), name); - return null; - } - - private Query setQueryParameters(OpeningSearchFiltersDto filtersDto, String nativeQuery) { - Query query = em.createNativeQuery(nativeQuery); - - // set parameters - if (filtersDto.hasValue(SilvaOracleConstants.MAIN_SEARCH_TERM)) { - boolean itsNumeric = filtersDto.getMainSearchTerm().replaceAll("[0-9]", "").isEmpty(); - if (itsNumeric) { - log.info("Setting mainSearchTerm as numeric filter value"); - // Opening id or File id - query.setParameter("openingOrFile", filtersDto.getMainSearchTerm()); - } else { - log.info("Setting mainSearchTerm as non-numeric filter value"); - // Opening number, Timber Mark, or Forest File Id - query.setParameter("numberOrTimber", filtersDto.getMainSearchTerm()); - } - } - - // 1. Org Unit code - if (filtersDto.hasValue(SilvaOracleConstants.ORG_UNIT)) { - log.info("Setting orgUnit filter values"); - // No need to set value since the query already dit it. Didn't work set through named param - } - // 2. Category code - if (filtersDto.hasValue(SilvaOracleConstants.CATEGORY)) { - log.info("Setting category filter values"); - // No need to set value since the query already dit it. Didn't work set through named param - } - // 3. Status list codes - if (filtersDto.hasValue(SilvaOracleConstants.STATUS_LIST)) { - - log.info("Setting statusList filter values"); - // No need to set value since the query already dit it. Didn't work set through named param - } - // similarly for openingIds - if (filtersDto.hasValue(SilvaOracleConstants.OPENING_IDS)) { - log.info("Setting openingIds filter values"); - // No need to set value since the query already dit it. Didn't work set through - // named param - } - // 4. User entry id - if (filtersDto.hasValue(SilvaOracleConstants.MY_OPENINGS)) { - log.info("Setting myOpenings filter value"); - query.setParameter("entryUserId", filtersDto.getRequestUserId()); - } - // 5. Submitted to FRPA Section 108 - if (filtersDto.hasValue(SilvaOracleConstants.SUBMITTED_TO_FRPA)) { - log.info("Setting submitted to FRPA filter!"); - // No need to set value since the query already dit it. - } - // 6. Disturbance start date - if (filtersDto.hasValue(SilvaOracleConstants.DISTURBANCE_DATE_START)) { - log.info("Setting disturbanceDateStart filter value"); - query.setParameter("disturbStartDate", filtersDto.getDisturbanceDateStart()); - } - // 7. Disturbance end date - if (filtersDto.hasValue(SilvaOracleConstants.DISTURBANCE_DATE_END)) { - log.info("Setting disturbanceDateEnd filter value"); - query.setParameter("disturbEndDate", filtersDto.getDisturbanceDateEnd()); - } - // 8. Regen delay start date - if (filtersDto.hasValue(SilvaOracleConstants.REGEN_DELAY_DATE_START)) { - log.info("Setting regenDelayDateStart filter value"); - query.setParameter("regenDelayDateStart", filtersDto.getRegenDelayDateStart()); - } - // 9. Regen delay end date - if (filtersDto.hasValue(SilvaOracleConstants.REGEN_DELAY_DATE_END)) { - log.info("Setting regenDelayDateEnd filter value"); - query.setParameter("regenDelayDateEnd", filtersDto.getRegenDelayDateEnd()); - } - // 10. Free growing start date - if (filtersDto.hasValue(SilvaOracleConstants.FREE_GROWING_DATE_START)) { - log.info("Setting freeGrowingDateStart filter value"); - query.setParameter("freeGrowingDateStart", filtersDto.getFreeGrowingDateStart()); - } - // 11. Free growing end date - if (filtersDto.hasValue(SilvaOracleConstants.FREE_GROWING_DATE_END)) { - log.info("Setting freeGrowingDateEnd filter value"); - query.setParameter("freeGrowingDateEnd", filtersDto.getFreeGrowingDateEnd()); - } - // 12. Update date start - if (filtersDto.hasValue(SilvaOracleConstants.UPDATE_DATE_START)) { - log.info("Setting updateDateStart filter value"); - query.setParameter("updateStartDate", filtersDto.getUpdateDateStart()); - } - // 13. Update date end - if (filtersDto.hasValue(SilvaOracleConstants.UPDATE_DATE_END)) { - log.info("Setting updateDateEnd filter value"); - query.setParameter("updateEndDate", filtersDto.getUpdateDateEnd()); - } - // 14. Cutting permit id - if (filtersDto.hasValue(SilvaOracleConstants.CUTTING_PERMIT_ID)) { - log.info("Setting cuttingPermitId filter value"); - query.setParameter("cuttingPermitId", filtersDto.getCuttingPermitId()); - } - // 15. Cut block id - if (filtersDto.hasValue(SilvaOracleConstants.CUT_BLOCK_ID)) { - log.info("Setting cutBlockId filter value"); - query.setParameter("cutBlockId", filtersDto.getCutBlockId()); - } - // 16. Timber mark - if (filtersDto.hasValue(SilvaOracleConstants.TIMBER_MARK)) { - log.info("Setting timberMark filter value"); - query.setParameter("timberMark", filtersDto.getTimberMark()); - } - - return query; - } - - private String createNativeSqlQuery(OpeningSearchFiltersDto filtersDto) { - StringBuilder builder = new StringBuilder(); - builder.append("SELECT o.OPENING_ID AS openingId"); - builder.append(",o.OPENING_NUMBER AS openingNumber"); - builder.append(",o.OPEN_CATEGORY_CODE AS category"); - builder.append(",o.OPENING_STATUS_CODE AS status"); - builder.append(",cboa.CUTTING_PERMIT_ID AS cuttingPermitId"); - builder.append(",cboa.TIMBER_MARK AS timberMark"); - builder.append(",cboa.CUT_BLOCK_ID AS cutBlockId"); - builder.append(",cboa.OPENING_GROSS_AREA AS openingGrossArea"); - builder.append(",cboa.DISTURBANCE_START_DATE AS disturbanceStartDate"); - builder.append(",cboa.FOREST_FILE_ID AS forestFileId"); - builder.append(",ou.ORG_UNIT_CODE AS orgUnitCode"); - builder.append(",ou.ORG_UNIT_NAME AS orgUnitName"); - builder.append(",res.CLIENT_NUMBER AS clientNumber"); - builder.append(",res.CLIENT_LOCN_CODE AS clientLocation"); - - String sql; - sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMRG.LATE_OFFSET_YEARS,0)*12))"; - builder.append(sql).append(" AS regenDelayDate"); - - sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.EARLY_OFFSET_YEARS,0)*12))"; - builder.append(sql).append(" AS earlyFreeGrowingDate"); - - sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS,0)*12))"; - builder.append(sql).append(" AS lateFreeGrowingDate"); - - builder.append(",o.UPDATE_TIMESTAMP AS updateTimestamp"); - builder.append(",o.ENTRY_USERID AS entryUserId"); - builder.append(",COALESCE(sra.SILV_RELIEF_APPLICATION_ID, 0) AS submittedToFrpa108 "); - builder.append("FROM THE.OPENING o "); - builder.append("LEFT JOIN THE.CUT_BLOCK_OPEN_ADMIN cboa ON (cboa.OPENING_ID = o.OPENING_ID)"); - builder.append("LEFT JOIN THE.ORG_UNIT ou ON (ou.ORG_UNIT_NO = o.ADMIN_DISTRICT_NO)"); - builder.append("LEFT JOIN the.RESULTS_ELECTRONIC_SUBMISSION res ON ("); - builder.append(" res.RESULTS_SUBMISSION_ID = o.RESULTS_SUBMISSION_ID)"); - builder.append("LEFT JOIN THE.CLIENT_ACRONYM ca ON (ca.CLIENT_NUMBER = res.CLIENT_NUMBER) "); - builder.append("LEFT JOIN THE.ACTIVITY_TREATMENT_UNIT atu ON (atu.OPENING_ID = o.OPENING_ID)"); - builder.append("LEFT JOIN THE.SILV_RELIEF_APPLICATION sra ON ("); - builder.append(" sra.ACTIVITY_TREATMENT_UNIT_ID = atu.ACTIVITY_TREATMENT_UNIT_ID"); - builder.append(" AND sra.SILV_RELIEF_APPL_STATUS_CODE = 'APP') "); - builder.append("LEFT JOIN THE.STOCKING_STANDARD_UNIT ssu ON (ssu.OPENING_ID = o.OPENING_ID) "); - builder.append("LEFT JOIN THE.STOCKING_MILESTONE smrg ON ("); - builder.append(" smrg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID"); - builder.append(" AND SMRG.SILV_MILESTONE_TYPE_CODE = 'RG') "); - builder.append("LEFT JOIN THE.STOCKING_MILESTONE smfg ON ("); - builder.append(" smfg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID"); - builder.append(" AND smfg.SILV_MILESTONE_TYPE_CODE = 'FG') "); - builder.append("WHERE 1=1 "); - - /* Filters */ - // List of openings from the openingIds of the filterDto object for the recent openings - if (filtersDto.hasValue(SilvaOracleConstants.OPENING_IDS)) { - String openingIds = filtersDto.getOpeningIds().stream().map(String::valueOf).collect( - Collectors.joining(",")); - log.info("Filter for openingIds detected! openingIds={}", openingIds); - builder.append(String.format("AND o.OPENING_ID IN (%s) ", openingIds)); - } - // 0. Main number filter [opening_id, opening_number, timber_mark, file_id] - // if it's a number, filter by openingId or fileId, otherwise filter by timber mark and opening - // number - if (filtersDto.hasValue(SilvaOracleConstants.MAIN_SEARCH_TERM)) { - log.info("Filter mainSearchTerm detected! mainSearchTerm={}", filtersDto.getMainSearchTerm()); - boolean itsNumeric = filtersDto.getMainSearchTerm().replaceAll("[0-9]", "").isEmpty(); - if (itsNumeric) { - log.info("Filter mainSearchTerm it's numeric! Looking for Opening ID"); - // Opening id - builder.append("AND o.OPENING_ID = :openingOrFile "); - } else { - log.info( - "Filter mainSearchTerm NOT numeric! Looking for Opening Number, Timber Mark, or Forest" - + " File Id"); - // Opening number, Timber Mark, or Forest File Id - builder.append("AND ("); - builder.append("o.OPENING_NUMBER = :numberOrTimber "); - builder.append(" or cboa.TIMBER_MARK = :numberOrTimber "); - builder.append(" or cboa.FOREST_FILE_ID = :numberOrTimber)"); - } - } - - // 1. Org Unit code - if (filtersDto.hasValue(SilvaOracleConstants.ORG_UNIT)) { - String orgUnits = String.join(",", filtersDto.getOrgUnit()); - log.info("Filter orgUnit detected! orgUnit={}", orgUnits); - builder.append(String.format("AND ou.ORG_UNIT_CODE IN (%s) ", orgUnits)); - } - // 2. Category code - if (filtersDto.hasValue(SilvaOracleConstants.CATEGORY)) { - String categories = String.join(",", filtersDto.getCategory()); - log.info("Filter category detected! statusList={}", categories); - builder.append(String.format("AND o.OPEN_CATEGORY_CODE IN (%s) ", categories)); - } - // 3. Status code - if (filtersDto.hasValue(SilvaOracleConstants.STATUS_LIST)) { - String statuses = String.join(",", filtersDto.getStatusList()); - log.info("Filter statusList detected! statusList={}", statuses); - builder.append(String.format("AND o.OPENING_STATUS_CODE IN (%s) ", statuses)); - } - // 4. My openings - if (filtersDto.hasValue(SilvaOracleConstants.MY_OPENINGS)) { - log.info("Filter myOpenings detected! entryUserId={}", filtersDto.getRequestUserId()); - builder.append("AND o.ENTRY_USERID = :entryUserId "); - } - // 5. Submitted to FRPA - if (filtersDto.hasValue(SilvaOracleConstants.SUBMITTED_TO_FRPA)) { - Boolean value = filtersDto.getSubmittedToFrpa(); - if (Boolean.TRUE.equals(value)) { - log.info( - "Filter submitted to FRPA detected! submitted={}", filtersDto.getSubmittedToFrpa()); - builder.append("AND sra.SILV_RELIEF_APPLICATION_ID IS NOT NULL "); - } - } - // 6. Disturbance start date - if (filtersDto.hasValue(SilvaOracleConstants.DISTURBANCE_DATE_START)) { - log.info( - "Filter disturbanceDateStart detected! date={}", filtersDto.getDisturbanceDateStart()); - builder.append( - "AND cboa.DISTURBANCE_START_DATE >= to_timestamp(:disturbStartDate, 'YYYY-MM-DD') "); - } - // 7. Disturbance end date - if (filtersDto.hasValue(SilvaOracleConstants.DISTURBANCE_DATE_END)) { - log.info("Filter disturbanceDateEnd detected! date={}", filtersDto.getDisturbanceDateEnd()); - builder.append( - "AND cboa.DISTURBANCE_START_DATE <= to_timestamp(:disturbEndDate, 'YYYY-MM-DD') "); - } - // 8. Regen delay start date - if (filtersDto.hasValue(SilvaOracleConstants.REGEN_DELAY_DATE_START)) { - log.info("Filter regenDelayDateStart detected! date={}", filtersDto.getRegenDelayDateStart()); - builder.append("AND ADD_MONTHS(cboa.DISTURBANCE_START_DATE, "); - builder.append("COALESCE(SMRG.LATE_OFFSET_YEARS,0)*12) "); - builder.append("> to_timestamp(:regenDelayDateStart,'YYYY-MM-DD')"); - } - // 9. Regen delay end date - if (filtersDto.hasValue(SilvaOracleConstants.REGEN_DELAY_DATE_END)) { - log.info("Filter regenDelayDateEnd detected! date={}", filtersDto.getRegenDelayDateEnd()); - builder.append("AND ADD_MONTHS(cboa.DISTURBANCE_START_DATE, "); - builder.append("COALESCE(SMRG.LATE_OFFSET_YEARS,0)*12) "); - builder.append("< to_timestamp(:regenDelayDateEnd,'YYYY-MM-DD')"); - } - // 10. Free growing start date - if (filtersDto.hasValue(SilvaOracleConstants.FREE_GROWING_DATE_START)) { - log.info( - "Filter freeGrowingDateStart detected! date={}", filtersDto.getFreeGrowingDateStart()); - builder.append("AND ADD_MONTHS(cboa.DISTURBANCE_START_DATE, "); - builder.append("COALESCE(SMFG.EARLY_OFFSET_YEARS,0)*12) "); - builder.append("> to_timestamp(:freeGrowingDateStart,'YYYY-MM-DD')"); - } - // 11. Free growing end date - if (filtersDto.hasValue(SilvaOracleConstants.FREE_GROWING_DATE_END)) { - log.info("Filter freeGrowingDateEnd detected! date={}", filtersDto.getFreeGrowingDateEnd()); - builder.append("AND ADD_MONTHS(cboa.DISTURBANCE_START_DATE, "); - builder.append("COALESCE(SMFG.LATE_OFFSET_YEARS,0)*12) "); - builder.append("< to_timestamp(:freeGrowingDateEnd, 'YYYY-MM-DD')"); - } - // 12. Update date start - if (filtersDto.hasValue(SilvaOracleConstants.UPDATE_DATE_START)) { - log.info("Filter updateDateStart detected! date={}", filtersDto.getUpdateDateStart()); - builder.append("AND o.UPDATE_TIMESTAMP >= to_timestamp(:updateStartDate, 'YYYY-MM-DD') "); - } - // 13. Update date end - if (filtersDto.hasValue(SilvaOracleConstants.UPDATE_DATE_END)) { - log.info("Filter updateDateEnd detected! date={}", filtersDto.getUpdateDateEnd()); - builder.append("AND o.UPDATE_TIMESTAMP <= to_timestamp(:updateEndDate, 'YYYY-MM-DD') "); - } - // 14. Cutting permit id - if (filtersDto.hasValue(SilvaOracleConstants.CUTTING_PERMIT_ID)) { - log.info( - "Filter cuttingPermitId detected! cuttingPermitId={}", filtersDto.getCuttingPermitId()); - builder.append("AND cboa.CUTTING_PERMIT_ID = :cuttingPermitId "); - } - // 15. Cut block id - if (filtersDto.hasValue(SilvaOracleConstants.CUT_BLOCK_ID)) { - log.info("Filter cutBlockId detected! cutBlockId={}", filtersDto.getCutBlockId()); - builder.append("AND cboa.CUT_BLOCK_ID = :cutBlockId "); - } - // 16. Timber mark - if (filtersDto.hasValue(SilvaOracleConstants.TIMBER_MARK)) { - log.info("Filter timberMark detected! timberMark={}", filtersDto.getTimberMark()); - builder.append("AND cboa.TIMBER_MARK = :timberMark "); - } - - /* Group by - to avoid duplications */ - builder.append("GROUP BY o.OPENING_ID "); - builder.append(",o.OPENING_NUMBER "); - builder.append(",o.OPEN_CATEGORY_CODE "); - builder.append(",o.OPENING_STATUS_CODE "); - builder.append(",cboa.CUTTING_PERMIT_ID "); - builder.append(",cboa.TIMBER_MARK "); - builder.append(",cboa.CUT_BLOCK_ID "); - builder.append(",cboa.OPENING_GROSS_AREA "); - builder.append(",cboa.DISTURBANCE_START_DATE "); - builder.append(",cboa.FOREST_FILE_ID "); - builder.append(",ou.ORG_UNIT_CODE "); - builder.append(",ou.ORG_UNIT_NAME "); - builder.append(",res.CLIENT_NUMBER "); - builder.append(",res.CLIENT_LOCN_CODE "); - - sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMRG.LATE_OFFSET_YEARS, 0) * 12)) "; - builder.append(sql); - - sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.EARLY_OFFSET_YEARS, 0) * 12)) "; - builder.append(sql); - - sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS, 0) * 12)) "; - builder.append(sql); - - builder.append(",o.UPDATE_TIMESTAMP "); - builder.append(",o.ENTRY_USERID "); - builder.append(",COALESCE(sra.SILV_RELIEF_APPLICATION_ID, 0) "); - - // Order by - builder.append("ORDER BY o.OPENING_ID DESC"); - - return builder.toString(); - } -} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningService.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningService.java index ab357f69..2c630eac 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningService.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningService.java @@ -13,11 +13,12 @@ import ca.bc.gov.restapi.results.oracle.dto.RecentOpeningDto; import ca.bc.gov.restapi.results.oracle.entity.CutBlockOpenAdminEntity; import ca.bc.gov.restapi.results.oracle.entity.OpeningEntity; +import ca.bc.gov.restapi.results.oracle.entity.SilvicultureSearchProjection; import ca.bc.gov.restapi.results.oracle.enums.OpeningCategoryEnum; import ca.bc.gov.restapi.results.oracle.enums.OpeningStatusEnum; import ca.bc.gov.restapi.results.oracle.repository.OpeningRepository; -import ca.bc.gov.restapi.results.oracle.repository.OpeningSearchRepository; import ca.bc.gov.restapi.results.postgres.service.UserOpeningService; +import jakarta.transaction.Transactional; import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; @@ -25,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -45,7 +47,6 @@ public class OpeningService { private final OpeningRepository openingRepository; private final CutBlockOpenAdminService cutBlockOpenAdminService; private final LoggedUserService loggedUserService; - private final OpeningSearchRepository openingSearchRepository; private final ForestClientApiProvider forestClientApiProvider; private final UserOpeningService userOpeningService; @@ -103,33 +104,51 @@ public PaginatedResult getRecentOpenings(PaginationParameters * @param pagination An instance of {@link PaginationParameters} with pagination settings. * @return Paginated result with found content. */ + @Transactional public PaginatedResult openingSearch( OpeningSearchFiltersDto filtersDto, PaginationParameters pagination) { log.info( - "Search Openings with page index {} and page size {}", + "Search Openings with page index {} and page size {} with filters {}", pagination.page(), - pagination.perPage()); + pagination.perPage(), + filtersDto + ); if (pagination.perPage() > SilvaConstants.MAX_PAGE_SIZE_OPENING_SEARCH) { throw new MaxPageSizeException(SilvaConstants.MAX_PAGE_SIZE_OPENING_SEARCH); } // Set the user in the filter, if required - if (filtersDto.hasValue(SilvaOracleConstants.MY_OPENINGS)) { + if (filtersDto.hasValue(SilvaOracleConstants.MY_OPENINGS) && Boolean.TRUE.equals( + filtersDto.getMyOpenings())) { String userId = loggedUserService.getLoggedUserId().replace("@", "\\"); if (!userId.startsWith("IDIR")) { userId = "BCEID" + userId.substring(5); } filtersDto.setRequestUserId(userId); } - - PaginatedResult result = - openingSearchRepository.searchOpeningQuery(filtersDto, pagination); + Page searchResultPage = + openingRepository.searchBy( + filtersDto, + pagination.toPageable(Sort.by("opening_id").descending()) + ); + + PaginatedResult result = new PaginatedResult<>(); + result.setTotalItems(searchResultPage.getTotalElements()); + result.setPageIndex(pagination.page()); + result.setPerPage(pagination.perPage()); + result.setTotalPages(searchResultPage.getTotalPages()); + result.setData(searchResultPage + .get() + .map(mapToSearchResponse()) + .toList() + ); return fetchClientAcronyms(fetchFavorites(result)); } - private PaginatedResult fetchClientAcronyms(PaginatedResult result) { + private PaginatedResult fetchClientAcronyms( + PaginatedResult result) { Map forestClientsMap = new HashMap<>(); List clientNumbers = @@ -229,4 +248,37 @@ private List createDtoFromEntity( return recentOpeningDtos; } + + + private static Function mapToSearchResponse() { + return projection -> + new OpeningSearchResponseDto( + projection.getOpeningId().intValue(), + projection.getOpeningNumber(), + OpeningCategoryEnum.of(projection.getCategory()), + OpeningStatusEnum.of(projection.getStatus()), + projection.getCuttingPermitId(), + projection.getTimberMark(), + projection.getCutBlockId(), + projection.getOpeningGrossArea(), + projection.getDisturbanceStartDate(), + projection.getOrgUnitCode(), + projection.getOrgUnitName(), + projection.getClientNumber(), + projection.getClientLocation(), + "", + "", + projection.getRegenDelayDate(), + projection.getEarlyFreeGrowingDate(), + projection.getLateFreeGrowingDate(), + projection.getUpdateTimestamp(), + projection.getEntryUserId(), + projection.getSubmittedToFrpa108() > 0, + projection.getForestFileId(), + projection.getSubmittedToFrpa108(), + null, + false + ); + } + } diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointTest.java index 3514f8f4..10d40aa2 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointTest.java @@ -73,7 +73,7 @@ void openingSearch_happyPath_shouldSucceed() throws Exception { .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.pageIndex").value("0")) - .andExpect(jsonPath("$.perPage").value("1")) + .andExpect(jsonPath("$.perPage").value("5")) .andExpect(jsonPath("$.totalPages").value("1")) .andExpect(jsonPath("$.hasNextPage").value("false")) .andExpect(jsonPath("$.data[0].openingId").value(response.getOpeningId())) diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepositoryTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepositoryTest.java deleted file mode 100644 index 6a629696..00000000 --- a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepositoryTest.java +++ /dev/null @@ -1,676 +0,0 @@ -package ca.bc.gov.restapi.results.oracle.repository; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -import ca.bc.gov.restapi.results.common.pagination.PaginatedResult; -import ca.bc.gov.restapi.results.common.pagination.PaginationParameters; -import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchFiltersDto; -import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchResponseDto; -import ca.bc.gov.restapi.results.oracle.enums.OpeningCategoryEnum; -import ca.bc.gov.restapi.results.oracle.enums.OpeningStatusEnum; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.LockModeType; -import jakarta.persistence.Parameter; -import jakarta.persistence.Query; -import jakarta.persistence.TemporalType; -import java.math.BigDecimal; -import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -@ExtendWith(SpringExtension.class) -class OpeningSearchRepositoryTest { - - @Mock EntityManager entityManager; - @Mock EntityManagerFactory entityManagerFactory; - - private OpeningSearchRepository openingSearchRepository; - - private OpeningSearchFiltersDto mockFilter( - List orgUnit, - List category, - List statusList, - Boolean myOpenings, - Boolean submittedToFrpa, - String disturbanceDateStart, - String disturbanceDateEnd, - String regenDelayDateStart, - String regenDelayDateEnd, - String freeGrowingDateStart, - String freeGrowingDateEnd, - String updateDateStart, - String updateDateEnd, - String cuttingPermitId, - String cutBlockId, - String timberMark, - String mainSearchTerm) { - return new OpeningSearchFiltersDto( - orgUnit, - category, - statusList, - myOpenings, - submittedToFrpa, - disturbanceDateStart, - disturbanceDateEnd, - regenDelayDateStart, - regenDelayDateEnd, - freeGrowingDateStart, - freeGrowingDateEnd, - updateDateStart, - updateDateEnd, - cuttingPermitId, - cutBlockId, - timberMark, - mainSearchTerm); - } - - private OpeningSearchFiltersDto mockOrgUnit(List orgUnit) { - return mockFilter( - orgUnit, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null); - } - - private OpeningSearchFiltersDto mockAllFilters() { - return mockFilter( - List.of("DCR"), - List.of("FTML"), - List.of("APP"), - true, - false, - "2023-01-01", - "2023-05-31", - "2020-01-01", - "2023-12-31", - "2024-01-01", - "2024-06-30", - "2024-01-01", - "2025-01-01", - "207", - "HS", - "47/206", - "407"); - } - - private OpeningSearchFiltersDto mockMainFilter(String mainSearchTerm) { - return mockFilter( - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - mainSearchTerm); - } - - private Query mockQuery(List resultList) { - Query query = - new Query() { - - @Override - public List getResultList() { - return resultList; - } - - @Override - public Object getSingleResult() { - return null; - } - - @Override - public int executeUpdate() { - return 0; - } - - @Override - public Query setMaxResults(int maxResult) { - return this; - } - - @Override - public int getMaxResults() { - return 0; - } - - @Override - public Query setFirstResult(int startPosition) { - return this; - } - - @Override - public int getFirstResult() { - return 0; - } - - @Override - public Query setHint(String hintName, Object value) { - return this; - } - - @Override - public Map getHints() { - return Map.of(); - } - - @Override - public Query setParameter(Parameter param, T value) { - return this; - } - - @Override - public Query setParameter( - Parameter param, Calendar value, TemporalType temporalType) { - return this; - } - - @Override - public Query setParameter(Parameter param, Date value, TemporalType temporalType) { - return this; - } - - @Override - public Query setParameter(String name, Object value) { - return this; - } - - @Override - public Query setParameter(String name, Calendar value, TemporalType temporalType) { - return this; - } - - @Override - public Query setParameter(String name, Date value, TemporalType temporalType) { - return this; - } - - @Override - public Query setParameter(int position, Object value) { - return this; - } - - @Override - public Query setParameter(int position, Calendar value, TemporalType temporalType) { - return this; - } - - @Override - public Query setParameter(int position, Date value, TemporalType temporalType) { - return this; - } - - @Override - public Set> getParameters() { - return Set.of(); - } - - @Override - public Parameter getParameter(String name) { - return null; - } - - @Override - public Parameter getParameter(String name, Class type) { - return null; - } - - @Override - public Parameter getParameter(int position) { - return null; - } - - @Override - public Parameter getParameter(int position, Class type) { - return null; - } - - @Override - public boolean isBound(Parameter param) { - return true; - } - - @Override - public T getParameterValue(Parameter param) { - return null; - } - - @Override - public Object getParameterValue(String name) { - return null; - } - - @Override - public Object getParameterValue(int position) { - return null; - } - - @Override - public Query setFlushMode(FlushModeType flushMode) { - return this; - } - - @Override - public FlushModeType getFlushMode() { - return FlushModeType.AUTO; - } - - @Override - public Query setLockMode(LockModeType lockMode) { - return this; - } - - @Override - public LockModeType getLockMode() { - return LockModeType.NONE; - } - - @Override - public T unwrap(Class cls) { - return null; - } - }; - return query; - } - - @BeforeEach - void setup() { - - when(entityManagerFactory.createEntityManager()).thenReturn(entityManager); - - openingSearchRepository = new OpeningSearchRepository(entityManagerFactory); - } - - @Test - @DisplayName("Search opening query main filter numeric should succeed") - void searchOpeningQuery_mainFilterNumeric_shouldSucceed() { - OpeningSearchFiltersDto filters = mockMainFilter("407"); - - PaginationParameters pagination = new PaginationParameters(0, 10); - - Integer openingId = 123456789; - String openingNumber = "589"; - OpeningCategoryEnum category = OpeningCategoryEnum.FTML; - OpeningStatusEnum status = OpeningStatusEnum.APP; - String cuttingPermitId = null; - String timberMark = null; - String cutBlockId = null; - BigDecimal openingGrossArea = new BigDecimal("11"); - Timestamp disturbanceStartDate = Timestamp.valueOf(LocalDateTime.now()); - String forestFileId = "TFL47"; - String orgUnitCode = null; - String orgUnitName = null; - String clientNumber = null; - String clientLocation = null; - Timestamp regenDelay = null; - Timestamp earlyFreeGrowing = null; - Timestamp lateFreeGrowing = null; - Timestamp updateTimestamp = Timestamp.valueOf(LocalDateTime.now()); - String userId = "TEST"; - BigDecimal submittedToFrpa108 = new BigDecimal("33"); - List resultList = new ArrayList<>(1); - resultList.add( - new Object[] { - openingId, - openingNumber, - category.getCode(), - status.getCode(), - cuttingPermitId, - timberMark, - cutBlockId, - openingGrossArea, - disturbanceStartDate, - forestFileId, - orgUnitCode, - orgUnitName, - clientNumber, - clientLocation, - regenDelay, - earlyFreeGrowing, - lateFreeGrowing, - updateTimestamp, - userId, - submittedToFrpa108, - }); - Query query = mockQuery(resultList); - when(entityManager.createNativeQuery(anyString())).thenReturn(query); - - PaginatedResult result = - openingSearchRepository.searchOpeningQuery(filters, pagination); - - Assertions.assertNotNull(result); - Assertions.assertEquals(0, result.getPageIndex()); - Assertions.assertEquals(1, result.getPerPage()); - Assertions.assertEquals(1, result.getTotalPages()); - Assertions.assertEquals(1, result.getData().size()); - - OpeningSearchResponseDto dto = result.getData().get(0); - Assertions.assertEquals(openingId, dto.getOpeningId()); - Assertions.assertEquals(openingNumber, dto.getOpeningNumber()); - Assertions.assertEquals(category, dto.getCategory()); - Assertions.assertEquals(status, dto.getStatus()); - Assertions.assertEquals(cuttingPermitId, dto.getCuttingPermitId()); - Assertions.assertEquals(timberMark, dto.getTimberMark()); - Assertions.assertEquals(cutBlockId, dto.getCutBlockId()); - Assertions.assertEquals(openingGrossArea, dto.getOpeningGrossAreaHa()); - Assertions.assertEquals(disturbanceStartDate.toLocalDateTime(), dto.getDisturbanceStartDate()); - Assertions.assertEquals(forestFileId, dto.getForestFileId()); - Assertions.assertEquals(orgUnitCode, dto.getOrgUnitCode()); - Assertions.assertEquals(orgUnitName, dto.getOrgUnitName()); - Assertions.assertEquals(clientNumber, dto.getClientNumber()); - Assertions.assertNull(dto.getRegenDelayDate()); - Assertions.assertNull(dto.getEarlyFreeGrowingDate()); - Assertions.assertNull(dto.getLateFreeGrowingDate()); - Assertions.assertEquals(userId, dto.getEntryUserId()); - Assertions.assertEquals(true, dto.getSubmittedToFrpa()); - Assertions.assertEquals(33L, dto.getSilvaReliefAppId()); - Assertions.assertFalse(result.isHasNextPage()); - } - - @Test - @DisplayName("Search opening query main filter string should succeed") - void searchOpeningQuery_mainFilterString_shouldSucceed() { - OpeningSearchFiltersDto filters = mockMainFilter("EM2184"); - - PaginationParameters pagination = new PaginationParameters(0, 10); - - Integer openingId = 123456789; - String openingNumber = "589"; - OpeningCategoryEnum category = OpeningCategoryEnum.FTML; - OpeningStatusEnum status = OpeningStatusEnum.APP; - String cuttingPermitId = null; - String timberMark = "EM2184"; - String cutBlockId = null; - BigDecimal openingGrossArea = new BigDecimal("11"); - Timestamp disturbanceStartDate = Timestamp.valueOf(LocalDateTime.now()); - ; - String forestFileId = "TFL47"; - String orgUnitCode = null; - String orgUnitName = null; - String clientNumber = null; - String clientLocation = null; - Timestamp regenDelay = null; - Timestamp earlyFreeGrowing = null; - Timestamp lateFreeGrowing = null; - Timestamp updateTimestamp = Timestamp.valueOf(LocalDateTime.now()); - String userId = "TEST"; - BigDecimal submittedToFrpa = BigDecimal.ZERO; - List resultList = new ArrayList<>(1); - resultList.add( - new Object[] { - openingId, - openingNumber, - category.getCode(), - status.getCode(), - cuttingPermitId, - timberMark, - cutBlockId, - openingGrossArea, - disturbanceStartDate, - forestFileId, - orgUnitCode, - orgUnitName, - clientNumber, - clientLocation, - regenDelay, - earlyFreeGrowing, - lateFreeGrowing, - updateTimestamp, - userId, - submittedToFrpa, - }); - Query query = mockQuery(resultList); - when(entityManager.createNativeQuery(anyString())).thenReturn(query); - - PaginatedResult result = - openingSearchRepository.searchOpeningQuery(filters, pagination); - - Assertions.assertNotNull(result); - Assertions.assertEquals(0, result.getPageIndex()); - Assertions.assertEquals(1, result.getPerPage()); - Assertions.assertEquals(1, result.getTotalPages()); - Assertions.assertEquals(1, result.getData().size()); - - OpeningSearchResponseDto dto = result.getData().get(0); - Assertions.assertEquals(openingId, dto.getOpeningId()); - Assertions.assertEquals(openingNumber, dto.getOpeningNumber()); - Assertions.assertEquals(category, dto.getCategory()); - Assertions.assertEquals(status, dto.getStatus()); - Assertions.assertEquals(cuttingPermitId, dto.getCuttingPermitId()); - Assertions.assertEquals(timberMark, dto.getTimberMark()); - Assertions.assertEquals(cutBlockId, dto.getCutBlockId()); - Assertions.assertEquals(openingGrossArea, dto.getOpeningGrossAreaHa()); - Assertions.assertEquals(disturbanceStartDate.toLocalDateTime(), dto.getDisturbanceStartDate()); - Assertions.assertEquals(forestFileId, dto.getForestFileId()); - Assertions.assertEquals(orgUnitCode, dto.getOrgUnitCode()); - Assertions.assertEquals(orgUnitName, dto.getOrgUnitName()); - Assertions.assertEquals(clientNumber, dto.getClientNumber()); - Assertions.assertEquals(clientLocation, dto.getClientLocation()); - Assertions.assertNull(dto.getClientName()); - Assertions.assertNull(dto.getClientAcronym()); - Assertions.assertNull(dto.getRegenDelayDate()); - Assertions.assertNull(dto.getEarlyFreeGrowingDate()); - Assertions.assertNull(dto.getLateFreeGrowingDate()); - Assertions.assertEquals(userId, dto.getEntryUserId()); - Assertions.assertEquals(false, dto.getSubmittedToFrpa()); - Assertions.assertFalse(result.isHasNextPage()); - } - - @Test - @DisplayName("Search opening query org unit filter should succeed") - void searchOpeningQuery_orgUnitFilter_shouldSucceed() { - OpeningSearchFiltersDto filters = mockOrgUnit(List.of("DCR")); - - PaginationParameters pagination = new PaginationParameters(0, 10); - - Integer openingId = 123456789; - String openingNumber = "589"; - OpeningCategoryEnum category = OpeningCategoryEnum.FTML; - OpeningStatusEnum status = OpeningStatusEnum.SUB; - String cuttingPermitId = null; - String timberMark = "EM2184"; - String cutBlockId = null; - BigDecimal openingGrossArea = new BigDecimal("11"); - Timestamp disturbanceStartDate = Timestamp.valueOf(LocalDateTime.now()); - String forestFileId = "TFL47"; - String orgUnitCode = "DCR"; - String orgUnitName = null; - String clientNumber = null; - String clientLocation = null; - Timestamp regenDelay = null; - Timestamp earlyFreeGrowing = null; - Timestamp lateFreeGrowing = null; - Timestamp updateTimestamp = Timestamp.valueOf(LocalDateTime.now()); - String userId = "TEST"; - BigDecimal submittedToFrpa = BigDecimal.ZERO; - List resultList = new ArrayList<>(1); - resultList.add( - new Object[] { - openingId, - openingNumber, - category.getCode(), - status.getCode(), - cuttingPermitId, - timberMark, - cutBlockId, - openingGrossArea, - disturbanceStartDate, - forestFileId, - orgUnitCode, - orgUnitName, - clientNumber, - clientLocation, - regenDelay, - earlyFreeGrowing, - lateFreeGrowing, - updateTimestamp, - userId, - submittedToFrpa, - }); - Query query = mockQuery(resultList); - when(entityManager.createNativeQuery(anyString())).thenReturn(query); - - PaginatedResult result = - openingSearchRepository.searchOpeningQuery(filters, pagination); - - Assertions.assertNotNull(result); - Assertions.assertEquals(0, result.getPageIndex()); - Assertions.assertEquals(1, result.getPerPage()); - Assertions.assertEquals(1, result.getTotalPages()); - Assertions.assertEquals(1, result.getData().size()); - - OpeningSearchResponseDto dto = result.getData().get(0); - Assertions.assertEquals(openingId, dto.getOpeningId()); - Assertions.assertEquals(openingNumber, dto.getOpeningNumber()); - Assertions.assertEquals(category, dto.getCategory()); - Assertions.assertEquals(status, dto.getStatus()); - Assertions.assertEquals(cuttingPermitId, dto.getCuttingPermitId()); - Assertions.assertEquals(timberMark, dto.getTimberMark()); - Assertions.assertEquals(cutBlockId, dto.getCutBlockId()); - Assertions.assertEquals(openingGrossArea, dto.getOpeningGrossAreaHa()); - Assertions.assertEquals(disturbanceStartDate.toLocalDateTime(), dto.getDisturbanceStartDate()); - Assertions.assertEquals(forestFileId, dto.getForestFileId()); - Assertions.assertEquals(orgUnitCode, dto.getOrgUnitCode()); - Assertions.assertEquals(orgUnitName, dto.getOrgUnitName()); - Assertions.assertEquals(clientNumber, dto.getClientNumber()); - Assertions.assertEquals(clientLocation, dto.getClientLocation()); - Assertions.assertNull(dto.getClientName()); - Assertions.assertNull(dto.getClientAcronym()); - Assertions.assertNull(dto.getRegenDelayDate()); - Assertions.assertNull(dto.getEarlyFreeGrowingDate()); - Assertions.assertNull(dto.getLateFreeGrowingDate()); - Assertions.assertEquals(userId, dto.getEntryUserId()); - Assertions.assertEquals(false, dto.getSubmittedToFrpa()); - Assertions.assertFalse(result.isHasNextPage()); - } - - @Test - @DisplayName("Search opening query all filters should succeed") - void searchOpeningQuery_allFilters_shouldSucceed() { - OpeningSearchFiltersDto filters = mockAllFilters(); - - PaginationParameters pagination = new PaginationParameters(0, 10); - - Integer openingId = 123456789; - String openingNumber = "589"; - OpeningCategoryEnum category = OpeningCategoryEnum.of("FTML"); - OpeningStatusEnum status = OpeningStatusEnum.of(filters.getStatusList().get(0)); - String cuttingPermitId = "123"; - String timberMark = "EM2184"; - String cutBlockId = "456"; - BigDecimal openingGrossArea = new BigDecimal("11"); - Timestamp disturbanceStartDate = Timestamp.valueOf(LocalDateTime.now()); - String forestFileId = "TFL47"; - String orgUnitCode = "DCR"; - String orgUnitName = "Org Name"; - String clientNumber = "00012797"; - String clientLocation = "00"; - Timestamp regenDelay = null; - Timestamp earlyFreeGrowing = Timestamp.valueOf(LocalDateTime.now()); - Timestamp lateFreeGrowing = Timestamp.valueOf(LocalDateTime.now()); - Timestamp updateTimestamp = Timestamp.valueOf(LocalDateTime.now()); - String entryUserId = filters.getRequestUserId(); - BigDecimal submittedToFrpa = BigDecimal.ZERO; - List resultList = new ArrayList<>(1); - resultList.add( - new Object[] { - openingId, - openingNumber, - category.getCode(), - status.getCode(), - cuttingPermitId, - timberMark, - cutBlockId, - openingGrossArea, - disturbanceStartDate, - forestFileId, - orgUnitCode, - orgUnitName, - clientNumber, - clientLocation, - regenDelay, - earlyFreeGrowing, - lateFreeGrowing, - updateTimestamp, - entryUserId, - submittedToFrpa, - }); - Query query = mockQuery(resultList); - when(entityManager.createNativeQuery(anyString())).thenReturn(query); - - PaginatedResult result = - openingSearchRepository.searchOpeningQuery(filters, pagination); - - Assertions.assertNotNull(result); - Assertions.assertEquals(0, result.getPageIndex()); - Assertions.assertEquals(1, result.getPerPage()); - Assertions.assertEquals(1, result.getTotalPages()); - Assertions.assertEquals(1, result.getData().size()); - - OpeningSearchResponseDto dto = result.getData().get(0); - Assertions.assertEquals(openingId, dto.getOpeningId()); - Assertions.assertEquals(openingNumber, dto.getOpeningNumber()); - Assertions.assertEquals(category, dto.getCategory()); - Assertions.assertEquals(status, dto.getStatus()); - Assertions.assertEquals(cuttingPermitId, dto.getCuttingPermitId()); - Assertions.assertEquals(timberMark, dto.getTimberMark()); - Assertions.assertEquals(cutBlockId, dto.getCutBlockId()); - Assertions.assertEquals(openingGrossArea, dto.getOpeningGrossAreaHa()); - Assertions.assertEquals(disturbanceStartDate.toLocalDateTime(), dto.getDisturbanceStartDate()); - Assertions.assertEquals(forestFileId, dto.getForestFileId()); - Assertions.assertEquals(orgUnitCode, dto.getOrgUnitCode()); - Assertions.assertEquals(orgUnitName, dto.getOrgUnitName()); - Assertions.assertEquals(clientNumber, dto.getClientNumber()); - Assertions.assertEquals(clientLocation, dto.getClientLocation()); - Assertions.assertNull(dto.getRegenDelayDate()); - Assertions.assertEquals(earlyFreeGrowing, Timestamp.valueOf(dto.getEarlyFreeGrowingDate())); - Assertions.assertEquals(lateFreeGrowing, Timestamp.valueOf(dto.getLateFreeGrowingDate())); - Assertions.assertEquals(entryUserId, dto.getEntryUserId()); - Assertions.assertEquals(false, dto.getSubmittedToFrpa()); - Assertions.assertFalse(result.isHasNextPage()); - } - - @Test - @DisplayName("Search opening query no records found should succeed") - void searchOpeningQuery_noRecordsFound_shouldSucceed() { - OpeningSearchFiltersDto filters = mockOrgUnit(List.of("AAA")); - - PaginationParameters pagination = new PaginationParameters(0, 10); - - List resultList = new ArrayList<>(); - Query query = mockQuery(resultList); - when(entityManager.createNativeQuery(anyString())).thenReturn(query); - - PaginatedResult result = - openingSearchRepository.searchOpeningQuery(filters, pagination); - - Assertions.assertNotNull(result); - Assertions.assertEquals(0, result.getPageIndex()); - Assertions.assertEquals(10, result.getPerPage()); - Assertions.assertEquals(0, result.getTotalPages()); - Assertions.assertTrue(result.getData().isEmpty()); - Assertions.assertFalse(result.isHasNextPage()); - } -} diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningServiceTest.java index f7918785..986b0dd6 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningServiceTest.java @@ -86,7 +86,7 @@ void openingSearch_fileId_shouldSucceed() { Assertions.assertNotNull(result); Assertions.assertEquals(0, result.getPageIndex()); - Assertions.assertEquals(1, result.getPerPage()); + Assertions.assertEquals(10, result.getPerPage()); Assertions.assertEquals(1, result.getTotalPages()); Assertions.assertEquals(1, result.getData().size()); Assertions.assertEquals(103, result.getData().get(0).getOpeningId()); @@ -144,7 +144,7 @@ void openingSearch_orgUnit_shouldSucceed() { Assertions.assertNotNull(result); Assertions.assertEquals(0, result.getPageIndex()); - Assertions.assertEquals(1, result.getPerPage()); + Assertions.assertEquals(10, result.getPerPage()); Assertions.assertEquals(1, result.getTotalPages()); Assertions.assertEquals(1, result.getData().size()); Assertions.assertEquals(103, result.getData().get(0).getOpeningId()); diff --git a/backend/src/test/resources/migration/oracle/V001__oracle_init.sql b/backend/src/test/resources/migration/oracle/V001__oracle_init.sql index 6ebb03fb..bbf3a124 100644 --- a/backend/src/test/resources/migration/oracle/V001__oracle_init.sql +++ b/backend/src/test/resources/migration/oracle/V001__oracle_init.sql @@ -751,4 +751,47 @@ SELECT fc.client_number client_number , fc.revision_count revision_count FROM forest_client fc WHERE client_acronym IS NOT NULL - WITH READ ONLY; \ No newline at end of file + WITH READ ONLY; + + +CREATE OR REPLACE VIEW SILV_SILVICULTURE_SEARCH_V +AS + SELECT opening_id, opening_number, category, status, cutting_permit_id, timber_mark, + cut_block_id, opening_gross_area, disturbance_start_date, forest_file_id, + org_unit_code, org_unit_name, client_number, client_location, regen_delay_date, + early_free_growing_date, late_free_growing_date, update_timestamp, entry_user_id, + submitted_to_frpa108 +FROM ( + SELECT + o.OPENING_ID AS opening_id, + o.OPENING_NUMBER AS opening_number, + o.OPEN_CATEGORY_CODE AS category, + o.OPENING_STATUS_CODE AS status, + cboa.CUTTING_PERMIT_ID AS cutting_permit_id, + cboa.TIMBER_MARK AS timber_mark, + cboa.CUT_BLOCK_ID AS cut_block_id, + cboa.OPENING_GROSS_AREA AS opening_gross_area, + cboa.DISTURBANCE_START_DATE AS disturbance_start_date, + cboa.FOREST_FILE_ID AS forest_file_id, + ou.ORG_UNIT_CODE AS org_unit_code, + ou.ORG_UNIT_NAME AS org_unit_name, + res.CLIENT_NUMBER AS client_number, + res.CLIENT_LOCN_CODE AS client_location, + ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMRG.LATE_OFFSET_YEARS, 0) * 12)) AS regen_delay_date, + ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.EARLY_OFFSET_YEARS, 0) * 12)) AS early_free_growing_date, + ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS, 0) * 12)) AS late_free_growing_date, + o.UPDATE_TIMESTAMP AS update_timestamp, + o.ENTRY_USERID AS entry_user_id, + COALESCE(sra.SILV_RELIEF_APPLICATION_ID, 0) AS submitted_to_frpa108, + ROW_NUMBER() OVER (PARTITION BY o.OPENING_ID ORDER BY o.UPDATE_TIMESTAMP DESC) AS rn + FROM THE.OPENING o + LEFT JOIN THE.CUT_BLOCK_OPEN_ADMIN cboa ON (cboa.OPENING_ID = o.OPENING_ID) + LEFT JOIN THE.ORG_UNIT ou ON (ou.ORG_UNIT_NO = o.ADMIN_DISTRICT_NO) + LEFT JOIN THE.RESULTS_ELECTRONIC_SUBMISSION res ON (res.RESULTS_SUBMISSION_ID = o.RESULTS_SUBMISSION_ID) + LEFT JOIN THE.SILV_RELIEF_APPLICATION sra ON (sra.ACTIVITY_TREATMENT_UNIT_ID = o.OPENING_ID AND sra.SILV_RELIEF_APPL_STATUS_CODE = 'APP') + LEFT JOIN THE.STOCKING_STANDARD_UNIT ssu ON (ssu.OPENING_ID = o.OPENING_ID) + LEFT JOIN THE.STOCKING_MILESTONE smrg ON (smrg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID AND SMRG.SILV_MILESTONE_TYPE_CODE = 'RG') + LEFT JOIN THE.STOCKING_MILESTONE smfg ON (smfg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID AND smfg.SILV_MILESTONE_TYPE_CODE = 'FG') +) +WHERE rn = 1 +WITH READ ONLY; \ No newline at end of file diff --git a/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx b/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx index ae98bfe0..1c7a2d66 100644 --- a/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx +++ b/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx @@ -152,7 +152,7 @@ const OpeningsSearchTab: React.FC = () => { handleCheckboxChange={handleCheckboxChange} toggleSpatial={toggleSpatial} showSpatial={showSpatial} - totalItems={(data?.perPage ?? 0) * (data?.totalPages ?? 0)} + totalItems={data?.totalItems || 0} setOpeningIds={setSelectedOpeningIds} /> )}