Skip to content

Commit 8e52e7c

Browse files
authored
Merge pull request #5904 from espoon-voltti/income-statements-unit-filter
Lisätään tuloselvitykset-listaan yksikön mukaan suodatus
2 parents de81a28 + c54e176 commit 8e52e7c

File tree

13 files changed

+141
-44
lines changed

13 files changed

+141
-44
lines changed

frontend/src/employee-frontend/components/common/Filters.tsx

+4-5
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ export function AreaFilter({
299299
}
300300

301301
interface UnitFilterProps {
302-
units: { id: string; label: string }[]
303-
selected?: { id: string; label: string }
302+
units: { id: string; name: string }[]
303+
selected?: { id: string; name: string }
304304
select: (unit?: string) => void
305305
}
306306

@@ -310,19 +310,18 @@ export const UnitFilter = React.memo(function UnitFilter({
310310
select
311311
}: UnitFilterProps) {
312312
const { i18n } = useTranslation()
313-
const options = units.map(({ id, label }) => ({ id, label, value: id }))
314313
return (
315314
<>
316315
<Label>{i18n.filters.unit}</Label>
317316
<Gap size="xs" />
318317
<Combobox
319-
items={options}
318+
items={units}
320319
placeholder={i18n.filters.unitPlaceholder}
321320
selectedItem={selected ?? null}
322321
onChange={(option) => select(option?.id)}
323322
clearable
324323
fullWidth
325-
getItemLabel={(item) => item.label}
324+
getItemLabel={(item) => item.name}
326325
/>
327326
</>
328327
)

frontend/src/employee-frontend/components/fee-decisions/FeeDecisionFilters.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,9 @@ function FeeDecisionFilters() {
186186
/>
187187
<Gap size="L" />
188188
<UnitFilter
189-
units={units
190-
.map((us) => us.map(({ id, name }) => ({ id, label: name })))
191-
.getOrElse([])}
189+
units={units.getOrElse([])}
192190
selected={units
193-
.map((us) =>
194-
us
195-
.map(({ id, name }) => ({ id, label: name }))
196-
.find((unit) => unit.id === searchFilters.unit)
197-
)
191+
.map((us) => us.find((unit) => unit.id === searchFilters.unit))
198192
.getOrElse(undefined)}
199193
select={selectUnit}
200194
/>

frontend/src/employee-frontend/components/income-statements/IncomeStatementFilters.tsx

+28-6
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@ import React, { Fragment, useCallback, useContext } from 'react'
66

77
import { ProviderType } from 'lib-common/generated/api-types/daycare'
88
import LocalDate from 'lib-common/local-date'
9+
import { useQueryResult } from 'lib-common/query'
910
import { DatePickerClearableDeprecated } from 'lib-components/molecules/DatePickerDeprecated'
1011
import { Label } from 'lib-components/typography'
1112
import { Gap } from 'lib-components/white-space'
1213

1314
import { useTranslation } from '../../state/i18n'
1415
import { InvoicingUiContext } from '../../state/invoicing-ui'
16+
import { renderResult } from '../async-rendering'
1517
import {
1618
AreaFilter,
1719
DateFilter,
1820
Filters,
19-
ProviderTypeFilter
21+
ProviderTypeFilter,
22+
UnitFilter
2023
} from '../common/Filters'
24+
import { unitFilterQuery } from '../unit/queries'
2125

2226
export default React.memo(function IncomeStatementsFilters({
2327
onSearch
@@ -28,6 +32,9 @@ export default React.memo(function IncomeStatementsFilters({
2832
incomeStatements: { searchFilters, setSearchFilters, clearSearchFilters },
2933
shared: { availableAreas }
3034
} = useContext(InvoicingUiContext)
35+
const unitsResult = useQueryResult(
36+
unitFilterQuery({ areaIds: null, type: 'DAYCARE', from: null })
37+
)
3138

3239
const { i18n } = useTranslation()
3340

@@ -48,6 +55,11 @@ export default React.memo(function IncomeStatementsFilters({
4855
[setSearchFilters]
4956
)
5057

58+
const setUnit = useCallback(
59+
(unit: string | undefined) => setSearchFilters((old) => ({ ...old, unit })),
60+
[setSearchFilters]
61+
)
62+
5163
const setSentStartDate = useCallback(
5264
(sentStartDate: LocalDate | undefined) =>
5365
setSearchFilters((old) => ({ ...old, sentStartDate })),
@@ -80,11 +92,21 @@ export default React.memo(function IncomeStatementsFilters({
8092
clearFilters={clearSearchFilters}
8193
onSearch={onSearch}
8294
column1={
83-
<AreaFilter
84-
areas={availableAreas.getOrElse([])}
85-
toggled={searchFilters.area}
86-
toggle={toggleArea}
87-
/>
95+
<>
96+
<AreaFilter
97+
areas={availableAreas.getOrElse([])}
98+
toggled={searchFilters.area}
99+
toggle={toggleArea}
100+
/>
101+
<Gap size="L" />
102+
{renderResult(unitsResult, (units) => (
103+
<UnitFilter
104+
units={units}
105+
select={setUnit}
106+
selected={units.find(({ id }) => id === searchFilters.unit)}
107+
/>
108+
))}
109+
</>
88110
}
89111
column2={
90112
<Fragment>

frontend/src/employee-frontend/components/income-statements/IncomeStatementsPage.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export default React.memo(function IncomeStatementsPage() {
156156
const [sortDirection, setSortDirection] = useState<SortDirection>('ASC')
157157
const [searchParams, setSearchParams] = useState<{
158158
areas: string[] | null
159+
unit: string | null
159160
providerTypes: ProviderType[] | null
160161
sentStartDate: LocalDate | null
161162
sentEndDate: LocalDate | null
@@ -182,6 +183,7 @@ export default React.memo(function IncomeStatementsPage() {
182183
) {
183184
setSearchParams({
184185
areas: searchFilters.area.length > 0 ? searchFilters.area : null,
186+
unit: searchFilters.unit ?? null,
185187
providerTypes:
186188
searchFilters.providerTypes.length > 0
187189
? searchFilters.providerTypes

frontend/src/employee-frontend/components/invoices/InvoiceFilters.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,9 @@ export default React.memo(function InvoiceFilters() {
134134
/>
135135
<Gap size="L" />
136136
<UnitFilter
137-
units={units
138-
.map((us) => us.map(({ id, name }) => ({ id, label: name })))
139-
.getOrElse([])}
137+
units={units.getOrElse([])}
140138
selected={units
141-
.map((us) =>
142-
us
143-
.map(({ id, name }) => ({ id, label: name }))
144-
.find((unit) => unit.id === searchFilters.unit)
145-
)
139+
.map((us) => us.find((unit) => unit.id === searchFilters.unit))
146140
.getOrElse(undefined)}
147141
select={selectUnit}
148142
/>

frontend/src/employee-frontend/components/payments/PaymentFilters.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,9 @@ export default React.memo(function PaymentFilters() {
131131
/>
132132
<Gap size="L" />
133133
<UnitFilter
134-
units={units
135-
.map((us) => us.map(({ id, name }) => ({ id, label: name })))
136-
.getOrElse([])}
134+
units={units.getOrElse([])}
137135
selected={units
138-
.map((us) =>
139-
us
140-
.map(({ id, name }) => ({ id, label: name }))
141-
.find((unit) => unit.id === searchFilters.unit)
142-
)
136+
.map((us) => us.find((unit) => unit.id === searchFilters.unit))
143137
.getOrElse(undefined)}
144138
select={selectUnit}
145139
/>

frontend/src/employee-frontend/components/unit/queries.ts

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
getGroups,
2727
getUnitGroupDetails,
2828
getUnitNotifications,
29+
getUnits,
2930
updateDaycare,
3031
updateGroup
3132
} from '../../generated/api-clients/daycare'
@@ -51,6 +52,7 @@ import { createQueryKeys } from '../../query'
5152

5253
export const queryKeys = createQueryKeys('unit', {
5354
areas: () => ['areas'],
55+
unitFilters: (arg: Arg0<typeof getUnits>) => ['unitFilters', arg],
5456
units: (arg: Arg0<typeof getDaycares>) => ['units', arg],
5557
unit: (unitId: UUID) => ['unit', unitId],
5658
unitNotifications: (unitId: UUID) => ['unitNotifications', unitId],
@@ -106,6 +108,11 @@ export const areaQuery = query({
106108
queryKey: queryKeys.areas
107109
})
108110

111+
export const unitFilterQuery = query({
112+
api: getUnits,
113+
queryKey: queryKeys.unitFilters
114+
})
115+
109116
export const unitsQuery = query({
110117
api: getDaycares,
111118
queryKey: queryKeys.units

frontend/src/employee-frontend/components/voucher-value-decisions/VoucherValueDecisionFilters.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,9 @@ export default React.memo(function VoucherValueDecisionFilters() {
191191
/>
192192
<Gap size="L" />
193193
<UnitFilter
194-
units={units
195-
.map((us) => us.map(({ id, name }) => ({ id, label: name })))
196-
.getOrElse([])}
194+
units={units.getOrElse([])}
197195
selected={units
198-
.map((us) =>
199-
us
200-
.map(({ id, name }) => ({ id, label: name }))
201-
.find((unit) => unit.id === searchFilters.unit)
202-
)
196+
.map((us) => us.find((unit) => unit.id === searchFilters.unit))
203197
.getOrElse(undefined)}
204198
select={selectUnit}
205199
/>

frontend/src/employee-frontend/state/invoicing-ui.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ interface PaymentSearchFilterState {
123123

124124
export interface IncomeStatementSearchFilters {
125125
area: string[]
126+
unit: string | undefined
126127
providerTypes: ProviderType[]
127128
sentStartDate: LocalDate | undefined
128129
sentEndDate: LocalDate | undefined
@@ -224,6 +225,7 @@ const defaultState: UiState = {
224225
incomeStatements: {
225226
searchFilters: {
226227
area: [],
228+
unit: undefined,
227229
providerTypes: [],
228230
sentStartDate: undefined,
229231
sentEndDate: undefined,

frontend/src/lib-common/generated/api-types/incomestatement.ts

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ export interface SearchIncomeStatementsRequest {
294294
sentStartDate: LocalDate | null
295295
sortBy: IncomeStatementSortParam | null
296296
sortDirection: SortDirection | null
297+
unit: UUID | null
297298
}
298299

299300
/**

service/src/integrationTest/kotlin/fi/espoo/evaka/incomestatement/IncomeStatementControllerIntegrationTest.kt

+81-1
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,77 @@ class IncomeStatementControllerIntegrationTest : FullApplicationTest(resetDbBefo
576576
)
577577
}
578578

579+
@Test
580+
fun `list income statements awaiting handler - unit filter`() {
581+
val placementId1 = PlacementId(UUID.randomUUID())
582+
val placementId2 = PlacementId(UUID.randomUUID())
583+
val placementStart = today.minusDays(30)
584+
val placementEnd = today.plusDays(30)
585+
db.transaction { tx ->
586+
tx.insert(
587+
DevParentship(
588+
childId = testChild_1.id,
589+
headOfChildId = citizenId,
590+
startDate = placementStart,
591+
endDate = placementEnd,
592+
)
593+
)
594+
tx.insert(
595+
DevPlacement(
596+
id = placementId1,
597+
type = PlacementType.PRESCHOOL_DAYCARE,
598+
childId = testChild_1.id,
599+
unitId = daycare1.id,
600+
startDate = placementStart,
601+
endDate = placementEnd,
602+
)
603+
)
604+
605+
tx.insert(
606+
DevParentship(
607+
childId = testChild_2.id,
608+
headOfChildId = testAdult_2.id,
609+
startDate = placementStart,
610+
endDate = placementEnd,
611+
)
612+
)
613+
tx.insert(
614+
DevPlacement(
615+
id = placementId2,
616+
type = PlacementType.PRESCHOOL_DAYCARE,
617+
childId = testChild_2.id,
618+
unitId = daycare2.id,
619+
startDate = placementStart,
620+
endDate = placementEnd,
621+
)
622+
)
623+
}
624+
625+
createTestIncomeStatement(citizenId)
626+
val incomeStatement2 = createTestIncomeStatement(testAdult_2.id)
627+
628+
assertEquals(
629+
PagedIncomeStatementsAwaitingHandler(
630+
listOf(
631+
IncomeStatementAwaitingHandler(
632+
id = incomeStatement2.id,
633+
created = incomeStatement2.created,
634+
startDate = incomeStatement2.startDate,
635+
incomeEndDate = null,
636+
handlerNote = "",
637+
type = IncomeStatementType.HIGHEST_FEE,
638+
personId = testAdult_2.id,
639+
personName = "Doe Joan",
640+
primaryCareArea = area2.name,
641+
)
642+
),
643+
1,
644+
1,
645+
),
646+
getIncomeStatementsAwaitingHandler(SearchIncomeStatementsRequest(unit = daycare2.id)),
647+
)
648+
}
649+
579650
@Test
580651
fun `list income statements awaiting handler - provider type filter`() {
581652
val placementId1 = PlacementId(UUID.randomUUID())
@@ -1184,7 +1255,16 @@ class IncomeStatementControllerIntegrationTest : FullApplicationTest(resetDbBefo
11841255

11851256
private fun getIncomeStatementsAwaitingHandler(
11861257
body: SearchIncomeStatementsRequest =
1187-
SearchIncomeStatementsRequest(1, null, null, emptyList(), emptyList(), null, null),
1258+
SearchIncomeStatementsRequest(
1259+
1,
1260+
null,
1261+
null,
1262+
emptyList(),
1263+
null,
1264+
emptyList(),
1265+
null,
1266+
null,
1267+
),
11881268
clock: EvakaClock = MockEvakaClock(now),
11891269
): PagedIncomeStatementsAwaitingHandler {
11901270
return incomeStatementController.getIncomeStatementsAwaitingHandler(

service/src/main/kotlin/fi/espoo/evaka/incomestatement/IncomeStatementController.kt

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import fi.espoo.evaka.Audit
88
import fi.espoo.evaka.AuditId
99
import fi.espoo.evaka.daycare.domain.ProviderType
1010
import fi.espoo.evaka.invoicing.controller.SortDirection
11+
import fi.espoo.evaka.shared.DaycareId
1112
import fi.espoo.evaka.shared.IncomeStatementId
1213
import fi.espoo.evaka.shared.PersonId
1314
import fi.espoo.evaka.shared.auth.AuthenticatedUser
@@ -135,6 +136,7 @@ class IncomeStatementController(private val accessControl: AccessControl) {
135136
it.fetchIncomeStatementsAwaitingHandler(
136137
clock.now().toLocalDate(),
137138
body.areas ?: emptyList(),
139+
body.unit,
138140
body.providerTypes ?: emptyList(),
139141
body.sentStartDate,
140142
body.sentEndDate,
@@ -182,6 +184,7 @@ data class SearchIncomeStatementsRequest(
182184
val sortBy: IncomeStatementSortParam? = null,
183185
val sortDirection: SortDirection? = null,
184186
val areas: List<String>? = emptyList(),
187+
val unit: DaycareId? = null,
185188
val providerTypes: List<ProviderType>? = emptyList(),
186189
val sentStartDate: LocalDate? = null,
187190
val sentEndDate: LocalDate? = null,

0 commit comments

Comments
 (0)