diff --git a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/refusjon/ArrangorflateRoutes.kt b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/refusjon/ArrangorflateRoutes.kt index 70cd20f387..437b8ab065 100644 --- a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/refusjon/ArrangorflateRoutes.kt +++ b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/refusjon/ArrangorflateRoutes.kt @@ -111,14 +111,12 @@ fun Route.arrangorflateRoutes() { ?: throw NotFoundException("Fant ikke refusjonskrav med id=$id") requireTilgangHosArrangor(krav.arrangor.organisasjonsnummer) - val deltakere = deltakerRepository.getAll(krav.gjennomforing.id) - val forslagByDeltakerId = deltakerForslagRepository.get(deltakere.map { it.id }) + val forslagByDeltakerId = deltakerForslagRepository.getForslagByGjennomforing(krav.gjennomforing.id) - val relevanteForslag = deltakere - .map { deltaker -> - val forslag = forslagByDeltakerId[deltaker.id] ?: emptyList() + val relevanteForslag = forslagByDeltakerId + .map { (deltakerId, forslag) -> RelevanteForslag( - deltakerId = deltaker.id, + deltakerId = deltakerId, antallRelevanteForslag = forslag.count { it.relevantForDeltakelse(krav) }, ) } @@ -133,7 +131,11 @@ fun Route.arrangorflateRoutes() { requireTilgangHosArrangor(krav.arrangor.organisasjonsnummer) val request = call.receive() - validerGodkjennRefusjonskrav(request, krav) + validerGodkjennRefusjonskrav( + request, + krav, + deltakerForslagRepository.getForslagByGjennomforing(krav.gjennomforing.id), + ) .onLeft { return@post call.respondWithStatusResponse(BadRequest(errors = it).left()) } db.transactionSuspend { tx -> @@ -249,8 +251,21 @@ fun DeltakerForslag.relevantForDeltakelse( fun validerGodkjennRefusjonskrav( request: GodkjennRefusjonskrav, krav: RefusjonskravDto, + forslagByDeltakerId: Map>, ): Either, GodkjennRefusjonskrav> { - return if (request.digest != krav.beregning.getDigest()) { + val finnesRelevanteForslag = forslagByDeltakerId + .any { (_, forslag) -> + forslag.count { it.relevantForDeltakelse(krav) } > 0 + } + + return if (finnesRelevanteForslag) { + listOf( + ValidationError.ofCustomLocation( + "info", + "Det finnes forslag på deltakere som påvirker refusjonen. Disse må godkjennes av Nav først.", + ), + ).left() + } else if (request.digest != krav.beregning.getDigest()) { listOf( ValidationError.ofCustomLocation( "info", diff --git a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/refusjon/db/DeltakerForslagRepository.kt b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/refusjon/db/DeltakerForslagRepository.kt index 5a0aaedf0c..52590434fe 100644 --- a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/refusjon/db/DeltakerForslagRepository.kt +++ b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/refusjon/db/DeltakerForslagRepository.kt @@ -56,19 +56,20 @@ class DeltakerForslagRepository(private val db: Database) { .let { db.run(it) } } - fun get(deltakerIds: List): Map> { + fun getForslagByGjennomforing(gjennomforingId: UUID): Map> { @Language("PostgreSQL") val query = """ - select - id, - deltaker_id, - endring, - status - from deltaker_forslag - where deltaker_id = any (?) + select + deltaker.id as deltaker_id, + deltaker_forslag.id, + deltaker_forslag.endring, + deltaker_forslag.status + from deltaker + inner join deltaker_forslag on deltaker.id = deltaker_forslag.deltaker_id + where deltaker.gjennomforing_id = ?::uuid """.trimIndent() - return queryOf(query, db.createUuidArray(deltakerIds)) + return queryOf(query, gjennomforingId) .map { it.toForslagDbo() } .asList .let { db.run(it) } diff --git a/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/refusjon/ArrangorflateRoutesTest.kt b/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/refusjon/ArrangorflateRoutesTest.kt index dbc1c30b11..ce81594145 100644 --- a/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/refusjon/ArrangorflateRoutesTest.kt +++ b/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/refusjon/ArrangorflateRoutesTest.kt @@ -22,16 +22,21 @@ import no.nav.mulighetsrommet.api.createAuthConfig import no.nav.mulighetsrommet.api.createTestApplicationConfig import no.nav.mulighetsrommet.api.databaseConfig import no.nav.mulighetsrommet.api.fixtures.* +import no.nav.mulighetsrommet.api.refusjon.db.DeltakerDbo +import no.nav.mulighetsrommet.api.refusjon.db.DeltakerForslag +import no.nav.mulighetsrommet.api.refusjon.db.DeltakerForslag.Status +import no.nav.mulighetsrommet.api.refusjon.db.DeltakerForslagRepository import no.nav.mulighetsrommet.api.refusjon.db.RefusjonskravDbo -import no.nav.mulighetsrommet.api.refusjon.model.RefusjonKravAft -import no.nav.mulighetsrommet.api.refusjon.model.RefusjonKravBeregningAft -import no.nav.mulighetsrommet.api.refusjon.model.RefusjonskravPeriode +import no.nav.mulighetsrommet.api.refusjon.model.* import no.nav.mulighetsrommet.api.withTestApplication import no.nav.mulighetsrommet.database.kotest.extensions.FlywayDatabaseTestListener import no.nav.mulighetsrommet.database.kotest.extensions.truncateAll +import no.nav.mulighetsrommet.domain.dto.DeltakerStatus import no.nav.mulighetsrommet.domain.dto.Kontonummer import no.nav.mulighetsrommet.domain.dto.NorskIdent import no.nav.mulighetsrommet.domain.dto.Organisasjonsnummer +import no.nav.mulighetsrommet.domain.dto.amt.EndringAarsak +import no.nav.mulighetsrommet.domain.dto.amt.Melding import no.nav.mulighetsrommet.ktor.createMockEngine import no.nav.mulighetsrommet.ktor.respondJson import no.nav.security.mock.oauth2.MockOAuth2Server @@ -59,6 +64,20 @@ class ArrangorflateRoutesTest : FunSpec({ poststed = "Oslo", overordnetEnhet = hovedenhet.organisasjonsnummer, ) + val deltaker = DeltakerDbo( + id = UUID.randomUUID(), + gjennomforingId = TiltaksgjennomforingFixtures.AFT1.id, + startDato = TiltaksgjennomforingFixtures.AFT1.startDato, + sluttDato = TiltaksgjennomforingFixtures.AFT1.sluttDato, + registrertTidspunkt = TiltaksgjennomforingFixtures.AFT1.startDato.atStartOfDay(), + endretTidspunkt = LocalDateTime.now(), + deltakelsesprosent = 100.0, + status = DeltakerStatus( + type = DeltakerStatus.Type.DELTAR, + aarsak = null, + opprettetDato = LocalDateTime.now(), + ), + ) val krav = RefusjonskravDbo( id = UUID.randomUUID(), gjennomforingId = TiltaksgjennomforingFixtures.AFT1.id, @@ -67,11 +86,27 @@ class ArrangorflateRoutesTest : FunSpec({ input = RefusjonKravBeregningAft.Input( periode = RefusjonskravPeriode.fromDayInMonth(LocalDate.of(2024, 8, 1)), sats = 20205, - deltakelser = emptySet(), + deltakelser = setOf( + DeltakelsePerioder( + deltakelseId = deltaker.id, + perioder = listOf( + DeltakelsePeriode( + start = LocalDate.of(2024, 8, 1), + slutt = LocalDate.of(2024, 8, 31), + deltakelsesprosent = 100.0, + ), + ), + ), + ), ), output = RefusjonKravBeregningAft.Output( belop = 0, - deltakelser = emptySet(), + deltakelser = setOf( + DeltakelseManedsverk( + deltakelseId = deltaker.id, + manedsverk = 1.0, + ), + ), ), ), kontonummer = Kontonummer("12312312312"), @@ -89,7 +124,7 @@ class ArrangorflateRoutesTest : FunSpec({ ), ), gjennomforinger = listOf(TiltaksgjennomforingFixtures.AFT1.copy(arrangorId = barnevernsNembda.id)), - deltakere = emptyList(), + deltakere = listOf(deltaker), arrangorer = listOf(hovedenhet, barnevernsNembda), refusjonskrav = listOf(krav), ) @@ -291,4 +326,40 @@ class ArrangorflateRoutesTest : FunSpec({ response.status shouldBe HttpStatusCode.OK } } + + test("ikke lov å godkjenne når det finnes relevante forslag") { + val deltakerForslagRepository = DeltakerForslagRepository(database.db) + deltakerForslagRepository.upsert( + DeltakerForslag( + id = UUID.randomUUID(), + deltakerId = deltaker.id, + endring = Melding.Forslag.Endring.AvsluttDeltakelse( + aarsak = EndringAarsak.Syk, + harDeltatt = false, + ), + status = Status.VENTER_PA_SVAR, + ), + ) + withTestApplication(appConfig()) { + val client = createClient { + install(ContentNegotiation) { + json() + } + } + val response = client.post("/api/v1/intern/arrangorflate/refusjonskrav/${krav.id}/godkjenn-refusjon") { + bearerAuth(oauth.issueToken(claims = mapOf("pid" to identMedTilgang.value)).serialize()) + contentType(ContentType.Application.Json) + setBody( + GodkjennRefusjonskrav( + digest = krav.beregning.getDigest(), + betalingsinformasjon = GodkjennRefusjonskrav.Betalingsinformasjon( + kontonummer = Kontonummer("12312312312"), + kid = null, + ), + ), + ) + } + response.status shouldBe HttpStatusCode.BadRequest + } + } }) diff --git a/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/refusjon/db/DeltakerForslagRepositoryTest.kt b/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/refusjon/db/DeltakerForslagRepositoryTest.kt index 836a88dabf..55a1b733c2 100644 --- a/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/refusjon/db/DeltakerForslagRepositoryTest.kt +++ b/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/refusjon/db/DeltakerForslagRepositoryTest.kt @@ -50,10 +50,10 @@ class DeltakerForslagRepositoryTest : FunSpec({ repository.upsert(forslag) - repository.get(listOf(deltaker.id))[deltaker.id] shouldBe listOf(forslag) + repository.getForslagByGjennomforing(TiltaksgjennomforingFixtures.Oppfolging1.id)[deltaker.id] shouldBe listOf(forslag) repository.delete(forslag.id) - repository.get(listOf(deltaker.id)).containsKey(deltaker.id) shouldBe false + repository.getForslagByGjennomforing(TiltaksgjennomforingFixtures.Oppfolging1.id).containsKey(deltaker.id) shouldBe false } })