diff --git a/common/ktor-clients/src/main/kotlin/no/nav/mulighetsrommet/ktor/clients/HttpClients.kt b/common/ktor-clients/src/main/kotlin/no/nav/mulighetsrommet/ktor/clients/HttpClients.kt index 1bebbda4c3..97c0d51d4c 100644 --- a/common/ktor-clients/src/main/kotlin/no/nav/mulighetsrommet/ktor/clients/HttpClients.kt +++ b/common/ktor-clients/src/main/kotlin/no/nav/mulighetsrommet/ktor/clients/HttpClients.kt @@ -43,6 +43,7 @@ fun httpJsonClient(engine: HttpClientEngine = CIO.create()) = HttpClient(engine) json( Json { ignoreUnknownKeys = true + coerceInputValues = true }, ) } diff --git a/frontend/arrangor-flate/app/domene/domene.ts b/frontend/arrangor-flate/app/domene/domene.ts index bb2726d8bb..d29b5a344c 100644 --- a/frontend/arrangor-flate/app/domene/domene.ts +++ b/frontend/arrangor-flate/app/domene/domene.ts @@ -41,7 +41,7 @@ export type TilsagnDetaljer = { export interface Deltaker { id: string; - person?: RefusjonKravDeltakelsePerson; + person: RefusjonKravDeltakelsePerson; veileder?: string; startDatoTiltaket?: string; startDatoPerioden?: string; diff --git a/frontend/arrangor-flate/app/mocks/arrangorflateMocks.ts b/frontend/arrangor-flate/app/mocks/arrangorflateMocks.ts index 7a9d113bbc..da8ddf1933 100644 --- a/frontend/arrangor-flate/app/mocks/arrangorflateMocks.ts +++ b/frontend/arrangor-flate/app/mocks/arrangorflateMocks.ts @@ -29,8 +29,9 @@ const mockKrav: RefusjonKravAft[] = [ { id: uuid(), person: { - norskIdent: "01012212345", navn: "Per Petterson", + fodselsdato: "1980-01-01", + fodselsaar: 1980, }, manedsverk: 0.3, perioder: [ @@ -44,8 +45,8 @@ const mockKrav: RefusjonKravAft[] = [ { id: uuid(), person: { - norskIdent: "10012212346", navn: "Stian Bjærvik", + fodselsaar: 1980, }, manedsverk: 1, perioder: [ @@ -90,8 +91,9 @@ const mockKrav: RefusjonKravAft[] = [ { id: uuid(), person: { - norskIdent: "01012212345", navn: "Per Petterson", + fodselsdato: "1980-01-01", + fodselsaar: 1980, }, manedsverk: 0.3, perioder: [ @@ -105,7 +107,6 @@ const mockKrav: RefusjonKravAft[] = [ { id: uuid(), person: { - norskIdent: "10012212346", navn: "Stian Bjærvik", }, manedsverk: 1, @@ -151,8 +152,9 @@ const mockKrav: RefusjonKravAft[] = [ { id: uuid(), person: { - norskIdent: "01012212345", navn: "Per Petterson", + fodselsdato: "1980-01-01", + fodselsaar: 1980, }, manedsverk: 0.3, perioder: [ @@ -166,7 +168,6 @@ const mockKrav: RefusjonKravAft[] = [ { id: uuid(), person: { - norskIdent: "10012212346", navn: "Stian Bjærvik", }, manedsverk: 1, diff --git a/frontend/arrangor-flate/app/routes/refusjonskrav.$id.beregning.tsx b/frontend/arrangor-flate/app/routes/refusjonskrav.$id.beregning.tsx index 79b14717d7..e621ff879f 100644 --- a/frontend/arrangor-flate/app/routes/refusjonskrav.$id.beregning.tsx +++ b/frontend/arrangor-flate/app/routes/refusjonskrav.$id.beregning.tsx @@ -11,6 +11,7 @@ import { formaterDato } from "~/utils"; import { formaterNOK } from "@mr/frontend-common/utils/utils"; import { GenerelleDetaljer } from "~/components/refusjonskrav/GenerelleDetaljer"; import { sortBy, SortBySelector, SortOrder } from "~/utils/sort-by"; +import { RefusjonKravDeltakelsePerson } from "@mr/api-client"; export const meta: MetaFunction = () => { return [{ title: "Refusjon" }, { name: "description", content: "Refusjonsdetaljer" }]; @@ -97,16 +98,18 @@ export default function RefusjonskravBeregning() { {sortedData.map((deltaker) => { + const { id, person, startDatoPerioden, sluttDatoPerioden } = deltaker; + const fodselsdato = getFormattedFodselsdato(person); return ( - - {deltaker.person?.navn} - {deltaker.person?.norskIdent} + + {person.navn} + {fodselsdato} {deltaker.startDatoTiltaket} - {deltaker.startDatoPerioden && formaterDato(deltaker.startDatoPerioden)} + {startDatoPerioden && formaterDato(startDatoPerioden)} - {deltaker.sluttDatoPerioden && formaterDato(deltaker.sluttDatoPerioden)} + {sluttDatoPerioden && formaterDato(sluttDatoPerioden)} {deltaker.stillingsprosent} {deltaker.maanedsverk} @@ -152,3 +155,11 @@ function getDeltakerSelector(sortKey: DeltakerSortKey): SortBySelector return (d) => d.veileder; } } + +function getFormattedFodselsdato(person: RefusjonKravDeltakelsePerson) { + return person.fodselsdato + ? formaterDato(person.fodselsdato) + : person.fodselsaar + ? `Fødselsår: ${person.fodselsaar}` + : null; +} diff --git a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/clients/pdl/PdlOpplysning.kt b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/clients/pdl/PdlOpplysning.kt index dd7166c81a..64beea483c 100644 --- a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/clients/pdl/PdlOpplysning.kt +++ b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/clients/pdl/PdlOpplysning.kt @@ -40,3 +40,10 @@ enum class TypeGeografiskTilknytning { UDEFINERT, UTLAND, } + +enum class PdlGradering { + FORTROLIG, + STRENGT_FORTROLIG, + STRENGT_FORTROLIG_UTLAND, + UGRADERT, +} diff --git a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/okonomi/refusjon/ArrangorflateRoutes.kt b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/okonomi/refusjon/ArrangorflateRoutes.kt index 4f17fd3bf0..a4ecc73d66 100644 --- a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/okonomi/refusjon/ArrangorflateRoutes.kt +++ b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/okonomi/refusjon/ArrangorflateRoutes.kt @@ -6,13 +6,13 @@ import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.auth.* import io.ktor.server.plugins.* -import io.ktor.server.request.receive +import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.server.util.* import io.ktor.util.pipeline.* import kotlinx.serialization.Serializable -import no.nav.mulighetsrommet.api.clients.pdl.HentPersonBolkPdlQuery +import no.nav.mulighetsrommet.api.clients.pdl.PdlGradering import no.nav.mulighetsrommet.api.clients.pdl.PdlIdent import no.nav.mulighetsrommet.api.domain.dto.DeltakerDto import no.nav.mulighetsrommet.api.okonomi.models.DeltakelsePeriode @@ -51,7 +51,7 @@ fun Route.arrangorflateRoutes() { val refusjonskrav: RefusjonskravRepository by inject() val deltakerRepository: DeltakerRepository by inject() - val pdl: HentPersonBolkPdlQuery by inject() + val pdl: HentAdressebeskyttetPersonBolkPdlQuery by inject() suspend fun PipelineContext.arrangorerMedTilgang(): List { return call.principal() @@ -165,7 +165,7 @@ fun Route.arrangorflateRoutes() { } suspend fun toRefusjonskrav( - pdl: HentPersonBolkPdlQuery, + pdl: HentAdressebeskyttetPersonBolkPdlQuery, deltakerRepository: DeltakerRepository, krav: RefusjonskravDto, ) = when (val beregning = krav.beregning) { @@ -219,7 +219,7 @@ suspend fun toRefusjonskrav( } private suspend fun getPersoner( - pdl: HentPersonBolkPdlQuery, + pdl: HentAdressebeskyttetPersonBolkPdlQuery, deltakere: List, ): Map { val identer = deltakere @@ -229,16 +229,12 @@ private suspend fun getPersoner( return pdl.hentPersonBolk(identer) .map { - it.entries - .mapNotNull { (ident, person) -> - person.navn.firstOrNull()?.let { navn -> - RefusjonKravDeltakelse.Person( - norskIdent = NorskIdent(ident.value), - navn = "${navn.etternavn}, ${navn.fornavn}", - ) - } + buildMap { + it.entries.forEach { (ident, person) -> + val refusjonskravPerson = toRefusjonskravPerson(person) + put(NorskIdent(ident.value), refusjonskravPerson) } - .associateBy { person -> person.norskIdent } + } } .getOrElse { throw StatusException( @@ -248,6 +244,30 @@ private suspend fun getPersoner( } } +private fun toRefusjonskravPerson(person: HentPersonBolkResponse.Person) = + when (person.adressebeskyttelse.gradering) { + PdlGradering.UGRADERT -> { + val navn = person.navn.firstOrNull()?.let { navn -> + val fornavnOgMellomnavn = listOfNotNull(navn.fornavn, navn.mellomnavn) + .joinToString(" ") + .takeIf { it.isNotEmpty() } + listOfNotNull(navn.etternavn, fornavnOgMellomnavn).joinToString(", ") + } + val foedselsdato = person.foedselsdato.firstOrNull() + RefusjonKravDeltakelse.Person( + navn = navn ?: "Mangler navn", + fodselsaar = foedselsdato?.foedselsaar, + fodselsdato = foedselsdato?.foedselsdato, + ) + } + + else -> RefusjonKravDeltakelse.Person( + navn = "Adressebeskyttet", + fodselsaar = null, + fodselsdato = null, + ) + } + @Serializable data class RefusjonKravAft( @Serializable(with = UUIDSerializer::class) @@ -288,8 +308,10 @@ data class RefusjonKravDeltakelse( ) { @Serializable data class Person( - val norskIdent: NorskIdent, val navn: String, + @Serializable(with = LocalDateSerializer::class) + val fodselsdato: LocalDate?, + val fodselsaar: Int?, ) } diff --git a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/clients/pdl/HentPersonBolkPdlQuery.kt b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/okonomi/refusjon/HentAdressebeskyttetPersonBolkPdlQuery.kt similarity index 72% rename from mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/clients/pdl/HentPersonBolkPdlQuery.kt rename to mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/okonomi/refusjon/HentAdressebeskyttetPersonBolkPdlQuery.kt index 34c0b256a9..00c49ccc23 100644 --- a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/clients/pdl/HentPersonBolkPdlQuery.kt +++ b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/okonomi/refusjon/HentAdressebeskyttetPersonBolkPdlQuery.kt @@ -1,14 +1,17 @@ -package no.nav.mulighetsrommet.api.clients.pdl +package no.nav.mulighetsrommet.api.okonomi.refusjon import arrow.core.Either import arrow.core.NonEmptySet import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import no.nav.mulighetsrommet.api.clients.pdl.* +import no.nav.mulighetsrommet.domain.serializers.LocalDateSerializer import no.nav.mulighetsrommet.securelog.SecureLog import no.nav.mulighetsrommet.tokenprovider.AccessType import org.slf4j.LoggerFactory +import java.time.LocalDate -class HentPersonBolkPdlQuery( +class HentAdressebeskyttetPersonBolkPdlQuery( private val pdl: PdlClient, ) { private val log = LoggerFactory.getLogger(javaClass) @@ -18,14 +21,21 @@ class HentPersonBolkPdlQuery( query = """ query(${'$'}identer: [ID!]!) { hentPersonBolk(identer: ${'$'}identer) { - ident, + ident person { navn { fornavn mellomnavn etternavn } - }, + adressebeskyttelse { + gradering + } + foedselsdato { + foedselsdato + foedselsaar + } + } code } } @@ -41,7 +51,7 @@ class HentPersonBolkPdlQuery( val person = requireNotNull(it.person) { "person forventet siden response var OK" } - PdlIdent(it.ident) to HentPersonBolkResponse.Person(person.navn) + PdlIdent(it.ident) to person } else -> { @@ -63,6 +73,20 @@ data class HentPersonBolkResponse( @Serializable data class Person( val navn: List, + val adressebeskyttelse: Adressebeskyttelse, + val foedselsdato: List, + ) + + @Serializable + data class Adressebeskyttelse( + val gradering: PdlGradering = PdlGradering.UGRADERT, + ) + + @Serializable + data class Foedselsdato( + val foedselsaar: Int, + @Serializable(with = LocalDateSerializer::class) + val foedselsdato: LocalDate?, ) @Serializable diff --git a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/plugins/DependencyInjection.kt b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/plugins/DependencyInjection.kt index b6c0907ccb..12a8738283 100644 --- a/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/plugins/DependencyInjection.kt +++ b/mulighetsrommet-api/src/main/kotlin/no/nav/mulighetsrommet/api/plugins/DependencyInjection.kt @@ -24,11 +24,11 @@ import no.nav.mulighetsrommet.api.clients.msgraph.MicrosoftGraphClient import no.nav.mulighetsrommet.api.clients.norg2.Norg2Client import no.nav.mulighetsrommet.api.clients.oppfolging.VeilarboppfolgingClient import no.nav.mulighetsrommet.api.clients.pamOntologi.PamOntologiClient -import no.nav.mulighetsrommet.api.clients.pdl.HentPersonBolkPdlQuery import no.nav.mulighetsrommet.api.clients.pdl.PdlClient import no.nav.mulighetsrommet.api.clients.sanity.SanityClient import no.nav.mulighetsrommet.api.clients.tiltakshistorikk.TiltakshistorikkClient import no.nav.mulighetsrommet.api.clients.vedtak.VeilarbvedtaksstotteClient +import no.nav.mulighetsrommet.api.okonomi.refusjon.HentAdressebeskyttetPersonBolkPdlQuery import no.nav.mulighetsrommet.api.okonomi.refusjon.RefusjonService import no.nav.mulighetsrommet.api.okonomi.refusjon.RefusjonskravRepository import no.nav.mulighetsrommet.api.okonomi.tilsagn.TilsagnRepository @@ -226,7 +226,7 @@ private fun services(appConfig: AppConfig) = module { tokenProvider = cachedTokenProvider.withScope(appConfig.pdl.scope), ) } - single { HentPersonBolkPdlQuery(get()) } + single { HentAdressebeskyttetPersonBolkPdlQuery(get()) } single { PoaoTilgangHttpClient( baseUrl = appConfig.poaoTilgang.url, diff --git a/mulighetsrommet-api/src/main/resources/web/openapi.yaml b/mulighetsrommet-api/src/main/resources/web/openapi.yaml index 25508b3604..5e73669f97 100644 --- a/mulighetsrommet-api/src/main/resources/web/openapi.yaml +++ b/mulighetsrommet-api/src/main/resources/web/openapi.yaml @@ -5028,16 +5028,19 @@ components: - id - perioder - manedsverk + - person RefusjonKravDeltakelsePerson: type: object properties: - norskIdent: - type: string navn: type: string + fodselsdato: + type: string + format: date + fodselsaar: + type: integer required: - - norskIdent - navn RefusjonKravDeltakelsePeriode: diff --git a/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/clients/pdl/HentPersonBolkPdlQueryTest.kt b/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/clients/pdl/HentPersonBolkPdlQueryTest.kt index 4904a4ebc1..4ab914f29a 100644 --- a/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/clients/pdl/HentPersonBolkPdlQueryTest.kt +++ b/mulighetsrommet-api/src/test/kotlin/no/nav/mulighetsrommet/api/clients/pdl/HentPersonBolkPdlQueryTest.kt @@ -4,8 +4,11 @@ import arrow.core.nonEmptySetOf import io.kotest.assertions.arrow.core.shouldBeRight import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe +import io.ktor.client.engine.mock.* import io.ktor.http.content.* import kotlinx.serialization.json.Json +import no.nav.mulighetsrommet.api.okonomi.refusjon.HentAdressebeskyttetPersonBolkPdlQuery +import no.nav.mulighetsrommet.api.okonomi.refusjon.HentPersonBolkResponse import no.nav.mulighetsrommet.ktor.createMockEngine import no.nav.mulighetsrommet.ktor.respondJson @@ -13,64 +16,122 @@ class HentPersonBolkPdlQueryTest : FunSpec({ test("happy case hentPersonBolk") { val identer = nonEmptySetOf(PdlIdent("12345678910"), PdlIdent("12345678911"), PdlIdent("test")) - val pdl = PdlClient( - config = PdlClient.Config(baseUrl = "https://pdl.no"), - tokenProvider = { "token" }, - clientEngine = createMockEngine( - "/graphql" to { + val clientEngine = createMockEngine( + "/graphql" to { - val body = Json.decodeFromString>( - (it.body as TextContent).text, - ) - body.variables.identer shouldBe identer + val body = Json.decodeFromString>( + (it.body as TextContent).text, + ) + body.variables.identer shouldBe identer - respondJson( - """ - { - "data": { - "hentPersonBolk": [ - { - "ident": "12345678910", - "person": { - "navn": [ - { - "fornavn": "Ola", - "mellomnavn": null, - "etternavn": "Normann" - } - ] - }, - "code": "ok" + respondJson( + """ + { + "data": { + "hentPersonBolk": [ + { + "ident": "12345678910", + "person": { + "navn": [ + { + "fornavn": "Ola", + "mellomnavn": null, + "etternavn": "Normann" + } + ], + "adressebeskyttelse": { + "gradering": "STRENGT_FORTROLIG" + }, + "foedselsdato": [ + { + "foedselsaar": 1980, + "foedselsdato": null + } + ] }, - { - "ident": "12345678911", - "person": null, - "code": "not_found" - }, - { - "ident": "test", - "person": null, - "code": "bad_request" - } - ] - } + "code": "ok" + }, + { + "ident": "12345678911", + "person": null, + "code": "not_found" + }, + { + "ident": "test", + "person": null, + "code": "bad_request" + } + ] } - """.trimIndent(), - ) - }, - ), + } + """.trimIndent(), + ) + }, ) - val query = HentPersonBolkPdlQuery(pdl) - - val response = query.hentPersonBolk(identer).shouldBeRight() + val query = HentAdressebeskyttetPersonBolkPdlQuery(createPdlClient(clientEngine)) - response shouldBe mapOf( + query.hentPersonBolk(identer) shouldBeRight mapOf( PdlIdent("12345678910") to HentPersonBolkResponse.Person( navn = listOf( PdlNavn(fornavn = "Ola", etternavn = "Normann"), ), + adressebeskyttelse = HentPersonBolkResponse.Adressebeskyttelse( + gradering = PdlGradering.STRENGT_FORTROLIG, + ), + foedselsdato = listOf( + HentPersonBolkResponse.Foedselsdato( + foedselsaar = 1980, + foedselsdato = null, + ), + ), + ), + ) + } + + test("tolker manglende gradering som UGRADERT") { + val clientEngine = createMockEngine( + "/graphql" to { + respondJson( + """ + { + "data": { + "hentPersonBolk": [ + { + "ident": "12345678910", + "person": { + "navn": [], + "adressebeskyttelse": { + "gradering": null + }, + "foedselsdato": [] + }, + "code": "ok" + } + ] + } + } + """.trimIndent(), + ) + }, + ) + + val query = HentAdressebeskyttetPersonBolkPdlQuery(createPdlClient(clientEngine)) + + query.hentPersonBolk(nonEmptySetOf(PdlIdent("12345678910"))) shouldBeRight mapOf( + PdlIdent("12345678910") to HentPersonBolkResponse.Person( + navn = listOf(), + adressebeskyttelse = HentPersonBolkResponse.Adressebeskyttelse( + gradering = PdlGradering.UGRADERT, + ), + foedselsdato = listOf(), ), ) } }) + +private fun createPdlClient(clientEngine: MockEngine) = PdlClient( + config = PdlClient.Config(baseUrl = "https://pdl.no"), + tokenProvider = { "token" }, + clientEngine = clientEngine, +)