Skip to content

Commit

Permalink
Beregn feilutbetaling og lagre inntekt for 4 måneder tilbake
Browse files Browse the repository at this point in the history
  • Loading branch information
olekvernberg committed Oct 29, 2023
1 parent 50ccdc6 commit 4331628
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,5 @@ data class ForventetInntektForPerson(
val forventetInntektForrigeMåned: Int?,
val forventetInntektToMånederTilbake: Int?,
val forventetInntektTreMånederTilbake: Int?,
val forventetInntektFireMånederTilbake: Int?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,6 @@ data class Tilleggsinformasjon(
val tilleggsinformasjonDetaljer: TilleggsinformasjonDetaljer?,
)

data class Inntektsendring(
val treMånederTilbake: Int,
val toMånederTilbake: Int,
val forrigeMåned: Int,
val beløpTreMånederTilbake: Int,
val beløpToMånederTilbake: Int,
val beløpForrigeMåned: Int,
) {
fun harEndretInntekt() = treMånederTilbake >= 10 && toMånederTilbake >= 10 && forrigeMåned >= 10
}

enum class AktørType {
AKTOER_ID,
NATURLIG_IDENT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import no.nav.familie.ef.personhendelse.client.OppgaveClient
import no.nav.familie.ef.personhendelse.client.SakClient
import no.nav.familie.ef.personhendelse.client.pdl.secureLogger
import org.springframework.stereotype.Service
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.YearMonth

@Service
Expand All @@ -13,7 +15,9 @@ class InntektsendringerService(
val sakClient: SakClient,
) {

private val halvtGrunnbeløpMånedlig = (118620 / 2) / 12
private val grunnbeløp = 118_620
private val halvtGrunnbeløpMånedlig = (grunnbeløp / 2) / 12
private val reduksjonsfaktor = BigDecimal(0.45)

fun beregnEndretInntekt(inntektshistorikkResponse: InntektshistorikkResponse, forventetInntektForPerson: ForventetInntektForPerson): Inntektsendring {
// hent alle registrerte vedtak som var på personen sist beregning
Expand All @@ -23,6 +27,14 @@ class InntektsendringerService(
inntektshistorikkResponse.inntektForMåned(YearMonth.now().minusMonths(2))
val inntektTreMånederTilbake =
inntektshistorikkResponse.inntektForMåned(YearMonth.now().minusMonths(3))
val inntektFireMånederTilbake =
inntektshistorikkResponse.inntektForMåned(YearMonth.now().minusMonths(4))

val inntektsendringFireMånederTilbake = beregnInntektsendring(
inntektFireMånederTilbake,
forventetInntektForPerson.personIdent,
forventetInntektForPerson.forventetInntektTreMånederTilbake,
)

val inntektsendringTreMånederTilbake = beregnInntektsendring(
inntektTreMånederTilbake,
Expand All @@ -41,21 +53,20 @@ class InntektsendringerService(
)

return Inntektsendring(
treMånederTilbake = inntektsendringTreMånederTilbake.prosent,
toMånederTilbake = inntektsendringToMånederTilbake.prosent,
forrigeMåned = inntektsendringForrigeMåned.prosent,
beløpTreMånederTilbake = inntektsendringTreMånederTilbake.beløp,
beløpToMånederTilbake = inntektsendringToMånederTilbake.beløp,
beløpForrigeMåned = inntektsendringForrigeMåned.beløp,
fireMånederTilbake = inntektsendringFireMånederTilbake,
treMånederTilbake = inntektsendringTreMånederTilbake,
toMånederTilbake = inntektsendringToMånederTilbake,
forrigeMåned = inntektsendringForrigeMåned,
)
}

private fun beregnInntektsendring(nyesteRegistrerteInntekt: List<InntektVersjon>?, ident: String, forventetInntekt: Int?): BeregningResultat {
if (forventetInntekt == null || nyesteRegistrerteInntekt?.maxOfOrNull { it.versjon } == null) {
secureLogger.warn("Ingen gjeldende inntekt funnet på person $ident har personen løpende stønad?")
return BeregningResultat(0, 0)
return BeregningResultat(0, 0, 0)
}
if (forventetInntekt > 585000) return BeregningResultat(0, 0) // Ignorer alle med over 585000 i årsinntekt, da de har 0 i utbetaling.

if (forventetInntekt > 652000) return BeregningResultat(0, 0, 0) // Ignorer alle med over 652000 i årsinntekt, da de har 0 i utbetaling.
val månedligForventetInntekt = (forventetInntekt / 12)

val orgNrToNyesteVersjonMap = nyesteRegistrerteInntekt.associate { it.opplysningspliktig to it.versjon }
Expand All @@ -67,13 +78,38 @@ class InntektsendringerService(
(it.inntektType == InntektType.YTELSE_FRA_OFFENTLIGE && it.tilleggsinformasjon?.tilleggsinformasjonDetaljer?.detaljerType == "ETTERBETALINGSPERIODE")
}.sumOf { it.beløp }

if (samletInntekt < halvtGrunnbeløpMånedlig) return BeregningResultat(0, 0)
if (samletInntekt < halvtGrunnbeløpMånedlig) return BeregningResultat(0, 0, 0)

secureLogger.info("Samlet inntekt: $samletInntekt - månedlig forventet inntekt: $månedligForventetInntekt (årlig: $forventetInntekt) for person $ident")
val inntektsendringProsent = (((samletInntekt - månedligForventetInntekt) / månedligForventetInntekt.toDouble()) * 100).toInt()
val beløp = samletInntekt - månedligForventetInntekt
if (månedligForventetInntekt == 0) return BeregningResultat(beløp, 100) // Prioriterer personer registrert med uredusert stønad, men har samlet inntekt over 1/2 G
return BeregningResultat(beløp, inntektsendringProsent)
val endretInntektBeløp = samletInntekt - månedligForventetInntekt
val feilutbetaling = beregnFeilutbetaling(månedligForventetInntekt, samletInntekt)
if (månedligForventetInntekt == 0) return BeregningResultat(endretInntektBeløp, 100, feilutbetaling) // Prioriterer personer registrert med uredusert stønad, men har samlet inntekt over 1/2 G
return BeregningResultat(endretInntektBeløp, inntektsendringProsent, feilutbetaling)
}

fun beregnFeilutbetaling(forventetInntekt: Int, samletInntekt: Int): Int {
return beregnUtbetaling(samletInntekt) - beregnUtbetaling(forventetInntekt)
}

private fun beregnUtbetaling(inntekt: Int): Int {
val avkortningPerMåned = beregnAvkortning(inntekt).setScale(0, RoundingMode.HALF_DOWN)

val fullOvergangsstønadPerMåned =
BigDecimal(grunnbeløp).multiply(BigDecimal(2.25)).divide(BigDecimal(12)).setScale(0, RoundingMode.HALF_EVEN)

val utbetaling = fullOvergangsstønadPerMåned.subtract(avkortningPerMåned).setScale(0, RoundingMode.HALF_UP)

return if (utbetaling <= BigDecimal.ZERO) 0 else utbetaling.intValueExact()
}

private fun beregnAvkortning(inntekt: Int): BigDecimal {
val inntektOverHalveGrunnbeløp = BigDecimal(inntekt).subtract(BigDecimal(grunnbeløp).multiply(BigDecimal(0.5)))
return if (inntektOverHalveGrunnbeløp > BigDecimal.ZERO) {
inntektOverHalveGrunnbeløp.multiply(reduksjonsfaktor).setScale(5, RoundingMode.HALF_DOWN)
} else {
BigDecimal.ZERO
}
}

// Ignorterte ytelser: Alle uføre går under annet regelverk (samordning) og skal derfor ignoreres.
Expand All @@ -86,7 +122,17 @@ class InntektsendringerService(
)
}

data class Inntektsendring(
val fireMånederTilbake: BeregningResultat,
val treMånederTilbake: BeregningResultat,
val toMånederTilbake: BeregningResultat,
val forrigeMåned: BeregningResultat,
) {
fun harEndretInntekt() = fireMånederTilbake.prosent >= 10 && treMånederTilbake.prosent >= 10 && toMånederTilbake.prosent >= 10 && forrigeMåned.prosent >= 10
}

data class BeregningResultat(
val beløp: Int,
val prosent: Int,
val feilutbetaling: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class VedtakendringerService(
@Async
fun beregnInntektsendringerOgLagreIDb(skalOppretteOppgave: Boolean = false) {
logger.info("Starter beregning av inntektsendringer")
val personerMedAktivStønad = sakClient.hentPersonerMedAktivStønadIkkeManueltRevurdertSisteMåneder()
val personerMedAktivStønad = sakClient.hentPersonerMedAktivStønadIkkeManueltRevurdertSisteMåneder(4)
efVedtakRepository.clearInntektsendringer()
logger.info("Antall personer med aktiv stønad: ${personerMedAktivStønad.size}")
var counter = 0
Expand Down Expand Up @@ -67,24 +67,19 @@ class VedtakendringerService(
) {
val nyeVedtak = nyeVedtak(response)
val endretInntekt = inntektsendringerService.beregnEndretInntekt(response, forventetInntektForPerson)
efVedtakRepository.lagreInntektsendring(
efVedtakRepository.lagreVedtakOgInntektsendringForPersonIdent(
forventetInntektForPerson.personIdent,
nyeVedtak?.isNotEmpty() ?: false,
endretInntekt.treMånederTilbake,
endretInntekt.toMånederTilbake,
endretInntekt.forrigeMåned,
nyeVedtak?.joinToString(),
endretInntekt.beløpTreMånederTilbake,
endretInntekt.beløpToMånederTilbake,
endretInntekt.beløpForrigeMåned,
endretInntekt,
)
}

fun opprettOppgaverForInntektsendringer(skalOppretteOppgave: Boolean): Int {
val inntektsendringer = efVedtakRepository.hentInntektsendringer()
val inntektsendringer = efVedtakRepository.hentInntektOgVedtakEndring()
if (skalOppretteOppgave) {
inntektsendringer.forEach {
opprettOppgave(it.harNyttVedtak, it.inntektsendringToMånederTilbake, it.inntektsendringForrigeMåned, it.personIdent)
opprettOppgave(it.harNyeVedtak, it.inntektsendringToMånederTilbake.prosent, it.inntektsendringForrigeMåned.prosent, it.personIdent)
}
} else {
logger.info("Ville opprettet inntektsendring-oppgave for ${inntektsendringer.size} personer")
Expand Down Expand Up @@ -129,7 +124,7 @@ class VedtakendringerService(
try {
return inntektClient.hentInntektshistorikk(
fnr,
YearMonth.now().minusMonths(4),
YearMonth.now().minusMonths(5),
null,
)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package no.nav.familie.ef.personhendelse.inntekt.vedtak

import no.nav.familie.ef.personhendelse.inntekt.BeregningResultat
import no.nav.familie.ef.personhendelse.inntekt.Inntektsendring
import no.nav.familie.kontrakter.felles.ef.EnsligForsørgerVedtakhendelse
import no.nav.familie.kontrakter.felles.ef.StønadType
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource
Expand Down Expand Up @@ -63,57 +65,83 @@ class EfVedtakRepository(val namedParameterJdbcTemplate: NamedParameterJdbcTempl
namedParameterJdbcTemplate.update(sql, mapSqlParameterSource)
}

fun lagreInntektsendring(
fun lagreVedtakOgInntektsendringForPersonIdent(
personIdent: String,
harNyeVedtak: Boolean,
inntektsendringTreMånederTilbake: Int,
inntektsendringToMånederTilbake: Int,
inntektsendringForrigeMåned: Int,
nyYtelse: String?,
inntektsendringTreMånederTilbakeBeløp: Int,
inntektsendringToMånederTilbakeBeløp: Int,
inntektsendringForrigeMånedBeløp: Int,
nyeYtelser: String?,
inntektsendring: Inntektsendring,
) {
val sql =
"INSERT INTO inntektsendringer VALUES(:id, :personIdent, :harNyeVedtak, :prosessertTid, " +
":inntektsendringToMånederTilbake, :inntektsendringForrigeMåned, :nyYtelse, " +
":inntektsendringTreMånederTilbakeBeløp, :inntektsendringToMånederTilbakeBeløp, :inntektsendringForrigeMånedBeløp, :inntektsendringTreMånederTilbake)" +
" ON CONFLICT DO NOTHING"
"INSERT INTO inntektsendringer(" +
"id, person_ident, harnyttvedtak, ny_ytelse_type, prosessert_tid, " +
"inntekt_endret_fire_maaneder_tilbake, inntekt_endret_tre_maaneder_tilbake, inntekt_endret_to_maaneder_tilbake, inntekt_endret_forrige_maaned, " +
"inntekt_endret_fire_maaneder_tilbake_belop, inntekt_endret_tre_maaneder_tilbake_belop, inntekt_endret_to_maaneder_tilbake_belop, inntekt_endret_forrige_maaned_belop," +
"feilutbetaling_fire_maaneder_tilbake, feilutbetaling_tre_maaneder_tilbake, feilutbetaling_to_maaneder_tilbake, feilutbetaling_forrige_maaned" +
") VALUES" +
"(:id, :personIdent, :harNyeVedtak, :nyeYtelser, :prosessertTid, " +
":inntektsendringFireMånederTilbake, :inntektsendringTreMånederTilbake, :inntektsendringToMånederTilbake, :inntektsendringForrigeMåned, " +
":inntektsendringFireMånederTilbakeBeløp, :inntektsendringTreMånederTilbakeBeløp, :inntektsendringToMånederTilbakeBeløp, :inntektsendringForrigeMånedBeløp, " +
":feilutbetalingFireMånederTilbake, :feilutbetalingTreMånederTilbake, :feilutbetalingToMånederTilbake, :feilutbetalingForrigeMåned) " +
"ON CONFLICT DO NOTHING"
val params = MapSqlParameterSource(
mapOf(
"id" to UUID.randomUUID(),
"personIdent" to personIdent,
"harNyeVedtak" to harNyeVedtak,
"nyeYtelser" to nyeYtelser,
"prosessertTid" to LocalDateTime.now(),
"inntektsendringTreMånederTilbake" to inntektsendringTreMånederTilbake,
"inntektsendringToMånederTilbake" to inntektsendringToMånederTilbake,
"inntektsendringForrigeMåned" to inntektsendringForrigeMåned,
"nyYtelse" to nyYtelse,
"inntektsendringTreMånederTilbakeBeløp" to inntektsendringTreMånederTilbakeBeløp,
"inntektsendringToMånederTilbakeBeløp" to inntektsendringToMånederTilbakeBeløp,
"inntektsendringForrigeMånedBeløp" to inntektsendringForrigeMånedBeløp,
"inntektsendringFireMånederTilbake" to inntektsendring.fireMånederTilbake.prosent,
"inntektsendringTreMånederTilbake" to inntektsendring.treMånederTilbake.prosent,
"inntektsendringToMånederTilbake" to inntektsendring.toMånederTilbake.prosent,
"inntektsendringForrigeMåned" to inntektsendring.forrigeMåned.prosent,
"inntektsendringFireMånederTilbakeBeløp" to inntektsendring.fireMånederTilbake.beløp,
"inntektsendringTreMånederTilbakeBeløp" to inntektsendring.treMånederTilbake.beløp,
"inntektsendringToMånederTilbakeBeløp" to inntektsendring.toMånederTilbake.beløp,
"inntektsendringForrigeMånedBeløp" to inntektsendring.forrigeMåned.beløp,
"feilutbetalingFireMånederTilbake" to inntektsendring.fireMånederTilbake.feilutbetaling,
"feilutbetalingTreMånederTilbake" to inntektsendring.treMånederTilbake.feilutbetaling,
"feilutbetalingToMånederTilbake" to inntektsendring.toMånederTilbake.feilutbetaling,
"feilutbetalingForrigeMåned" to inntektsendring.forrigeMåned.feilutbetaling,
),
)
namedParameterJdbcTemplate.update(sql, params)
}

fun hentInntektsendringer(): List<Inntektsendring> {
val sql = "SELECT * FROM inntektsendringer WHERE harNyttVedtak = true OR (inntekt_endret_tre_maaneder_tilbake >= 10 AND inntekt_endret_to_maaneder_tilbake >= 10 AND inntekt_endret_forrige_maaned >= 10)"
fun hentInntektOgVedtakEndring(): List<InntektOgVedtakEndring> {
val sql = "SELECT * FROM inntektsendringer WHERE harNyttVedtak = true OR " +
"(inntekt_endret_fire_maaneder_tilbake >= 10 AND " +
"inntekt_endret_tre_maaneder_tilbake >= 10 AND " +
"inntekt_endret_to_maaneder_tilbake >= 10 AND " +
"inntekt_endret_forrige_maaned >= 10)"
return namedParameterJdbcTemplate.query(sql, inntektsendringerMapper)
}

private val inntektsendringerMapper = { rs: ResultSet, _: Int ->
Inntektsendring(
InntektOgVedtakEndring(
rs.getString("person_ident"),
rs.getBoolean("harNyttVedtak"),
rs.getObject("prosessert_tid", LocalDateTime::class.java),
rs.getInt("inntekt_endret_tre_maaneder_tilbake"),
rs.getInt("inntekt_endret_to_maaneder_tilbake"),
rs.getInt("inntekt_endret_forrige_maaned"),
BeregningResultat(
rs.getInt("inntekt_endret_fire_maaneder_tilbake_belop"),
rs.getInt("inntekt_endret_fire_maaneder_tilbake"),
rs.getInt("feilutbetaling_fire_maaneder_tilbake"),
),
BeregningResultat(
rs.getInt("inntekt_endret_tre_maaneder_tilbake_belop"),
rs.getInt("inntekt_endret_tre_maaneder_tilbake"),
rs.getInt("feilutbetaling_tre_maaneder_tilbake"),
),
BeregningResultat(
rs.getInt("inntekt_endret_to_maaneder_tilbake_belop"),
rs.getInt("inntekt_endret_to_maaneder_tilbake"),
rs.getInt("feilutbetaling_to_maaneder_tilbake"),
),
BeregningResultat(
rs.getInt("inntekt_endret_forrige_maaned_belop"),
rs.getInt("inntekt_endret_forrige_maaned"),
rs.getInt("feilutbetaling_forrige_maaned"),
),
rs.getString("ny_ytelse_type"),
rs.getInt("inntekt_endret_tre_maaneder_tilbake_belop"),
rs.getInt("inntekt_endret_to_maaneder_tilbake_belop"),
rs.getInt("inntekt_endret_forrige_maaned_belop"),
)
}

Expand All @@ -123,15 +151,13 @@ class EfVedtakRepository(val namedParameterJdbcTemplate: NamedParameterJdbcTempl
}
}

data class Inntektsendring(
data class InntektOgVedtakEndring(
val personIdent: String,
val harNyttVedtak: Boolean,
val harNyeVedtak: Boolean,
val prosessertTid: LocalDateTime,
val inntektsendringTreMånederTilbake: Int,
val inntektsendringToMånederTilbake: Int,
val inntektsendringForrigeMåned: Int,
val nyYtelse: String?,
val inntektsendringTreMånederTilbakeBeløp: Int,
val inntektsendringToMånederTilbakeBeløp: Int,
val inntektsendringForrigeMånedBeløp: Int,
val inntektsendringFireMånederTilbake: BeregningResultat,
val inntektsendringTreMånederTilbake: BeregningResultat,
val inntektsendringToMånederTilbake: BeregningResultat,
val inntektsendringForrigeMåned: BeregningResultat,
val nyeYtelser: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ALTER TABLE inntektsendringer ADD COLUMN inntekt_endret_fire_maaneder_tilbake INT DEFAULT 0;
ALTER TABLE inntektsendringer ADD COLUMN inntekt_endret_fire_maaneder_tilbake_belop INT DEFAULT 0;
ALTER TABLE inntektsendringer ADD COLUMN feilutbetaling_fire_maaneder_tilbake INT DEFAULT 0;
ALTER TABLE inntektsendringer ADD COLUMN feilutbetaling_tre_maaneder_tilbake INT DEFAULT 0;
ALTER TABLE inntektsendringer ADD COLUMN feilutbetaling_to_maaneder_tilbake INT DEFAULT 0;
ALTER TABLE inntektsendringer ADD COLUMN feilutbetaling_forrige_maaned INT DEFAULT 0;
Loading

0 comments on commit 4331628

Please sign in to comment.