Skip to content

Commit

Permalink
Merge pull request #6043 from espoon-voltti/open-income-statements-fi…
Browse files Browse the repository at this point in the history
…lter

Maksupäätöksien hakuun uusi hakuehto: ei avoimia tuloselvityksiä
  • Loading branch information
terolaakso authored Nov 28, 2024
2 parents da2e09b + bdcb389 commit f0fb447
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 22 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 @@ -54,7 +54,8 @@ export const feeDecisionDistinctiveParams = [
'RETROACTIVE',
'NO_STARTING_PLACEMENTS',
'MAX_FEE_ACCEPTED',
'PRESCHOOL_CLUB'
'PRESCHOOL_CLUB',
'NO_OPEN_INCOME_STATEMENTS'
] as const

export type DistinctiveParams = typeof feeDecisionDistinctiveParams[number]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3076,7 +3076,8 @@ export const fi = {
RETROACTIVE: 'Takautuva päätös',
NO_STARTING_PLACEMENTS: 'Piilota uudet aloittavat lapset',
MAX_FEE_ACCEPTED: 'Suostumus korkeimpaan maksuun',
PRESCHOOL_CLUB: 'Vain esiopetuksen kerho'
PRESCHOOL_CLUB: 'Vain esiopetuksen kerho',
NO_OPEN_INCOME_STATEMENTS: 'Ei avoimia tuloselvityksiä'
},
status: {
DRAFT: 'Luonnos',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import fi.espoo.evaka.FullApplicationTest
import fi.espoo.evaka.emailclient.Email
import fi.espoo.evaka.emailclient.IEmailMessageProvider
import fi.espoo.evaka.emailclient.MockEmailClient
import fi.espoo.evaka.incomestatement.IncomeStatementBody
import fi.espoo.evaka.incomestatement.createIncomeStatement
import fi.espoo.evaka.incomestatement.updateIncomeStatementHandled
import fi.espoo.evaka.invoicing.controller.DistinctiveParams
import fi.espoo.evaka.invoicing.controller.FeeDecisionController
import fi.espoo.evaka.invoicing.controller.FeeDecisionSortParam
Expand Down Expand Up @@ -524,6 +527,181 @@ class FeeDecisionIntegrationTest : FullApplicationTest(resetDbBeforeEach = true)
assertEqualEnough(listOf(toSummary(testDecisionWithNoStartingChild)), result.data)
}

@Test
fun `search works with distinctions param PRESCHOOL_CLUB`() {
db.transaction { tx -> tx.upsertFeeDecisions(testDecisions + preschoolClubDecisions) }
val result =
searchDecisions(
SearchFeeDecisionRequest(
page = 0,
distinctions = listOf(DistinctiveParams.PRESCHOOL_CLUB),
)
)

assertEquals(2, result.data.size)
assertEqualEnough(preschoolClubDecisions.map { toSummary(it) }, result.data)
}

@Test
fun `search works with distinctions param NO_OPEN_INCOME_STATEMENTS`() {
val clock = RealEvakaClock()
val decisionWithHandledStatement =
createFeeDecisionsForFamily(testAdult_1, testAdult_2, listOf(testChild_1))
val decisionWithOpenAdultStatement =
createFeeDecisionsForFamily(testAdult_3, testAdult_4, listOf(testChild_2))
val decisionWithFarAwayAndFutureOpenStatements =
createFeeDecisionsForFamily(testAdult_5, testAdult_6, listOf(testChild_3))
val decisionWithOpenChildStatement =
createFeeDecisionsForFamily(testAdult_7, partner = null, listOf(testChild_4))

db.transaction { tx ->
tx.upsertFeeDecisions(
listOf(
decisionWithHandledStatement,
decisionWithOpenAdultStatement,
decisionWithFarAwayAndFutureOpenStatements,
decisionWithOpenChildStatement,
)
)
val adult1StatementId =
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
personId = testAdult_1.id,
)
// testAdult_2 statement not submitted
tx.updateIncomeStatementHandled(adult1StatementId, "handled", testDecisionMaker_1.id)
val adult3StatementId =
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
personId = testAdult_3.id,
)
tx.updateIncomeStatementHandled(adult3StatementId, "handled", testDecisionMaker_1.id)
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
personId = testAdult_4.id,
)
// testAdult_4 statement not handled
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(20),
clock.today().minusMonths(14).minusDays(1),
),
personId = testAdult_5.id,
)
// testAdult_5 statement not handled
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().plusDays(1),
clock.today().plusMonths(12),
),
personId = testAdult_6.id,
)
// testAdult_6 statement not handled
val adult7StatementId =
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
personId = testAdult_7.id,
)
tx.updateIncomeStatementHandled(adult7StatementId, "handled", testDecisionMaker_1.id)
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
personId = testChild_4.id,
)
// testChild_4 statement not handled
}

val result =
searchDecisions(
SearchFeeDecisionRequest(
page = 0,
distinctions = listOf(DistinctiveParams.NO_OPEN_INCOME_STATEMENTS),
)
)

assertEqualEnough(
listOf(
toSummary(decisionWithHandledStatement),
toSummary(decisionWithFarAwayAndFutureOpenStatements),
),
result.data,
)
}

@Test
fun `search works with distinctions param NO_OPEN_INCOME_STATEMENTS for childless decisions`() {
val clock = RealEvakaClock()
val decisionWithHandledStatements =
createFeeDecisionsForFamily(testAdult_1, testAdult_2, listOf())
val decisionWithOpenStatements =
createFeeDecisionsForFamily(testAdult_3, testAdult_4, listOf())

db.transaction { tx ->
tx.upsertFeeDecisions(listOf(decisionWithHandledStatements, decisionWithOpenStatements))
val adult1StatementId =
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
personId = testAdult_1.id,
)
// testAdult_2 statement not submitted
tx.updateIncomeStatementHandled(adult1StatementId, "handled", testDecisionMaker_1.id)
val adult3StatementId =
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
personId = testAdult_3.id,
)
tx.updateIncomeStatementHandled(adult3StatementId, "handled", testDecisionMaker_1.id)
tx.createIncomeStatement(
body =
IncomeStatementBody.HighestFee(
clock.today().minusMonths(2),
clock.today().minusMonths(1),
),
personId = testAdult_4.id,
)
// testAdult_4 statement not handled
}

val result =
searchDecisions(
SearchFeeDecisionRequest(
page = 0,
distinctions = listOf(DistinctiveParams.NO_OPEN_INCOME_STATEMENTS),
)
)

assertEqualEnough(listOf(toSummary(decisionWithHandledStatements)), result.data)
}

@Test
fun `search works as expected with existing area param`() {
db.transaction { tx -> tx.upsertFeeDecisions(testDecisions) }
Expand Down Expand Up @@ -2025,21 +2203,6 @@ class FeeDecisionIntegrationTest : FullApplicationTest(resetDbBeforeEach = true)
getPdf(decision.id, adminUser)
}

@Test
fun `search works with distinctions param PRESCHOOL_CLUB`() {
db.transaction { tx -> tx.upsertFeeDecisions(testDecisions + preschoolClubDecisions) }
val result =
searchDecisions(
SearchFeeDecisionRequest(
page = 0,
distinctions = listOf(DistinctiveParams.PRESCHOOL_CLUB),
)
)

assertEquals(2, result.data.size)
assertEqualEnough(preschoolClubDecisions.map { toSummary(it) }, result.data)
}

@Test
fun `Email notification is sent to hof when decision in WAITING_FOR_SENDING is set to SENT`() {
// optInAdult has an email address, and does not require manual sending of PDF decision
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ enum class DistinctiveParams {
NO_STARTING_PLACEMENTS,
MAX_FEE_ACCEPTED,
PRESCHOOL_CLUB,
NO_OPEN_INCOME_STATEMENTS,
}

@RestController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,16 +420,13 @@ fun Database.Read.searchFeeDecisions(
freeTextSearchQuery(listOf("head", "partner", "child"), searchTextWithoutNumbers)

val withNullHours = distinctiveParams.contains(DistinctiveParams.UNCONFIRMED_HOURS)

val havingExternalChildren = distinctiveParams.contains(DistinctiveParams.EXTERNAL_CHILD)

val retroactiveOnly = distinctiveParams.contains(DistinctiveParams.RETROACTIVE)

val noStartingPlacements = distinctiveParams.contains(DistinctiveParams.NO_STARTING_PLACEMENTS)

val maxFeeAccepted = distinctiveParams.contains(DistinctiveParams.MAX_FEE_ACCEPTED)

val preschoolClub = distinctiveParams.contains(DistinctiveParams.PRESCHOOL_CLUB)
val noOpenIncomeStatements =
distinctiveParams.contains(DistinctiveParams.NO_OPEN_INCOME_STATEMENTS)

val (numberQuery, numberParams) =
disjointNumberQuery("decision", "decision_number", numberParamsRaw)
Expand Down Expand Up @@ -469,6 +466,16 @@ fun Database.Read.searchFeeDecisions(
"(decision.head_of_family_income->>'effect' = 'MAX_FEE_ACCEPTED' OR decision.partner_income->>'effect' = 'MAX_FEE_ACCEPTED')"
else null,
if (preschoolClub) "decisions_with_preschool_club_placement IS NOT NULL" else null,
if (noOpenIncomeStatements)
"""
NOT EXISTS (
SELECT FROM income_statement
WHERE person_id IN (decision.head_of_family_id, decision.partner_id, part.child_id) AND
daterange(start_date, end_date, '[]') && daterange((:now - interval '14 months')::date, :now::date, '[]') AND
handler_id IS NULL
)
"""
else null,
)

val youngestChildQuery =
Expand Down

0 comments on commit f0fb447

Please sign in to comment.