diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025Controller.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025Controller.kt index 192b1a26..ba0b3226 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025Controller.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025Controller.kt @@ -2,12 +2,10 @@ package no.nav.tjenestepensjon.simulering.rest import io.github.oshai.kotlinlogging.KotlinLogging import no.nav.tjenestepensjon.simulering.ping.PingResponse -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.Tjenestepensjon2025Mapper.mapToVellykketTjenestepensjonSimuleringResponse +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.Tjenestepensjon2025Aggregator.aggregerRespons import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.ResultatTypeDto import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.SimulerTjenestepensjonResponseDto -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.SimuleringsResultatTypeDto -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.BrukerErIkkeMedlemException -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TpOrdningStoettesIkkeException import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.TjenestepensjonV2025Service import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping @@ -22,20 +20,12 @@ class TjenestepensjonSimuleringV2025Controller( @PostMapping("/v2025/tjenestepensjon/v1/simulering") fun simuler(@RequestBody request: SimulerTjenestepensjonRequestDto): SimulerTjenestepensjonResponseDto { - try { - return tjenestepensjonV2025Service.simuler(request).fold( - onSuccess = { data -> - mapToVellykketTjenestepensjonSimuleringResponse(data) - }, - onFailure = { e -> - log.error(e) { "Simulering feilet: ${e.message}" } - SimulerTjenestepensjonResponseDto(SimuleringsResultatTypeDto.ERROR, "Simulering feilet") - }) - } catch (e: BrukerErIkkeMedlemException) { - return SimulerTjenestepensjonResponseDto(SimuleringsResultatTypeDto.ERROR, e.message) - } catch (e: TpOrdningStoettesIkkeException) { - return SimulerTjenestepensjonResponseDto(SimuleringsResultatTypeDto.ERROR, e.message) - } + return tjenestepensjonV2025Service.simuler(request).fold( + onSuccess = { aggregerRespons(it) }, + onFailure = { e -> + log.error(e) { "Simulering feilet: ${e.message}" } + SimulerTjenestepensjonResponseDto(ResultatTypeDto.ERROR, e.message) + }) } @GetMapping("/v2025/tjenestepensjon/ping") diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/afp/v1/AlderForDelingstallBeregner.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/afp/v1/AlderForDelingstallBeregner.kt index d5b500de..3411af92 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/afp/v1/AlderForDelingstallBeregner.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/afp/v1/AlderForDelingstallBeregner.kt @@ -26,7 +26,7 @@ object AlderForDelingstallBeregner { return listOf(AlderForDelingstall(bestemAlderVedDato(fodselsdato, uttaksdato), uttaksdato)) } - private fun bestemAlderVedDato(fodselsdato: LocalDate, date: LocalDate): Alder { + fun bestemAlderVedDato(fodselsdato: LocalDate, date: LocalDate): Alder { val periode = Period.between(fodselsdato, date) return Alder(periode.years, periode.months) } diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/Maanedsutbetaling.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/Maanedsutbetaling.kt new file mode 100644 index 00000000..adec216a --- /dev/null +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/Maanedsutbetaling.kt @@ -0,0 +1,10 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain + +import no.nav.tjenestepensjon.simulering.model.domain.pen.Alder +import java.time.LocalDate + +data class Maanedsutbetaling( + val fraOgMedDato: LocalDate, + val fraOgMedAlder: Alder, + var maanedsBeloep: Int, +) diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/SimulertTjenestepensjon.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/SimulertTjenestepensjon.kt index a67f0e2e..61b7ac47 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/SimulertTjenestepensjon.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/SimulertTjenestepensjon.kt @@ -1,9 +1,17 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain -data class SimulertTjenestepensjon( +open class SimulertTjenestepensjon( + val tpLeverandoer: String, var ordningsListe: List = emptyList(), var utbetalingsperioder: List = emptyList(), var aarsakIngenUtbetaling: List = emptyList(), ) -data class Ordning(val tpNummer: String) \ No newline at end of file +data class Ordning(val tpNummer: String) + +open class SimulertTjenestepensjonMedMaanedsUtbetalinger( + val tpLeverandoer: String, + var ordningsListe: List = emptyList(), + var utbetalingsperioder: List = emptyList(), + var aarsakIngenUtbetaling: List = emptyList(), +) \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025Aggregator.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025Aggregator.kt new file mode 100644 index 00000000..e3c71ebe --- /dev/null +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025Aggregator.kt @@ -0,0 +1,41 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto + +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Maanedsutbetaling +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjonMedMaanedsUtbetalinger +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.* + +object Tjenestepensjon2025Aggregator { + + fun aggregerRespons(simulertTjenestepensjon: SimulertTjenestepensjonMedMaanedsUtbetalinger) = + SimulerTjenestepensjonResponseDto( + simuleringsResultatStatus = SimuleringsResultatStatusDto(ResultatTypeDto.SUCCESS), + simuleringsResultat = SimuleringsResultatDto( + tpLeverandoer = simulertTjenestepensjon.tpLeverandoer, + utbetalingsperioder = aggregerTilAarligePerioder(simulertTjenestepensjon.utbetalingsperioder), + ) + ) + + fun aggregerTilAarligePerioder(maanedsutbetalinger: List): List { + val aarligeUtbetalinger = mutableMapOf>() //år -> månedsnummer(1-12) -> beløp for måneden + + maanedsutbetalinger.forEach { maanedsutbetaling -> + val (startAar, startMaaned) = maanedsutbetaling.fraOgMedAlder.let { it.aar to it.maaneder + 1 } + + // Bruk beløp for start år-måned og fremtidige år + (startAar..SISTE_UTBETALINGSAAR).forEach { aar -> + val maanedFra = if (aar == startAar) startMaaned else 1 + aarligeUtbetalinger + .getOrPut(aar) { mutableMapOf() } + .putAll((maanedFra..MAANEDER_I_AAR).associateWith { maanedsutbetaling.maanedsBeloep }) + } + } + + // Summer beløp per år + return aarligeUtbetalinger + .map { (aar, maanedToBeloep) -> UtbetalingPerAar(aar, maanedToBeloep.values.sum()) } + .sortedBy { it.aar } + } + + const val MAANEDER_I_AAR = 12 + const val SISTE_UTBETALINGSAAR = 85 +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025Mapper.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025Mapper.kt deleted file mode 100644 index dd3cdf7c..00000000 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025Mapper.kt +++ /dev/null @@ -1,16 +0,0 @@ -package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto - -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.* - -object Tjenestepensjon2025Mapper { - - fun mapToVellykketTjenestepensjonSimuleringResponse(simulertTjenestepensjon: SimulertTjenestepensjon) = - SimulerTjenestepensjonResponseDto( - simuleringsResultatStatus = SimuleringsResultatStatusDto(SimuleringsResultatTypeDto.SUCCESS), - simuleringsResultatDto = SimuleringsResultatDto( - utbetalingsperioder = simulertTjenestepensjon.utbetalingsperioder.map { UtbetalingsperiodeDto(it.fom, it.maanedligBelop, it.ytelseType) }, - aarsakIngenUtbetaling = simulertTjenestepensjon.aarsakIngenUtbetaling - ) - ) -} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/request/SimulerTjenestepensjonRequestDto.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/request/SimulerTjenestepensjonRequestDto.kt index 0306c62b..e8df1787 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/request/SimulerTjenestepensjonRequestDto.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/request/SimulerTjenestepensjonRequestDto.kt @@ -1,10 +1,14 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request +import java.time.LocalDate + data class SimulerTjenestepensjonRequestDto( - val fnr: String, - val uttaksListe: List, - val fremtidigInntektListe: List, + val pid: String, + val foedselsdato: LocalDate, + val uttaksdato: LocalDate, + val sisteInntekt: Int, val aarIUtlandetEtter16: Int, + val brukerBaOmAfp: Boolean, val epsPensjon: Boolean, val eps2G: Boolean, ) \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/response/SimulerTjenestepensjonResponseDto.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/response/SimulerTjenestepensjonResponseDto.kt index b126ca68..ab61cb2e 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/response/SimulerTjenestepensjonResponseDto.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/response/SimulerTjenestepensjonResponseDto.kt @@ -2,21 +2,26 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response data class SimulerTjenestepensjonResponseDto( val simuleringsResultatStatus: SimuleringsResultatStatusDto, - val simuleringsResultatDto: SimuleringsResultatDto? = null, + val simuleringsResultat: SimuleringsResultatDto? = null, ){ - constructor(simuleringsResultatTypeDto: SimuleringsResultatTypeDto, feilmelding: String) : this( - SimuleringsResultatStatusDto(simuleringsResultatTypeDto, feilmelding) + constructor(resultatTypeDto: ResultatTypeDto, feilmelding: String?) : this( + SimuleringsResultatStatusDto(resultatTypeDto, feilmelding) ) } data class SimuleringsResultatStatusDto( - val simuleringsResultatStatus: SimuleringsResultatTypeDto, + val resultatType: ResultatTypeDto, val feilmelding: String? = null, ) -enum class SimuleringsResultatTypeDto { SUCCESS, ERROR } +enum class ResultatTypeDto { SUCCESS, ERROR } data class SimuleringsResultatDto( - val utbetalingsperioder: List, - val aarsakIngenUtbetaling: List, + val tpLeverandoer: String, + val utbetalingsperioder: List, +) + +data class UtbetalingPerAar( + val aar: Int, + val beloep: Int, ) \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/response/UtbetalingsperiodeDto.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/response/UtbetalingsperiodeDto.kt deleted file mode 100644 index 0acea97f..00000000 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/response/UtbetalingsperiodeDto.kt +++ /dev/null @@ -1,5 +0,0 @@ -package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response - -import java.time.LocalDate - -data class UtbetalingsperiodeDto(val fraOgMedDato: LocalDate, val manedligUtbetaling: Int, val ytelseType: String) diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Service.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Service.kt index 44da0f62..f15d0a3b 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Service.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Service.kt @@ -1,26 +1,30 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service +import io.github.oshai.kotlinlogging.KotlinLogging import no.nav.tjenestepensjon.simulering.ping.PingResponse import no.nav.tjenestepensjon.simulering.service.TpClient -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjonMedMaanedsUtbetalinger import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.BrukerErIkkeMedlemException import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TpOrdningStoettesIkkeException -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp.KLPTjenestepensjonClient -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk.SPKTjenestepensjonClient +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp.KLPTjenestepensjonService +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk.SPKTjenestepensjonService import org.springframework.stereotype.Service @Service class TjenestepensjonV2025Service( private val tp: TpClient, - private val spk: SPKTjenestepensjonClient, - private val klp: KLPTjenestepensjonClient, + private val spk: SPKTjenestepensjonService, + private val klp: KLPTjenestepensjonService, ) { + private val log = KotlinLogging.logger {} @Throws(BrukerErIkkeMedlemException::class, TpOrdningStoettesIkkeException::class) - fun simuler(request: SimulerTjenestepensjonRequestDto): Result { - val tpOrdningNavn = tp.findTPForhold(request.fnr).flatMap { it.alias }.firstOrNull() - ?: "spk" //TODO throw BrukerErIkkeMedlemException() + fun simuler(request: SimulerTjenestepensjonRequestDto): Result { + val response = tp.findTPForhold(request.pid) + val tpOrdningNavn = response.flatMap { it.alias }.firstOrNull() + ?: "spk" //TODO før prodsetting -> throw BrukerErIkkeMedlemException() + log.info { "Fant aktive tp-ordninger for bruker: $response, skal bruke $tpOrdningNavn for å simulere" } return when (tpOrdningNavn.lowercase()) { "spk" -> spk.simuler(request) diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPMapper.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPMapper.kt index 2eff249c..d5829d08 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPMapper.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPMapper.kt @@ -4,32 +4,37 @@ import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Ordning import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Utbetalingsperiode import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto +import java.time.LocalDate object KLPMapper { + private const val LEVERANDOER = "Kommunal landspensjonskasse" + fun mapToRequest(request: SimulerTjenestepensjonRequestDto) = KLPSimulerTjenestepensjonRequest( - personId = request.fnr, - uttaksListe = request.uttaksListe.map { uttakDto -> - Uttak( - ytelseType = uttakDto.ytelseType, - fraOgMedDato = uttakDto.fraOgMedDato, - uttaksgrad = uttakDto.uttaksgrad + personId = request.pid, + uttaksListe = listOf(Uttak( + ytelseType = "ALLE", + fraOgMedDato = request.uttaksdato, + uttaksgrad = 100 ) - }, - fremtidigInntektsListe = request.fremtidigInntektListe.map { fremtidigInntektDto -> + ), + fremtidigInntektsListe = listOf( FremtidigInntekt( - fraOgMedDato = fremtidigInntektDto.fraOgMedDato, - arligInntekt = fremtidigInntektDto.aarligInntekt + fraOgMedDato = aarUtenRegistrertInntektHosSkatteetaten(), + arligInntekt = request.sisteInntekt ) - }, + ), arIUtlandetEtter16 = request.aarIUtlandetEtter16, epsPensjon = request.epsPensjon, eps2G = request.eps2G, ) + private fun aarUtenRegistrertInntektHosSkatteetaten(): LocalDate = LocalDate.now().minusYears(2).withDayOfYear(1) + fun mapToResponse(response: KLPSimulerTjenestepensjonResponse) = SimulertTjenestepensjon( + tpLeverandoer = LEVERANDOER, ordningsListe = response.inkludertOrdningListe.map { Ordning(it.tpnr) }, utbetalingsperioder = response.utbetalingsListe.map { Utbetalingsperiode(it.fraOgMedDato, it.manedligUtbetaling, it.ytelseType) }, aarsakIngenUtbetaling = response.arsakIngenUtbetaling diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPTjenestepensjonService.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPTjenestepensjonService.kt new file mode 100644 index 00000000..7ba0949c --- /dev/null +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPTjenestepensjonService.kt @@ -0,0 +1,16 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp + +import no.nav.tjenestepensjon.simulering.ping.Pingable +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjonMedMaanedsUtbetalinger +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto +import org.springframework.stereotype.Service + +@Service +class KLPTjenestepensjonService(private val client: KLPTjenestepensjonClient) : Pingable { + + fun simuler(request: SimulerTjenestepensjonRequestDto): Result { + return Result.failure(RuntimeException("Aggregation is not implemented for KLP")) + } + + override fun ping() = client.ping() +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKMapper.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKMapper.kt index d5a09947..1f29c9c3 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKMapper.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKMapper.kt @@ -4,25 +4,22 @@ import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Ordning import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Utbetalingsperiode import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto +import java.time.LocalDate object SPKMapper { + const val LEVERANDOER = "Statens pensjonskasse" + fun mapToRequest(request: SimulerTjenestepensjonRequestDto) = SPKSimulerTjenestepensjonRequest( - personId = request.fnr, - uttaksListe = request.uttaksListe.map { uttakDto -> - Uttak( - ytelseType = uttakDto.ytelseType, - fraOgMedDato = uttakDto.fraOgMedDato, - uttaksgrad = uttakDto.uttaksgrad - ) - }, - fremtidigInntektListe = request.fremtidigInntektListe.map { fremtidigInntektDto -> + personId = request.pid, + uttaksListe = opprettUttaksliste(request), + fremtidigInntektListe = listOf( FremtidigInntekt( - fraOgMedDato = fremtidigInntektDto.fraOgMedDato, - aarligInntekt = fremtidigInntektDto.aarligInntekt + fraOgMedDato = LocalDate.now().withDayOfYear(1), + aarligInntekt = request.sisteInntekt ) - }, + ), aarIUtlandetEtter16 = request.aarIUtlandetEtter16, epsPensjon = request.epsPensjon, eps2G = request.eps2G, @@ -30,6 +27,7 @@ object SPKMapper { fun mapToResponse(response: SPKSimulerTjenestepensjonResponse) = SimulertTjenestepensjon( + tpLeverandoer = LEVERANDOER, ordningsListe = response.inkludertOrdningListe.map { Ordning(it.tpnr) }, utbetalingsperioder = response.utbetalingListe.flatMap { periode -> val fraOgMed = periode.fraOgMedDato @@ -37,4 +35,14 @@ object SPKMapper { }, aarsakIngenUtbetaling = response.aarsakIngenUtbetaling.map { it.statusBeskrivelse + ": " + it.ytelseType } ) + + fun opprettUttaksliste(request: SimulerTjenestepensjonRequestDto) = + mutableListOf("PAASLAG", "APOF2020", "OAFP", "OT6370", "SAERALDERSPAASLAG", if (request.brukerBaOmAfp) "AFP" else "BTP") + .map { + Uttak( + ytelseType = it, + fraOgMedDato = request.uttaksdato, + uttaksgrad = null + ) + } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKSimulerTjenestepensjonResponse.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKSimulerTjenestepensjonResponse.kt index c6dc2b28..ecff0740 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKSimulerTjenestepensjonResponse.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKSimulerTjenestepensjonResponse.kt @@ -1,5 +1,6 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk +import com.fasterxml.jackson.annotation.JsonFormat import java.time.LocalDate data class SPKSimulerTjenestepensjonResponse( @@ -13,6 +14,7 @@ data class InkludertOrdning( ) data class Utbetaling( + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") val fraOgMedDato: LocalDate, val delytelseListe: List, ) diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClient.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClient.kt index ffdfec62..ec408afe 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClient.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClient.kt @@ -60,7 +60,7 @@ class SPKTjenestepensjonClient(private val spkWebClient: WebClient) : Tjenestepe } companion object { - private const val SIMULER_PATH = "/nav/v2/tjenestepensjon/simuler/3010" + const val SIMULER_PATH = "/nav/v2/tjenestepensjon/simuler/3010" private const val PING_PATH = "/nav/admin/ping" private const val PROVIDER = "SPK" } diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonService.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonService.kt new file mode 100644 index 00000000..789608ed --- /dev/null +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonService.kt @@ -0,0 +1,42 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk + +import no.nav.tjenestepensjon.simulering.ping.Pingable +import no.nav.tjenestepensjon.simulering.v2025.afp.v1.AlderForDelingstallBeregner.bestemAlderVedDato +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Maanedsutbetaling +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjonMedMaanedsUtbetalinger +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Utbetalingsperiode +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto +import org.springframework.stereotype.Service +import java.time.LocalDate + +@Service +class SPKTjenestepensjonService(private val client: SPKTjenestepensjonClient) : Pingable { + + fun simuler(request: SimulerTjenestepensjonRequestDto): Result { + return client.simuler(request) + .fold( + onSuccess = { + Result.success( + SimulertTjenestepensjonMedMaanedsUtbetalinger( + tpLeverandoer = it.tpLeverandoer, + ordningsListe = it.ordningsListe, + utbetalingsperioder = grupperMedDatoFra(it.utbetalingsperioder, request.foedselsdato) + ) + ) + }, + onFailure = { Result.failure(it) } + ) + } + + fun grupperMedDatoFra(utbetalingsliste: List, foedselsdato: LocalDate): List { + return utbetalingsliste + .groupBy { it.fom } + .map { (datoFra, ytelser) -> + val totalMaanedsBelop = ytelser.sumOf { it.maanedligBelop } + Maanedsutbetaling(datoFra, bestemAlderVedDato(foedselsdato, datoFra), totalMaanedsBelop) + } + .sortedBy { it.fraOgMedDato } + } + + override fun ping() = client.ping() +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/rest/SimuleringAFPEndpointTest.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/rest/SimuleringAFPEndpointTest.kt index 1375945b..87814332 100644 --- a/src/test/kotlin/no/nav/tjenestepensjon/simulering/rest/SimuleringAFPEndpointTest.kt +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/rest/SimuleringAFPEndpointTest.kt @@ -81,26 +81,25 @@ class SimuleringAFPEndpointTest { .andExpect { status { isOk() } content { - content { + json( """ { - "fnr": "$defaultFNRString" - "ytelser": [ + "fnr": "$defaultFNRString", + "afpYtelser": [ { "pensjonsbeholdning": 250000, "afpYtelsePerAar": 50000.1, "delingstall": 18.13, "gjelderFraOgMed": "2027-01-01", "gjelderFraOgMedAlder": { - "ar": 64, + "aar": 64, "maaneder": 1 } } - ], - "tpLeverandoerer": "SPK,KLP" + ] } """.trimIndent() - } + ) } } } @@ -129,26 +128,25 @@ class SimuleringAFPEndpointTest { .andExpect { status { isOk() } content { - content { + json( """ { - "fnr": "$fnrMedEttMedlemskapITPOrdning" - "ytelser": [ + "fnr": "$defaultFNRString", + "afpYtelser": [ { "pensjonsbeholdning": 250000, "afpYtelsePerAar": 50000.1, "delingstall": 18.13, "gjelderFraOgMed": "2027-01-01", "gjelderFraOgMedAlder": { - "ar": 64, + "aar": 64, "maaneder": 1 } } - ], - "tpLeverandoerer": "SPK" + ] } """.trimIndent() - } + ) } } } @@ -177,26 +175,25 @@ class SimuleringAFPEndpointTest { .andExpect { status { isOk() } content { - content { + json( """ { - "fnr": "$fnrUtenMedlemskap" - "ytelser": [ + "fnr": "$fnrUtenMedlemskap", + "afpYtelser": [ { "pensjonsbeholdning": 250000, "afpYtelsePerAar": 50000.1, "delingstall": 18.13, "gjelderFraOgMed": "2027-01-01", "gjelderFraOgMedAlder": { - "ar": 64, + "aar": 64, "maaneder": 1 } } - ], - "tpLeverandoerer": "" + ] } """.trimIndent() - } + ) } } } diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025ControllerTest.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025ControllerTest.kt new file mode 100644 index 00000000..9cb53e25 --- /dev/null +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025ControllerTest.kt @@ -0,0 +1,292 @@ +package no.nav.tjenestepensjon.simulering.rest + +import com.github.tomakehurst.wiremock.WireMockServer +import no.nav.tjenestepensjon.simulering.model.domain.pen.Alder +import no.nav.tjenestepensjon.simulering.service.AADClient +import no.nav.tjenestepensjon.simulering.v2.models.defaultSimulerTjenestepensjonHosSPKJson +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Maanedsutbetaling +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjonMedMaanedsUtbetalinger +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.Tjenestepensjon2025AggregatorTest.Companion.MAANEDER_I_AAR +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.BrukerErIkkeMedlemException +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TpOrdningStoettesIkkeException +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.TjenestepensjonV2025Service +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.mockito.Mockito.`when` +import org.mockito.kotlin.any +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.security.test.context.support.WithMockUser +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.post +import org.springframework.web.server.ResponseStatusException +import java.time.LocalDate + +@AutoConfigureMockMvc +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@SpringBootTest +class TjenestepensjonSimuleringV2025ControllerTest { + + @Autowired + private lateinit var mockMvc: MockMvc + + @MockBean + private lateinit var aadClient: AADClient + + @MockBean + private lateinit var service: TjenestepensjonV2025Service + + private var wireMockServer = WireMockServer().apply { + start() + } + + @AfterAll + fun afterAll() { + wireMockServer.stop() + } + + @Test + @WithMockUser + fun `test simulering med beregnet resultat`() { + val perioder = listOf( + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2026-02-01"), + fraOgMedAlder = Alder(62, 1), + maanedsBeloep = 1000 + ), + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2027-05-01"), + fraOgMedAlder = Alder(63, 4), + maanedsBeloep = 2000 + ), + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2027-08-01"), + fraOgMedAlder = Alder(63, 7), + maanedsBeloep = 3000 + ) + ) + val mockRespons = SimulertTjenestepensjonMedMaanedsUtbetalinger( + tpLeverandoer = "pensjonskasse", + ordningsListe = emptyList(), + utbetalingsperioder = perioder, + aarsakIngenUtbetaling = emptyList() + ) + + `when`(service.simuler(any())).thenReturn(Result.success(mockRespons)) + `when`(aadClient.getToken("api://bogus")).thenReturn("") + val aarsBeloepVed63 = perioder[0].maanedsBeloep * (perioder[1].fraOgMedAlder.maaneder) + + perioder[1].maanedsBeloep * (perioder[2].fraOgMedAlder.maaneder - perioder[1].fraOgMedAlder.maaneder) + + perioder[2].maanedsBeloep * (MAANEDER_I_AAR - perioder[2].fraOgMedAlder.maaneder) + + mockMvc.post("/v2025/tjenestepensjon/v1/simulering") { + content = defaultSimulerTjenestepensjonHosSPKJson + contentType = MediaType.APPLICATION_JSON + } + .andExpect { + status { isOk() } + content { + json( + """ + { + "simuleringsResultatStatus": { + "resultatType": "SUCCESS", + "feilmelding": null + }, + "simuleringsResultat": { + "tpLeverandoer": "${mockRespons.tpLeverandoer}", + "utbetalingsperioder": [ + { + "aar": 62, + "beloep": 11000 + }, + { + "aar": 63, + "beloep": $aarsBeloepVed63 + }, + { + "aar": 64, + "beloep": 36000 + }, + { + "aar": 65, + "beloep": 36000 + }, + { + "aar": 66, + "beloep": 36000 + }, + { + "aar": 67, + "beloep": 36000 + }, + { + "aar": 68, + "beloep": 36000 + }, + { + "aar": 69, + "beloep": 36000 + }, + { + "aar": 70, + "beloep": 36000 + }, + { + "aar": 71, + "beloep": 36000 + }, + { + "aar": 72, + "beloep": 36000 + }, + { + "aar": 73, + "beloep": 36000 + }, + { + "aar": 74, + "beloep": 36000 + }, + { + "aar": 75, + "beloep": 36000 + }, + { + "aar": 76, + "beloep": 36000 + }, + { + "aar": 77, + "beloep": 36000 + }, + { + "aar": 78, + "beloep": 36000 + }, + { + "aar": 79, + "beloep": 36000 + }, + { + "aar": 80, + "beloep": 36000 + }, + { + "aar": 81, + "beloep": 36000 + }, + { + "aar": 82, + "beloep": 36000 + }, + { + "aar": 83, + "beloep": 36000 + }, + { + "aar": 84, + "beloep": 36000 + }, + { + "aar": 85, + "beloep": 36000 + } + ] + } + } + """.trimIndent() + ) + } + } + } + + @Test + @WithMockUser + fun `test simulering naar bruker ikke er medlem`() { + `when`(service.simuler(any())).thenReturn(Result.failure(BrukerErIkkeMedlemException())) + `when`(aadClient.getToken("api://bogus")).thenReturn("") + + mockMvc.post("/v2025/tjenestepensjon/v1/simulering") { + content = defaultSimulerTjenestepensjonHosSPKJson + contentType = MediaType.APPLICATION_JSON + } + .andExpect { + status { isOk() } + content { + json( + """ + { + "simuleringsResultatStatus": { + "resultatType": "ERROR", + "feilmelding": "Bruker er ikke medlem av en offentlig tjenestepensjonsordning" + }, + "simuleringsResultat": null + } + """.trimIndent() + ) + } + } + } + + @Test + @WithMockUser + fun `test simulering naar tp-ordning ikke stoettes`() { + val tpOrdning = "opf" + `when`(service.simuler(any())).thenReturn(Result.failure(TpOrdningStoettesIkkeException(tpOrdning))) + `when`(aadClient.getToken("api://bogus")).thenReturn("") + + mockMvc.post("/v2025/tjenestepensjon/v1/simulering") { + content = defaultSimulerTjenestepensjonHosSPKJson + contentType = MediaType.APPLICATION_JSON + } + .andExpect { + status { isOk() } + content { + json( + """ + { + "simuleringsResultatStatus": { + "resultatType": "ERROR", + "feilmelding": "$tpOrdning støtter ikke simulering av tjenestepensjon v2025" + }, + "simuleringsResultat": null + } + """.trimIndent() + ) + } + } + } + + @Test + @WithMockUser + fun `test simulering naar tpregisteret feiler`() { + `when`(service.simuler(any())).thenReturn(Result.failure(ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR))) + `when`(aadClient.getToken("api://bogus")).thenReturn("") + + mockMvc.post("/v2025/tjenestepensjon/v1/simulering") { + content = defaultSimulerTjenestepensjonHosSPKJson + contentType = MediaType.APPLICATION_JSON + } + .andExpect { + status { isOk() } + content { + json( + """ + { + "simuleringsResultatStatus": { + "resultatType": "ERROR", + "feilmelding": "500 INTERNAL_SERVER_ERROR" + }, + "simuleringsResultat": null + } + """.trimIndent() + ) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2/models/DefaultObjects.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2/models/DefaultObjects.kt index ba4f4f8c..1ecbc874 100644 --- a/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2/models/DefaultObjects.kt +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2/models/DefaultObjects.kt @@ -154,4 +154,14 @@ const val simulerBeregningAFPOffentligUtenMedlemskapITPOrdningJson = } ], "fom": "2040-01-01" + }""" +const val defaultSimulerTjenestepensjonHosSPKJson = + """{"pid": "$defaultFNRString", + "foedselsdato": "1963-12-05", + "uttaksdato": "2026-02-01", + "sisteInntekt": 600000, + "aarIUtlandetEtter16": 0, + "brukerBaOmAfp": false, + "epsPensjon": false, + "eps2G": false }""" \ No newline at end of file diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025AggregatorTest.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025AggregatorTest.kt new file mode 100644 index 00000000..88d95fb3 --- /dev/null +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/dto/Tjenestepensjon2025AggregatorTest.kt @@ -0,0 +1,131 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto + +import no.nav.tjenestepensjon.simulering.model.domain.pen.Alder +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Maanedsutbetaling +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Ordning +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjonMedMaanedsUtbetalinger +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.SimulerTjenestepensjonResponseDto +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.ResultatTypeDto +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.Assertions.* +import java.time.LocalDate + +class Tjenestepensjon2025AggregatorTest { + + @Test + fun `aggreger varierende maanedsperioder til aarlige perioder`() { + val simulertTjenestepensjon = SimulertTjenestepensjonMedMaanedsUtbetalinger( + tpLeverandoer = "tpLeverandoer", + ordningsListe = listOf(Ordning("3010")), + utbetalingsperioder = listOf( + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2025-03-01"), + maanedsBeloep = 1000, + fraOgMedAlder = Alder(aar = FOERSTE_MULIGE_UTTAKS_AAR, maaneder = 0) + ), + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2025-09-01"), + maanedsBeloep = 2000, + fraOgMedAlder = Alder(aar = FOERSTE_MULIGE_UTTAKS_AAR, maaneder = 6) + ), + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2026-03-01"), + maanedsBeloep = 3000, + fraOgMedAlder = Alder(aar = 63, maaneder = 0) + ), + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2030-03-01"), + maanedsBeloep = 4000, + fraOgMedAlder = Alder(aar = 67, maaneder = 0) + ), + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2032-01-01"), + maanedsBeloep = 5000, + fraOgMedAlder = Alder(aar = 68, maaneder = 10) + ), + ), + aarsakIngenUtbetaling = emptyList() + ) + + val result: SimulerTjenestepensjonResponseDto = Tjenestepensjon2025Aggregator.aggregerRespons(simulertTjenestepensjon) + + assertEquals(ResultatTypeDto.SUCCESS, result.simuleringsResultatStatus.resultatType) + + val antallAarligeUtbetalinger = SISTE_UTBETALINGSAAR - FOERSTE_MULIGE_UTTAKS_AAR + 1 // inkluderer første uttaksår + + assertEquals(antallAarligeUtbetalinger, result.simuleringsResultat?.utbetalingsperioder?.size) + assertEquals(simulertTjenestepensjon.tpLeverandoer, result.simuleringsResultat?.tpLeverandoer) + assertEquals(FOERSTE_MULIGE_UTTAKS_AAR, result.simuleringsResultat?.utbetalingsperioder?.get(0)?.aar) + assertEquals(1000 * 6 + 2000 * 6, result.simuleringsResultat?.utbetalingsperioder?.get(0)?.beloep) + assertEquals(FOERSTE_MULIGE_UTTAKS_AAR + 1, result.simuleringsResultat?.utbetalingsperioder?.get(1)?.aar) + assertEquals(3000 * MAANEDER_I_AAR, result.simuleringsResultat?.utbetalingsperioder?.get(1)?.beloep) + assertEquals(FOERSTE_MULIGE_UTTAKS_AAR + 2, result.simuleringsResultat?.utbetalingsperioder?.get(2)?.aar) + assertEquals(3000 * MAANEDER_I_AAR, result.simuleringsResultat?.utbetalingsperioder?.get(2)?.beloep) + assertEquals(FOERSTE_MULIGE_UTTAKS_AAR + 3, result.simuleringsResultat?.utbetalingsperioder?.get(3)?.aar) + assertEquals(3000 * MAANEDER_I_AAR, result.simuleringsResultat?.utbetalingsperioder?.get(3)?.beloep) + assertEquals(FOERSTE_MULIGE_UTTAKS_AAR + 4, result.simuleringsResultat?.utbetalingsperioder?.get(4)?.aar) + assertEquals(3000 * MAANEDER_I_AAR, result.simuleringsResultat?.utbetalingsperioder?.get(4)?.beloep) + assertEquals(FOERSTE_MULIGE_UTTAKS_AAR + 5, result.simuleringsResultat?.utbetalingsperioder?.get(5)?.aar) + assertEquals(4000 * MAANEDER_I_AAR, result.simuleringsResultat?.utbetalingsperioder?.get(5)?.beloep) + assertEquals(FOERSTE_MULIGE_UTTAKS_AAR + 6, result.simuleringsResultat?.utbetalingsperioder?.get(6)?.aar) + assertEquals(4000 * 10 + 5000 * 2, result.simuleringsResultat?.utbetalingsperioder?.get(6)?.beloep) + + var aarAlder = FOERSTE_MULIGE_UTTAKS_AAR + 7 + + for (index in 7 until result.simuleringsResultat!!.utbetalingsperioder.size) { + assertEquals(aarAlder, result.simuleringsResultat!!.utbetalingsperioder[index].aar) + assertEquals(5000 * 12, result.simuleringsResultat!!.utbetalingsperioder[index].beloep) + aarAlder++ + } + } + + @Test + fun `aggreger en maanedsutbetaling til aarlige perioder`() { + val simulertTjenestepensjon = SimulertTjenestepensjonMedMaanedsUtbetalinger( + tpLeverandoer = "pensjonskasse", + ordningsListe = listOf(Ordning("3010")), + utbetalingsperioder = listOf( + Maanedsutbetaling( + fraOgMedDato = LocalDate.parse("2026-03-01"), + maanedsBeloep = 1000, + fraOgMedAlder = Alder(aar = 63, maaneder = 3) + ))) + + val result: SimulerTjenestepensjonResponseDto = Tjenestepensjon2025Aggregator.aggregerRespons(simulertTjenestepensjon) + + assertEquals(ResultatTypeDto.SUCCESS, result.simuleringsResultatStatus.resultatType) + + val antallAarligeUtbetalinger = SISTE_UTBETALINGSAAR - 63 + 1 // inkluderer første uttaksår + assertEquals(antallAarligeUtbetalinger, result.simuleringsResultat?.utbetalingsperioder?.size) + assertEquals(simulertTjenestepensjon.tpLeverandoer, result.simuleringsResultat?.tpLeverandoer) + assertEquals(63, result.simuleringsResultat?.utbetalingsperioder?.get(0)?.aar) + assertEquals(1000 * (MAANEDER_I_AAR - 3), result.simuleringsResultat?.utbetalingsperioder?.get(0)?.beloep) + for (index in 1 until result.simuleringsResultat!!.utbetalingsperioder.size) { + assertEquals(63 + index, result.simuleringsResultat!!.utbetalingsperioder[index].aar) + assertEquals(1000 * MAANEDER_I_AAR, result.simuleringsResultat!!.utbetalingsperioder[index].beloep) + } + } + + @Test + fun `aggregering haandterer tom liste`() { + val simulertTjenestepensjon = SimulertTjenestepensjonMedMaanedsUtbetalinger( + tpLeverandoer = "pensjpnskasse", + ordningsListe = listOf(Ordning("3010")), + utbetalingsperioder = emptyList(), + aarsakIngenUtbetaling = listOf("Ingen utbetaling"), + ) + + val result = Tjenestepensjon2025Aggregator.aggregerRespons(simulertTjenestepensjon) + + assertEquals(ResultatTypeDto.SUCCESS, result.simuleringsResultatStatus.resultatType) + assertTrue(result.simuleringsResultat!!.utbetalingsperioder.isEmpty()) + assertEquals(simulertTjenestepensjon.tpLeverandoer, result.simuleringsResultat!!.tpLeverandoer) + } + + companion object { + const val FOERSTE_MULIGE_UTTAKS_AAR = 62 + const val MAANEDER_I_AAR = 12 + const val SISTE_UTBETALINGSAAR = 85 + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025ServiceTest.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025ServiceTest.kt new file mode 100644 index 00000000..352b2cb3 --- /dev/null +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025ServiceTest.kt @@ -0,0 +1,124 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service + +import no.nav.tjenestepensjon.simulering.model.domain.TpOrdningDto +import no.nav.tjenestepensjon.simulering.model.domain.pen.Alder +import no.nav.tjenestepensjon.simulering.service.TpClient +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Maanedsutbetaling +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Ordning +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjonMedMaanedsUtbetalinger +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp.KLPTjenestepensjonService +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk.SPKTjenestepensjonService +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.mockito.Mockito.`when` +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.http.HttpStatus +import org.springframework.web.reactive.function.client.WebClientResponseException +import org.springframework.web.server.ResponseStatusException +import java.time.LocalDate +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.fail + +@SpringBootTest +class TjenestepensjonV2025ServiceTest { + + @MockBean + private lateinit var tp: TpClient + + @MockBean + private lateinit var spk: SPKTjenestepensjonService + + @MockBean + private lateinit var klp: KLPTjenestepensjonService + + @Autowired + private lateinit var tjenestepensjonV2025Service: TjenestepensjonV2025Service + + @Test + fun `simuler success fra spk`() { + val req = dummyRequest("1963-02-05") + `when`(tp.findTPForhold(req.pid)).thenReturn(listOf(dummyTpOrdning())) + `when`(spk.simuler(req)).thenReturn(dummyResult()) + + val res: Result = tjenestepensjonV2025Service.simuler(req) + + assertTrue(res.isSuccess) + val tjenestepensjon = res.getOrNull() + assertNotNull(tjenestepensjon) + assertEquals("spk", tjenestepensjon.tpLeverandoer) + assertEquals(1, tjenestepensjon.ordningsListe.size) + assertEquals(1, tjenestepensjon.utbetalingsperioder.size) + } + + @Test + fun `simuler failure fra spk`() { + val req = dummyRequest("1963-02-05") + `when`(tp.findTPForhold(req.pid)).thenReturn(listOf(dummyTpOrdning())) + `when`(spk.simuler(req)).thenReturn(Result.failure(WebClientResponseException("Failed to simulate", 500, "error", null, null, null))) + + val res: Result = tjenestepensjonV2025Service.simuler(req) + + assertTrue(res.isFailure) + val tjenestepensjonFailure = res.exceptionOrNull() + assertNotNull(tjenestepensjonFailure) + assertEquals("Failed to simulate", tjenestepensjonFailure.message) + } + + @Test + fun `simuler naar tp-ordning stoettes ikke`() { + //TODO før prodsetting + } + + @Test + fun `simuler tp naar bruker ikke er medlem i tp ordning`() { + //TODO før prodsetting + } + + @Test + fun `simuler naar tpregisteret feilet`() { + val req = dummyRequest("1963-02-05") + `when`(tp.findTPForhold(req.pid)).thenThrow(ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR)) + + try { + tjenestepensjonV2025Service.simuler(req) + fail("Expected ResponseStatusException") + } catch ( + e: ResponseStatusException + ) { + assertTrue(e.statusCode.is5xxServerError) + } + } + + companion object { + fun dummyRequest(foedselsdato: String, brukerBaOmAfp: Boolean = false) = SimulerTjenestepensjonRequestDto( + "12345678910", + LocalDate.parse(foedselsdato), + LocalDate.parse("2025-03-01"), + 500000, + 0, + brukerBaOmAfp, + false, + false + ) + + fun dummyTpOrdning() = TpOrdningDto("Statens pensjonskasse", "3010", "123456789", listOf("spk")) + + fun dummyResult() = Result.success( + SimulertTjenestepensjonMedMaanedsUtbetalinger( + "spk", + listOf(Ordning("3010")), + listOf( + Maanedsutbetaling( + LocalDate.parse("2025-03-01"), + Alder(61, 2), + 5000 + ) + ) + ) + ) + } +} diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClientTest.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClientTest.kt new file mode 100644 index 00000000..e5caf8c7 --- /dev/null +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClientTest.kt @@ -0,0 +1,128 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk + +import com.fasterxml.jackson.databind.ObjectMapper +import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.WireMock.* +import no.nav.tjenestepensjon.simulering.service.AADClient +import no.nav.tjenestepensjon.simulering.testHelper.anyNonNull +import no.nav.tjenestepensjon.simulering.v2.consumer.MaskinportenTokenClient +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TjenestepensjonSimuleringException +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.TjenestepensjonV2025ServiceTest.Companion.dummyRequest +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk.SPKMapper.LEVERANDOER +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk.SPKTjenestepensjonClient.Companion.SIMULER_PATH +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.mockito.Mockito +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import java.time.LocalDate + +@SpringBootTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class SPKTjenestepensjonClientTest{ + + @MockBean + private lateinit var aadClient: AADClient + @MockBean + private lateinit var maskinportenTokenClient: MaskinportenTokenClient + @Autowired + private lateinit var spkClient: SPKTjenestepensjonClient + @Autowired + private lateinit var objectMapper: ObjectMapper + + private var wireMockServer = WireMockServer().apply { + start() + } + + @BeforeAll + fun beforeAll() { + Mockito.`when`(maskinportenTokenClient.pensjonsimuleringToken(anyNonNull())).thenReturn("bogustoken") + } + + @AfterAll + fun afterAll() { + wireMockServer.stop() + } + + @Test + fun `send request og les respons med tjenestepensjon fra spk`() { + val mockResponse = spkSimulerTjenestepensjonResponse() + val stub = wireMockServer.stubFor(post(urlPathEqualTo(SIMULER_PATH)).willReturn(okJson(objectMapper.writeValueAsString(mockResponse)))) + + val response: Result = spkClient.simuler(dummyRequest("1963-02-05", brukerBaOmAfp = true)) + + assertTrue(response.isSuccess) + val tjenestepensjon = response.getOrNull() + assertNotNull(tjenestepensjon) + assertEquals(LEVERANDOER, tjenestepensjon!!.tpLeverandoer) + assertEquals(1, tjenestepensjon.ordningsListe.size) + assertEquals("3010", tjenestepensjon.ordningsListe[0].tpNummer) + assertEquals(5, tjenestepensjon.utbetalingsperioder.size) + assertEquals(mockResponse.utbetalingListe[0].fraOgMedDato, tjenestepensjon.utbetalingsperioder[0].fom) + assertEquals(mockResponse.utbetalingListe[0].delytelseListe[0].maanedligBelop, tjenestepensjon.utbetalingsperioder[0].maanedligBelop) + assertEquals(mockResponse.utbetalingListe[0].delytelseListe[0].ytelseType, tjenestepensjon.utbetalingsperioder[0].ytelseType) + assertEquals(mockResponse.utbetalingListe[1].fraOgMedDato, tjenestepensjon.utbetalingsperioder[1].fom) + assertEquals(mockResponse.utbetalingListe[1].delytelseListe[0].maanedligBelop, tjenestepensjon.utbetalingsperioder[1].maanedligBelop) + assertEquals(mockResponse.utbetalingListe[1].delytelseListe[0].ytelseType, tjenestepensjon.utbetalingsperioder[1].ytelseType) + assertEquals(mockResponse.utbetalingListe[2].fraOgMedDato, tjenestepensjon.utbetalingsperioder[2].fom) + assertEquals(mockResponse.utbetalingListe[2].delytelseListe[0].maanedligBelop, tjenestepensjon.utbetalingsperioder[2].maanedligBelop) + assertEquals(mockResponse.utbetalingListe[2].delytelseListe[0].ytelseType, tjenestepensjon.utbetalingsperioder[2].ytelseType) + assertEquals(mockResponse.utbetalingListe[3].fraOgMedDato, tjenestepensjon.utbetalingsperioder[3].fom) + assertEquals(mockResponse.utbetalingListe[3].delytelseListe[0].maanedligBelop, tjenestepensjon.utbetalingsperioder[3].maanedligBelop) + assertEquals(mockResponse.utbetalingListe[3].delytelseListe[0].ytelseType, tjenestepensjon.utbetalingsperioder[3].ytelseType) + assertEquals(mockResponse.utbetalingListe[4].fraOgMedDato, tjenestepensjon.utbetalingsperioder[4].fom) + assertEquals(mockResponse.utbetalingListe[4].delytelseListe[0].maanedligBelop, tjenestepensjon.utbetalingsperioder[4].maanedligBelop) + assertEquals(mockResponse.utbetalingListe[4].delytelseListe[0].ytelseType, tjenestepensjon.utbetalingsperioder[4].ytelseType) + assertEquals(1, tjenestepensjon.aarsakIngenUtbetaling.size) + assertTrue(tjenestepensjon.aarsakIngenUtbetaling[0].contains(mockResponse.aarsakIngenUtbetaling[0].ytelseType)) + + wireMockServer.removeStub(stub.uuid) + } + + @Test + fun `send request og faa error fra spk`() { + val stub = wireMockServer.stubFor(post(urlPathEqualTo(SIMULER_PATH)).willReturn(serverError())) + + val response: Result = spkClient.simuler(dummyRequest("1963-02-05", brukerBaOmAfp = true)) + assertTrue(response.isFailure) + assertTrue(response.exceptionOrNull() is TjenestepensjonSimuleringException) + + wireMockServer.removeStub(stub.uuid) + } + + private fun spkSimulerTjenestepensjonResponse() = SPKSimulerTjenestepensjonResponse( + inkludertOrdningListe = listOf(InkludertOrdning("3010")), + utbetalingListe = listOf( + Utbetaling( + fraOgMedDato = LocalDate.parse("2025-03-01"), + delytelseListe = listOf(Delytelse("OAFP", 1)), + ), + Utbetaling( + fraOgMedDato = LocalDate.parse("2025-03-01"), + delytelseListe = listOf(Delytelse("PAASLAG", 2)), + ), + Utbetaling( + fraOgMedDato = LocalDate.parse("2025-03-01"), + delytelseListe = listOf(Delytelse("APOF2020", 3)), + ), + Utbetaling( + fraOgMedDato = LocalDate.parse("2025-03-01"), + delytelseListe = listOf(Delytelse("OT6370", 4)), + ), + Utbetaling( + fraOgMedDato = LocalDate.parse("2025-03-01"), + delytelseListe = listOf(Delytelse("AFP", 5)), + ) + ), + aarsakIngenUtbetaling = listOf( + AarsakIngenUtbetaling("IKKE_STOETTET", "Ikke stoettet", "SAERALDERSPAASLAG"), + ) + ) + + +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonServiceTest.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonServiceTest.kt new file mode 100644 index 00000000..5d8663b7 --- /dev/null +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonServiceTest.kt @@ -0,0 +1,79 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk + +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Ordning +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjonMedMaanedsUtbetalinger +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Utbetalingsperiode +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.TjenestepensjonV2025ServiceTest.Companion.dummyRequest +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import org.mockito.Mockito.`when` +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import java.time.LocalDate + +@SpringBootTest +class SPKTjenestepensjonServiceTest { + + @MockBean + private lateinit var client: SPKTjenestepensjonClient + + @Autowired + private lateinit var spkTjenestepensjonService: SPKTjenestepensjonService + + @Test + fun `simuler gruppering og sortering av tjenestepensjon fra spk`() { + val req = dummyRequest("1963-02-05") + `when`(client.simuler(req)).thenReturn(dummyResult()) + + val res : Result = spkTjenestepensjonService.simuler(req) + + assertTrue(res.isSuccess) + val tjenestepensjon = res.getOrNull() + assertNotNull(tjenestepensjon) + assertEquals("spk", tjenestepensjon!!.tpLeverandoer) + assertEquals(1, tjenestepensjon.ordningsListe.size) + assertEquals(2, tjenestepensjon.utbetalingsperioder.size) + + assertEquals(LocalDate.parse("2026-01-01"), tjenestepensjon.utbetalingsperioder[0].fraOgMedDato) + assertEquals(3000, tjenestepensjon.utbetalingsperioder[0].maanedsBeloep) + assertEquals(62 , tjenestepensjon.utbetalingsperioder[0].fraOgMedAlder.aar) + assertEquals(10, tjenestepensjon.utbetalingsperioder[0].fraOgMedAlder.maaneder) + + assertEquals(LocalDate.parse("2026-03-01"), tjenestepensjon.utbetalingsperioder[1].fraOgMedDato) + assertEquals(7000, tjenestepensjon.utbetalingsperioder[1].maanedsBeloep) + assertEquals(63 , tjenestepensjon.utbetalingsperioder[1].fraOgMedAlder.aar) + assertEquals(0, tjenestepensjon.utbetalingsperioder[1].fraOgMedAlder.maaneder) + } + + fun dummyResult() : Result { + return Result.success(SimulertTjenestepensjon( + tpLeverandoer = "spk", + ordningsListe = listOf(Ordning("3010")), + utbetalingsperioder = listOf( + Utbetalingsperiode( + fom = LocalDate.parse("2026-03-01"), + maanedligBelop = 3000, + ytelseType = "OAFP" + ), + Utbetalingsperiode( + fom = LocalDate.parse("2026-03-01"), + maanedligBelop = 4000, + ytelseType = "OT6370" + ), + Utbetalingsperiode( + fom = LocalDate.parse("2026-01-01"), + maanedligBelop = 1000, + ytelseType = "PAASLAG" + ), + Utbetalingsperiode( + fom = LocalDate.parse("2026-01-01"), + maanedligBelop = 2000, + ytelseType = "APOF2020" + ), + ) + )) + } + +} \ No newline at end of file diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index d96a3318..b8b544ba 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -68,7 +68,7 @@ oftp: maskinportenscope: klp:oftp/simulering spk: endpoint: - url: https://api.test.spk.no + url: http://localhost:8080 maskinportenscope: spk:nav before2025: spk: