Skip to content

Commit

Permalink
income can be set to force creation of new finance decisions
Browse files Browse the repository at this point in the history
  • Loading branch information
Joosakur committed Feb 7, 2025
1 parent 4c0ed94 commit 7946986
Show file tree
Hide file tree
Showing 13 changed files with 69 additions and 16 deletions.
3 changes: 2 additions & 1 deletion frontend/src/e2e-test/dev-api/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3168,7 +3168,8 @@ export const DecisionIncomeFixture = (total: number): DecisionIncome => ({
total: total,
totalExpenses: 0,
totalIncome: total,
worksAtECHA: false
worksAtECHA: false,
forceNewDecision: false
})

const nullUUID = evakaUserId(fromUuid('00000000-0000-0000-0000-000000000000'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ const IncomeItemBody = React.memo(function IncomeItemBody({
<span>{income.notes}</span>
<Label>{i18n.personProfile.income.details.created}</Label>
<span>{income.createdAt.toLocalDate().format()}</span>
<Label>{i18n.personProfile.income.details.forceDecisions}</Label>
<span>
{income.forceNewDecision
? i18n.personProfile.income.details.forceDecisionsYes
: i18n.personProfile.income.details.forceDecisions}
</span>
<Label>{i18n.personProfile.income.details.handler}</Label>
<span>
{income.applicationId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface IncomeForm {
validFrom: LocalDate | null
validTo: LocalDate | null
notes: string
forceNewDecision: boolean
attachments: Attachment[]
}

Expand All @@ -76,6 +77,7 @@ const emptyIncome: IncomeForm = {
isEntrepreneur: false,
worksAtECHA: false,
notes: '',
forceNewDecision: false,
validFrom: null,
validTo: null,
attachments: []
Expand Down Expand Up @@ -370,6 +372,15 @@ const IncomeItemEditor = React.memo(function IncomeItemEditor(props: Props) {
/>
</>
) : null}

<Checkbox
label={i18n.personProfile.income.details.forceDecisionsCheckbox}
checked={editedIncome.forceNewDecision}
onChange={(checked) =>
setEditedIncome((prev) => ({ ...prev, forceNewDecision: checked }))
}
/>

<IncomeAttachments
incomeId={isUpdate(props) ? props.baseIncome.id : null}
attachments={isUpdate(props) ? props.baseIncome.attachments : []}
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/lib-common/generated/api-types/invoicing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface CreateRetroactiveFeeDecisionsBody {
export interface DecisionIncome {
data: Partial<Record<string, number>>
effect: IncomeEffect
forceNewDecision: boolean
total: number
totalExpenses: number
totalIncome: number
Expand Down Expand Up @@ -406,6 +407,7 @@ export interface Income {
createdBy: EvakaUser
data: Partial<Record<string, IncomeValue>>
effect: IncomeEffect
forceNewDecision: boolean
id: IncomeId
isEntrepreneur: boolean
modifiedAt: HelsinkiDateTime
Expand Down Expand Up @@ -480,6 +482,7 @@ export interface IncomeRequest {
attachments: Attachment[]
data: Partial<Record<string, IncomeValue>>
effect: IncomeEffect
forceNewDecision: boolean
isEntrepreneur: boolean
notes: string
personId: PersonId
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/lib-customizations/defaults/employee/i18n/fi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,11 @@ export const fi = {
attachments: 'Liitteet',
name: 'Nimi',
created: 'Tulotiedot luotu',
forceDecisions: 'Uusi maksu/arvopäätös',
forceDecisionsYes: 'Aina, vaikka tiedot eivät olisi muuttuneet',
forceDecisionsNo: 'Vain jos tiedot ovat muuttuneet',
forceDecisionsCheckbox:
'Luo uusi maksu/arvopäätös vaikka tiedot eivät olisi muuttuneet',
handler: 'Käsittelijä',
originApplication:
'Huoltaja on hakemuksella suostunut korkeimpaan maksuluokkaan',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ fun mapIncomeToDecisionIncome(
totalExpenses = calculateTotalExpense(income.data, coefficientMultiplierProvider),
total = calculateIncomeTotal(income.data, coefficientMultiplierProvider),
worksAtECHA = income.worksAtECHA,
forceNewDecision = income.forceNewDecision,
)

fun calculateIncomeTotal(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fun Database.Transaction.insertIncome(
data,
is_entrepreneur,
works_at_echa,
force_new_decision,
valid_from,
valid_to,
notes,
Expand All @@ -53,6 +54,7 @@ fun Database.Transaction.insertIncome(
${bindJson(income.data)},
${bind(income.isEntrepreneur)},
${bind(income.worksAtECHA)},
${bind(income.forceNewDecision)},
${bind(income.validFrom)},
${bind(income.validTo)},
${bind(income.notes)},
Expand Down Expand Up @@ -85,6 +87,7 @@ fun Database.Transaction.updateIncome(
data = ${bindJson(income.data)},
is_entrepreneur = ${bind(income.isEntrepreneur)},
works_at_echa = ${bind(income.worksAtECHA)},
force_new_decision = ${bind(income.forceNewDecision)},
valid_from = ${bind(income.validFrom)},
valid_to = ${bind(income.validTo)},
notes = ${bind(income.notes)},
Expand Down Expand Up @@ -272,6 +275,7 @@ fun Row.toIncome(
totalIncome = calculateTotalIncome(data, coefficientMultiplierProvider),
totalExpenses = calculateTotalExpense(data, coefficientMultiplierProvider),
total = calculateIncomeTotal(data, coefficientMultiplierProvider),
forceNewDecision = column("force_new_decision"),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,22 @@ enum class FeeDecisionDifference(val contentEquals: (d1: FeeDecision, d2: FeeDec
d1.children.map { it.child.id }.toSet() == d2.children.map { it.child.id }.toSet()
}),
INCOME({ d1, d2 ->
setOf(
d1.headOfFamilyIncome?.effectiveComparable(),
d1.partnerIncome?.effectiveComparable(),
) ==
val incomesEqual =
setOf(
d2.headOfFamilyIncome?.effectiveComparable(),
d2.partnerIncome?.effectiveComparable(),
) && decisionChildrenEquals(d1, d2) { it.childIncome?.effectiveComparable() }
d1.headOfFamilyIncome?.effectiveComparable(),
d1.partnerIncome?.effectiveComparable(),
) ==
setOf(
d2.headOfFamilyIncome?.effectiveComparable(),
d2.partnerIncome?.effectiveComparable(),
) && decisionChildrenEquals(d1, d2) { it.childIncome?.effectiveComparable() }

val forceNewDecision =
d2.headOfFamilyIncome?.forceNewDecision == true ||
d2.partnerIncome?.forceNewDecision == true ||
d2.children.any { child -> child.childIncome?.forceNewDecision == true }

incomesEqual && !forceNewDecision
}),
PLACEMENT({ d1, d2 -> decisionChildrenEquals(d1, d2) { it.placement } }),
SERVICE_NEED({ d1, d2 ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ data class Income(
val totalIncome: Int,
val totalExpenses: Int,
val total: Int,
val forceNewDecision: Boolean,
)

data class IncomeRequest(
Expand All @@ -49,6 +50,7 @@ data class IncomeRequest(
val validFrom: LocalDate,
val validTo: LocalDate?,
val notes: String,
val forceNewDecision: Boolean,
val attachments: List<Attachment> = listOf(),
)

Expand All @@ -60,12 +62,13 @@ data class DecisionIncome(
val totalExpenses: Int,
val total: Int,
val worksAtECHA: Boolean,
val forceNewDecision: Boolean,
) {
fun effectiveComparable(): DecisionIncome? {
return when (this.effect) {
IncomeEffect.NOT_AVAILABLE,
IncomeEffect.INCOMPLETE -> null
else -> this
else -> this.copy(forceNewDecision = false)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,22 @@ enum class VoucherValueDecisionDifference(
setOf(d1.headOfFamilyId, d1.partnerId) == setOf(d2.headOfFamilyId, d2.partnerId)
}),
INCOME({ d1, d2 ->
setOf(
d1.headOfFamilyIncome?.effectiveComparable(),
d1.partnerIncome?.effectiveComparable(),
) ==
val incomesEqual =
setOf(
d2.headOfFamilyIncome?.effectiveComparable(),
d2.partnerIncome?.effectiveComparable(),
) && d1.childIncome?.effectiveComparable() == d2.childIncome?.effectiveComparable()
d1.headOfFamilyIncome?.effectiveComparable(),
d1.partnerIncome?.effectiveComparable(),
) ==
setOf(
d2.headOfFamilyIncome?.effectiveComparable(),
d2.partnerIncome?.effectiveComparable(),
) && d1.childIncome?.effectiveComparable() == d2.childIncome?.effectiveComparable()

val forceNewDecision =
d2.headOfFamilyIncome?.forceNewDecision == true ||
d2.partnerIncome?.forceNewDecision == true ||
d2.childIncome?.forceNewDecision == true

incomesEqual && !forceNewDecision
}),
FAMILY_SIZE({ d1, d2 -> d1.familySize == d2.familySize }),
PLACEMENT({ d1, d2 -> d1.placement == d2.placement }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ class OutdatedIncomeNotifications(
validTo = null,
data = emptyMap(),
notes = "Created automatically because previous income expired",
forceNewDecision = false,
),
createdBy = AuthenticatedUser.SystemInternalUser.evakaUserId,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE income ADD COLUMN force_new_decision BOOLEAN NOT NULL DEFAULT FALSE;
1 change: 1 addition & 0 deletions service/src/main/resources/migrations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,4 @@ V491__password_blacklist.sql
V492__finance_note.sql
V493__acl_end_date.sql
V494__daycare_acl_schedule.sql
V495__income_force_new_decision.sql

0 comments on commit 7946986

Please sign in to comment.