Skip to content

Commit

Permalink
Merge pull request #6177 from espoon-voltti/filter-voucher-value-deci…
Browse files Browse the repository at this point in the history
…sions

Arvopäätöksien hakuun uusi hakuehto: ei avoimia tuloselvityksiä
  • Loading branch information
terolaakso authored Dec 30, 2024
2 parents 9027d41 + 1b14426 commit d0213d6
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 25 deletions.
3 changes: 2 additions & 1 deletion frontend/src/lib-common/generated/api-types/invoicing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1075,7 +1075,8 @@ export const voucherValueDecisionDistinctiveParams = [
'EXTERNAL_CHILD',
'RETROACTIVE',
'NO_STARTING_PLACEMENTS',
'MAX_FEE_ACCEPTED'
'MAX_FEE_ACCEPTED',
'NO_OPEN_INCOME_STATEMENTS'
] as const

export type VoucherValueDecisionDistinctiveParams = typeof voucherValueDecisionDistinctiveParams[number]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

package fi.espoo.evaka.invoicing.data

import fi.espoo.evaka.PureJdbiTest
import fi.espoo.evaka.*
import fi.espoo.evaka.incomestatement.IncomeStatementBody
import fi.espoo.evaka.incomestatement.IncomeStatementStatus
import fi.espoo.evaka.invoicing.controller.SortDirection
import fi.espoo.evaka.invoicing.controller.VoucherValueDecisionDistinctiveParams
import fi.espoo.evaka.invoicing.controller.VoucherValueDecisionSortParam
Expand All @@ -15,32 +17,14 @@ import fi.espoo.evaka.invoicing.domain.VoucherValueDecisionDifference
import fi.espoo.evaka.invoicing.domain.VoucherValueDecisionStatus
import fi.espoo.evaka.invoicing.domain.VoucherValueDecisionSummary
import fi.espoo.evaka.placement.PlacementType
import fi.espoo.evaka.shared.ChildId
import fi.espoo.evaka.shared.PersonId
import fi.espoo.evaka.shared.VoucherValueDecisionId
import fi.espoo.evaka.shared.dev.DevPerson
import fi.espoo.evaka.shared.dev.DevPersonType
import fi.espoo.evaka.shared.dev.DevPlacement
import fi.espoo.evaka.shared.dev.insert
import fi.espoo.evaka.shared.dev.*
import fi.espoo.evaka.shared.domain.FiniteDateRange
import fi.espoo.evaka.shared.domain.HelsinkiDateTime
import fi.espoo.evaka.shared.domain.MockEvakaClock
import fi.espoo.evaka.snDaycareFullDay35
import fi.espoo.evaka.snDefaultDaycare
import fi.espoo.evaka.testAdult_1
import fi.espoo.evaka.testAdult_2
import fi.espoo.evaka.testAdult_3
import fi.espoo.evaka.testAdult_4
import fi.espoo.evaka.testAdult_5
import fi.espoo.evaka.testAdult_6
import fi.espoo.evaka.testAdult_7
import fi.espoo.evaka.testArea
import fi.espoo.evaka.testChild_1
import fi.espoo.evaka.testChild_2
import fi.espoo.evaka.testChild_3
import fi.espoo.evaka.testChild_4
import fi.espoo.evaka.testChild_5
import fi.espoo.evaka.testDaycare
import fi.espoo.evaka.toValueDecisionServiceNeed
import fi.espoo.evaka.shared.domain.RealEvakaClock
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
Expand Down Expand Up @@ -1001,7 +985,7 @@ internal class VoucherValueDecisionQueriesTest : PureJdbiTest(resetDbBeforeEach
}

@Test
fun `search with NO_STARTING_CHILDREN`() {
fun `search with NO_STARTING_PLACEMENTS`() {
val now = MockEvakaClock(HelsinkiDateTime.of(LocalDateTime.of(2022, 1, 1, 12, 0)))

db.transaction { tx ->
Expand Down Expand Up @@ -1080,6 +1064,183 @@ internal class VoucherValueDecisionQueriesTest : PureJdbiTest(resetDbBeforeEach
}
}

@Test
fun `search with NO_OPEN_INCOME_STATEMENTS`() {
val clock = RealEvakaClock()

fun createTestDecision(headOfFamilyId: PersonId, childId: ChildId, partnerId: PersonId?) =
createVoucherValueDecisionFixture(
status = VoucherValueDecisionStatus.DRAFT,
validFrom = clock.now().toLocalDate(),
validTo = clock.now().toLocalDate(),
headOfFamilyId = headOfFamilyId,
partnerId = partnerId,
childId = childId,
dateOfBirth = testChild_1.dateOfBirth,
unitId = testDaycare.id,
placementType = PlacementType.DAYCARE,
serviceNeed = snDefaultDaycare.toValueDecisionServiceNeed(),
)

val decisionWithHandledStatement =
createTestDecision(testAdult_1.id, testChild_1.id, testAdult_2.id)
val decisionWithOpenAdultStatement =
createTestDecision(testAdult_3.id, testChild_2.id, testAdult_4.id)
val decisionWithFarAwayAndFutureOpenStatements =
createTestDecision(testAdult_5.id, testChild_3.id, testAdult_6.id)
val decisionWithOpenChildStatement =
createTestDecision(testAdult_7.id, testChild_4.id, null)

db.transaction { tx ->
tx.upsertValueDecisions(
listOf(
decisionWithHandledStatement,
decisionWithOpenAdultStatement,
decisionWithFarAwayAndFutureOpenStatements,
decisionWithOpenChildStatement,
)
)
tx.insert(testDecisionMaker_1)
tx.insert(
DevIncomeStatement(
personId = testAdult_1.id,
data =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
status = IncomeStatementStatus.HANDLED,
handledAt = clock.now(),
handlerId = testDecisionMaker_1.id,
)
)

// testAdult_2 statement not submitted
tx.insert(
DevIncomeStatement(
personId = testAdult_2.id,
data =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
status = IncomeStatementStatus.DRAFT,
sentAt = null,
)
)

tx.insert(
DevIncomeStatement(
personId = testAdult_3.id,
data =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
status = IncomeStatementStatus.HANDLED,
handledAt = clock.now(),
handlerId = testDecisionMaker_1.id,
)
)

// testAdult_4 statement not handled
tx.insert(
DevIncomeStatement(
personId = testAdult_4.id,
data =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
status = IncomeStatementStatus.SENT,
handledAt = null,
handlerId = null,
)
)

// testAdult_5 statement not handled
tx.insert(
DevIncomeStatement(
personId = testAdult_5.id,
data =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(20),
clock.today().minusMonths(14).minusDays(1),
),
status = IncomeStatementStatus.SENT,
handledAt = null,
handlerId = null,
)
)

// testAdult_6 statement not handled
tx.insert(
DevIncomeStatement(
personId = testAdult_6.id,
data =
IncomeStatementBody.HighestFee(
clock.today().plusDays(1),
clock.today().plusMonths(12),
),
status = IncomeStatementStatus.SENT,
handledAt = null,
handlerId = null,
)
)

tx.insert(
DevIncomeStatement(
personId = testAdult_7.id,
data =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
status = IncomeStatementStatus.HANDLED,
handledAt = clock.now(),
handlerId = testDecisionMaker_1.id,
)
)

// testChild_4 statement not handled
tx.insert(
DevIncomeStatement(
personId = testChild_4.id,
data =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
status = IncomeStatementStatus.SENT,
handledAt = null,
handlerId = null,
)
)

val result =
tx.searchValueDecisions(
evakaClock = clock,
postOffice = "ESPOO",
page = 0,
pageSize = 100,
sortBy = VoucherValueDecisionSortParam.HEAD_OF_FAMILY,
sortDirection = SortDirection.ASC,
statuses = listOf(VoucherValueDecisionStatus.DRAFT),
areas = emptyList(),
unit = null,
startDate = null,
endDate = null,
difference = emptySet(),
financeDecisionHandlerId = null,
distinctiveParams =
listOf(VoucherValueDecisionDistinctiveParams.NO_OPEN_INCOME_STATEMENTS),
)

assertThat(result.data.map { it.child.id })
.containsExactlyInAnyOrder(testChild_1.id, testChild_3.id)
}
}

@Test
fun `search with status`() {
db.transaction { tx ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ enum class VoucherValueDecisionDistinctiveParams {
RETROACTIVE,
NO_STARTING_PLACEMENTS,
MAX_FEE_ACCEPTED,
NO_OPEN_INCOME_STATEMENTS,
}

data class SearchVoucherValueDecisionRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ fun Database.Read.searchValueDecisions(
val maxFeeAccepted =
distinctiveParams.contains(VoucherValueDecisionDistinctiveParams.MAX_FEE_ACCEPTED)

val noOpenIncomeStatements =
distinctiveParams.contains(VoucherValueDecisionDistinctiveParams.NO_OPEN_INCOME_STATEMENTS)

val noStartingPlacementsQuery =
"""
NOT EXISTS (
Expand Down Expand Up @@ -378,6 +381,16 @@ NOT EXISTS (
if (maxFeeAccepted)
"(decision.head_of_family_income->>'effect' = 'MAX_FEE_ACCEPTED' OR decision.partner_income->>'effect' = 'MAX_FEE_ACCEPTED')"
else null,
if (noOpenIncomeStatements)
"""
NOT EXISTS (
SELECT FROM income_statement
WHERE person_id IN (decision.head_of_family_id, decision.partner_id, decision.child_id) AND
daterange(start_date, end_date, '[]') && daterange((:now - interval '14 months')::date, :now::date, '[]') AND
status = 'SENT'
)
"""
else null,
)
val sql =
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ fun createVoucherValueDecisionFixture(
validFrom: LocalDate,
validTo: LocalDate,
headOfFamilyId: PersonId,
partnerId: PersonId? = null,
childId: ChildId,
dateOfBirth: LocalDate,
unitId: DaycareId,
Expand All @@ -267,7 +268,7 @@ fun createVoucherValueDecisionFixture(
validFrom = validFrom,
validTo = validTo,
headOfFamilyId = headOfFamilyId,
partnerId = null,
partnerId = partnerId,
headOfFamilyIncome = null,
partnerIncome = null,
childIncome = null,
Expand Down

0 comments on commit d0213d6

Please sign in to comment.