Skip to content

Commit

Permalink
Legg til generering av andeler for praksisendring 2024 (#1077)
Browse files Browse the repository at this point in the history
### 💰 Hva skal gjøres, og hvorfor?

Favro: NAV-24111

Legger til generering av andeler med ytelsestype `PRAKSISENDRING_2024`

For at et barn skal få en slik andel i måneden det fylte 13 måneder må
disse kravene være oppfylt:
- Fylte 13 måneder i aug-des 2024
- Startet i barnehage samme måned som barnet fylte 13 måneder
- Fikk utbetalt KS samme måned som barnet fylte 13 måneder
- Andre vilkår er oppfylt i måneden barnet fylte 13 måneder
- Det har ikke blitt generert en ordinær andel i måneden barnet fylte 13
måneder
- Fagsak og barn må ligge i tabellen over fagsaker praksisendringen
treffer (NAV-24204)

### ✅ Checklist
_Har du husket alle punktene i listen?_
- [ ] Jeg har testet mine endringer i henhold til akseptansekriteriene
🕵️
- [ ] Jeg har config- eller sql-endringer. I så fall, husk manuell
deploy til miljø for å verifisere endringene.
- [x] Jeg har skrevet tester. Hvis du ikke har skrevet tester, beskriv
hvorfor under 👇

### 💬 Ønsker du en muntlig gjennomgang?
- [ ] Ja
- [x] Nei
  • Loading branch information
MagnusTonnessen authored Feb 7, 2025
1 parent 466b3ab commit 0051d9b
Show file tree
Hide file tree
Showing 11 changed files with 624 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum class FeatureToggle(
KAN_OPPRETTE_REVURDERING_MED_ÅRSAK_IVERKSETTE_KA_VEDTAK("familie-ks-sak.kan-opprette-revurdering-med-aarsak-iverksette-ka-vedtak"),
STØTTER_LOVENDRING_2025("familie-ks-sak.stotter-lovendring-2025"),
STØTTER_ADOPSJON("familie-ks-sak.stotter-adopsjon"),
SKAL_GENERERE_ANDELER_FOR_PRAKSISENDRING_2024("familie-ks-sak.skal-generere-andel-for-praksisendring-2024"),

BRUK_OMSKRIVING_AV_HJEMLER_I_BREV("familie-ks-sak.bruk_omskriving_av_hjemler_i_brev"),
ALLEREDE_UTBETALT_SOM_ENDRINGSÅRSAK("familie-ks-sak.allerede-utbetalt"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import no.nav.familie.ks.sak.kjerne.overgangsordning.domene.OvergangsordningAnde
import no.nav.familie.ks.sak.kjerne.overgangsordning.domene.utfyltePerioder
import no.nav.familie.ks.sak.kjerne.personopplysninggrunnlag.domene.PersonType
import no.nav.familie.ks.sak.kjerne.personopplysninggrunnlag.domene.PersonopplysningGrunnlag
import no.nav.familie.ks.sak.kjerne.praksisendring.Praksisendring2024Service
import org.springframework.stereotype.Service
import java.time.LocalDate

@Service
class TilkjentYtelseService(
private val beregnAndelTilkjentYtelseService: BeregnAndelTilkjentYtelseService,
private val overgangsordningAndelRepository: OvergangsordningAndelRepository,
private val praksisendring2024Service: Praksisendring2024Service,
) {
fun beregnTilkjentYtelse(
vilkårsvurdering: Vilkårsvurdering,
Expand Down Expand Up @@ -47,7 +49,17 @@ class TilkjentYtelseService(
tilkjentYtelse = tilkjentYtelse,
)

val alleAndelerTilkjentYtelse = andelerTilkjentYtelseBarnaMedAlleEndringer.map { it.andel } + overgangsordningAndelerSomAndelTilkjentYtelse
val andelerForPraksisendring2024 =
praksisendring2024Service.genererAndelerForPraksisendring2024(
personopplysningGrunnlag = personopplysningGrunnlag,
vilkårsvurdering = vilkårsvurdering,
tilkjentYtelse = tilkjentYtelse,
)

val alleAndelerTilkjentYtelse =
andelerTilkjentYtelseBarnaMedAlleEndringer.map { it.andel } +
overgangsordningAndelerSomAndelTilkjentYtelse +
andelerForPraksisendring2024

tilkjentYtelse.andelerTilkjentYtelse.addAll(alleAndelerTilkjentYtelse)
return tilkjentYtelse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,7 @@ data class AndelTilkjentYtelse(
this.differanseberegnetPeriodebeløp == andel.differanseberegnetPeriodebeløp
}

fun Iterable<AndelTilkjentYtelse>.filtrerAndelerSomSkalSendesTilOppdrag(): List<AndelTilkjentYtelse> =
overgangsordningAndelerPerAktør().map { it.value.minBy { it.stønadFom } } + ordinæreAndeler().filter { it.kalkulertUtbetalingsbeløp != 0 }
fun Iterable<AndelTilkjentYtelse>.filtrerAndelerSomSkalSendesTilOppdrag(): List<AndelTilkjentYtelse> = overgangsordningAndelerPerAktør().map { it.value.minBy { it.stønadFom } } + ordinæreAndeler().filter { it.kalkulertUtbetalingsbeløp != 0 }

fun List<AndelTilkjentYtelse>.slåSammenBack2BackAndelsperioderMedSammeBeløp(): List<AndelTilkjentYtelse> =
this.fold(emptyList()) { acc, andelTilkjentYtelse ->
Expand All @@ -167,22 +166,22 @@ fun List<AndelTilkjentYtelse>.slåSammenBack2BackAndelsperioderMedSammeBeløp():

fun AndelTilkjentYtelse.totalKalkulertUtbetalingsbeløpForPeriode(): Int = kalkulertUtbetalingsbeløp * stønadsPeriode().antallMåneder()

fun Iterable<AndelTilkjentYtelse>.ordinæreAndeler() =
this.filter { it.type == YtelseType.ORDINÆR_KONTANTSTØTTE }
fun Iterable<AndelTilkjentYtelse>.ordinæreAndeler() = this.filter { it.type == YtelseType.ORDINÆR_KONTANTSTØTTE }

fun Iterable<AndelTilkjentYtelse>.overgangsordningAndelerPerAktør() =
this.filter { it.type == YtelseType.OVERGANGSORDNING }.groupBy { it.aktør }
fun Iterable<AndelTilkjentYtelse>.overgangsordningAndelerPerAktør() = this.filter { it.type == YtelseType.OVERGANGSORDNING }.groupBy { it.aktør }

enum class YtelseType(
val klassifisering: String,
) {
ORDINÆR_KONTANTSTØTTE("KS"),
OVERGANGSORDNING("OO"),
PRAKSISENDRING_2024("KS"),
;

fun tilYtelseType(): YtelsetypeKS =
when (this) {
ORDINÆR_KONTANTSTØTTE -> YtelsetypeKS.ORDINÆR_KONTANTSTØTTE
OVERGANGSORDNING -> YtelsetypeKS.ORDINÆR_KONTANTSTØTTE
PRAKSISENDRING_2024 -> YtelsetypeKS.ORDINÆR_KONTANTSTØTTE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package no.nav.familie.ks.sak.kjerne.praksisendring

import no.nav.familie.ks.sak.common.util.TIDENES_MORGEN
import no.nav.familie.ks.sak.common.util.inkluderer
import no.nav.familie.ks.sak.common.util.toYearMonth
import no.nav.familie.ks.sak.config.featureToggle.FeatureToggle.SKAL_GENERERE_ANDELER_FOR_PRAKSISENDRING_2024
import no.nav.familie.ks.sak.config.featureToggle.UnleashNextMedContextService
import no.nav.familie.ks.sak.kjerne.behandling.steg.vilkårsvurdering.domene.Resultat
import no.nav.familie.ks.sak.kjerne.behandling.steg.vilkårsvurdering.domene.UtdypendeVilkårsvurdering
import no.nav.familie.ks.sak.kjerne.behandling.steg.vilkårsvurdering.domene.Vilkår
import no.nav.familie.ks.sak.kjerne.behandling.steg.vilkårsvurdering.domene.VilkårResultat
import no.nav.familie.ks.sak.kjerne.behandling.steg.vilkårsvurdering.domene.Vilkårsvurdering
import no.nav.familie.ks.sak.kjerne.behandling.steg.vilkårsvurdering.forskyvning.lovverkFørFebruar2025.ForskyvVilkårFørFebruar2025
import no.nav.familie.ks.sak.kjerne.beregning.domene.AndelTilkjentYtelse
import no.nav.familie.ks.sak.kjerne.beregning.domene.TilkjentYtelse
import no.nav.familie.ks.sak.kjerne.beregning.domene.YtelseType
import no.nav.familie.ks.sak.kjerne.beregning.domene.hentGyldigSatsFor
import no.nav.familie.ks.sak.kjerne.beregning.domene.prosent
import no.nav.familie.ks.sak.kjerne.personopplysninggrunnlag.domene.Person
import no.nav.familie.ks.sak.kjerne.personopplysninggrunnlag.domene.PersonopplysningGrunnlag
import org.springframework.stereotype.Service
import java.math.BigDecimal
import java.time.YearMonth

@Service
class Praksisendring2024Service(
private val unleashService: UnleashNextMedContextService,
) {
private val gyldigeMånederForPraksisendring = (8..12).map { YearMonth.of(2024, it) }

fun genererAndelerForPraksisendring2024(
personopplysningGrunnlag: PersonopplysningGrunnlag,
vilkårsvurdering: Vilkårsvurdering,
tilkjentYtelse: TilkjentYtelse,
): List<AndelTilkjentYtelse> =
if (unleashService.isEnabled(SKAL_GENERERE_ANDELER_FOR_PRAKSISENDRING_2024)) {
personopplysningGrunnlag.barna.mapNotNull {
genererAndelerForPraksisendring2024(it, vilkårsvurdering, tilkjentYtelse)
}
} else {
emptyList()
}

private fun genererAndelerForPraksisendring2024(
barn: Person,
vilkårsvurdering: Vilkårsvurdering,
tilkjentYtelse: TilkjentYtelse,
): AndelTilkjentYtelse? {
val barnetsVilkårResultater =
vilkårsvurdering.personResultater.find { it.aktør == barn.aktør }?.vilkårResultater
?: error("Finner ikke vilkårresultater for barn")

if (!skalHaAndel(barn, barnetsVilkårResultater, tilkjentYtelse.andelerTilkjentYtelse)) {
return null
}

val barn13Måneder = barn.fødselsdato.plusMonths(13).toYearMonth()
val satsperiode =
hentGyldigSatsFor(
antallTimer = BigDecimal.ZERO,
erDeltBosted = erDeltBostedIMåned(barn, barnetsVilkårResultater),
stønadFom = barn13Måneder,
stønadTom = barn13Måneder,
)

val kalkulertUtbetalingsbeløp = satsperiode.sats.prosent(satsperiode.prosent)

return AndelTilkjentYtelse(
behandlingId = tilkjentYtelse.behandling.id,
tilkjentYtelse = tilkjentYtelse,
aktør = barn.aktør,
stønadFom = barn13Måneder,
stønadTom = barn13Måneder,
kalkulertUtbetalingsbeløp = kalkulertUtbetalingsbeløp,
nasjonaltPeriodebeløp = kalkulertUtbetalingsbeløp,
type = YtelseType.PRAKSISENDRING_2024,
sats = satsperiode.sats,
prosent = satsperiode.prosent,
)
}

private fun skalHaAndel(
barn: Person,
vilkårResultater: Set<VilkårResultat>,
andelerTilkjentYtelse: Set<AndelTilkjentYtelse>,
): Boolean {
val barn13Måneder = barn.fødselsdato.plusMonths(13).toYearMonth()
if (barn13Måneder !in gyldigeMånederForPraksisendring) {
return false
}

val harOrdinærAndelISammeMånedSom13Måneder = andelerTilkjentYtelse.any { it.aktør == barn.aktør && it.stønadsPeriode().inkluderer(barn13Måneder) }
if (harOrdinærAndelISammeMånedSom13Måneder) {
return false
}

val starterIBarnehageSammeMånedSom13Måneder =
vilkårResultater.any {
it.periodeFom?.toYearMonth() == barn13Måneder && it.vilkårType == Vilkår.BARNEHAGEPLASS && it.resultat == Resultat.IKKE_OPPFYLT
}

val vilkårResultaterUtenBarnehageplass = vilkårResultater.filter { it.vilkårType != Vilkår.BARNEHAGEPLASS }.toSet()
val forskøvedeVilkår = ForskyvVilkårFørFebruar2025.forskyvVilkårResultater(vilkårResultaterUtenBarnehageplass)

val andreVilkårErOppfyltSammeMånedSom13Måneder =
forskøvedeVilkår.all {
it.value.any {
val fom = (it.fom ?: TIDENES_MORGEN).toYearMonth()
val tom = (it.tom ?: TIDENES_MORGEN).toYearMonth()
barn13Måneder in fom..tom && it.verdi.resultat == Resultat.OPPFYLT
}
}

// TODO: Sjekk at fagsak og barn ligger i uttrekk

return starterIBarnehageSammeMånedSom13Måneder && andreVilkårErOppfyltSammeMånedSom13Måneder
}

private fun erDeltBostedIMåned(
barn: Person,
barnetsVilkårResultater: Collection<VilkårResultat>,
): Boolean =
barnetsVilkårResultater.any { vilkårResultat ->

val barn13Måneder = barn.fødselsdato.plusMonths(13).toYearMonth()

val fom = vilkårResultat.periodeFom?.toYearMonth() ?: error("Finner ikke fom for vilkårresultat")
val tom = vilkårResultat.periodeTom?.toYearMonth() ?: barn13Måneder
val barnEr13MånederIPeriode = barn13Måneder in fom..tom

vilkårResultat.vilkårType == Vilkår.BOR_MED_SØKER &&
vilkårResultat.utdypendeVilkårsvurderinger.any { it == UtdypendeVilkårsvurdering.DELT_BOSTED } &&
vilkårResultat.resultat == Resultat.OPPFYLT &&
barnEr13MånederIPeriode
}
}
76 changes: 39 additions & 37 deletions src/test/common/TestdataGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,7 @@ fun lagPersonopplysningGrunnlag(
return personopplysningGrunnlag
}

fun Person.tilPersonEnkel() =
PersonEnkel(this.type, this.aktør, this.fødselsdato, this.dødsfall?.dødsfallDato, this.målform)
fun Person.tilPersonEnkel() = PersonEnkel(this.type, this.aktør, this.fødselsdato, this.dødsfall?.dødsfallDato, this.målform)

fun lagFagsak(
aktør: Aktør = randomAktør(randomFnr()),
Expand Down Expand Up @@ -399,8 +398,7 @@ fun lagStatsborgerskap(land: String = "NOR"): Statsborgerskap =
bekreftelsesdato = LocalDate.of(1987, 9, 1),
)

fun lagInitieltTilkjentYtelse(behandling: Behandling) =
TilkjentYtelse(behandling = behandling, opprettetDato = LocalDate.now(), endretDato = LocalDate.now())
fun lagInitieltTilkjentYtelse(behandling: Behandling) = TilkjentYtelse(behandling = behandling, opprettetDato = LocalDate.now(), endretDato = LocalDate.now())

fun lagAndelTilkjentYtelse(
tilkjentYtelse: TilkjentYtelse? = null,
Expand Down Expand Up @@ -466,18 +464,17 @@ fun tilfeldigPerson(
aktør: Aktør = randomAktør(),
personId: Long = nestePersonId(),
dsfall: Dødsfall? = null,
) =
Person(
id = personId,
aktør = aktør,
fødselsdato = fødselsdato,
type = personType,
personopplysningGrunnlag = PersonopplysningGrunnlag(behandlingId = 0),
navn = "",
kjønn = kjønn,
målform = Målform.NB,
dødsfall = dødsfall,
).apply { sivilstander = mutableListOf(GrSivilstand(type = SIVILSTANDTYPE.UGIFT, person = this)) }
) = Person(
id = personId,
aktør = aktør,
fødselsdato = fødselsdato,
type = personType,
personopplysningGrunnlag = PersonopplysningGrunnlag(behandlingId = 0),
navn = "",
kjønn = kjønn,
målform = Målform.NB,
dødsfall = dødsfall,
).apply { sivilstander = mutableListOf(GrSivilstand(type = SIVILSTANDTYPE.UGIFT, person = this)) }

fun lagVilkårsvurderingMedSøkersVilkår(
søkerAktør: Aktør,
Expand Down Expand Up @@ -618,6 +615,14 @@ fun lagVilkårResultaterForBarn(
periodeTom = perioderMedAntallTimer.first.tom,
behandlingId = behandlingId,
antallTimer = perioderMedAntallTimer.second,
resultat =
if (perioderMedAntallTimer.second == null ||
perioderMedAntallTimer.second!! < BigDecimal(33)
) {
Resultat.OPPFYLT
} else {
Resultat.IKKE_OPPFYLT
},
)
},
)
Expand Down Expand Up @@ -761,8 +766,7 @@ fun lagUtvidetVedtaksperiodeMedBegrunnelser(
type: Vedtaksperiodetype = Vedtaksperiodetype.FORTSATT_INNVILGET,
begrunnelser: List<NasjonalEllerFellesBegrunnelseDB> = emptyList(),
sBegrunnelser: List<EØSBegrunnelseDB> = emptyList(),
): UtvidetVedtaksperiodeMedBegrunnelser =
UtvidetVedtaksperiodeMedBegrunnelser(id = 0, fom = fom, tom = tom, type = type, begrunnelser = begrunnelser, eøsBegrunnelser = eøsBegrunnelser, støtterFritekst = false)
): UtvidetVedtaksperiodeMedBegrunnelser = UtvidetVedtaksperiodeMedBegrunnelser(id = 0, fom = fom, tom = tom, type = type, begrunnelser = begrunnelser, eøsBegrunnelser = eøsBegrunnelser, støtterFritekst = false)

fun lagPersonResultat(
vilkårsvurdering: Vilkårsvurdering,
Expand Down Expand Up @@ -844,18 +848,17 @@ fun lagPersonResultatFraVilkårResultater(
fun lagBeregnetUtbetalingsoppdrag(
vedtak: Vedtak,
utbetlingsperioder: List<no.nav.familie.felles.utbetalingsgenerator.domain.Utbetalingsperiode> = emptyList(),
) =
BeregnetUtbetalingsoppdragLongId(
no.nav.familie.felles.utbetalingsgenerator.domain.Utbetalingsoppdrag(
aktoer = "",
fagSystem = "KS",
saksnummer = "1234",
kodeEndring = no.nav.familie.felles.utbetalingsgenerator.domain.Utbetalingsoppdrag.KodeEndring.NY,
saksbehandlerId = "123abc",
utbetalingsperiode = utbetlingsperioder,
),
listOf(AndelMedPeriodeIdLongId(id = 0L, periodeId = 0L, forrigePeriodeId = null, kildeBehandlingId = vedtak.behandling.id)),
)
) = BeregnetUtbetalingsoppdragLongId(
no.nav.familie.felles.utbetalingsgenerator.domain.Utbetalingsoppdrag(
aktoer = "",
fagSystem = "KS",
saksnummer = "1234",
kodeEndring = no.nav.familie.felles.utbetalingsgenerator.domain.Utbetalingsoppdrag.KodeEndring.NY,
saksbehandlerId = "123abc",
utbetalingsperiode = utbetlingsperioder,
),
listOf(AndelMedPeriodeIdLongId(id = 0L, periodeId = 0L, forrigePeriodeId = null, kildeBehandlingId = vedtak.behandling.id)),
)

fun lagUtbetalingsperiode(vedtak: Vedtak) =
no.nav.familie.felles.utbetalingsgenerator.domain.Utbetalingsperiode(
Expand Down Expand Up @@ -1210,13 +1213,12 @@ fun lagTestPersonopplysningGrunnlag(
fun lagVedtak(
behandling: Behandling = lagBehandling(),
stønadBrevPdF: ByteArray? = null,
) =
Vedtak(
id = nesteVedtakId(),
behandling = behandling,
vedtaksdato = LocalDateTime.now(),
stønadBrevPdf = stønadBrevPdF,
)
) = Vedtak(
id = nesteVedtakId(),
behandling = behandling,
vedtaksdato = LocalDateTime.now(),
stønadBrevPdf = stønadBrevPdF,
)

fun lagVilkårsvurdering(
søkerAktør: Aktør,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,15 @@ class CucumberMock(
val personRepository = mockk<PersonRepository>()
val tilbakekrevingsbehandlingHentService = mockk<TilbakekrevingsbehandlingHentService>()
val arbeidsfordelingServiceMock = mockk<ArbeidsfordelingService>()
val praksisendring2024Service = mockPraksisendring2024Service()

val beregnAndelTilkjentYtelseService =
BeregnAndelTilkjentYtelseService(
andelGeneratorLookup = AndelGenerator.Lookup(listOf(LovverkFebruar2025AndelGenerator(), LovverkFørFebruar2025AndelGenerator())),
unleashService = mockUnleashNextMedContextService(),
)
val tilkjentYtelseService = TilkjentYtelseService(beregnAndelTilkjentYtelseService, overgangsordningAndelRepositoryMock)

val tilkjentYtelseService = TilkjentYtelseService(beregnAndelTilkjentYtelseService, overgangsordningAndelRepositoryMock, praksisendring2024Service)

val tilpassDifferanseberegningEtterTilkjentYtelseService =
TilpassDifferanseberegningEtterTilkjentYtelseService(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package no.nav.familie.ks.sak.cucumber.mocking

import io.mockk.every
import io.mockk.mockk
import no.nav.familie.ks.sak.kjerne.praksisendring.Praksisendring2024Service

fun mockPraksisendring2024Service(): Praksisendring2024Service =
mockk<Praksisendring2024Service>().apply {
every { genererAndelerForPraksisendring2024(any(), any(), any()) } returns emptyList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import no.nav.familie.ks.sak.kjerne.eøs.kompetanse.domene.KompetanseAktivitet
import no.nav.familie.ks.sak.kjerne.overgangsordning.domene.OvergangsordningAndelRepository
import no.nav.familie.ks.sak.kjerne.personopplysninggrunnlag.domene.Person
import no.nav.familie.ks.sak.kjerne.personopplysninggrunnlag.domene.PersonType
import no.nav.familie.ks.sak.kjerne.praksisendring.Praksisendring2024Service
import no.nav.familie.tidslinje.Periode
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertEquals
Expand Down Expand Up @@ -580,6 +581,7 @@ internal class UtbetalingsperiodeUtilTest {
unleashService = mockUnleashNextMedContextService(),
),
overgangsordningAndelRepository = mockOvergangsordningAndelRepository(),
praksisendring2024Service = mockPraksisendring2024Service(),
)

val tilkjentYtelse =
Expand Down Expand Up @@ -616,4 +618,9 @@ internal class UtbetalingsperiodeUtilTest {
mockk<OvergangsordningAndelRepository>().apply {
every { hentOvergangsordningAndelerForBehandling(any()) } returns emptyList()
}

private fun mockPraksisendring2024Service() =
mockk<Praksisendring2024Service>().apply {
every { genererAndelerForPraksisendring2024(any(), any(), any()) } returns emptyList()
}
}
Loading

0 comments on commit 0051d9b

Please sign in to comment.