Skip to content

Commit 10e0c8b

Browse files
authored
Kopierte klassifiseringslogikk fra dp-inntekt-klassifiserer (#98)
* Kopierte klassifiseringslogikk fra dp-inntekt-klassifiserer Lagde HTTP endepunkt som svarer på klassifisertinntekt, aka Inntekt fra dapgenger-events Lagde gRPC endepunkt som svarer på klassifisertinntekt, aka Inntekt fra dagpenger-events Oppdaterte gRPC klient til å behandle nytt gRPC endepunkt navikt/dagpenger#406
1 parent 10dca03 commit 10e0c8b

24 files changed

+3347
-119
lines changed

dp-inntekt-api/src/main/kotlin/no/nav/dagpenger/inntekt/BehandlingsInntektsGetter.kt

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,42 @@
11
package no.nav.dagpenger.inntekt
22

3+
import no.nav.dagpenger.events.inntekt.v1.Inntekt
4+
import no.nav.dagpenger.events.inntekt.v1.SpesifisertInntekt
35
import no.nav.dagpenger.inntekt.db.InntektStore
46
import no.nav.dagpenger.inntekt.db.Inntektparametre
57
import no.nav.dagpenger.inntekt.db.StoreInntektCommand
68
import no.nav.dagpenger.inntekt.db.StoredInntekt
79
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.InntektkomponentRequest
810
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.InntektskomponentClient
9-
import no.nav.dagpenger.inntekt.opptjeningsperiode.Opptjeningsperiode
11+
import no.nav.dagpenger.inntekt.klassifiserer.klassifiserOgMapInntekt
12+
import no.nav.dagpenger.inntekt.mapping.mapToSpesifisertInntekt
1013

1114
class BehandlingsInntektsGetter(
1215
private val inntektskomponentClient: InntektskomponentClient,
1316
private val inntektStore: InntektStore
1417
) {
1518

16-
suspend fun getBehandlingsInntekt(inntektparametre: Inntektparametre): StoredInntekt {
17-
val opptjeningsperiode = Opptjeningsperiode(inntektparametre.beregningsdato)
19+
suspend fun getKlassifisertInntekt(inntektparametre: Inntektparametre): Inntekt {
20+
return klassifiserOgMapInntekt(getSpesifisertInntekt(inntektparametre))
21+
}
1822

19-
val inntektkomponentRequest = InntektkomponentRequest(
20-
inntektparametre.aktørId,
21-
opptjeningsperiode.førsteMåned,
22-
opptjeningsperiode.sisteAvsluttendeKalenderMåned
23-
)
23+
suspend fun getSpesifisertInntekt(inntektparametre: Inntektparametre): SpesifisertInntekt {
24+
return mapToSpesifisertInntekt(getBehandlingsInntekt(inntektparametre), inntektparametre.opptjeningsperiode.sisteAvsluttendeKalenderMåned)
25+
}
2426

27+
internal suspend fun getBehandlingsInntekt(inntektparametre: Inntektparametre): StoredInntekt {
2528
return isInntektStored(inntektparametre)?.let { inntektStore.getInntekt(it) }
26-
?: fetchAndStoreInntekt(inntektparametre, inntektkomponentRequest)
29+
?: fetchAndStoreInntekt(inntektparametre)
2730
}
2831

2932
private suspend fun fetchAndStoreInntekt(
30-
inntektparametre: Inntektparametre,
31-
inntektkomponentRequest: InntektkomponentRequest
33+
inntektparametre: Inntektparametre
3234
): StoredInntekt {
35+
val inntektkomponentRequest = InntektkomponentRequest(
36+
inntektparametre.aktørId,
37+
inntektparametre.opptjeningsperiode.førsteMåned,
38+
inntektparametre.opptjeningsperiode.sisteAvsluttendeKalenderMåned
39+
)
3340
return inntektStore.storeInntekt(
3441
StoreInntektCommand(
3542
inntektparametre = inntektparametre,

dp-inntekt-api/src/main/kotlin/no/nav/dagpenger/inntekt/InntektApi.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ import no.nav.dagpenger.inntekt.oppslag.OppslagClient
5050
import no.nav.dagpenger.inntekt.rpc.InntektGrpcServer
5151
import no.nav.dagpenger.inntekt.subsumsjonbrukt.KafkaSubsumsjonBruktDataConsumer
5252
import no.nav.dagpenger.inntekt.subsumsjonbrukt.Vaktmester
53+
import no.nav.dagpenger.inntekt.v1.inntekt
5354
import no.nav.dagpenger.inntekt.v1.opptjeningsperiodeApi
54-
import no.nav.dagpenger.inntekt.v1.spesifisertInntekt
5555
import no.nav.dagpenger.inntekt.v1.uklassifisertInntekt
5656
import no.nav.dagpenger.ktor.auth.ApiKeyCredential
5757
import no.nav.dagpenger.ktor.auth.ApiKeyVerifier
@@ -272,7 +272,7 @@ fun Application.inntektApi(
272272
routing {
273273
route("/v1") {
274274
route("/inntekt") {
275-
spesifisertInntekt(behandlingsInntektsGetter)
275+
inntekt(behandlingsInntektsGetter)
276276
uklassifisertInntekt(inntektskomponentHttpClient, inntektStore, oppslagClient)
277277
}
278278
opptjeningsperiodeApi(inntektStore)

dp-inntekt-api/src/main/kotlin/no/nav/dagpenger/inntekt/db/InntektStore.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import java.time.ZoneOffset
77
import java.time.ZonedDateTime
88
import no.nav.dagpenger.events.inntekt.v1.SpesifisertInntekt
99
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.InntektkomponentResponse
10+
import no.nav.dagpenger.inntekt.opptjeningsperiode.Opptjeningsperiode
1011

1112
interface InntektStore {
1213
fun getInntekt(inntektId: InntektId): StoredInntekt
@@ -24,7 +25,7 @@ data class Inntektparametre(
2425
val beregningsdato: LocalDate,
2526
valdselnummer: String? = null
2627
) {
27-
internal fun migrateCandidate(): Boolean = kotlin.runCatching { vedtakId.toLong() }.isSuccess
28+
val opptjeningsperiode: Opptjeningsperiode = Opptjeningsperiode(beregningsdato)
2829
}
2930

3031
data class StoreInntektCommand(
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package no.nav.dagpenger.inntekt.klassifiserer
2+
3+
import java.math.BigDecimal
4+
import java.time.YearMonth
5+
import no.nav.dagpenger.events.inntekt.v1.Avvik
6+
import no.nav.dagpenger.events.inntekt.v1.Inntekt
7+
import no.nav.dagpenger.events.inntekt.v1.KlassifisertInntekt
8+
import no.nav.dagpenger.events.inntekt.v1.KlassifisertInntektMåned
9+
import no.nav.dagpenger.events.inntekt.v1.SpesifisertInntekt
10+
11+
fun klassifiserOgMapInntekt(spesifisertInntekt: SpesifisertInntekt): Inntekt {
12+
val klassifisertePosteringer = klassifiserPosteringer(spesifisertInntekt.posteringer)
13+
14+
val avvikMåneder = spesifisertInntekt.avvik.groupBy { it.avvikPeriode }
15+
16+
val klassifisertInntektMåneder = mapTilKlassifisertInntektMåneder(klassifisertePosteringer, avvikMåneder)
17+
18+
return Inntekt(
19+
inntektsId = spesifisertInntekt.inntektId.id,
20+
inntektsListe = klassifisertInntektMåneder,
21+
manueltRedigert = spesifisertInntekt.manueltRedigert,
22+
sisteAvsluttendeKalenderMåned = spesifisertInntekt.sisteAvsluttendeKalenderMåned
23+
)
24+
}
25+
26+
private fun mapTilKlassifisertInntektMåneder(klassifisertePosteringer: List<KlassifisertPostering>, avvikMåneder: Map<YearMonth, List<Avvik>>) =
27+
klassifisertePosteringer
28+
.groupBy { klassifisertPostering -> klassifisertPostering.postering.posteringsMåned }
29+
.map { (årmåned, klassifisertePosteringerForMåned) ->
30+
mapTilKlassifisertInntektMåned(årmåned, klassifisertePosteringerForMåned, avvikMåneder)
31+
}
32+
33+
private fun mapTilKlassifisertInntektMåned(
34+
årmåned: YearMonth,
35+
klassifisertePosteringer: List<KlassifisertPostering>,
36+
avvikMåneder: Map<YearMonth, List<Avvik>>
37+
) = KlassifisertInntektMåned(
38+
årMåned = årmåned,
39+
harAvvik = avvikMåneder.containsKey(årmåned),
40+
klassifiserteInntekter = klassifisertePosteringer
41+
.groupBy { (_, inntektKlasse) -> inntektKlasse }
42+
.map { (klasse, klassifisertePosteringerForKlasse) ->
43+
KlassifisertInntekt(
44+
beløp = klassifisertePosteringerForKlasse.fold(BigDecimal.ZERO) { sum, postering -> sum + postering.postering.beløp },
45+
inntektKlasse = klasse
46+
)
47+
}
48+
)
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package no.nav.dagpenger.inntekt.klassifiserer
2+
3+
import no.nav.dagpenger.events.inntekt.v1.InntektKlasse
4+
import no.nav.dagpenger.events.inntekt.v1.Postering
5+
import no.nav.dagpenger.events.inntekt.v1.PosteringsType
6+
7+
internal data class KlassifisertPostering(val postering: Postering, val inntektKlasse: InntektKlasse)
8+
9+
internal fun klassifiserPosteringer(posteringer: List<Postering>): List<KlassifisertPostering> {
10+
return posteringer.map { KlassifisertPostering(it, klassifiserPosteringsType(it.posteringsType)) }
11+
}
12+
13+
private fun klassifiserPosteringsType(posteringsType: PosteringsType): InntektKlasse {
14+
return when {
15+
isArbeidsInntekt(posteringsType) -> InntektKlasse.ARBEIDSINNTEKT
16+
isFangstFiske(posteringsType) -> InntektKlasse.FANGST_FISKE
17+
isDagpenger(posteringsType) -> InntektKlasse.DAGPENGER
18+
isDagpengerFangstFiske(posteringsType) -> InntektKlasse.DAGPENGER_FANGST_FISKE
19+
isSykepengerFangstFiske(posteringsType) -> InntektKlasse.SYKEPENGER_FANGST_FISKE
20+
isSykepenger(posteringsType) -> InntektKlasse.SYKEPENGER
21+
isTiltakslønn(posteringsType) -> InntektKlasse.TILTAKSLØNN
22+
else -> throw KlassifiseringsException("Unknown inntektklasse for $posteringsType")
23+
}
24+
}
25+
26+
private fun isArbeidsInntekt(posteringsType: PosteringsType): Boolean {
27+
val arbeidsPosteringsTyper = listOf(
28+
PosteringsType.L_SKATTEPLIKTIG_PERSONALRABATT,
29+
PosteringsType.L_TIPS,
30+
PosteringsType.L_AKSJER_GRUNNFONDSBEVIS_TIL_UNDERKURS,
31+
PosteringsType.L_ANNET,
32+
PosteringsType.L_ARBEIDSOPPHOLD_KOST,
33+
PosteringsType.L_ARBEIDSOPPHOLD_LOSJI,
34+
PosteringsType.L_BEREGNET_SKATT,
35+
PosteringsType.L_BESØKSREISER_HJEMMET_ANNET,
36+
PosteringsType.L_BESØKSREISER_HJEMMET_KILOMETERGODTGJØRELSE_BIL,
37+
PosteringsType.L_BETALT_UTENLANDSK_SKATT,
38+
PosteringsType.L_BIL,
39+
PosteringsType.L_BOLIG,
40+
PosteringsType.L_BONUS,
41+
PosteringsType.L_BONUS_FRA_FORSVARET,
42+
PosteringsType.L_ELEKTRONISK_KOMMUNIKASJON,
43+
PosteringsType.L_FAST_BILGODTGJØRELSE,
44+
PosteringsType.L_FAST_TILLEGG,
45+
PosteringsType.L_FASTLØNN,
46+
PosteringsType.L_FERIEPENGER,
47+
PosteringsType.L_FOND_FOR_IDRETTSUTØVERE,
48+
PosteringsType.Y_FORELDREPENGER,
49+
PosteringsType.L_HELLIGDAGSTILLEGG,
50+
PosteringsType.L_HONORAR_AKKORD_PROSENT_PROVISJON,
51+
PosteringsType.L_HYRETILLEGG,
52+
PosteringsType.L_INNBETALING_TIL_UTENLANDSK_PENSJONSORDNING,
53+
PosteringsType.L_KILOMETERGODTGJØRELSE_BIL,
54+
PosteringsType.L_KOMMUNAL_OMSORGSLØNN_OG_FOSTERHJEMSGODTGJØRELSE,
55+
PosteringsType.L_KOST_DAGER,
56+
PosteringsType.L_KOST_DØGN,
57+
PosteringsType.L_KOSTBESPARELSE_I_HJEMMET,
58+
PosteringsType.L_LOSJI,
59+
PosteringsType.L_IKKE_SKATTEPLIKTIG_LØNN_FRA_UTENLANDSK_DIPLOM_KONSUL_STASJON,
60+
PosteringsType.L_LØNN_FOR_BARNEPASS_I_BARNETS_HJEM,
61+
PosteringsType.L_LØNN_TIL_PRIVATPERSONER_FOR_ARBEID_I_HJEMMET,
62+
PosteringsType.L_LØNN_UTBETALT_AV_VELDEDIG_ELLER_ALLMENNYTTIG_INSTITUSJON_ELLER_ORGANISASJON,
63+
PosteringsType.L_LØNN_TIL_VERGE_FRA_FYLKESMANNEN,
64+
PosteringsType.L_OPSJONER,
65+
PosteringsType.L_OVERTIDSGODTGJØRELSE,
66+
PosteringsType.L_REISE_ANNET,
67+
PosteringsType.L_REISE_KOST,
68+
PosteringsType.L_REISE_LOSJI,
69+
PosteringsType.L_RENTEFORDEL_LÅN,
70+
PosteringsType.L_SKATTEPLIKTIG_DEL_FORSIKRINGER,
71+
PosteringsType.L_SLUTTVEDERLAG,
72+
PosteringsType.L_SMUSSTILLEGG,
73+
PosteringsType.L_STIPEND,
74+
PosteringsType.L_STYREHONORAR_OG_GODTGJØRELSE_VERV,
75+
PosteringsType.Y_SVANGERSKAPSPENGER,
76+
PosteringsType.L_TIMELØNN,
77+
PosteringsType.L_TREKK_I_LØNN_FOR_FERIE,
78+
PosteringsType.L_UREGELMESSIGE_TILLEGG_KNYTTET_TIL_ARBEIDET_TID,
79+
PosteringsType.L_UREGELMESSIGE_TILLEGG_KNYTTET_TIL_IKKE_ARBEIDET_TID,
80+
PosteringsType.L_YRKEBIL_TJENESTLIGBEHOV_KILOMETER,
81+
PosteringsType.L_YRKEBIL_TJENESTLIGBEHOV_LISTEPRIS,
82+
PosteringsType.L_ANNET_H,
83+
PosteringsType.L_BONUS_H,
84+
PosteringsType.L_FAST_TILLEGG_H,
85+
PosteringsType.L_FASTLØNN_H,
86+
PosteringsType.L_FERIEPENGER_H,
87+
PosteringsType.L_HELLIGDAGSTILLEGG_H,
88+
PosteringsType.L_OVERTIDSGODTGJØRELSE_H,
89+
PosteringsType.L_SLUTTVEDERLAG_H,
90+
PosteringsType.L_TIMELØNN_H,
91+
PosteringsType.L_UREGELMESSIGE_TILLEGG_KNYTTET_TIL_ARBEIDET_TID_H,
92+
PosteringsType.L_UREGELMESSIGE_TILLEGG_KNYTTET_TIL_IKKE_ARBEIDET_TID_H,
93+
PosteringsType.L_TREKK_I_LØNN_FOR_FERIE_H,
94+
PosteringsType.L_ANNET_IKKE_SKATTEPLIKTIG,
95+
PosteringsType.L_ANNET_UTLANDET,
96+
PosteringsType.L_ANNET_KONKURS,
97+
PosteringsType.L_HONORAR_AKKORD_PROSENT_PROVISJON_H
98+
99+
)
100+
return arbeidsPosteringsTyper.contains(posteringsType)
101+
}
102+
103+
private fun isDagpenger(posteringsType: PosteringsType): Boolean {
104+
val dagpengerPosteringsTyper = listOf(
105+
PosteringsType.Y_DAGPENGER_VED_ARBEIDSLØSHET,
106+
PosteringsType.Y_DAGPENGER_TIL_FISKER_SOM_BARE_HAR_HYRE
107+
)
108+
return dagpengerPosteringsTyper.contains(posteringsType)
109+
}
110+
111+
private fun isSykepenger(posteringsType: PosteringsType): Boolean {
112+
val sykepengerPosteringsTyper = listOf(
113+
PosteringsType.Y_SYKEPENGER,
114+
PosteringsType.Y_SYKEPENGER_TIL_FISKER_SOM_BARE_HAR_HYRE
115+
)
116+
return sykepengerPosteringsTyper.contains(posteringsType)
117+
}
118+
119+
private fun isFangstFiske(posteringsType: PosteringsType): Boolean {
120+
val fangstFiskePosteringsTyper = listOf(
121+
PosteringsType.N_LOTT_KUN_TRYGDEAVGIFT,
122+
PosteringsType.N_VEDERLAG
123+
)
124+
return fangstFiskePosteringsTyper.contains(posteringsType)
125+
}
126+
127+
private fun isDagpengerFangstFiske(posteringsType: PosteringsType): Boolean {
128+
val dagpengerFangstFiskePosteringsTyper = listOf(
129+
PosteringsType.N_DAGPENGER_TIL_FISKER
130+
)
131+
return dagpengerFangstFiskePosteringsTyper.contains(posteringsType)
132+
}
133+
134+
private fun isSykepengerFangstFiske(posteringsType: PosteringsType): Boolean {
135+
val sykepengerFangstFiskePosteringsTyper = listOf(
136+
PosteringsType.N_SYKEPENGER_TIL_FISKER
137+
)
138+
return sykepengerFangstFiskePosteringsTyper.contains(posteringsType)
139+
}
140+
141+
private fun isTiltakslønn(posteringsType: PosteringsType): Boolean {
142+
val tiltakslønnPosteringsTyper = listOf(
143+
PosteringsType.L_ANNET_T,
144+
PosteringsType.L_BONUS_T,
145+
PosteringsType.L_FAST_TILLEGG_T,
146+
PosteringsType.L_FASTLØNN_T,
147+
PosteringsType.L_FERIEPENGER_T,
148+
PosteringsType.L_HELLIGDAGSTILLEGG_T,
149+
PosteringsType.L_OVERTIDSGODTGJØRELSE_T,
150+
PosteringsType.L_SLUTTVEDERLAG_T,
151+
PosteringsType.L_TIMELØNN_T,
152+
PosteringsType.L_UREGELMESSIGE_TILLEGG_KNYTTET_TIL_ARBEIDET_TID_T,
153+
PosteringsType.L_UREGELMESSIGE_TILLEGG_KNYTTET_TIL_IKKE_ARBEIDET_TID_T,
154+
PosteringsType.L_TREKK_I_LØNN_FOR_FERIE_T
155+
)
156+
return tiltakslønnPosteringsTyper.contains(posteringsType)
157+
}
158+
159+
class KlassifiseringsException(message: String) : RuntimeException(message)

dp-inntekt-api/src/main/kotlin/no/nav/dagpenger/inntekt/mapping/Verdikoder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ val dataGrunnlagKlassifiseringToVerdikode = biMapOf(
119119
)
120120

121121
@Deprecated(
122-
"Used by verdikoder, need to be replaced by new dp-inntekt-klassifiserer logic"
122+
message = "Used by verdikoder, need to be replaced by klassifiserer logic"
123123
)
124124
data class DatagrunnlagKlassifisering(
125125
val type: InntektType,

dp-inntekt-api/src/main/kotlin/no/nav/dagpenger/inntekt/rpc/InntektGrpcServer.kt

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ import io.grpc.Status
1111
import io.grpc.StatusException
1212
import io.grpc.StatusRuntimeException
1313
import mu.KotlinLogging
14+
import no.nav.dagpenger.events.inntekt.v1.Inntekt
1415
import no.nav.dagpenger.events.inntekt.v1.SpesifisertInntekt
1516
import no.nav.dagpenger.inntekt.AuthApiKeyVerifier
1617
import no.nav.dagpenger.inntekt.db.IllegalInntektIdException
1718
import no.nav.dagpenger.inntekt.db.InntektNotFoundException
1819
import no.nav.dagpenger.inntekt.db.InntektStore
20+
import no.nav.dagpenger.inntekt.klassifiserer.klassifiserOgMapInntekt
1921
import no.nav.dagpenger.inntekt.moshiInstance
2022

2123
private val logger = KotlinLogging.logger {}
24+
2225
internal class InntektGrpcServer(
2326
private val port: Int,
2427
private val apiKeyVerifier: AuthApiKeyVerifier,
@@ -74,17 +77,34 @@ internal class ApiKeyServerInterceptor(private val apiKeyVerifier: AuthApiKeyVer
7477
}
7578
}
7679

77-
internal class InntektGrpcApi(private val inntektStore: InntektStore) : SpesifisertInntektHenterGrpcKt.SpesifisertInntektHenterCoroutineImplBase() {
80+
internal class InntektGrpcApi(private val inntektStore: InntektStore) :
81+
InntektHenterGrpcKt.InntektHenterCoroutineImplBase() {
7882
companion object {
79-
val spesifisertInntektAdapter: JsonAdapter<SpesifisertInntekt> = moshiInstance.adapter(SpesifisertInntekt::class.java)
83+
val spesifisertInntektAdapter: JsonAdapter<SpesifisertInntekt> =
84+
moshiInstance.adapter(SpesifisertInntekt::class.java)
85+
val klassifsertInntektAdapter: JsonAdapter<Inntekt> = moshiInstance.adapter(Inntekt::class.java)
8086
}
8187

8288
override suspend fun hentSpesifisertInntektAsJson(request: InntektId): SpesifisertInntektAsJson {
89+
90+
val inntekt = getSpesifisertInntekt(request)
91+
return SpesifisertInntektAsJson.newBuilder()
92+
.setInntektId(inntekt.inntektId.let { InntektId.newBuilder().setId(it.id).build() })
93+
.setJson(spesifisertInntektAdapter.toJson(inntekt)).build()
94+
}
95+
96+
override suspend fun hentKlassifisertInntektAsJson(request: InntektId): KlassifisertInntektAsJson {
97+
98+
val inntekt = getSpesifisertInntekt(request)
99+
val klassifisert = klassifiserOgMapInntekt(inntekt)
100+
return KlassifisertInntektAsJson.newBuilder()
101+
.setInntektId(inntekt.inntektId.let { InntektId.newBuilder().setId(it.id).build() })
102+
.setJson(klassifsertInntektAdapter.toJson(klassifisert)).build()
103+
}
104+
105+
private fun getSpesifisertInntekt(request: InntektId): SpesifisertInntekt {
83106
try {
84-
val inntekt = inntektStore.getSpesifisertInntekt(request.id.let { no.nav.dagpenger.inntekt.db.InntektId(it) })
85-
return SpesifisertInntektAsJson.newBuilder()
86-
.setInntektId(inntekt.inntektId.let { InntektId.newBuilder().setId(it.id).build() })
87-
.setJson(spesifisertInntektAdapter.toJson(inntekt)).build()
107+
return inntektStore.getSpesifisertInntekt(request.id.let { no.nav.dagpenger.inntekt.db.InntektId(it) })
88108
} catch (e: InntektNotFoundException) {
89109
throw StatusException(Status.NOT_FOUND.withDescription("Inntekt with id ${request.id} not found"))
90110
} catch (e: IllegalInntektIdException) {

0 commit comments

Comments
 (0)