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,
+)