Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c8c4064

Browse files
committedMar 17, 2025··
TSFF-1194: Lagrer kontrollert inntektperioder i egen datastruktur og begrenser tilkjent ytelse til perioder som er ferdig kontrollert
1 parent c689fd8 commit c8c4064

File tree

11 files changed

+418
-38
lines changed

11 files changed

+418
-38
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package no.nav.ung.sak.behandlingslager.tilkjentytelse;
2+
3+
import jakarta.persistence.*;
4+
import no.nav.ung.sak.behandlingslager.BaseEntitet;
5+
import no.nav.ung.sak.behandlingslager.PostgreSQLRangeType;
6+
import no.nav.ung.sak.behandlingslager.Range;
7+
import no.nav.ung.sak.domene.typer.tid.DatoIntervallEntitet;
8+
import org.hibernate.annotations.Type;
9+
10+
import java.math.BigDecimal;
11+
import java.time.LocalDate;
12+
13+
@Entity(name = "KontrollertInntektPeriode")
14+
@Table(name = "KONTROLLERT_INNTEKT_PERIODE")
15+
public class KontrollertInntektPeriode extends BaseEntitet {
16+
17+
18+
@Id
19+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_KONTROLLERT_INNTEKT_PERIODE")
20+
private Long id;
21+
22+
@Type(PostgreSQLRangeType.class)
23+
@Column(name = "periode", columnDefinition = "daterange")
24+
private Range<LocalDate> periode;
25+
26+
@Column(name = "arbeidsinntekt", nullable = false)
27+
private BigDecimal arbeidsinntekt;
28+
29+
@Column(name = "ytelse", nullable = false)
30+
private BigDecimal ytelse;
31+
32+
protected KontrollertInntektPeriode() {
33+
}
34+
35+
KontrollertInntektPeriode(KontrollertInntektPeriode eksisterende) {
36+
this.periode = Range.closed(eksisterende.getPeriode().getFomDato(), eksisterende.getPeriode().getTomDato());
37+
this.arbeidsinntekt = eksisterende.getArbeidsinntekt();
38+
this.ytelse = eksisterende.getYtelse();
39+
}
40+
41+
private KontrollertInntektPeriode(DatoIntervallEntitet periode,
42+
BigDecimal arbeidsinntekt,
43+
BigDecimal ytelse) {
44+
this.periode = Range.closed(periode.getFomDato(), periode.getTomDato());
45+
this.arbeidsinntekt = arbeidsinntekt;
46+
this.ytelse = ytelse;
47+
}
48+
49+
public DatoIntervallEntitet getPeriode() {
50+
return DatoIntervallEntitet.fra(periode);
51+
}
52+
53+
public BigDecimal getArbeidsinntekt() {
54+
return arbeidsinntekt;
55+
}
56+
57+
public BigDecimal getYtelse() {
58+
return ytelse;
59+
}
60+
61+
public static Builder ny() {
62+
return new Builder();
63+
}
64+
65+
public static class Builder {
66+
67+
private DatoIntervallEntitet periode;
68+
private BigDecimal arbeidsinntekt;
69+
private BigDecimal ytelse;
70+
71+
private Builder() {}
72+
73+
public Builder medPeriode(DatoIntervallEntitet periode) {
74+
if (periode == null) {
75+
throw new IllegalArgumentException("periode kan ikke være null");
76+
}
77+
this.periode = periode;
78+
return this;
79+
}
80+
81+
public Builder medArbeidsinntekt(BigDecimal arbeidsinntekt) {
82+
if (arbeidsinntekt == null) {
83+
throw new IllegalArgumentException("arbeidsinntekt kan ikke være null");
84+
}
85+
this.arbeidsinntekt = arbeidsinntekt;
86+
return this;
87+
}
88+
89+
public Builder medYtelse(BigDecimal ytelse) {
90+
if (ytelse == null) {
91+
throw new IllegalArgumentException("reduksjon kan ikke være null");
92+
}
93+
this.ytelse = ytelse;
94+
return this;
95+
}
96+
97+
98+
public KontrollertInntektPeriode build() {
99+
return new KontrollertInntektPeriode(periode, arbeidsinntekt, ytelse);
100+
}
101+
102+
103+
}
104+
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package no.nav.ung.sak.behandlingslager.tilkjentytelse;
2+
3+
import jakarta.persistence.*;
4+
import no.nav.ung.sak.behandlingslager.BaseEntitet;
5+
import org.hibernate.annotations.Immutable;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
/**
11+
* Holder på perioder der det er utført kontroll av inntekt mot opplysninger i a-ordningen.
12+
*/
13+
@Entity(name = "KontrollertInntektPerioder")
14+
@Table(name = "KONTROLLERT_INNTEKT_PERIODER")
15+
public class KontrollertInntektPerioder extends BaseEntitet {
16+
17+
@Id
18+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_KONTROLLERT_INNTEKT_PERIODER")
19+
private Long id;
20+
21+
@Column(name = "behandling_id", nullable = false, updatable = false)
22+
private Long behandlingId;
23+
24+
@Column(name = "aktiv", nullable = false, updatable = true)
25+
private boolean aktiv = true;
26+
27+
@Immutable
28+
@JoinColumn(name = "kontrollert_perioder_id", nullable = false, updatable = false)
29+
@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REFRESH})
30+
private List<KontrollertInntektPeriode> perioder = new ArrayList<>();
31+
32+
33+
public Long getBehandlingId() {
34+
return behandlingId;
35+
}
36+
37+
public boolean isAktiv() {
38+
return aktiv;
39+
}
40+
41+
void setIkkeAktiv() {
42+
this.aktiv = false;
43+
}
44+
45+
public List<KontrollertInntektPeriode> getPerioder() {
46+
return perioder;
47+
}
48+
49+
public static Builder ny(Long behandlingId) {
50+
if (behandlingId == null) {
51+
throw new IllegalArgumentException("behandlingId kan ikke være null");
52+
}
53+
return new Builder(behandlingId);
54+
}
55+
56+
public static class Builder {
57+
private Long behandlingId;
58+
private List<KontrollertInntektPeriode> perioder = new ArrayList<>();
59+
private String input;
60+
private String sporing;
61+
62+
63+
public Builder(Long behandlingId) {
64+
this.behandlingId = behandlingId;
65+
}
66+
67+
public Builder medPerioder(List<KontrollertInntektPeriode> perioder) {
68+
if (perioder == null) {
69+
throw new IllegalArgumentException("perioder kan ikke være null");
70+
}
71+
this.perioder.addAll(perioder);
72+
return this;
73+
}
74+
75+
76+
public KontrollertInntektPerioder build() {
77+
KontrollertInntektPerioder tilkjentYtelse = new KontrollertInntektPerioder();
78+
tilkjentYtelse.behandlingId = this.behandlingId;
79+
tilkjentYtelse.perioder = this.perioder;
80+
return tilkjentYtelse;
81+
}
82+
83+
}
84+
85+
86+
}

‎behandlingslager/domene/src/main/java/no/nav/ung/sak/behandlingslager/tilkjentytelse/TilkjentYtelseRepository.java

+32
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import no.nav.k9.felles.jpa.HibernateVerktøy;
99
import no.nav.ung.sak.domene.typer.tid.DatoIntervallEntitet;
1010

11+
import java.util.ArrayList;
1112
import java.util.List;
1213
import java.util.Optional;
1314
import java.util.stream.Collectors;
@@ -23,6 +24,28 @@ public TilkjentYtelseRepository(EntityManager entityManager) {
2324
}
2425

2526

27+
public void lagre(long behandlingId, List<KontrollertInntektPeriode> perioder) {
28+
final var eksisterende = hentKontrollertInntektPerioder(behandlingId);
29+
if (eksisterende.isPresent()) {
30+
eksisterende.get().setIkkeAktiv();
31+
entityManager.persist(eksisterende.get());
32+
}
33+
final var eksisterendePerioderSomSkalBeholdes = eksisterende.stream().flatMap(it -> it.getPerioder().stream())
34+
.filter(p -> perioder.stream().map(KontrollertInntektPeriode::getPeriode).noneMatch(p2 -> p.getPeriode().overlapper(p2)))
35+
.map(KontrollertInntektPeriode::new).toList();
36+
final var allePerioder = new ArrayList<KontrollertInntektPeriode>();
37+
allePerioder.addAll(eksisterendePerioderSomSkalBeholdes);
38+
allePerioder.addAll(perioder);
39+
40+
allePerioder.forEach(entityManager::persist);
41+
42+
final var ny = KontrollertInntektPerioder.ny(behandlingId)
43+
.medPerioder(allePerioder)
44+
.build();
45+
entityManager.persist(ny);
46+
entityManager.flush();
47+
}
48+
2649
public void lagre(long behandlingId, List<TilkjentYtelsePeriode> perioder, String input, String sporing) {
2750
final var eksisterende = hentTilkjentYtelse(behandlingId);
2851
if (eksisterende.isPresent()) {
@@ -45,6 +68,14 @@ public Optional<TilkjentYtelse> hentTilkjentYtelse(Long behandlingId) {
4568
return HibernateVerktøy.hentUniktResultat(query);
4669
}
4770

71+
public Optional<KontrollertInntektPerioder> hentKontrollertInntektPerioder(Long behandlingId) {
72+
var query = entityManager.createQuery("SELECT t FROM KontrollertInntektPerioder t WHERE t.behandlingId=:id AND t.aktiv = true", KontrollertInntektPerioder.class)
73+
.setParameter("id", behandlingId);
74+
75+
return HibernateVerktøy.hentUniktResultat(query);
76+
}
77+
78+
4879
public LocalDateTimeline<TilkjentYtelseVerdi> hentTidslinje(Long behandlingId) {
4980
var query = entityManager.createQuery(
5081
"SELECT p FROM TilkjentYtelse t JOIN t.perioder p " +
@@ -67,6 +98,7 @@ public LocalDateTimeline<TilkjentYtelseVerdi> hentTidslinje(Long behandlingId) {
6798
return new LocalDateTimeline<>(segments);
6899
}
69100

101+
70102
public void lagre(Long behandlingId, LocalDateTimeline<TilkjentYtelseVerdi> tilkjentYtelseTidslinje, String input, String sporing) {
71103
final var tilkjentYtelsePerioder = tilkjentYtelseTidslinje.toSegments().stream()
72104
.map(it -> TilkjentYtelsePeriode.ny()

‎behandlingslager/domene/src/main/resources/META-INF/pu-default.tilkjentytelse.orm.xml

+6
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@
66

77
<sequence-generator name="SEQ_TILKJENT_YTELSE" allocation-size="50" sequence-name="SEQ_TILKJENT_YTELSE"/>
88
<sequence-generator name="SEQ_TILKJENT_YTELSE_PERIODE" allocation-size="50" sequence-name="SEQ_TILKJENT_YTELSE_PERIODE"/>
9+
<sequence-generator name="SEQ_KONTROLLERT_INNTEKT_PERIODER" allocation-size="50" sequence-name="SEQ_KONTROLLERT_INNTEKT_PERIODER"/>
10+
<sequence-generator name="SEQ_KONTROLLERT_INNTEKT_PERIODE" allocation-size="50" sequence-name="SEQ_KONTROLLERT_INNTEKT_PERIODE"/>
911

1012
<entity class="no.nav.ung.sak.behandlingslager.tilkjentytelse.TilkjentYtelse"/>
1113
<entity class="no.nav.ung.sak.behandlingslager.tilkjentytelse.TilkjentYtelsePeriode"/>
1214

15+
<entity class="no.nav.ung.sak.behandlingslager.tilkjentytelse.KontrollertInntektPerioder"/>
16+
<entity class="no.nav.ung.sak.behandlingslager.tilkjentytelse.KontrollertInntektPeriode"/>
17+
18+
1319
</entity-mappings>

‎behandlingsprosess/src/main/java/no/nav/ung/sak/domene/behandling/steg/beregnytelse/BeregnYtelseSteg.java

+15-16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.List;
44
import java.util.Map;
5+
import java.util.Set;
56
import java.util.function.Function;
67
import java.util.logging.Logger;
78

@@ -25,10 +26,7 @@
2526
import no.nav.ung.sak.behandlingslager.ytelse.sats.UngdomsytelseSatser;
2627
import no.nav.ung.sak.domene.typer.tid.JsonObjectMapper;
2728
import no.nav.ung.sak.domene.typer.tid.Virkedager;
28-
import no.nav.ung.sak.ytelse.BeregnetSats;
29-
import no.nav.ung.sak.ytelse.RapportertInntektMapper;
30-
import no.nav.ung.sak.ytelse.RapporterteInntekter;
31-
import no.nav.ung.sak.ytelse.TilkjentYtelsePeriodeResultat;
29+
import no.nav.ung.sak.ytelse.*;
3230
import no.nav.ung.sak.ytelseperioder.YtelseperiodeUtleder;
3331

3432

@@ -42,21 +40,21 @@ public class BeregnYtelseSteg implements BehandlingSteg {
4240

4341
private UngdomsytelseGrunnlagRepository ungdomsytelseGrunnlagRepository;
4442
private TilkjentYtelseRepository tilkjentYtelseRepository;
45-
private RapportertInntektMapper rapportertInntektMapper;
4643
private YtelseperiodeUtleder ytelseperiodeUtleder;
44+
private KontrollerteInntektperioderTjeneste kontrollerteInntektperioderTjeneste;
4745

4846
public BeregnYtelseSteg() {
4947
}
5048

5149
@Inject
5250
public BeregnYtelseSteg(UngdomsytelseGrunnlagRepository ungdomsytelseGrunnlagRepository,
5351
TilkjentYtelseRepository tilkjentYtelseRepository,
54-
RapportertInntektMapper rapportertInntektMapper,
55-
YtelseperiodeUtleder ytelseperiodeUtleder) {
52+
YtelseperiodeUtleder ytelseperiodeUtleder,
53+
KontrollerteInntektperioderTjeneste kontrollerteInntektperioderTjeneste) {
5654
this.ungdomsytelseGrunnlagRepository = ungdomsytelseGrunnlagRepository;
5755
this.tilkjentYtelseRepository = tilkjentYtelseRepository;
58-
this.rapportertInntektMapper = rapportertInntektMapper;
5956
this.ytelseperiodeUtleder = ytelseperiodeUtleder;
57+
this.kontrollerteInntektperioderTjeneste = kontrollerteInntektperioderTjeneste;
6058
}
6159

6260
@Override
@@ -66,31 +64,32 @@ public BeregnYtelseSteg(UngdomsytelseGrunnlagRepository ungdomsytelseGrunnlagRep
6664
if (ungdomsytelseGrunnlag.isEmpty()) {
6765
return BehandleStegResultat.utførtUtenAksjonspunkter();
6866
}
69-
final var rapportertInntektTidslinje = rapportertInntektMapper.mapAlleGjeldendeRegisterOgBrukersInntekter(kontekst.getBehandlingId());
7067
final var ytelseTidslinje = ytelseperiodeUtleder.utledYtelsestidslinje(kontekst.getBehandlingId());
7168

69+
final var kontrollertInntektperiodeTidslinje = kontrollerteInntektperioderTjeneste.hentTidslinje(kontekst.getBehandlingId());
70+
7271
// Validerer at periodene for rapporterte inntekter er konsistent med ytelsetidslinje
73-
validerPerioderForRapporterteInntekter(rapportertInntektTidslinje, ytelseTidslinje);
72+
validerPerioderForRapporterteInntekter(kontrollertInntektperiodeTidslinje, ytelseTidslinje);
7473

7574
final var satsTidslinje = ungdomsytelseGrunnlag.get().getSatsTidslinje();
7675
final var totalsatsTidslinje = mapSatserTilTotalbeløpForPerioder(satsTidslinje, ytelseTidslinje);
7776
final var godkjentUttakTidslinje = finnGodkjentUttakstidslinje(ungdomsytelseGrunnlag.get());
7877

7978
// Utfør reduksjon og map til tilkjent ytelse
80-
final var tilkjentYtelseTidslinje = LagTilkjentYtelse.lagTidslinje(godkjentUttakTidslinje, totalsatsTidslinje, rapportertInntektTidslinje);
81-
final var regelInput = lagRegelInput(satsTidslinje, ytelseTidslinje, godkjentUttakTidslinje, totalsatsTidslinje, rapportertInntektTidslinje);
79+
final var tilkjentYtelseTidslinje = LagTilkjentYtelse.lagTidslinje(godkjentUttakTidslinje, totalsatsTidslinje, kontrollertInntektperiodeTidslinje);
80+
final var regelInput = lagRegelInput(satsTidslinje, ytelseTidslinje, godkjentUttakTidslinje, totalsatsTidslinje, kontrollertInntektperiodeTidslinje);
8281
final var regelSporing = lagSporing(tilkjentYtelseTidslinje);
8382
tilkjentYtelseRepository.lagre(kontekst.getBehandlingId(), tilkjentYtelseTidslinje.mapValue(TilkjentYtelsePeriodeResultat::verdi), regelInput, regelSporing);
8483
return BehandleStegResultat.utførtUtenAksjonspunkter();
8584
}
8685

87-
private static String lagRegelInput(LocalDateTimeline<UngdomsytelseSatser> satsTidslinje, LocalDateTimeline<Boolean> ytelseTidslinje, LocalDateTimeline<Boolean> godkjentUttakTidslinje, LocalDateTimeline<BeregnetSats> totalsatsTidslinje, LocalDateTimeline<RapporterteInntekter> rapportertInntektTidslinje) {
86+
private static String lagRegelInput(LocalDateTimeline<UngdomsytelseSatser> satsTidslinje, LocalDateTimeline<Boolean> ytelseTidslinje, LocalDateTimeline<Boolean> godkjentUttakTidslinje, LocalDateTimeline<BeregnetSats> totalsatsTidslinje, LocalDateTimeline<Set<RapportertInntekt>> rapportertInntektTidslinje) {
8887
final var sporingsMap = Map.of(
8988
"satsTidslinje", satsTidslinje,
9089
"ytelseTidslinje", ytelseTidslinje.mapValue(IngenVerdi::ingenVerdi),
9190
"godkjentUttakTidslinje", godkjentUttakTidslinje.mapValue(IngenVerdi::ingenVerdi),
9291
"totalsatsTidslinje", totalsatsTidslinje,
93-
"rapportertInntektTidslinje", rapportertInntektTidslinje
92+
"kontrollertInntektTidslinje", rapportertInntektTidslinje
9493
);
9594
final var regelInput = LagRegelSporing.lagRegelSporingFraTidslinjer(sporingsMap);
9695
return regelInput;
@@ -116,14 +115,14 @@ private static LocalDateTimeline<Boolean> finnGodkjentUttakstidslinje(Ungdomsyte
116115
.orElse(LocalDateTimeline.empty());
117116
}
118117

119-
private static void validerPerioderForRapporterteInntekter(LocalDateTimeline<RapporterteInntekter> rapportertInntektTidslinje, LocalDateTimeline<Boolean> stønadTidslinje) {
118+
private static void validerPerioderForRapporterteInntekter(LocalDateTimeline<Set<RapportertInntekt>> rapportertInntektTidslinje, LocalDateTimeline<Boolean> stønadTidslinje) {
120119
final var rapporterteInntekterSomIkkeMatcherYtelsesperiode = rapportertInntektTidslinje.stream().filter(s -> harIkkeMatchendeStønadsperiode(s, stønadTidslinje)).toList();
121120
if (!rapporterteInntekterSomIkkeMatcherYtelsesperiode.isEmpty()) {
122121
throw new IllegalStateException("Rapportert inntekt har perioder som ikke er dekket av stønadstidslinjen: " + rapporterteInntekterSomIkkeMatcherYtelsesperiode.stream().map(LocalDateSegment::getLocalDateInterval).toList());
123122
}
124123
}
125124

126-
private static boolean harIkkeMatchendeStønadsperiode(LocalDateSegment<RapporterteInntekter> s, LocalDateTimeline<Boolean> stønadTidslinje) {
125+
private static boolean harIkkeMatchendeStønadsperiode(LocalDateSegment<Set<RapportertInntekt>> s, LocalDateTimeline<Boolean> stønadTidslinje) {
127126
return stønadTidslinje.getLocalDateIntervals().stream().noneMatch(intervall -> intervall.equals(s.getLocalDateInterval()));
128127
}
129128

0 commit comments

Comments
 (0)
Please sign in to comment.