Skip to content

Commit 83ababe

Browse files
committed
Støtte for lagring og uthenting av begrunnelse
1 parent fac542d commit 83ababe

File tree

10 files changed

+142
-38
lines changed

10 files changed

+142
-38
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,7 @@ fun Route.uklassifisertInntekt(
165165
storedInntektMedMetadata.inntekt.mapToFrontend(
166166
person = inntektsmottaker,
167167
organisasjoner = organisasjoner,
168-
beregningsdato = storedInntektMedMetadata.beregningsdato,
169-
storedInntektPeriode = storedInntektMedMetadata.storedInntektPeriode,
168+
storedInntektMedMetadata,
170169
)
171170
}.let {
172171
call.respond(HttpStatusCode.OK, it)
@@ -201,8 +200,9 @@ fun Route.uklassifisertInntekt(
201200
inntekt = it.inntekt,
202201
manueltRedigert =
203202
ManueltRedigert.from(
204-
true,
205-
call.getSubject(),
203+
bool = true,
204+
redigertAv = call.getSubject(),
205+
begrunnelse = inntekterDto.begrunnelse,
206206
),
207207
),
208208
)

dp-inntekt-api/src/main/kotlin/no/nav/dagpenger/inntekt/api/v1/models/InntekterDto.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ data class InntekterDto(
1818
val virksomheter: List<Virksomhet>,
1919
val mottaker: Inntektsmottaker,
2020
val periode: PeriodeDto,
21+
val begrunnelse: String? = null,
2122
)
2223

2324
data class PeriodeDto(

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,15 @@ data class InntektPersonMapping(
6767

6868
data class ManueltRedigert(
6969
val redigertAv: String,
70+
val begrunnelse: String? = null,
7071
) {
7172
companion object {
7273
fun from(
7374
bool: Boolean,
7475
redigertAv: String,
76+
begrunnelse: String? = null,
7577
) = when (bool) {
76-
true -> ManueltRedigert(redigertAv)
78+
true -> ManueltRedigert(redigertAv, begrunnelse)
7779
false -> null
7880
}
7981
}
@@ -111,6 +113,7 @@ data class StoredInntektMedMetadata(
111113
valdselsnummer: String,
112114
val beregningsdato: LocalDate,
113115
val storedInntektPeriode: StoredInntektPeriode?,
116+
val begrunnelse: String? = null,
114117
)
115118

116119
data class StoredInntektPeriode(

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal class PostgresInntektStore(
4242
@Language("sql")
4343
val statement =
4444
"""
45-
SELECT redigert_av
45+
SELECT redigert_av, begrunnelse
4646
FROM inntekt_V1_manuelt_redigert
4747
WHERE inntekt_id = ?
4848
""".trimMargin()
@@ -51,7 +51,7 @@ internal class PostgresInntektStore(
5151
session.run(
5252
queryOf(statement, inntektId.id)
5353
.map { row ->
54-
ManueltRedigert(row.string(1))
54+
ManueltRedigert(row.string("redigert_av"), row.string("begrunnelse"))
5555
}.asSingle,
5656
)
5757
}
@@ -192,9 +192,10 @@ internal class PostgresInntektStore(
192192
@Language("sql")
193193
val statement =
194194
"""
195-
SELECT inntekt.id, inntekt.inntekt, inntekt.manuelt_redigert, inntekt.timestamp, mapping.fnr, mapping.beregningsdato, mapping.periodeFraOgMed, mapping.periodeTilOgMed
195+
SELECT inntekt.id, inntekt.inntekt, inntekt.manuelt_redigert, inntekt.timestamp, mapping.fnr, mapping.beregningsdato, mapping.periodeFraOgMed, mapping.periodeTilOgMed, manuelt_redigert.begrunnelse
196196
FROM inntekt_V1 inntekt
197-
INNER JOIN inntekt_V1_person_mapping mapping ON inntekt.id = mapping.inntektid
197+
INNER JOIN inntekt_V1_person_mapping mapping ON inntekt.id = mapping.inntektid
198+
LEFT JOIN inntekt_V1_manuelt_redigert manuelt_redigert ON inntekt.id = manuelt_redigert.inntektid
198199
WHERE inntekt.id = ?
199200
""".trimIndent()
200201

@@ -214,6 +215,7 @@ internal class PostgresInntektStore(
214215
fraOgMed = it.localDateOrNull("periodeFraOgMed")?.let { localDate -> YearMonth.from(localDate) },
215216
tilOgMed = it.localDateOrNull("periodeTilOgMed")?.let { localDate -> YearMonth.from(localDate) },
216217
),
218+
begrunnelse = it.stringOrNull("begrunnelse"),
217219
)
218220
}.asSingle,
219221
) ?: throw InntektNotFoundException("Inntekt with id $inntektId not found.")
@@ -272,12 +274,17 @@ internal class PostgresInntektStore(
272274
)
273275

274276
command.manueltRedigert?.let {
277+
it.begrunnelse?.length?.let { lengde ->
278+
require(lengde <= 1024) { "Begrunnelsen kan ikke være lengre enn 1024 tegn." }
279+
}
280+
275281
tx.run(
276282
queryOf(
277-
"INSERT INTO inntekt_V1_manuelt_redigert VALUES(:id,:redigert)",
283+
"INSERT INTO inntekt_V1_manuelt_redigert (inntekt_id, redigert_av, begrunnelse) VALUES(:id, :redigert, :begrunnelse)",
278284
mapOf(
279285
"id" to inntektId.id,
280286
"redigert" to it.redigertAv,
287+
"begrunnelse" to it.begrunnelse,
281288
),
282289
).asUpdate,
283290
)

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package no.nav.dagpenger.inntekt.mapping
22

33
import no.nav.dagpenger.inntekt.api.v1.models.InntekterDto
44
import no.nav.dagpenger.inntekt.api.v1.models.PeriodeDto
5-
import no.nav.dagpenger.inntekt.db.StoredInntektPeriode
5+
import no.nav.dagpenger.inntekt.db.StoredInntektMedMetadata
66
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.Aktoer
77
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.Avvik
88
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.InntektBeskrivelse
@@ -12,14 +12,12 @@ import no.nav.dagpenger.inntekt.inntektskomponenten.v1.Periode
1212
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.TilleggInformasjon
1313
import no.nav.dagpenger.inntekt.opptjeningsperiode.Opptjeningsperiode
1414
import java.math.BigDecimal
15-
import java.time.LocalDate
1615
import java.time.YearMonth
1716

1817
fun InntektkomponentResponse.mapToFrontend(
1918
person: Inntektsmottaker,
2019
organisasjoner: List<Organisasjon>,
21-
beregningsdato: LocalDate,
22-
storedInntektPeriode: StoredInntektPeriode?,
20+
storedInntektMedMetadata: StoredInntektMedMetadata,
2321
): InntekterDto {
2422
val inntekt = arbeidsInntektMaaned
2523
val virksomheter: MutableList<Virksomhet> = mutableListOf()
@@ -105,18 +103,16 @@ fun InntektkomponentResponse.mapToFrontend(
105103
return InntekterDto(
106104
virksomheter = virksomheter,
107105
mottaker = person,
108-
periode = getPeriode(storedInntektPeriode, beregningsdato),
106+
periode = getPeriode(storedInntektMedMetadata),
107+
begrunnelse = storedInntektMedMetadata.begrunnelse,
109108
)
110109
}
111110

112-
private fun getPeriode(
113-
storedInntektPeriode: StoredInntektPeriode?,
114-
beregningsdato: LocalDate,
115-
): PeriodeDto {
116-
val opptjeningsperiode = Opptjeningsperiode(beregningsdato)
111+
private fun getPeriode(storedInntektMedMetadata: StoredInntektMedMetadata): PeriodeDto {
112+
val opptjeningsperiode = Opptjeningsperiode(beregningsdato = storedInntektMedMetadata.beregningsdato)
117113
return PeriodeDto(
118-
storedInntektPeriode?.fraOgMed ?: opptjeningsperiode.førsteMåned,
119-
storedInntektPeriode?.tilOgMed ?: opptjeningsperiode.sisteAvsluttendeKalenderMåned,
114+
fraOgMed = storedInntektMedMetadata.storedInntektPeriode?.fraOgMed ?: opptjeningsperiode.førsteMåned,
115+
tilOgMed = storedInntektMedMetadata.storedInntektPeriode?.tilOgMed ?: opptjeningsperiode.sisteAvsluttendeKalenderMåned,
120116
)
121117
}
122118

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE inntekt_v1_manuelt_redigert
2+
ADD COLUMN begrunnelse VARCHAR(1024);

dp-inntekt-api/src/test/kotlin/no/nav/dagpenger/inntekt/api/v1/UklassifisertInntektRouteTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,5 +574,6 @@ internal class UklassifisertInntektRouteTest {
574574
YearMonth.of(2025, 4)
575575
storeInntektCommandSlot.captured.manueltRedigert.shouldNotBeNull()
576576
storeInntektCommandSlot.captured.manueltRedigert!!.redigertAv shouldBe TEST_OAUTH_USER
577+
storeInntektCommandSlot.captured.manueltRedigert!!.begrunnelse shouldBe "Dette er en begrunnelse."
577578
}
578579
}

dp-inntekt-api/src/test/kotlin/no/nav/dagpenger/inntekt/db/PostgresTest.kt

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

33
import io.kotest.assertions.assertSoftly
4+
import io.kotest.assertions.throwables.shouldThrow
45
import io.kotest.core.spec.style.StringSpec
56
import io.kotest.matchers.shouldBe
67
import io.kotest.matchers.shouldNotBe
8+
import io.kotest.matchers.throwable.shouldHaveMessage
79
import io.kotest.property.Arb
810
import io.kotest.property.arbitrary.arbitrary
911
import io.kotest.property.arbitrary.localDateTime
@@ -29,7 +31,7 @@ internal class PostgresTest {
2931
fun `Migration scripts are applied successfully`() {
3032
withCleanDb {
3133
val migrations = PostgresDataSourceBuilder.runMigration()
32-
assertEquals(17, migrations, "Wrong number of migrations")
34+
assertEquals(18, migrations, "Wrong number of migrations")
3335
}
3436
}
3537

@@ -45,7 +47,7 @@ internal class PostgresTest {
4547
fun `Migration of testdata `() {
4648
withCleanDb {
4749
val migrations = PostgresDataSourceBuilder.runMigration(locations = listOf("db/migration", "db/testdata"))
48-
assertEquals(22, migrations, "Wrong number of migrations")
50+
assertEquals(23, migrations, "Wrong number of migrations")
4951
}
5052
}
5153
}
@@ -209,7 +211,7 @@ internal class PostgresInntektStoreTest {
209211
emptyList(),
210212
Aktoer(AktoerType.AKTOER_ID, "1234"),
211213
)
212-
val manueltRedigert = ManueltRedigert("user")
214+
val manueltRedigert = ManueltRedigert("user", "Dette er en begrunnelse.")
213215

214216
val storedInntekt =
215217
storeInntekt(
@@ -219,14 +221,82 @@ internal class PostgresInntektStoreTest {
219221
manueltRedigert = manueltRedigert,
220222
),
221223
)
222-
assertTrue(storedInntekt.manueltRedigert)
223-
224224
val storedInntektByRequest = getInntekt(storedInntekt.inntektId)
225-
assertTrue(storedInntektByRequest.manueltRedigert)
226-
227225
val storedManueltRedigert = getManueltRedigert(storedInntekt.inntektId)
228-
assertNotNull(storedManueltRedigert)
229-
assertEquals(manueltRedigert, storedManueltRedigert)
226+
storedManueltRedigert shouldNotBe null
227+
storedManueltRedigert shouldBe manueltRedigert
228+
storedInntekt.manueltRedigert shouldBe true
229+
storedInntektByRequest.manueltRedigert shouldBe true
230+
}
231+
}
232+
}
233+
234+
@Test
235+
fun `Lagring av inntekt skal kaste IllegalArgumentException når begrunnelse er lengre enn 1024 tegn`() {
236+
withMigratedDb {
237+
with(PostgresInntektStore(PostgresDataSourceBuilder.dataSource)) {
238+
val parameters = Inntektparametre("1234", "1234", LocalDate.now(), RegelKontekst("1234", "vedtak"))
239+
val hentInntektListeResponse =
240+
InntektkomponentResponse(
241+
emptyList(),
242+
Aktoer(AktoerType.AKTOER_ID, "1234"),
243+
)
244+
val manueltRedigert = ManueltRedigert("user", "A".repeat(1025))
245+
246+
val exception =
247+
shouldThrow<IllegalArgumentException> {
248+
storeInntekt(
249+
StoreInntektCommand(
250+
inntektparametre = parameters,
251+
inntekt = hentInntektListeResponse,
252+
manueltRedigert = manueltRedigert,
253+
),
254+
)
255+
}
256+
exception shouldHaveMessage "Begrunnelsen kan ikke være lengre enn 1024 tegn."
257+
}
258+
}
259+
}
260+
261+
@Test
262+
fun `getStoredInntektMedMetadata returnerer forventet resultat når inntekten ikke er manuelt redigert`() {
263+
withMigratedDb {
264+
with(PostgresInntektStore(PostgresDataSourceBuilder.dataSource)) {
265+
val parameters = Inntektparametre("1234", "1234", LocalDate.now(), RegelKontekst("1234", "vedtak"))
266+
val hentInntektListeResponse =
267+
InntektkomponentResponse(
268+
emptyList(),
269+
Aktoer(AktoerType.AKTOER_ID, "1234"),
270+
)
271+
storeInntekt(
272+
StoreInntektCommand(
273+
inntektparametre = parameters,
274+
inntekt = hentInntektListeResponse,
275+
manueltRedigert = null,
276+
),
277+
)
278+
}
279+
}
280+
}
281+
282+
@Test
283+
fun `getStoredInntektMedMetadata returnerer forventet resultat når inntekten er manuelt redigert`() {
284+
withMigratedDb {
285+
with(PostgresInntektStore(PostgresDataSourceBuilder.dataSource)) {
286+
val parameters = Inntektparametre("1234", "1234", LocalDate.now(), RegelKontekst("1234", "vedtak"))
287+
val hentInntektListeResponse =
288+
InntektkomponentResponse(
289+
emptyList(),
290+
Aktoer(AktoerType.AKTOER_ID, "1234"),
291+
)
292+
val manueltRedigert = ManueltRedigert("user", "Dette er en begrunnelse.")
293+
storeInntekt(
294+
StoreInntektCommand(
295+
inntektparametre = parameters,
296+
inntekt = hentInntektListeResponse,
297+
manueltRedigert = manueltRedigert,
298+
),
299+
)
230300
}
231301
}
232302
}

dp-inntekt-api/src/test/kotlin/no/nav/dagpenger/inntekt/mapping/MapToInntektFrontendTest.kt

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package no.nav.dagpenger.inntekt.mapping
22

3+
import de.huxhorn.sulky.ulid.ULID
4+
import io.kotest.matchers.shouldBe
5+
import no.nav.dagpenger.inntekt.db.InntektId
6+
import no.nav.dagpenger.inntekt.db.StoredInntektMedMetadata
7+
import no.nav.dagpenger.inntekt.db.StoredInntektPeriode
38
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.Aktoer
49
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.AktoerType
510
import no.nav.dagpenger.inntekt.inntektskomponenten.v1.ArbeidsInntektInformasjon
@@ -13,6 +18,7 @@ import no.nav.dagpenger.inntekt.serder.jacksonObjectMapper
1318
import org.junit.jupiter.api.Test
1419
import java.math.BigDecimal
1520
import java.time.LocalDate.now
21+
import java.time.LocalDateTime
1622
import java.time.YearMonth
1723
import kotlin.test.assertEquals
1824
import kotlin.test.assertFalse
@@ -188,12 +194,21 @@ class MapToInntektFrontendTest {
188194
fun `Map inntekt til InntektForVirksomhetMedPersonInformasjon`() {
189195
val mappedToInntektFrontend =
190196
inntektkomponentResponse.mapToFrontend(
191-
mottaker,
192-
organisasjoner,
193-
beregningsdato = now(),
194-
storedInntektPeriode = null,
197+
person = mottaker,
198+
organisasjoner = organisasjoner,
199+
StoredInntektMedMetadata(
200+
InntektId(ULID().nextULID().toString()),
201+
inntektkomponentResponse,
202+
true,
203+
LocalDateTime.now(),
204+
"01234567890",
205+
now(),
206+
StoredInntektPeriode(YearMonth.now(), YearMonth.now()),
207+
"Dette er en begrunnelse.",
208+
),
195209
)
196210

211+
mappedToInntektFrontend.begrunnelse shouldBe "Dette er en begrunnelse."
197212
assertEquals(2, mappedToInntektFrontend.virksomheter.size)
198213
assertEquals(mottaker, mappedToInntektFrontend.mottaker)
199214

@@ -337,8 +352,16 @@ class MapToInntektFrontendTest {
337352
inntektkomponentResponseMedTomVirksomhet.mapToFrontend(
338353
mottaker,
339354
organisasjoner,
340-
beregningsdato = now(),
341-
storedInntektPeriode = null,
355+
StoredInntektMedMetadata(
356+
InntektId(ULID().nextULID().toString()),
357+
inntektkomponentResponse,
358+
true,
359+
LocalDateTime.now(),
360+
"01234567890",
361+
now(),
362+
StoredInntektPeriode(YearMonth.now(), YearMonth.now()),
363+
"Dette er en begrunnelse.",
364+
),
342365
)
343366
assertEquals(3, mapTilFrontendMedNullVirksomhet.virksomheter.size)
344367
assertEquals(2, mapTilFrontendMedNullVirksomhet.virksomheter.filter { it.virksomhetsnummer == "" }.size)

dp-inntekt-api/src/test/resources/test-data/expected-uklassifisert-post-body.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,5 +313,6 @@
313313
"periode": {
314314
"fraOgMed": "2000-12",
315315
"tilOgMed": "2025-04"
316-
}
316+
},
317+
"begrunnelse": "Dette er en begrunnelse."
317318
}

0 commit comments

Comments
 (0)