Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lisätään päätöksen hyväksyminen/hylkäys toiselle huoltajalle #6048

Merged
merged 3 commits into from
Dec 10, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -41,9 +41,12 @@ import fi.espoo.evaka.shared.security.Action
import fi.espoo.evaka.test.DecisionTableRow
import fi.espoo.evaka.test.getApplicationStatus
import fi.espoo.evaka.test.getDecisionRowsByApplication
import fi.espoo.evaka.testAdult_1
import fi.espoo.evaka.testAdult_2
import fi.espoo.evaka.testAdult_5
import fi.espoo.evaka.testAdult_6
import fi.espoo.evaka.testArea
import fi.espoo.evaka.testChild_1
import fi.espoo.evaka.testChild_6
import fi.espoo.evaka.testDaycare
import fi.espoo.evaka.testDecisionMaker_1
@@ -66,6 +69,7 @@ class DecisionCreationIntegrationTest : FullApplicationTest(resetDbBeforeEach =
private val serviceWorker =
AuthenticatedUser.Employee(testDecisionMaker_1.id, setOf(UserRole.SERVICE_WORKER))
private val citizen = AuthenticatedUser.Citizen(testAdult_5.id, CitizenAuthLevel.STRONG)
private val citizenWeak = AuthenticatedUser.Citizen(testAdult_5.id, CitizenAuthLevel.WEAK)

@Autowired private lateinit var applicationController: ApplicationControllerV2
@Autowired private lateinit var applicationControllerCitizen: ApplicationControllerCitizen
@@ -356,6 +360,14 @@ WHERE id = ${bind(testDaycare.id)}
)
assertEquals(1, notificationCount)

val notificationCountAsWeak =
applicationControllerCitizen.getGuardianApplicationNotifications(
dbInstance(),
citizenWeak,
RealEvakaClock(),
)
assertEquals(1, notificationCountAsWeak)

val citizenDecisions =
applicationControllerCitizen.getDecisions(dbInstance(), citizen, RealEvakaClock())
assertEquals(
@@ -386,6 +398,240 @@ WHERE id = ${bind(testDaycare.id)}
)
}

@Test
fun `other guardian in different address as guardian sees decision but can't decide`() {
val period = FiniteDateRange(LocalDate.of(2020, 3, 17), LocalDate.of(2023, 7, 31))
val applicationId =
insertInitialData(
type = PlacementType.DAYCARE,
period = period,
adult = testAdult_6,
child = testChild_6,
)
checkDecisionDrafts(
applicationId,
adult = testAdult_6,
child = testChild_6,
decisions =
listOf(
DecisionDraft(
id = decisionId,
unitId = testDaycare.id,
type = DecisionType.DAYCARE,
startDate = period.start,
endDate = period.end,
planned = true,
)
),
otherGuardian = testAdult_5,
)
val createdDecisions = createDecisions(applicationId)
assertEquals(1, createdDecisions.size)

val otherGuardian = AuthenticatedUser.Citizen(testAdult_5.id, CitizenAuthLevel.STRONG)
val otherGuardianWeak = AuthenticatedUser.Citizen(testAdult_5.id, CitizenAuthLevel.WEAK)

val notificationCount =
applicationControllerCitizen.getGuardianApplicationNotifications(
dbInstance(),
otherGuardian,
RealEvakaClock(),
)
assertEquals(0, notificationCount)

val notificationCountAsWeak =
applicationControllerCitizen.getGuardianApplicationNotifications(
dbInstance(),
otherGuardianWeak,
RealEvakaClock(),
)
assertEquals(0, notificationCountAsWeak)

val citizenDecisions =
applicationControllerCitizen.getDecisions(dbInstance(), otherGuardian, RealEvakaClock())
assertEquals(
citizenDecisions,
ApplicationDecisions(
decisions =
listOf(
DecisionSummary(
id = createdDecisions[0].id,
applicationId = applicationId,
childId = testChild_6.id,
type = DecisionType.DAYCARE,
status = DecisionStatus.PENDING,
sentDate = LocalDate.now(),
resolved = null,
)
),
permittedActions =
mapOf(
createdDecisions[0].id to
setOf(
Action.Citizen.Decision.READ,
Action.Citizen.Decision.DOWNLOAD_PDF,
)
),
decidableApplications = emptySet(),
),
)
}

@Test
fun `other guardian in different address as child sees decision but can't decide`() {
val period = FiniteDateRange(LocalDate.of(2020, 3, 17), LocalDate.of(2023, 7, 31))
val applicationId = insertInitialData(type = PlacementType.DAYCARE, period = period)
checkDecisionDrafts(
applicationId,
decisions =
listOf(
DecisionDraft(
id = decisionId,
unitId = testDaycare.id,
type = DecisionType.DAYCARE,
startDate = period.start,
endDate = period.end,
planned = true,
)
),
otherGuardian = testAdult_6,
)
val createdDecisions = createDecisions(applicationId)
assertEquals(1, createdDecisions.size)

val otherGuardian = AuthenticatedUser.Citizen(testAdult_6.id, CitizenAuthLevel.STRONG)
val otherGuardianWeak = AuthenticatedUser.Citizen(testAdult_6.id, CitizenAuthLevel.WEAK)

val notificationCount =
applicationControllerCitizen.getGuardianApplicationNotifications(
dbInstance(),
otherGuardian,
RealEvakaClock(),
)
assertEquals(0, notificationCount)

val notificationCountAsWeak =
applicationControllerCitizen.getGuardianApplicationNotifications(
dbInstance(),
otherGuardianWeak,
RealEvakaClock(),
)
assertEquals(0, notificationCountAsWeak)

val citizenDecisions =
applicationControllerCitizen.getDecisions(dbInstance(), otherGuardian, RealEvakaClock())
assertEquals(
citizenDecisions,
ApplicationDecisions(
decisions =
listOf(
DecisionSummary(
id = createdDecisions[0].id,
applicationId = applicationId,
childId = testChild_6.id,
type = DecisionType.DAYCARE,
status = DecisionStatus.PENDING,
sentDate = LocalDate.now(),
resolved = null,
)
),
permittedActions =
mapOf(
createdDecisions[0].id to
setOf(
Action.Citizen.Decision.READ,
Action.Citizen.Decision.DOWNLOAD_PDF,
)
),
decidableApplications = emptySet(),
),
)
}

@Test
fun `other guardian in the same address sees decision and can decide`() {
db.transaction { tx ->
listOf(testAdult_1, testAdult_2).forEach { tx.insert(it, DevPersonType.ADULT) }
tx.insert(testChild_1, DevPersonType.CHILD)
}

val period = FiniteDateRange(LocalDate.of(2020, 3, 17), LocalDate.of(2023, 7, 31))
val applicationId =
insertInitialData(
type = PlacementType.DAYCARE,
period = period,
adult = testAdult_1,
child = testChild_1,
)
checkDecisionDrafts(
applicationId,
adult = testAdult_1,
child = testChild_1,
decisions =
listOf(
DecisionDraft(
id = decisionId,
unitId = testDaycare.id,
type = DecisionType.DAYCARE,
startDate = period.start,
endDate = period.end,
planned = true,
)
),
otherGuardian = testAdult_2,
)
val createdDecisions = createDecisions(applicationId)
assertEquals(1, createdDecisions.size)

val otherGuardian = AuthenticatedUser.Citizen(testAdult_2.id, CitizenAuthLevel.STRONG)
val otherGuardianWeak = AuthenticatedUser.Citizen(testAdult_2.id, CitizenAuthLevel.WEAK)

val notificationCount =
applicationControllerCitizen.getGuardianApplicationNotifications(
dbInstance(),
otherGuardian,
RealEvakaClock(),
)
assertEquals(1, notificationCount)

val notificationCountAsWeak =
applicationControllerCitizen.getGuardianApplicationNotifications(
dbInstance(),
otherGuardianWeak,
RealEvakaClock(),
)
assertEquals(1, notificationCountAsWeak)

val citizenDecisions =
applicationControllerCitizen.getDecisions(dbInstance(), otherGuardian, RealEvakaClock())
assertEquals(
citizenDecisions,
ApplicationDecisions(
decisions =
listOf(
DecisionSummary(
id = createdDecisions[0].id,
applicationId = applicationId,
childId = testChild_1.id,
type = DecisionType.DAYCARE,
status = DecisionStatus.PENDING,
sentDate = LocalDate.now(),
resolved = null,
)
),
permittedActions =
mapOf(
createdDecisions[0].id to
setOf(
Action.Citizen.Decision.READ,
Action.Citizen.Decision.DOWNLOAD_PDF,
)
),
decidableApplications = setOf(applicationId),
),
)
}

@Test
fun `citizen sees decision notification for hidden application`() {
val period = FiniteDateRange(LocalDate.of(2020, 3, 17), LocalDate.of(2023, 7, 31))
Original file line number Diff line number Diff line change
@@ -614,7 +614,7 @@ class ApplicationControllerCitizen(
Action.Citizen.Person.READ_APPLICATION_NOTIFICATIONS,
user.id,
)
tx.fetchApplicationNotificationCountForCitizen(user.id)
tx.fetchApplicationNotificationCountForCitizen(user.id, clock.today())
}
}
.also { Audit.ApplicationReadNotifications.log(targetId = AuditId(user.id)) }
Original file line number Diff line number Diff line change
@@ -1270,13 +1270,57 @@ RETURNING id
.executeAndReturnGeneratedKeys()
.toList<ApplicationId>()

fun Database.Read.fetchApplicationNotificationCountForCitizen(citizenId: PersonId): Int =
fun Database.Read.fetchApplicationNotificationCountForCitizen(
citizenId: PersonId,
today: LocalDate,
): Int =
createQuery {
sql(
"""
SELECT COUNT(*)
FROM application a
WHERE guardian_id = ${bind(citizenId)}
JOIN person guardian ON a.guardian_id = guardian.id
JOIN person child ON a.child_id = child.id
WHERE (a.guardian_id = ${bind(citizenId)} OR (
a.allow_other_guardian_access IS TRUE
AND EXISTS (
SELECT FROM application_other_guardian aog
JOIN person other_guardian ON aog.guardian_id = other_guardian.id
WHERE aog.application_id = a.id AND aog.guardian_id = ${bind(citizenId)}
AND (
EXISTS (SELECT FROM guardian g WHERE g.guardian_id = aog.guardian_id AND g.child_id = a.child_id)
OR EXISTS (SELECT FROM foster_parent fp WHERE fp.parent_id = aog.guardian_id AND fp.child_id = a.child_id AND valid_during @> ${bind(today)})
)
AND NOT other_guardian.restricted_details_enabled
AND NOT guardian.restricted_details_enabled
AND NOT child.restricted_details_enabled
AND other_guardian.street_address NOT ILIKE '%poste restante%'
AND guardian.street_address NOT ILIKE '%poste restante%'
AND child.street_address NOT ILIKE '%poste restante%'
AND (
(trim(other_guardian.residence_code) != '' AND
trim(guardian.residence_code) != '' AND
other_guardian.residence_code = guardian.residence_code) OR
(trim(other_guardian.street_address) != '' AND
trim(guardian.street_address) != '' AND
trim(other_guardian.postal_code) != '' AND
trim(guardian.postal_code) != '' AND
lower(other_guardian.street_address) = lower(guardian.street_address) AND
other_guardian.postal_code = guardian.postal_code)
)
AND (
(trim(other_guardian.residence_code) != '' AND
trim(child.residence_code) != '' AND
other_guardian.residence_code = child.residence_code) OR
(trim(other_guardian.street_address) != '' AND
trim(child.street_address) != '' AND
trim(other_guardian.postal_code) != '' AND
trim(child.postal_code) != '' AND
lower(other_guardian.street_address) = lower(child.street_address) AND
other_guardian.postal_code = child.postal_code)
Comment on lines +1312 to +1320
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tähän vois kans tarvii poste restante checkin a1d3970

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Korjattu

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

)
)
))
AND NOT EXISTS (
SELECT 1 FROM guardian_blocklist bl
WHERE bl.child_id = a.child_id
Original file line number Diff line number Diff line change
@@ -648,11 +648,13 @@ sealed interface Action {
HasGlobalRole(ADMIN, SERVICE_WORKER),
HasUnitRole(UNIT_SUPERVISOR).inPlacementPlanUnitOfApplication(),
IsCitizen(allowWeakLogin = false).ownerOfApplication(),
IsCitizen(allowWeakLogin = false).otherGuardianOfApplicationAndLivesInTheSameAddress(),
),
REJECT_DECISION(
HasGlobalRole(ADMIN, SERVICE_WORKER),
HasUnitRole(UNIT_SUPERVISOR).inPlacementPlanUnitOfApplication(),
IsCitizen(allowWeakLogin = false).ownerOfApplication(),
IsCitizen(allowWeakLogin = false).otherGuardianOfApplicationAndLivesInTheSameAddress(),
),
READ_NOTES(
HasGlobalRole(ADMIN, SERVICE_WORKER),
Original file line number Diff line number Diff line change
@@ -404,6 +404,54 @@ AND (
EXISTS (SELECT FROM guardian g WHERE g.guardian_id = aog.guardian_id AND g.child_id = a.child_id)
OR EXISTS (SELECT FROM foster_parent fp WHERE fp.parent_id = aog.guardian_id AND fp.child_id = a.child_id AND valid_during @> ${bind(now.toLocalDate())})
)
"""
)
}

fun otherGuardianOfApplicationAndLivesInTheSameAddress() =
rule<ApplicationId> { citizenId, now ->
sql(
"""
SELECT a.id
FROM application a
JOIN application_other_guardian aog ON a.id = aog.application_id
JOIN person other_guardian ON aog.guardian_id = other_guardian.id
JOIN person guardian ON a.guardian_id = guardian.id
JOIN person child ON a.child_id = child.id
WHERE aog.guardian_id = ${bind(citizenId)}
AND allow_other_guardian_access IS TRUE
AND (
EXISTS (SELECT FROM guardian g WHERE g.guardian_id = aog.guardian_id AND g.child_id = a.child_id)
OR EXISTS (SELECT FROM foster_parent fp WHERE fp.parent_id = aog.guardian_id AND fp.child_id = a.child_id AND valid_during @> ${bind(now.toLocalDate())})
)
AND NOT other_guardian.restricted_details_enabled
AND NOT guardian.restricted_details_enabled
AND NOT child.restricted_details_enabled
AND other_guardian.street_address NOT ILIKE '%poste restante%'
AND guardian.street_address NOT ILIKE '%poste restante%'
AND child.street_address NOT ILIKE '%poste restante%'
AND (
(trim(other_guardian.residence_code) != '' AND
trim(guardian.residence_code) != '' AND
other_guardian.residence_code = guardian.residence_code) OR
(trim(other_guardian.street_address) != '' AND
trim(guardian.street_address) != '' AND
trim(other_guardian.postal_code) != '' AND
trim(guardian.postal_code) != '' AND
lower(other_guardian.street_address) = lower(guardian.street_address) AND
other_guardian.postal_code = guardian.postal_code)
)
AND (
(trim(other_guardian.residence_code) != '' AND
trim(child.residence_code) != '' AND
other_guardian.residence_code = child.residence_code) OR
(trim(other_guardian.street_address) != '' AND
trim(child.street_address) != '' AND
trim(other_guardian.postal_code) != '' AND
trim(child.postal_code) != '' AND
lower(other_guardian.street_address) = lower(child.street_address) AND
other_guardian.postal_code = child.postal_code)
)
"""
)
}