Skip to content

Commit

Permalink
sjekker frilansarbeidsforhold fra aareg mot inntekter
Browse files Browse the repository at this point in the history
inntektskomponenten sluttet å svare med frilansarbeidsforhold 4. november, uten noen forvarsel eller ettervarsel. :)

Co-authored-by: Simen Ullern <simen.ullern@nav.no>
Co-authored-by: Sivert Lunsæter <Sivert.Frang.Lunseter@nav.no>
  • Loading branch information
3 people committed Nov 8, 2024
1 parent 90f2683 commit d4ce978
Show file tree
Hide file tree
Showing 49 changed files with 221 additions and 528 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ internal abstract class AbstractObservableTest {
Periode(FOM.minusMonths(3), FOM.minusDays(1)) inntekter {
ORGNUMMER inntekt INNTEKT
}
}, arbeidsforhold = emptyList()),
}),
inntekterForOpptjeningsvurdering: InntekterForOpptjeningsvurdering = InntekterForOpptjeningsvurdering(listOf(
ArbeidsgiverInntekt(ORGNUMMER, listOf(ArbeidsgiverInntekt.MånedligInntekt(YearMonth.from(FOM.minusMonths(1)),
INNTEKT, ArbeidsgiverInntekt.MånedligInntekt.Inntekttype.LØNNSINNTEKT, "kontantytelse", "fastloenn")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,7 @@ internal abstract class AbstractE2ETest {
Vilkårsgrunnlag.Arbeidsforhold(orgnr, oppstart, type = Arbeidsforholdtype.ORDINÆRT)
},
inntekter = InntektForSykepengegrunnlag(
inntekter = inntekter.map { (orgnr, inntekt) -> grunnlag(orgnr, behov.skjæringstidspunkt, (1..3).map { inntekt }) },
arbeidsforhold = emptyList()
inntekter = inntekter.map { (orgnr, inntekt) -> grunnlag(orgnr, behov.skjæringstidspunkt, (1..3).map { inntekt }) }
),
orgnummer = behov.orgnummer
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ internal class SykepengegrunnlagForArbeidsgiverRiver(
requireAny("inntektstype", listOf("LOENNSINNTEKT", "NAERINGSINNTEKT", "PENSJON_ELLER_TRYGD", "YTELSE_FRA_OFFENTLIGE"))
interestedIn("orgnummer", "fødselsnummer", "fordel", "beskrivelse")
}
requireArray("arbeidsforholdliste") {
requireKey("orgnummer", "type")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ internal class VilkårsgrunnlagRiver(
requireAny("inntektstype", listOf("LOENNSINNTEKT", "NAERINGSINNTEKT", "PENSJON_ELLER_TRYGD", "YTELSE_FRA_OFFENTLIGE"))
interestedIn("orgnummer", "fødselsnummer", "fordel", "beskrivelse")
}
requireArray("arbeidsforholdliste") {
requireKey("orgnummer", "type")
}
}
message.requireArray("@løsning.${InntekterForOpptjeningsvurdering.name}") {
require("årMåned", JsonNode::asYearMonth)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,6 @@ internal class VilkårsgrunnlagMessage(packet: JsonMessage, override val melding
private val organisasjonsnummer = packet["organisasjonsnummer"].asText()

private val inntekterForSykepengegrunnlag = mapSkatteopplysninger(packet["@løsning.${InntekterForSykepengegrunnlag.name}"])
private val arbeidsforholdForSykepengegrunnlag = packet["@løsning.${InntekterForSykepengegrunnlag.name}"]
.flatMap { måned ->
måned["arbeidsforholdliste"]
.groupBy({ arbeidsforhold -> arbeidsforhold["orgnummer"].asText() }) { arbeidsforhold ->
InntektForSykepengegrunnlag.MånedligArbeidsforhold(
yearMonth = måned["årMåned"].asYearMonth(),
erFrilanser = arbeidsforhold["type"].asText() == "frilanserOppdragstakerHonorarPersonerMm"
)
}.toList()
}
.groupBy({ (orgnummer, _) -> orgnummer }) { (_, arbeidsforhold) -> arbeidsforhold }
.map { (orgnummer, arbeidsforhold) ->
InntektForSykepengegrunnlag.Arbeidsforhold(orgnummer, arbeidsforhold.flatten())
}

private val inntekterForOpptjeningsvurdering = mapSkatteopplysninger(packet["@løsning.${Aktivitet.Behov.Behovtype.InntekterForOpptjeningsvurdering.name}"])
private val arbeidsforhold = packet["@løsning.${ArbeidsforholdV2.name}"]
Expand Down Expand Up @@ -89,8 +75,7 @@ internal class VilkårsgrunnlagMessage(packet: JsonMessage, override val melding
medlemskapstatus = medlemskapstatus
),
inntektsvurderingForSykepengegrunnlag = InntektForSykepengegrunnlag(
inntekter = inntekterForSykepengegrunnlag,
arbeidsforhold = arbeidsforholdForSykepengegrunnlag
inntekter = inntekterForSykepengegrunnlag
),
inntekterForOpptjeningsvurdering = no.nav.helse.hendelser.InntekterForOpptjeningsvurdering(inntekter = inntekterForOpptjeningsvurdering),
arbeidsforhold = arbeidsforhold
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,18 +689,12 @@ internal class TestMessageFactory(

data class InntekterForSykepengegrunnlagFraLøsning(
valned: YearMonth,
val inntekter: List<Inntekt>,
val arbeidsforhold: List<Arbeidsforhold>
val inntekter: List<Inntekt>
) {
data class Inntekt(
val beløp: Double,
val orgnummer: String
)

data class Arbeidsforhold(
val orgnummer: String,
val type: String = "frilanserOppdragstakerHonorarPersonerMm"
)
}

data class InntekterForOpptjeningsvurderingFraLøsning(
Expand Down Expand Up @@ -859,12 +853,6 @@ internal class TestMessageFactory(
"fordel" to "kontantytelse",
"beskrivelse" to "fastloenn"
)
},
"arbeidsforholdliste" to it.arbeidsforhold.map { arbeidsforhold ->
mapOf(
"orgnummer" to arbeidsforhold.orgnummer,
"type" to arbeidsforhold.type
)
}
)
}
Expand Down Expand Up @@ -919,12 +907,6 @@ internal class TestMessageFactory(
"fordel" to "kontantytelse",
"beskrivelse" to "fastloenn"
)
},
"arbeidsforholdliste" to it.arbeidsforhold.map { arbeidsforhold ->
mapOf(
"orgnummer" to arbeidsforhold.orgnummer,
"type" to arbeidsforhold.type
)
}
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,12 +535,11 @@ internal abstract class AbstractEndToEndMediatorTest() {

fun sykepengegrunnlag(
skjæringstidspunkt: LocalDate,
inntekter: List<InntekterForSykepengegrunnlagFraLøsning.Inntekt>,
arbeidsforhold: List<InntekterForSykepengegrunnlagFraLøsning.Arbeidsforhold> = emptyList()
inntekter: List<InntekterForSykepengegrunnlagFraLøsning.Inntekt>
): List<InntekterForSykepengegrunnlagFraLøsning> {
return (3L downTo 1L).map {
val mnd = YearMonth.from(skjæringstidspunkt).minusMonths(it)
InntekterForSykepengegrunnlagFraLøsning(mnd, inntekter, arbeidsforhold)
InntekterForSykepengegrunnlagFraLøsning(mnd, inntekter)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,12 @@
package no.nav.helse.spleis.mediator.e2e

import java.time.YearMonth
import no.nav.helse.flex.sykepengesoknad.kafka.SoknadsperiodeDTO
import no.nav.helse.januar
import no.nav.helse.spleis.mediator.TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning
import no.nav.helse.spleis.mediator.TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning.Arbeidsforhold
import no.nav.inntektsmeldingkontrakt.Periode
import org.junit.jupiter.api.Test


internal class FrilanserTest : AbstractEndToEndMediatorTest() {

@Test
fun `Person med frilanserinntekt i løpet av de siste 3 månedene sendes til infotrygd`() {
sendNySøknad(SoknadsperiodeDTO(fom = 3.januar, tom = 26.januar, sykmeldingsgrad = 100))
sendSøknad(
perioder = listOf(SoknadsperiodeDTO(fom = 3.januar, tom = 26.januar, sykmeldingsgrad = 100))
)
sendInntektsmelding(listOf(Periode(fom = 3.januar, tom = 18.januar)), førsteFraværsdag = 3.januar)
sendVilkårsgrunnlag(
vedtaksperiodeIndeks = 0,
inntekterForSykepengegrunnlag = (10..12).map {
InntekterForSykepengegrunnlagFraLøsning(
måned = YearMonth.of(2017, it),
inntekter = listOf(
InntekterForSykepengegrunnlagFraLøsning.Inntekt(INNTEKT, ORGNUMMER)
),
arbeidsforhold = listOf(Arbeidsforhold(orgnummer = ORGNUMMER, type = "frilanserOppdragstakerHonorarPersonerMm"))
)
}
)
assertTilstander(0, "AVVENTER_INFOTRYGDHISTORIKK", "AVVENTER_INNTEKTSMELDING", "AVVENTER_BLOKKERENDE_PERIODE", "AVVENTER_VILKÅRSPRØVING", "TIL_INFOTRYGD")
}

@Test
fun `frilansersøknad`() {
sendNySøknadFrilanser(SoknadsperiodeDTO(fom = 3.januar, tom = 26.januar, sykmeldingsgrad = 100))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,17 @@ internal class VilkårsgrunnlagRiverTest : RiverTest() {
inntekterForSykepengegrunnlag = listOf(
TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning(
måned = YearMonth.of(2017, 12),
inntekter = listOf(TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning.Inntekt(32000.0, ORGNUMMER)),
arbeidsforhold = listOf(TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning.Arbeidsforhold(ORGNUMMER, "frilanserOppdragstakerHonorarPersonerMm"))
inntekter = listOf(TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning.Inntekt(32000.0, ORGNUMMER))
)),
inntekterForOpptjeningsvurdering = listOf(
TestMessageFactory.InntekterForOpptjeningsvurderingFraLøsning(
måned = YearMonth.of(2017, 12),
inntekter = listOf(TestMessageFactory.InntekterForOpptjeningsvurderingFraLøsning.Inntekt(32000.0, ORGNUMMER))
)),
arbeidsforhold = listOf(TestMessageFactory.Arbeidsforhold(ORGNUMMER, LocalDate.EPOCH, null, Arbeidsforholdtype.ORDINÆRT)),
arbeidsforhold = listOf(
TestMessageFactory.Arbeidsforhold(ORGNUMMER, LocalDate.EPOCH, null, Arbeidsforholdtype.ORDINÆRT),
TestMessageFactory.Arbeidsforhold(ORGNUMMER, LocalDate.EPOCH, null, Arbeidsforholdtype.FRILANSER)
),
medlemskapstatus = Medlemskapsvurdering.Medlemskapstatus.Ja
)
)
Expand All @@ -65,8 +67,7 @@ internal class VilkårsgrunnlagRiverTest : RiverTest() {
inntekterForSykepengegrunnlag = listOf(
TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning(
måned = YearMonth.of(2017, 12),
inntekter = listOf(TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning.Inntekt(32000.0, "987654322")),
arbeidsforhold = emptyList()
inntekter = listOf(TestMessageFactory.InntekterForSykepengegrunnlagFraLøsning.Inntekt(32000.0, "987654322"))
)),
inntekterForOpptjeningsvurdering = listOf(
TestMessageFactory.InntekterForOpptjeningsvurderingFraLøsning(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,75 +1,9 @@
package no.nav.helse.hendelser

import java.time.LocalDate
import java.time.YearMonth
import java.util.UUID
import no.nav.helse.etterlevelse.Subsumsjonslogg
import no.nav.helse.hendelser.ArbeidsgiverInntekt.Companion.antallMåneder
import no.nav.helse.hendelser.ArbeidsgiverInntekt.Companion.avklarSykepengegrunnlag
import no.nav.helse.hendelser.ArbeidsgiverInntekt.Companion.harInntektFor
import no.nav.helse.hendelser.ArbeidsgiverInntekt.Companion.harInntektI
import no.nav.helse.person.Person
import no.nav.helse.person.aktivitetslogg.IAktivitetslogg
import no.nav.helse.person.aktivitetslogg.Varselkode.RV_IV_3
import no.nav.helse.person.inntekt.Inntektsgrunnlag
import no.nav.helse.person.inntekt.SkattSykepengegrunnlag

class InntektForSykepengegrunnlag(
private val inntekter: List<ArbeidsgiverInntekt>,
private val arbeidsforhold: List<Arbeidsforhold>
) {
data class InntektForSykepengegrunnlag(val inntekter: List<ArbeidsgiverInntekt>) {
init {
require(inntekter.antallMåneder() <= 3L) { "Forventer maks 3 inntektsmåneder" }
}

internal fun avklarSykepengegrunnlag(
aktivitetslogg: IAktivitetslogg,
person: Person,
rapporterteArbeidsforhold: Map<String, SkattSykepengegrunnlag>,
skjæringstidspunkt: LocalDate,
meldingsreferanseId: UUID,
subsumsjonslogg: Subsumsjonslogg
): Inntektsgrunnlag =
inntekter.avklarSykepengegrunnlag(aktivitetslogg, person, rapporterteArbeidsforhold, skjæringstidspunkt, meldingsreferanseId, subsumsjonslogg)

internal fun valider(
aktivitetslogg: IAktivitetslogg,
): IAktivitetslogg {
if (finnerFrilansinntektDeSiste3Månedene())
aktivitetslogg.funksjonellFeil(RV_IV_3)
return aktivitetslogg
}

private fun finnerFrilansinntektDeSiste3Månedene() =
arbeidsforhold.any { arbeidsforhold ->
arbeidsforhold.månedligeArbeidsforhold.any {
it.erFrilanser && inntekter.harInntektFor(arbeidsforhold.orgnummer, it.yearMonth)
}
}

internal fun finnerFrilansinntektDenSisteMåneden(skjæringstidspunkt: LocalDate): Boolean {
val månedFørSkjæringstidspunkt = YearMonth.from(skjæringstidspunkt.minusMonths(1))
return arbeidsforhold.any { arbeidsforhold ->
arbeidsforhold.månedligeArbeidsforhold.any {
it.erFrilanser && inntekter.harInntektFor(arbeidsforhold.orgnummer, månedFørSkjæringstidspunkt)
}
}
}

internal fun harInntektI(måned: YearMonth): Boolean {
return inntekter.harInntektI(måned)
}

class Arbeidsforhold(
val orgnummer: String,
valnedligeArbeidsforhold: List<MånedligArbeidsforhold>
)

class MånedligArbeidsforhold(
val yearMonth: YearMonth,
val erFrilanser: Boolean
)
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ import java.time.LocalDateTime
import java.time.YearMonth
import java.util.UUID
import no.nav.helse.etterlevelse.Subsumsjonslogg
import no.nav.helse.hendelser.ArbeidsgiverInntekt.Companion.avklarSykepengegrunnlag
import no.nav.helse.hendelser.ArbeidsgiverInntekt.Companion.harInntektFor
import no.nav.helse.hendelser.ArbeidsgiverInntekt.Companion.harInntektI
import no.nav.helse.hendelser.Avsender.SYSTEM
import no.nav.helse.hendelser.Vilkårsgrunnlag.Arbeidsforhold.Companion.opptjeningsgrunnlag
import no.nav.helse.person.Opptjening
import no.nav.helse.person.Person
import no.nav.helse.person.VilkårsgrunnlagHistorikk
import no.nav.helse.person.aktivitetslogg.IAktivitetslogg
import no.nav.helse.person.aktivitetslogg.Varselkode
import no.nav.helse.person.aktivitetslogg.Varselkode.RV_IV_3
import no.nav.helse.person.inntekt.AnsattPeriode
import no.nav.helse.person.inntekt.Inntektsgrunnlag
import no.nav.helse.person.inntekt.SkattSykepengegrunnlag
import no.nav.helse.yearMonth

class Vilkårsgrunnlag(
meldingsreferanseId: UUID,
Expand Down Expand Up @@ -69,19 +74,18 @@ class Vilkårsgrunnlag(
ansattPerioder = ansattPerioder.map { it.somAnsattPeriode() }
)
}
return inntektsvurderingForSykepengegrunnlag.avklarSykepengegrunnlag(aktivitetslogg, person, rapporterteArbeidsforhold, skjæringstidspunkt, metadata.meldingsreferanseId, subsumsjonslogg)
return inntektsvurderingForSykepengegrunnlag.inntekter.avklarSykepengegrunnlag(aktivitetslogg, person, rapporterteArbeidsforhold, skjæringstidspunkt, metadata.meldingsreferanseId, subsumsjonslogg)
}

internal fun valider(aktivitetslogg: IAktivitetslogg, inntektsgrunnlag: Inntektsgrunnlag, subsumsjonslogg: Subsumsjonslogg): IAktivitetslogg {
val sykepengegrunnlagOk = inntektsgrunnlag.valider(aktivitetslogg)
inntektsvurderingForSykepengegrunnlag.valider(aktivitetslogg)
arbeidsforhold.forEach { it.loggFrilans(aktivitetslogg, skjæringstidspunkt, arbeidsforhold) }
arbeidsforhold.forEach { it.validerFrilans(aktivitetslogg, skjæringstidspunkt, arbeidsforhold, inntektsvurderingForSykepengegrunnlag) }
val opptjening = opptjening()
subsumsjonslogg.logg(opptjening.subsumsjon)

if (!harInntektMånedenFørSkjæringstidspunkt) {
aktivitetslogg.varsel(Varselkode.RV_OV_3)
} else if (!inntektsvurderingForSykepengegrunnlag.harInntektI(YearMonth.from(skjæringstidspunkt.minusMonths(1)))) {
} else if (!inntektsvurderingForSykepengegrunnlag.inntekter.harInntektI(YearMonth.from(skjæringstidspunkt.minusMonths(1)))) {
aktivitetslogg.info("Har inntekt måneden før skjæringstidspunkt med inntekter for opptjeningsvurdering, men ikke med inntekter for sykepengegrunnlag")
}

Expand Down Expand Up @@ -120,8 +124,28 @@ class Vilkårsgrunnlag(
check(orgnummer.isNotBlank())
}

fun loggFrilans(aktivitetslogg: IAktivitetslogg, skjæringstidspunkt: LocalDate, andre: List<Arbeidsforhold>) {
fun validerFrilans(
aktivitetslogg: IAktivitetslogg,
skjæringstidspunkt: LocalDate,
andre: List<Arbeidsforhold>,
inntektsvurderingForSykepengegrunnlag: InntektForSykepengegrunnlag
) {
if (type != Arbeidsforholdtype.FRILANSER) return
harFrilansinntekterDeSiste3Månedene(aktivitetslogg, skjæringstidspunkt, inntektsvurderingForSykepengegrunnlag)
sjekkFrilansArbeidsforholdMotAndreArbeidsforhold(aktivitetslogg, skjæringstidspunkt, andre)
}

private fun harFrilansinntekterDeSiste3Månedene(aktivitetslogg: IAktivitetslogg, skjæringstidspunkt: LocalDate, inntektForSykepengegrunnlag: InntektForSykepengegrunnlag) {
val finnerFrilansinntektDeSiste3Månedene = (1..3).any { antallMånederFør ->
val måned = skjæringstidspunkt.yearMonth.minusMonths(antallMånederFør.toLong())
val månedenSomPeriode = måned.atDay(1) til måned.atEndOfMonth()
val ansattIMåneden = ansettelseperiode.overlapperMed(månedenSomPeriode)
ansattIMåneden && inntektForSykepengegrunnlag.inntekter.harInntektFor(orgnummer, måned)
}
if (finnerFrilansinntektDeSiste3Månedene) aktivitetslogg.funksjonellFeil(RV_IV_3)
}

private fun sjekkFrilansArbeidsforholdMotAndreArbeidsforhold(aktivitetslogg: IAktivitetslogg, skjæringstidspunkt: LocalDate, andre: List<Arbeidsforhold>) {
if (skjæringstidspunkt !in ansettelseperiode) return
aktivitetslogg.info("Vedkommende har et aktivt frilansoppdrag på skjæringstidspunktet")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,16 +456,15 @@ internal class TestPerson(

internal fun lagStandardSykepengegrunnlag(orgnummer: String, inntekt: Inntekt, skjæringstidspunkt: LocalDate) =
lagStandardSykepengegrunnlag(listOf(orgnummer to inntekt), skjæringstidspunkt)
internal fun lagStandardSykepengegrunnlag(arbeidsgivere: List<Pair<String, Inntekt>>, skjæringstidspunkt: LocalDate, arbeidsforhold: List<InntektForSykepengegrunnlag.Arbeidsforhold> = emptyList()) =
internal fun lagStandardSykepengegrunnlag(arbeidsgivere: List<Pair<String, Inntekt>>, skjæringstidspunkt: LocalDate) =
InntektForSykepengegrunnlag(
inntekter = inntektperioderForSykepengegrunnlag {
val måned = YearMonth.from(skjæringstidspunkt)
val periode = måned.minusMonths(3L).atDay(1) til måned.minusMonths(1).atDay(1)
periode inntekter {
arbeidsgivere.forEach { (orgnummer, inntekt) -> orgnummer inntekt inntekt }
}
},
arbeidsforhold = arbeidsforhold
}
)

internal fun List<String>.lagStandardSykepengegrunnlag(inntekt: Inntekt, skjæringstidspunkt: LocalDate) =
Expand Down
Loading

0 comments on commit d4ce978

Please sign in to comment.