Skip to content

Commit 4297479

Browse files
feat: #2174 Enable family lot filter for test activity search (#2260)
1 parent d7b2e2f commit 4297479

File tree

9 files changed

+74
-35
lines changed

9 files changed

+74
-35
lines changed

frontend/src/views/CONSEP/TestingActivities/TestSearch/AdvancedFilter/index.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,21 @@ const AdvancedFilters = ({
8989
searchField: keyof ActivitySearchRequest,
9090
value: boolean
9191
) => {
92-
setSearchParams((prev) => ({
93-
...prev,
94-
[searchField]: value
95-
}));
96-
};
92+
setSearchParams((prev) => {
93+
// If unchecked, remove the field by setting undefined
94+
if (!value) {
95+
return {
96+
...prev,
97+
[searchField]: undefined
98+
};
99+
}
97100

101+
return {
102+
...prev,
103+
[searchField]: true
104+
};
105+
});
106+
};
98107
const handleRequestIdChange = (e: ChangeEvent<HTMLInputElement>) => {
99108
const { value } = e.target;
100109

@@ -110,6 +119,7 @@ const AdvancedFilters = ({
110119
...prev,
111120
requestId: value
112121
}));
122+
113123
setValidateSearch((prev) => ({
114124
...prev,
115125
requestId: {
@@ -331,8 +341,9 @@ const AdvancedFilters = ({
331341
completeStatus: undefined,
332342
acceptanceStatus: undefined,
333343
seedlotClass: undefined,
334-
includeHistoricalTests: false,
335-
germTestsOnly: false
344+
includeHistoricalTests: undefined,
345+
germTestsOnly: undefined,
346+
familyLotsOnly: undefined
336347
}));
337348

338349
setValidateSearch((prev) => ({
@@ -391,19 +402,27 @@ const AdvancedFilters = ({
391402
<Checkbox
392403
id="historical-tests"
393404
labelText="Historical tests"
394-
checked={searchParams.includeHistoricalTests}
405+
checked={!!searchParams.includeHistoricalTests}
395406
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
396407
handleCheckboxesChanges('includeHistoricalTests', e.target.checked);
397408
}}
398409
/>
399410
<Checkbox
400411
id="germination-only"
401412
labelText="Germination tests only"
402-
checked={searchParams.germTestsOnly}
413+
checked={!!searchParams.germTestsOnly}
403414
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
404415
handleCheckboxesChanges('germTestsOnly', e.target.checked);
405416
}}
406417
/>
418+
<Checkbox
419+
id="family-lots-only"
420+
labelText="Family lot # only"
421+
checked={!!searchParams.familyLotsOnly}
422+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
423+
handleCheckboxesChanges('familyLotsOnly', e.target.checked);
424+
}}
425+
/>
407426
</CheckboxGroup>
408427
</Column>
409428
</Row>

frontend/src/views/CONSEP/TestingActivities/TestSearch/constants.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ export const getTestingActivityListColumns = (): MRT_ColumnDef<TestingSearchResp
154154
accessorKey: 'seedlotDisplay',
155155
header: 'Lot #',
156156
enableEditing: false,
157-
size: 118,
158-
...tableCellProps(118, 'left')
157+
size: 125,
158+
...tableCellProps(125, 'left')
159159
},
160160
{
161161
accessorKey: 'requestItem',

frontend/src/views/CONSEP/TestingActivities/TestSearch/definitions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type ActivitySearchRequest = {
2626
completeStatus?: number;
2727
acceptanceStatus?: number;
2828
seedlotClass?: 'A' | 'B';
29+
familyLotsOnly?: boolean;
2930
};
3031

3132
export type ValidationErrorType = {

oracle-api/src/main/java/ca/bc/gov/oracleapi/ConsepOracleQueryConstants.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class ConsepOracleQueryConstants {
2121
FROM ActivitySearchResultEntity a
2222
WHERE (:testType IS NULL OR a.activityTypeCd = :testType)
2323
AND (:activityId IS NULL OR a.activityId = :activityId)
24-
AND (:lotNumbers IS NULL OR a.seedlotDisplay IN :lotNumbers)
24+
AND (:lotNumbers IS NULL OR UPPER(a.seedlotDisplay) IN :lotNumbers)
2525
AND (:germinatorTrayId IS NULL OR a.germinatorTrayId = :germinatorTrayId)
2626
AND (:seedWithdrawalStartDate IS NULL OR a.seedWithdrawalDate >= :seedWithdrawalStartDate)
2727
AND (:seedWithdrawalEndDate IS NULL OR a.seedWithdrawalDate <= :seedWithdrawalEndDate)
@@ -47,6 +47,7 @@ public class ConsepOracleQueryConstants {
4747
AND (:completeStatus IS NULL OR a.testCompleteInd = :completeStatus)
4848
AND (:acceptanceStatus IS NULL OR a.acceptResultInd = :acceptanceStatus)
4949
AND (:geneticClassCode IS NULL OR a.geneticClassCode = :geneticClassCode)
50+
AND (:familyLotsOnly IS NULL OR :familyLotsOnly = false OR LOWER(a.seedlotDisplay) LIKE 'f%')
5051
""";
5152

5253
public static final Set<String> ALLOWED_SORT_FIELDS = Set.of(

oracle-api/src/main/java/ca/bc/gov/oracleapi/dto/consep/ActivitySearchRequestDto.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ public record ActivitySearchRequestDto(
131131
@Schema(description = "Genetic class code: A = A Class, B = B Class",
132132
allowableValues = {"A", "B"})
133133
@Pattern(regexp = "[AB]", message = "geneticClassCode must be 'A' or 'B'")
134-
String geneticClassCode
134+
String geneticClassCode,
135+
136+
@Schema(description = "Filter for family lot numbers only (lot numbers starting with 'F')")
137+
Boolean familyLotsOnly
135138
) {
136139
}

oracle-api/src/main/java/ca/bc/gov/oracleapi/repository/consep/ActivitySearchRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Page<ActivitySearchResultEntity> searchTestingActivities(
4646
@Param("completeStatus") Integer completeStatus,
4747
@Param("acceptanceStatus") Integer acceptanceStatus,
4848
@Param("geneticClassCode") String geneticClassCode,
49+
@Param("familyLotsOnly") Boolean familyLotsOnly,
4950
Pageable pageable
5051
);
5152
}

oracle-api/src/main/java/ca/bc/gov/oracleapi/service/consep/ActivitySearchService.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.time.LocalDate;
99
import java.time.LocalDateTime;
1010
import java.time.LocalTime;
11+
import java.util.List;
1112
import lombok.RequiredArgsConstructor;
1213
import org.springframework.data.domain.Page;
1314
import org.springframework.data.domain.PageRequest;
@@ -41,12 +42,11 @@ public ActivitySearchPageResponseDto searchTestingActivities(
4142
String sortBy,
4243
String sortDirection) {
4344

44-
Sort sort = Sort.by("seedlotSample").ascending()
45-
.and(Sort.by("actualBeginDtTm").ascending());
45+
Sort sort = Sort.by("seedlotSample").ascending().and(Sort.by("actualBeginDtTm").ascending());
4646

4747
if (sortBy != null && !sortBy.isBlank()) {
48-
sort = Sort.by("desc".equalsIgnoreCase(sortDirection) ? Sort.Direction.DESC : Sort.Direction.ASC,
49-
sortBy);
48+
sort = Sort.by("desc".equalsIgnoreCase(sortDirection)
49+
? Sort.Direction.DESC : Sort.Direction.ASC, sortBy);
5050
}
5151
if (pageable.isPaged()) {
5252
pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
@@ -67,10 +67,18 @@ public ActivitySearchPageResponseDto searchTestingActivities(
6767
LocalDateTime revisedStartDateTo = toEndOfDay(activitySearchRequestDto.revisedStartDateTo());
6868
LocalDateTime revisedEndDateFrom = toStartOfDay(activitySearchRequestDto.revisedEndDateFrom());
6969
LocalDateTime revisedEndDateTo = toEndOfDay(activitySearchRequestDto.revisedEndDateTo());
70+
List<String> upperLotNumbers = null;
71+
if (activitySearchRequestDto.lotNumbers() != null
72+
&& !activitySearchRequestDto.lotNumbers().isEmpty()) {
73+
upperLotNumbers = activitySearchRequestDto.lotNumbers()
74+
.stream()
75+
.map(String::toUpperCase)
76+
.toList();
77+
}
7078

7179
// Fetch paginated results from repository
7280
Page<ActivitySearchResultEntity> results = activitySearchRepository.searchTestingActivities(
73-
activitySearchRequestDto.lotNumbers(),
81+
upperLotNumbers,
7482
activitySearchRequestDto.testType(),
7583
activitySearchRequestDto.activityId(),
7684
activitySearchRequestDto.germinatorTrayId(),
@@ -97,6 +105,7 @@ public ActivitySearchPageResponseDto searchTestingActivities(
97105
activitySearchRequestDto.completeStatus(),
98106
activitySearchRequestDto.acceptanceStatus(),
99107
activitySearchRequestDto.geneticClassCode(),
108+
activitySearchRequestDto.familyLotsOnly(),
100109
pageable
101110
);
102111

oracle-api/src/test/java/ca/bc/gov/oracleapi/endpoint/consep/ActivitySearchEndpointTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ private ActivitySearchRequestDto createDummyRequestDto() {
136136
return new ActivitySearchRequestDto(List.of("00098"), "D1", "D1", null,
137137
LocalDate.of(1997, 10, 1), LocalDate.of(1998, 10, 31), false, true, "RTS19981299A", "RTS",
138138
1998, null, "STD", "A", "PLI", null, null, null, null, null, null, null, null, -1, -1, -1,
139-
"A");
139+
"A", false);
140140
}
141141

142142
private ActivitySearchResponseDto createDummyResponseDto() {

oracle-api/src/test/java/ca/bc/gov/oracleapi/service/consep/ActivitySearchServiceTest.java

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.mockito.ArgumentMatchers.any;
55
import static org.mockito.ArgumentMatchers.eq;
6-
import static org.mockito.Mockito.*;
6+
import static org.mockito.Mockito.times;
7+
import static org.mockito.Mockito.verify;
8+
import static org.mockito.Mockito.when;
79

810
import ca.bc.gov.oracleapi.dto.consep.ActivitySearchPageResponseDto;
911
import ca.bc.gov.oracleapi.dto.consep.ActivitySearchRequestDto;
@@ -55,7 +57,7 @@ class ActivitySearchServiceTest {
5557
actualBeginDateTo, actualEndDateFrom, actualEndDateTo, revisedStartDateFrom,
5658
revisedStartDateTo, revisedEndDateFrom, revisedEndDateTo;
5759
private LocalDateTime actualBeginDtTm, actualEndDtTm, seedWithdrawalDate, revisedEndDt;
58-
private Boolean includeHistoricalTests, germTestsOnly;
60+
private Boolean includeHistoricalTests, germTestsOnly, familyLotsOnly;
5961
private Sort defaultSort;
6062

6163
@BeforeEach
@@ -90,6 +92,7 @@ void setUp() {
9092
completeStatus = -1; // testCompleteInd
9193
acceptanceStatus = -1; // acceptResultInd
9294
geneticClassCode = "A";
95+
familyLotsOnly = false;
9396

9497
activitySearchRequestDto = new ActivitySearchRequestDto(
9598
lotNumbers, testType, activityId, germinatorTrayId,
@@ -100,7 +103,7 @@ void setUp() {
100103
actualEndDateFrom, actualEndDateTo,
101104
revisedStartDateFrom, revisedStartDateTo,
102105
revisedEndDateFrom, revisedEndDateTo,
103-
germTrayAssignment, completeStatus, acceptanceStatus, geneticClassCode
106+
germTrayAssignment, completeStatus, acceptanceStatus, geneticClassCode, familyLotsOnly
104107
);
105108

106109
// Search return result
@@ -195,7 +198,8 @@ void shouldReturnMappedResults() {
195198
expectedActualEndFrom, expectedActualEndTo,
196199
expectedRevisedStartDateFrom, expectedRevisedStartDateTo,
197200
expectedRevisedEndDateFrom, expectedRevisedEndDateTo,
198-
germTrayAssignment, completeStatus, acceptanceStatus, geneticClassCode, pageable
201+
germTrayAssignment, completeStatus, acceptanceStatus, geneticClassCode, familyLotsOnly,
202+
pageable
199203
)).thenReturn(mockPage);
200204

201205
ActivitySearchPageResponseDto pageResponse = activitySearchService.searchTestingActivities(
@@ -246,7 +250,8 @@ void shouldReturnMappedResults() {
246250
expectedActualEndFrom, expectedActualEndTo,
247251
expectedRevisedStartDateFrom, expectedRevisedStartDateTo,
248252
expectedRevisedEndDateFrom, expectedRevisedEndDateTo,
249-
germTrayAssignment, completeStatus, acceptanceStatus, geneticClassCode, pageable
253+
germTrayAssignment, completeStatus, acceptanceStatus, geneticClassCode, familyLotsOnly,
254+
pageable
250255
);
251256
}
252257

@@ -257,7 +262,7 @@ void shouldReturnAllResultsWhenNoFiltersProvided() {
257262
null, null, null, null, null, null,
258263
null, null, null, null, null, null,
259264
null, null, null, null, null, null,
260-
null, null, null
265+
null, null, null, null
261266
);
262267

263268
Pageable pageable = PageRequest.of(0, 10, defaultSort);
@@ -273,7 +278,7 @@ void shouldReturnAllResultsWhenNoFiltersProvided() {
273278
null, null, null, null, null, null,
274279
null, null, null, null, null, null,
275280
null, null, null, null, null, null,
276-
null, null, null, pageable
281+
null, null, null, null, pageable
277282
)).thenReturn(mockPage);
278283

279284
List<ActivitySearchResponseDto> result = activitySearchService
@@ -298,7 +303,7 @@ void shouldReturnEmptyListWhenNoResultsFound() {
298303
null, null, null, null, 2025, null,
299304
null, null, null, null, null, null,
300305
null, null, null, null, null, null,
301-
null, null, null
306+
null, null, null, null
302307
);
303308

304309
Pageable pageable = PageRequest.of(0, 10, defaultSort);
@@ -315,7 +320,7 @@ void shouldReturnEmptyListWhenNoResultsFound() {
315320
null, null, null, null, 2025, null,
316321
null, null, null, null, null, null,
317322
null, null, null, null, null, null,
318-
null, null, null, pageable
323+
null, null, null, null, pageable
319324
)).thenReturn(emptyPage);
320325

321326
List<ActivitySearchResponseDto> result = activitySearchService
@@ -337,7 +342,7 @@ void shouldSortByValidFieldAscending() {
337342

338343
when(activitySearchRepository.searchTestingActivities(any(), any(), any(), any(), any(), any(),
339344
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
340-
any(), any(), any(), any(), any(), any(), any(), any(), eq(sortedPageable))).thenReturn(
345+
any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(sortedPageable))).thenReturn(
341346
mockPage);
342347

343348
ActivitySearchPageResponseDto result =
@@ -348,7 +353,7 @@ void shouldSortByValidFieldAscending() {
348353

349354
verify(activitySearchRepository, times(1)).searchTestingActivities(any(), any(), any(), any(),
350355
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
351-
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(sortedPageable));
356+
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(sortedPageable));
352357
}
353358

354359
@Test
@@ -364,7 +369,7 @@ void shouldSortByValidFieldDescending() {
364369

365370
when(activitySearchRepository.searchTestingActivities(any(), any(), any(), any(), any(), any(),
366371
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
367-
any(), any(), any(), any(), any(), any(), any(), any(), eq(sortedPageable))).thenReturn(
372+
any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(sortedPageable))).thenReturn(
368373
mockPage);
369374

370375
ActivitySearchPageResponseDto result =
@@ -382,7 +387,7 @@ void shouldUseDefaultPageableWhenSortByNull() {
382387

383388
when(activitySearchRepository.searchTestingActivities(any(), any(), any(), any(), any(), any(),
384389
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
385-
any(), any(), any(), any(), any(), any(), any(), any(), eq(pageable))).thenReturn(mockPage);
390+
any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(pageable))).thenReturn(mockPage);
386391

387392
List<ActivitySearchResponseDto> result =
388393
activitySearchService.searchTestingActivities(activitySearchRequestDto, pageable, null,
@@ -399,7 +404,7 @@ void shouldUseDefaultPageableWhenSortByBlank() {
399404

400405
when(activitySearchRepository.searchTestingActivities(any(), any(), any(), any(), any(), any(),
401406
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
402-
any(), any(), any(), any(), any(), any(), any(), any(), eq(pageable))).thenReturn(mockPage);
407+
any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(pageable))).thenReturn(mockPage);
403408

404409
List<ActivitySearchResponseDto> result =
405410
activitySearchService.searchTestingActivities(activitySearchRequestDto, pageable, " ",
@@ -418,7 +423,7 @@ void shouldPassThroughInvalidSortField() {
418423

419424
when(activitySearchRepository.searchTestingActivities(any(), any(), any(), any(), any(), any(),
420425
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
421-
any(), any(), any(), any(), any(), any(), any(), any(), any(Pageable.class))).thenReturn(
426+
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(Pageable.class))).thenReturn(
422427
mockPage);
423428

424429
activitySearchService.searchTestingActivities(activitySearchRequestDto, defaultPageable, sortBy,
@@ -428,7 +433,7 @@ void shouldPassThroughInvalidSortField() {
428433

429434
verify(activitySearchRepository).searchTestingActivities(any(), any(), any(), any(), any(),
430435
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
431-
any(), any(), any(), any(), any(), any(), any(), any(), any(), pageableCaptor.capture());
436+
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), pageableCaptor.capture());
432437

433438
Pageable actualPageable = pageableCaptor.getValue();
434439

0 commit comments

Comments
 (0)