diff --git "a/kontrakter/src/main/kotlin/no/nav/pleiepengerbarn/uttak/kontrakter/\303\205rsak.kt" "b/kontrakter/src/main/kotlin/no/nav/pleiepengerbarn/uttak/kontrakter/\303\205rsak.kt" index 312b7277..38a61473 100644 --- "a/kontrakter/src/main/kotlin/no/nav/pleiepengerbarn/uttak/kontrakter/\303\205rsak.kt" +++ "b/kontrakter/src/main/kotlin/no/nav/pleiepengerbarn/uttak/kontrakter/\303\205rsak.kt" @@ -22,6 +22,7 @@ enum class Årsak(val oppfylt: Boolean) { BARNETS_DØDSFALL(false), INNGANGSVILKÅR_IKKE_OPPFYLT(false), FOR_LAV_INNTEKT(false), - FOR_MANGE_DAGER_UTENLANDSOPPHOLD(false) + FOR_MANGE_DAGER_UTENLANDSOPPHOLD(false), + MAKS_DAGER_OVERSTEGET(false) } diff --git a/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/UttaksplanRegler.kt b/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/UttaksplanRegler.kt index 0a3b0506..bd3478a8 100644 --- a/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/UttaksplanRegler.kt +++ b/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/UttaksplanRegler.kt @@ -21,7 +21,8 @@ internal object UttaksplanRegler { private val UttaksplanRegler = linkedSetOf( InngangsvilkårIkkeOppfyltRegel(), - UtenlandsoppholdRegel() + UtenlandsoppholdRegel(), + MaxAntallDagerRegel() // NB: erstartet inntil videre med BarnsDødPeriodeRegel // BarnsDødRegel() ) diff --git a/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/delregler/MaxAntallDagerRegel.kt b/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/delregler/MaxAntallDagerRegel.kt new file mode 100644 index 00000000..3e05d527 --- /dev/null +++ b/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/delregler/MaxAntallDagerRegel.kt @@ -0,0 +1,107 @@ +package no.nav.pleiepengerbarn.uttak.regler.delregler + +import no.nav.pleiepengerbarn.uttak.kontrakter.* +import no.nav.pleiepengerbarn.uttak.regler.HUNDRE_PROSENT +import no.nav.pleiepengerbarn.uttak.regler.domene.RegelGrunnlag +import no.nav.pleiepengerbarn.uttak.regler.kontrakter_ext.overlapperDelvis +import no.nav.pleiepengerbarn.uttak.regler.kontrakter_ext.tilVirkedager +import no.nav.pleiepengerbarn.uttak.regler.kontrakter_ext.virkedager +import java.math.BigDecimal +import java.math.RoundingMode +import java.time.LocalDate + +internal class MaxAntallDagerRegel : UttaksplanRegel { + + companion object { + val KVOTER = mapOf( + YtelseType.PLS to 60 + ) + } + + override fun kjør(uttaksplan: Uttaksplan, grunnlag: RegelGrunnlag): Uttaksplan { + + if (grunnlag.ytelseType != YtelseType.PLS) { + return uttaksplan + } + val maxDager = KVOTER[grunnlag.ytelseType] ?: throw IllegalArgumentException("Ulovlig ytelsestype ${grunnlag.ytelseType}") + val forBrukteDagerAndreParter = grunnlag.finnForbrukteDagerAndreParter() + var rest = BigDecimal(maxDager) - forBrukteDagerAndreParter + + val nyePerioder = mutableMapOf() + + uttaksplan.perioder.forEach { (periode, info) -> + if (info.utfall == Utfall.OPPFYLT) { + val forbrukteDagerDennePerioen = BigDecimal(periode.virkedager()) * (info.uttaksgrad / HUNDRE_PROSENT.setScale(2, RoundingMode.HALF_UP)) + + if (rest <= BigDecimal.ZERO) { + // Hvis ingenting igjen på kvoten så settes hele perioder til ikke oppfylt + nyePerioder[periode] = info.settIkkeoppfylt() + } else if (forbrukteDagerDennePerioen <= rest) { + // Hvis det er nok dager igjen, så settes hele periode til oppfylt + nyePerioder[periode] = info + rest -= forbrukteDagerDennePerioen + } else { + // Bare delvis nok dager igjen, så deler derfor opp perioden i en oppfylt og en ikke oppfylt periode + val restHeleDager = rest.setScale(0, RoundingMode.UP).toLong() + nyePerioder[LukketPeriode(periode.fom, periode.fom.plusDays(restHeleDager - 1L))] = info + nyePerioder[LukketPeriode(periode.fom.plusDays(restHeleDager), periode.tom)] = info.settIkkeoppfylt() + rest = BigDecimal.ZERO + } + } else { + // Gjør ingenting med perioder som ikke er oppfylt + nyePerioder[periode] = info + } + } + + return uttaksplan.copy(perioder = nyePerioder) + } + +} + + +private fun UttaksperiodeInfo.settIkkeoppfylt(): UttaksperiodeInfo { + return this.copy( + årsaker = setOf(Årsak.MAKS_DAGER_OVERSTEGET), + utfall = Utfall.IKKE_OPPFYLT, + inngangsvilkår = inngangsvilkår, + uttaksgrad = Prosent(0).setScale(2, RoundingMode.HALF_UP), + utbetalingsgrader = this.utbetalingsgrader.map {it.copy(utbetalingsgrad = Prosent(0).setScale(2, RoundingMode.HALF_UP))} + ) +} + +private fun RegelGrunnlag.finnForbrukteDagerAndreParter(): BigDecimal { + var antallDager = BigDecimal.ZERO + this.kravprioritetForBehandlinger.forEach { (kravprioritetsperiode, behandlingsUUIDer) -> + for (behandlingUUID in behandlingsUUIDer) { + if (behandlingUUID == this.behandlingUUID) { + //Avslutt loop dersom nåværende behandling + break + } + val annenPartsUttaksplan = this.andrePartersUttaksplanPerBehandling[behandlingUUID] ?: throw IllegalStateException("Skal ikke kunne skje at behandling ikke finnes") + annenPartsUttaksplan.perioder.forEach { (annenPartsPeriode, info) -> + if (annenPartsPeriode.overlapperDelvis(kravprioritetsperiode)) { + if (info.utfall == Utfall.OPPFYLT) { + antallDager += (info.uttaksgrad / HUNDRE_PROSENT.setScale(2, RoundingMode.HALF_UP)) + } + } + } + } + } + return antallDager +} + + +private fun RegelGrunnlag.finnForbrukteDagerFraForrigeUttaksplan(): Map { + if (this.forrigeUttaksplan == null) { + return mapOf() + } + val forbrukteDagerPerDato = mutableMapOf() + this.forrigeUttaksplan.perioder + .filter {it.value.utfall == Utfall.OPPFYLT} + .forEach { + it.key.tilVirkedager().forEach { virkedag -> + forbrukteDagerPerDato[virkedag] = it.value.uttaksgrad.divide(HUNDRE_PROSENT).setScale(2, RoundingMode.HALF_UP) + } + } + return forbrukteDagerPerDato +} \ No newline at end of file diff --git a/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/delregler/UtenlandsoppholdRegel.kt b/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/delregler/UtenlandsoppholdRegel.kt index 305cfe55..09353572 100644 --- a/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/delregler/UtenlandsoppholdRegel.kt +++ b/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/delregler/UtenlandsoppholdRegel.kt @@ -6,6 +6,8 @@ import no.nav.pleiepengerbarn.uttak.kontrakter.* import no.nav.pleiepengerbarn.uttak.regler.NULL_PROSENT import no.nav.pleiepengerbarn.uttak.regler.domene.RegelGrunnlag import no.nav.pleiepengerbarn.uttak.regler.kontrakter_ext.overlapperDelvis +import no.nav.pleiepengerbarn.uttak.regler.kontrakter_ext.stream +import no.nav.pleiepengerbarn.uttak.regler.kontrakter_ext.tilVirkedager import no.nav.pleiepengerbarn.uttak.regler.kontrakter_ext.virkedager import java.time.DayOfWeek import java.time.LocalDate @@ -88,18 +90,6 @@ private fun RegelGrunnlag.finnUtenlandsdager(): Set { } return this.forrigeUttaksplan.perioder .filter {it.value.utenlandsoppholdUtenÅrsak} - .flatMap {it.key.tilDatoer()} + .flatMap {it.key.tilVirkedager()} .toSet() } - -private fun LukketPeriode.tilDatoer(): Set { - val datoer = mutableSetOf() - this.stream().forEach { - if (it.dayOfWeek !in listOf(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY)) { - datoer.add(it) - } - } - return datoer -} - -private fun LukketPeriode.stream() = fom.datesUntil(tom.plusDays(1)) diff --git a/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/kontrakter_ext/LukketPeriodeExt.kt b/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/kontrakter_ext/LukketPeriodeExt.kt index 852a91ea..a3d62f90 100644 --- a/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/kontrakter_ext/LukketPeriodeExt.kt +++ b/regler/src/main/kotlin/no/nav/pleiepengerbarn/uttak/regler/kontrakter_ext/LukketPeriodeExt.kt @@ -6,6 +6,7 @@ import no.nav.pleiepengerbarn.uttak.kontrakter.LukketPeriode import no.nav.pleiepengerbarn.uttak.kontrakter.SøktUttak import java.lang.IllegalArgumentException import java.time.DayOfWeek +import java.time.LocalDate internal fun LukketPeriode.overlapperHelt(annen: LukketPeriode) = (fom == annen.fom || fom.isBefore(annen.fom)) && @@ -40,3 +41,15 @@ internal fun LukketPeriode.virkedager(): Int { } return antall } + +internal fun LukketPeriode.tilVirkedager(): Set { + val datoer = mutableSetOf() + this.stream().forEach { + if (it.dayOfWeek !in listOf(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY)) { + datoer.add(it) + } + } + return datoer +} + +internal fun LukketPeriode.stream() = fom.datesUntil(tom.plusDays(1)) diff --git a/server/src/main/kotlin/no/nav/pleiepengerbarn/uttak/server/UttakplanApi.kt b/server/src/main/kotlin/no/nav/pleiepengerbarn/uttak/server/UttakplanApi.kt index 17f0e809..e8e97f22 100644 --- a/server/src/main/kotlin/no/nav/pleiepengerbarn/uttak/server/UttakplanApi.kt +++ b/server/src/main/kotlin/no/nav/pleiepengerbarn/uttak/server/UttakplanApi.kt @@ -39,7 +39,7 @@ class UttakplanApi { fun opprettUttaksplan( @RequestBody uttaksgrunnlag: Uttaksgrunnlag, uriComponentsBuilder: UriComponentsBuilder): ResponseEntity { - logger.info("Opprett uttaksplan for behanding=${uttaksgrunnlag.behandlingUUID}") + logger.info("Opprett uttaksplan for behandling=${uttaksgrunnlag.behandlingUUID}") uttaksgrunnlag.valider() val forrigeUttaksplan = uttakRepository.hentForrige(uttaksgrunnlag.saksnummer, UUID.fromString(uttaksgrunnlag.behandlingUUID)) val nyUttaksplan = lagUttaksplan(uttaksgrunnlag, forrigeUttaksplan, true) @@ -53,7 +53,7 @@ class UttakplanApi { @PostMapping(EndringUttaksplanPath, consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE]) @Operation(description = "Endrer en uttaksplan. Kun enkelte endringer er lovlig, hvis endringen er ulovlig så vil endepunktet returnere en 'bad request'.") fun endrePerioder(@RequestBody endrePerioderGrunnlag: EndrePerioderGrunnlag): ResponseEntity { - logger.info("Endrer uttaksplan for behanding=${endrePerioderGrunnlag.behandlingUUID}") + logger.info("Endrer uttaksplan for behandling=${endrePerioderGrunnlag.behandlingUUID}") endrePerioderGrunnlag.valider() val eksisterendeUttaksplan = uttakRepository.hent(UUID.fromString(endrePerioderGrunnlag.behandlingUUID)) ?: return ResponseEntity.badRequest().body(null) @@ -68,7 +68,7 @@ class UttakplanApi { fun simulerUttaksplan( @RequestBody uttaksgrunnlag: Uttaksgrunnlag, uriComponentsBuilder: UriComponentsBuilder): ResponseEntity { - logger.info("Simulerer uttaksplan(PSB) for behanding=${uttaksgrunnlag.behandlingUUID}") + logger.info("Simulerer uttaksplan(PSB) for behandling=${uttaksgrunnlag.behandlingUUID}") val simulering = simuler(uttaksgrunnlag) return ResponseEntity.ok(simulering) } @@ -78,7 +78,7 @@ class UttakplanApi { fun simulerUttaksplanForLivetsSluttfase( @RequestBody uttaksgrunnlag: Uttaksgrunnlag, uriComponentsBuilder: UriComponentsBuilder): ResponseEntity { - logger.info("Simulerer uttaksplan(PLS) for behanding=${uttaksgrunnlag.behandlingUUID}") + logger.info("Simulerer uttaksplan(PLS) for behandling=${uttaksgrunnlag.behandlingUUID}") val simulering = simuler(uttaksgrunnlag) val erKvotenBruktOpp = BeregnBruktKvote.erKvotenOversteget(simulering.simulertUttaksplan, hentAndrePartersUttaksplanerPerBehandling(uttaksgrunnlag)) return ResponseEntity.ok(SimuleringLivetsSluttfase(simulering.forrigeUttaksplan, simulering.simulertUttaksplan, @@ -131,7 +131,7 @@ class UttakplanApi { ] ) fun hentUttaksplanForBehandling(@RequestParam behandlingUUID: BehandlingUUID, @RequestParam slåSammenLikePerioder: Boolean = false): ResponseEntity { - logger.info("Henter uttaksplan for behanding=$behandlingUUID slåSammenLikePerioder=$slåSammenLikePerioder") + logger.info("Henter uttaksplan for behandling=$behandlingUUID slåSammenLikePerioder=$slåSammenLikePerioder") val behandlingUUIDParsed = try { UUID.fromString(behandlingUUID) } catch (e: IllegalArgumentException) { @@ -153,7 +153,7 @@ class UttakplanApi { ] ) fun slettUttaksplab(@RequestParam behandlingUUID: BehandlingUUID): ResponseEntity { - logger.info("Sletter uttaksplan for behanding=$behandlingUUID") + logger.info("Sletter uttaksplan for behandling=$behandlingUUID") val behandlingUUIDParsed = try { UUID.fromString(behandlingUUID) } catch (e: IllegalArgumentException) { diff --git a/server/src/test/kotlin/no/nav/pleiepengerbarn/uttak/server/UttakplanApiTest.kt b/server/src/test/kotlin/no/nav/pleiepengerbarn/uttak/server/UttakplanApiTest.kt index 56fc0f07..679a1a76 100644 --- a/server/src/test/kotlin/no/nav/pleiepengerbarn/uttak/server/UttakplanApiTest.kt +++ b/server/src/test/kotlin/no/nav/pleiepengerbarn/uttak/server/UttakplanApiTest.kt @@ -965,7 +965,7 @@ class UttakplanApiTest(@Autowired val restTemplate: TestRestTemplate) { søknadsperiode = søknadsperiode, arbeid = listOf(arbeidSøker2), pleiebehov = mapOf(søknadsperiode to Pleiebehov.PROSENT_6000), - behandlingUUID = nesteBehandlingId(), + behandlingUUID = søker2BehandlingId, saksnummer = nesteSaksnummer()).copy(kravprioritetForBehandlinger = mapOf(søknadsperiode to listOf(søker2BehandlingId, grunnlag1Søker1.behandlingUUID))) grunnlagSøker2.opprettUttaksplan() @@ -978,7 +978,7 @@ class UttakplanApiTest(@Autowired val restTemplate: TestRestTemplate) { søknadsperiode = søknadsperiode, arbeid = listOf(arbeidSøker3), pleiebehov = mapOf(søknadsperiode to Pleiebehov.PROSENT_6000), - behandlingUUID = nesteBehandlingId(), + behandlingUUID = søker3BehandlingId, saksnummer = nesteSaksnummer()).copy(kravprioritetForBehandlinger = mapOf(søknadsperiode to listOf(søker3BehandlingId, grunnlagSøker2.behandlingUUID, grunnlag1Søker1.behandlingUUID)))