From 065a419bd4ab62cf758e5515e276050a450c992d Mon Sep 17 00:00:00 2001 From: Nicolas N Date: Fri, 19 Apr 2024 10:30:45 +0200 Subject: [PATCH] Flytte kode for oppgaveflytting til backend (#4572) --- .../main/kotlin/config/ApplicationContext.kt | 2 +- .../kotlin/oppgaveGosys/GosysOppgaveRoute.kt | 15 +++ .../oppgaveGosys/GosysOppgaveService.kt | 63 +++++++++- .../GosysOppgaveServiceImplTest.kt | 110 +++++++++++++++--- .../gosys/OverfoerOppgaveTilGjenny.tsx | 73 ++++-------- .../dokumenter/OppgaveFraJournalpostModal.tsx | 32 ++--- .../client/src/shared/api/gosys.ts | 3 + 7 files changed, 205 insertions(+), 93 deletions(-) diff --git a/apps/etterlatte-behandling/src/main/kotlin/config/ApplicationContext.kt b/apps/etterlatte-behandling/src/main/kotlin/config/ApplicationContext.kt index 309a7e1c933..34053a2d18e 100644 --- a/apps/etterlatte-behandling/src/main/kotlin/config/ApplicationContext.kt +++ b/apps/etterlatte-behandling/src/main/kotlin/config/ApplicationContext.kt @@ -304,7 +304,7 @@ internal class ApplicationContext( val tilbakekreving = TilbakekrevingHendelserServiceImpl(rapid) val oppgaveService = OppgaveService(oppgaveDaoEndringer, sakDao, behandlingsHendelser) - val gosysOppgaveService = GosysOppgaveServiceImpl(gosysOppgaveKlient, pdlTjenesterKlient) + val gosysOppgaveService = GosysOppgaveServiceImpl(gosysOppgaveKlient, pdlTjenesterKlient, oppgaveService) val grunnlagsService = GrunnlagServiceImpl(grunnlagKlient) val behandlingService = BehandlingServiceImpl( diff --git a/apps/etterlatte-behandling/src/main/kotlin/oppgaveGosys/GosysOppgaveRoute.kt b/apps/etterlatte-behandling/src/main/kotlin/oppgaveGosys/GosysOppgaveRoute.kt index 0303f8d8e25..fd47989ac8b 100644 --- a/apps/etterlatte-behandling/src/main/kotlin/oppgaveGosys/GosysOppgaveRoute.kt +++ b/apps/etterlatte-behandling/src/main/kotlin/oppgaveGosys/GosysOppgaveRoute.kt @@ -8,6 +8,8 @@ import io.ktor.server.routing.Route import io.ktor.server.routing.get import io.ktor.server.routing.post import io.ktor.server.routing.route +import kotlinx.coroutines.runBlocking +import no.nav.etterlatte.inTransaction import no.nav.etterlatte.libs.common.oppgave.RedigerFristGosysRequest import no.nav.etterlatte.libs.common.oppgave.SaksbehandlerEndringGosysDto import no.nav.etterlatte.libs.ktor.brukerTokenInfo @@ -41,6 +43,19 @@ internal fun Route.gosysOppgaveRoute(gosysService: GosysOppgaveService) { } } + post("/flytt-til-gjenny") { + kunSaksbehandler { + val sakId = call.request.queryParameters["sakid"]!!.toLong() + val nyOppgave = + inTransaction { + runBlocking { + gosysService.flyttTilGjenny(gosysOppgaveId.toLong(), sakId, brukerTokenInfo) + } + } + call.respond(nyOppgave) + } + } + post("tildel-saksbehandler") { kunSaksbehandler { val saksbehandlerEndringDto = call.receive() diff --git a/apps/etterlatte-behandling/src/main/kotlin/oppgaveGosys/GosysOppgaveService.kt b/apps/etterlatte-behandling/src/main/kotlin/oppgaveGosys/GosysOppgaveService.kt index 0a600b4b95a..b4ea9843c66 100644 --- a/apps/etterlatte-behandling/src/main/kotlin/oppgaveGosys/GosysOppgaveService.kt +++ b/apps/etterlatte-behandling/src/main/kotlin/oppgaveGosys/GosysOppgaveService.kt @@ -7,9 +7,14 @@ import no.nav.etterlatte.SaksbehandlerMedEnheterOgRoller import no.nav.etterlatte.User import no.nav.etterlatte.common.Enheter import no.nav.etterlatte.common.klienter.PdlTjenesterKlient +import no.nav.etterlatte.libs.common.feilhaandtering.UgyldigForespoerselException +import no.nav.etterlatte.libs.common.oppgave.OppgaveIntern +import no.nav.etterlatte.libs.common.oppgave.OppgaveKilde import no.nav.etterlatte.libs.common.oppgave.OppgaveSaksbehandler +import no.nav.etterlatte.libs.common.oppgave.OppgaveType import no.nav.etterlatte.libs.common.tidspunkt.Tidspunkt import no.nav.etterlatte.libs.ktor.token.BrukerTokenInfo +import no.nav.etterlatte.oppgave.OppgaveService import org.slf4j.LoggerFactory import java.time.Duration import java.time.LocalTime @@ -32,6 +37,12 @@ interface GosysOppgaveService { brukerTokenInfo: BrukerTokenInfo, ): GosysOppgave? + suspend fun flyttTilGjenny( + oppgaveId: Long, + sakId: Long, + brukerTokenInfo: BrukerTokenInfo, + ): OppgaveIntern + suspend fun tildelOppgaveTilSaksbehandler( oppgaveId: String, oppgaveVersjon: Long, @@ -62,6 +73,7 @@ interface GosysOppgaveService { class GosysOppgaveServiceImpl( private val gosysOppgaveKlient: GosysOppgaveKlient, private val pdltjenesterKlient: PdlTjenesterKlient, + private val oppgaveService: OppgaveService, ) : GosysOppgaveService { private val logger = LoggerFactory.getLogger(this::class.java) @@ -142,6 +154,50 @@ class GosysOppgaveServiceImpl( }.also { cache.put(id, it) } } + override suspend fun flyttTilGjenny( + oppgaveId: Long, + sakId: Long, + brukerTokenInfo: BrukerTokenInfo, + ): OppgaveIntern { + logger.info("Starter flytting av gosys-oppgave (id=$oppgaveId) til Gjenny") + + val gosysOppgave = hentOppgave(oppgaveId, brukerTokenInfo) + + if (gosysOppgave.oppgavetype !in listOf("JFR", "JFR_UT")) { + logger.error("Fikk forespørsel om flytting av oppgavetype=${gosysOppgave.oppgavetype}. Burde det støttes?") + throw StoetterKunFlyttingAvJournalfoeringsoppgave() + } + + check(!gosysOppgave.journalpostId.isNullOrBlank()) { + "Kan ikke flytte oppgave når journalpostId mangler (oppgaveId=${gosysOppgave.id})" + } + + val nyOppgave = + oppgaveService.opprettNyOppgaveMedSakOgReferanse( + referanse = gosysOppgave.journalpostId, + sakId = sakId, + oppgaveKilde = OppgaveKilde.SAKSBEHANDLER, + oppgaveType = OppgaveType.JOURNALFOERING, + merknad = gosysOppgave.beskrivelse, + frist = gosysOppgave.frist, + saksbehandler = brukerTokenInfo.ident(), + ) + + val feilregistrertOppgaveId = + feilregistrer( + oppgaveId.toString(), + FeilregistrerOppgaveRequest( + beskrivelse = "Oppgave overført til Gjenny", + versjon = gosysOppgave.versjon, + ), + brukerTokenInfo, + ) + + logger.info("Feilregistrerte Gosys-oppgave $feilregistrertOppgaveId") + + return nyOppgave + } + override suspend fun tildelOppgaveTilSaksbehandler( oppgaveId: String, oppgaveVersjon: Long, @@ -187,7 +243,7 @@ class GosysOppgaveServiceImpl( beskrivelse = request.beskrivelse, ) - return gosysOppgaveKlient.feilregistrer(oppgaveId, endreStatusRequest, brukerTokenInfo).versjon + return gosysOppgaveKlient.feilregistrer(oppgaveId, endreStatusRequest, brukerTokenInfo).id } companion object { @@ -212,3 +268,8 @@ class GosysOppgaveServiceImpl( } } } + +class StoetterKunFlyttingAvJournalfoeringsoppgave : UgyldigForespoerselException( + code = "UGYLDIG_OPPGAVETYPE_FOR_FLYTTING", + detail = "Støtter foreløpig kun flytting av journalføringsoppgaver", +) diff --git a/apps/etterlatte-behandling/src/test/kotlin/oppgaveGosys/GosysOppgaveServiceImplTest.kt b/apps/etterlatte-behandling/src/test/kotlin/oppgaveGosys/GosysOppgaveServiceImplTest.kt index b4550ca27e1..d46125d78af 100644 --- a/apps/etterlatte-behandling/src/test/kotlin/oppgaveGosys/GosysOppgaveServiceImplTest.kt +++ b/apps/etterlatte-behandling/src/test/kotlin/oppgaveGosys/GosysOppgaveServiceImplTest.kt @@ -2,9 +2,12 @@ package no.nav.etterlatte.oppgaveGosys import com.nimbusds.jwt.JWTClaimsSet import io.kotest.matchers.collections.shouldHaveSize +import io.mockk.clearAllMocks import io.mockk.coEvery +import io.mockk.coVerify import io.mockk.every import io.mockk.mockk +import io.mockk.verify import kotlinx.coroutines.runBlocking import no.nav.etterlatte.SaksbehandlerMedEnheterOgRoller import no.nav.etterlatte.azureAdAttestantClaim @@ -12,26 +15,33 @@ import no.nav.etterlatte.azureAdSaksbehandlerClaim import no.nav.etterlatte.azureAdStrengtFortroligClaim import no.nav.etterlatte.common.Enheter import no.nav.etterlatte.common.klienter.PdlTjenesterKlient +import no.nav.etterlatte.libs.common.oppgave.OppgaveKilde +import no.nav.etterlatte.libs.common.oppgave.OppgaveType import no.nav.etterlatte.libs.common.tidspunkt.Tidspunkt import no.nav.etterlatte.libs.ktor.token.BrukerTokenInfo import no.nav.etterlatte.libs.ktor.token.Saksbehandler import no.nav.etterlatte.nyKontekstMedBruker +import no.nav.etterlatte.oppgave.OppgaveService import no.nav.etterlatte.tilgangsstyring.AzureGroup import no.nav.etterlatte.tilgangsstyring.SaksbehandlerMedRoller import no.nav.security.token.support.core.jwt.JwtTokenClaims +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import java.time.LocalDate +import java.time.LocalTime import java.time.temporal.ChronoUnit +import kotlin.random.Random @TestInstance(TestInstance.Lifecycle.PER_METHOD) class GosysOppgaveServiceImplTest { private val gosysOppgaveKlient = mockk() private val pdltjenesterKlient = mockk() + private val oppgaveService = mockk() private val brukerTokenInfo = mockk() - private val service = GosysOppgaveServiceImpl(gosysOppgaveKlient, pdltjenesterKlient) + private val service = GosysOppgaveServiceImpl(gosysOppgaveKlient, pdltjenesterKlient, oppgaveService) private val saksbehandler = mockk() val azureGroupToGroupIDMap = @@ -60,6 +70,11 @@ class GosysOppgaveServiceImplTest { every { saksbehandler.saksbehandlerMedRoller } returns saksbehandlerRoller } + @AfterEach + fun afterEach() { + clearAllMocks() + } + @Test fun `skal hente oppgaver og deretter folkeregisterIdent for unike identer`() { val saksbehandlerRoller = generateSaksbehandlerMedRoller(AzureGroup.SAKSBEHANDLER) @@ -156,7 +171,14 @@ class GosysOppgaveServiceImplTest { every { saksbehandler.saksbehandlerMedRoller } returns saksbehandlerRoller - coEvery { gosysOppgaveKlient.hentOppgaver(any(), any(), Enheter.STRENGT_FORTROLIG.enhetNr, brukerTokenInfo) } returns + coEvery { + gosysOppgaveKlient.hentOppgaver( + any(), + any(), + Enheter.STRENGT_FORTROLIG.enhetNr, + brukerTokenInfo, + ) + } returns enhetsfiltrererGosysOppgaver( Enheter.STRENGT_FORTROLIG.enhetNr, listOf( @@ -232,25 +254,77 @@ class GosysOppgaveServiceImplTest { tildeles = "A012345", brukerTokenInfo, ) - } returns - GosysApiOppgave( - id = 123, - versjon = 2, - tema = "EYO", - behandlingstema = "", - oppgavetype = "", - journalpostId = null, - opprettetTidspunkt = Tidspunkt.now().minus(3L, ChronoUnit.DAYS), - tildeltEnhetsnr = Enheter.STEINKJER.enhetNr, - tilordnetRessurs = "A012345", - aktoerId = "78324720383742", - beskrivelse = "Omstillingsstønad oppgavebeskrivelse", - status = "NY", - fristFerdigstillelse = LocalDate.now().plusDays(4), - ) + } returns mockGosysOppgave("EYO", "GEN") runBlocking { service.tildelOppgaveTilSaksbehandler(oppgaveId = "123", oppgaveVersjon = 2L, "A012345", brukerTokenInfo) } } + + @Test + fun `Flytt oppgave til Gjenny`() { + val sakId = Random.nextLong() + val gosysOppgave = mockGosysOppgave("EYO", "JFR", Random.nextLong().toString()) + val brukerTokenInfo = BrukerTokenInfo.of("", "Z123456", null, null, null) + + coEvery { gosysOppgaveKlient.hentOppgave(any(), any()) } returns gosysOppgave + coEvery { gosysOppgaveKlient.feilregistrer(any(), any(), any()) } returns gosysOppgave + coEvery { + oppgaveService.opprettNyOppgaveMedSakOgReferanse(any(), any(), any(), any(), any(), any(), any()) + } returns mockk() + every { + pdltjenesterKlient.hentFolkeregisterIdenterForAktoerIdBolk(setOf(gosysOppgave.aktoerId!!)) + } returns mapOf("78324720383742" to "29048012345") + + runBlocking { + service.flyttTilGjenny(gosysOppgave.id, sakId, brukerTokenInfo) + } + + verify(exactly = 1) { + oppgaveService.opprettNyOppgaveMedSakOgReferanse( + gosysOppgave.journalpostId!!, + sakId, + OppgaveKilde.SAKSBEHANDLER, + OppgaveType.JOURNALFOERING, + gosysOppgave.beskrivelse, + Tidspunkt.ofNorskTidssone(gosysOppgave.fristFerdigstillelse!!, LocalTime.MIDNIGHT), + brukerTokenInfo.ident(), + ) + } + coVerify(exactly = 1) { + gosysOppgaveKlient.feilregistrer( + id = gosysOppgave.id.toString(), + request = + EndreStatusRequest( + versjon = gosysOppgave.versjon.toString(), + status = "FEILREGISTRERT", + beskrivelse = "Oppgave overført til Gjenny", + ), + brukerTokenInfo = brukerTokenInfo, + ) + } + coVerify(exactly = 1) { + pdltjenesterKlient.hentFolkeregisterIdenterForAktoerIdBolk(setOf(gosysOppgave.aktoerId!!)) + } + } + + private fun mockGosysOppgave( + tema: String, + oppgavetype: String, + journalpostId: String? = null, + ) = GosysApiOppgave( + id = Random.nextLong(), + versjon = Random.nextLong(), + tema = tema, + behandlingstema = "", + oppgavetype = oppgavetype, + journalpostId = journalpostId, + opprettetTidspunkt = Tidspunkt.now().minus(3L, ChronoUnit.DAYS), + tildeltEnhetsnr = Enheter.STEINKJER.enhetNr, + tilordnetRessurs = "A012345", + aktoerId = "78324720383742", + beskrivelse = "Beskrivelse for oppgaven", + status = "OPPRETTET", + fristFerdigstillelse = LocalDate.now().plusDays(4), + ) } diff --git a/apps/etterlatte-saksbehandling-ui/client/src/components/oppgavebenk/gosys/OverfoerOppgaveTilGjenny.tsx b/apps/etterlatte-saksbehandling-ui/client/src/components/oppgavebenk/gosys/OverfoerOppgaveTilGjenny.tsx index aed6e316798..53570f8e1a4 100644 --- a/apps/etterlatte-saksbehandling-ui/client/src/components/oppgavebenk/gosys/OverfoerOppgaveTilGjenny.tsx +++ b/apps/etterlatte-saksbehandling-ui/client/src/components/oppgavebenk/gosys/OverfoerOppgaveTilGjenny.tsx @@ -1,16 +1,13 @@ -import { opprettOppgave } from '~shared/api/oppgaver' import { useApiCall } from '~shared/hooks/useApiCall' import { hentSakForPerson } from '~shared/api/sak' -import { isInitial, isPending, isSuccess, mapResult, mapSuccess } from '~shared/api/apiUtils' +import { isPending, mapResult } from '~shared/api/apiUtils' import { Alert, Button, Checkbox, Select } from '@navikt/ds-react' import { FlexRow } from '~shared/styled' import { GosysActionToggle } from '~components/oppgavebenk/oppgaveModal/GosysOppgaveModal' import React, { useState } from 'react' import { useNavigate } from 'react-router-dom' -import { ResultAlert } from '~shared/alerts/ResultAlert' import { GosysOppgave, GosysTema, sakTypeFraTema } from '~shared/types/Gosys' -import { feilregistrerGosysOppgave } from '~shared/api/gosys' -import { OppgaveKilde, Oppgavetype } from '~shared/types/oppgave' +import { flyttTilGjenny } from '~shared/api/gosys' import { SakType } from '~shared/types/sak' import { formaterSakstype } from '~utils/formattering' import { useInnloggetSaksbehandler } from '~components/behandling/useInnloggetSaksbehandler' @@ -29,37 +26,18 @@ export const OverfoerOppgaveTilGjenny = ({ const [skalOppretteSak, setSkalOppretteSak] = useState(false) const [sakType, setSakType] = useState(sakTypeFraTema(oppgave.tema)) - const [opprettOppgaveStatus, apiOpprettOppgave] = useApiCall(opprettOppgave) - const [feilregistrerStatus, feilregistrerOppgave] = useApiCall(feilregistrerGosysOppgave) const [sakStatus, hentSak] = useApiCall(hentSakForPerson) + const [flyttOppgaveResult, flyttOppgaveTilGjenny] = useApiCall(flyttTilGjenny) const konverterTilGjennyoppgave = () => { hentSak({ fnr: oppgave.fnr!!, type: sakType!!, opprettHvisIkkeFinnes: skalOppretteSak }, (sak) => { if (!sak) return - apiOpprettOppgave( - { - sakId: sak.id, - request: { - oppgaveType: Oppgavetype.JOURNALFOERING, - referanse: oppgave.journalpostId!!, - merknad: oppgave.beskrivelse || 'Journalføringsoppgave konvertert fra Gosys', - oppgaveKilde: OppgaveKilde.SAKSBEHANDLER, - saksbehandler: innloggetSaksbehandler.ident, - }, - }, - () => { - feilregistrerOppgave({ - oppgaveId: oppgave.id, - versjon: oppgave.versjon!!, - beskrivelse: 'Oppgave ble flyttet til Gjenny', - }) - } - ) + flyttOppgaveTilGjenny({ oppgaveId: oppgave.id, sakId: sak.id }) }) } - const loading = isPending(sakStatus) || isPending(opprettOppgaveStatus) || isPending(feilregistrerStatus) + const loading = isPending(sakStatus) || isPending(flyttOppgaveResult) if (oppgave.saksbehandler?.ident !== innloggetSaksbehandler.ident) return Oppgaven er ikke tildelt deg! @@ -112,32 +90,29 @@ export const OverfoerOppgaveTilGjenny = ({ ), })} - -
- {isSuccess(opprettOppgaveStatus) && ( - - - {mapSuccess(opprettOppgaveStatus, (oppgave) => ( + {mapResult(flyttOppgaveResult, { + initial: ( + + + + + ), + success: (oppgave) => ( + + - ))} - - )} - - {isInitial(opprettOppgaveStatus) && ( - - - - - )} + + ), + })} ) } diff --git a/apps/etterlatte-saksbehandling-ui/client/src/components/person/dokumenter/OppgaveFraJournalpostModal.tsx b/apps/etterlatte-saksbehandling-ui/client/src/components/person/dokumenter/OppgaveFraJournalpostModal.tsx index 447d434d4c2..c3e4f253396 100644 --- a/apps/etterlatte-saksbehandling-ui/client/src/components/person/dokumenter/OppgaveFraJournalpostModal.tsx +++ b/apps/etterlatte-saksbehandling-ui/client/src/components/person/dokumenter/OppgaveFraJournalpostModal.tsx @@ -15,7 +15,7 @@ import { ConfigContext } from '~clientConfig' import { InfoWrapper } from '~components/behandling/soeknadsoversikt/styled' import { Info } from '~components/behandling/soeknadsoversikt/Info' import { ApiErrorAlert } from '~ErrorBoundary' -import { feilregistrerGosysOppgave, hentJournalfoeringsoppgaverFraGosys } from '~shared/api/gosys' +import { flyttTilGjenny, hentJournalfoeringsoppgaverFraGosys } from '~shared/api/gosys' import { GosysOppgave } from '~shared/types/Gosys' export const OppgaveFraJournalpostModal = ({ @@ -40,7 +40,7 @@ export const OppgaveFraJournalpostModal = ({ const [hentOppgaverStatus, hentOppgaver] = useApiCall(hentOppgaverMedReferanse) const [gosysResult, hentGosysOppgave] = useApiCall(hentJournalfoeringsoppgaverFraGosys) - const [, feilregistrerOppgave] = useApiCall(feilregistrerGosysOppgave) + const [flyttOppgaveResult, flyttOppgaveTilGjenny] = useApiCall(flyttTilGjenny) useEffect(() => { if (isOpen) { @@ -80,25 +80,9 @@ export const OppgaveFraJournalpostModal = ({ const konverterTilGjennyoppgave = (oppgave: GosysOppgave) => { if (isSuccess(sakStatus)) { - apiOpprettOppgave( - { - sakId: sakStatus.data.sak.id, - request: { - oppgaveType: Oppgavetype.JOURNALFOERING, - referanse: oppgave.journalpostId!!, - merknad: oppgave.beskrivelse || 'Journalføringsoppgave flyttet fra Gosys', - oppgaveKilde: OppgaveKilde.SAKSBEHANDLER, - saksbehandler: innloggetSaksbehandler.ident, - }, - }, - () => { - feilregistrerOppgave({ - oppgaveId: oppgave.id, - versjon: oppgave.versjon!!, - beskrivelse: 'Oppgave ble flyttet til Gjenny', - }) - } - ) + flyttOppgaveTilGjenny({ oppgaveId: oppgave.id, sakId: sakStatus.data.sak.id }, (oppgave) => { + navigate(`/oppgave/${oppgave.id}`) + }) } } @@ -147,16 +131,16 @@ export const OppgaveFraJournalpostModal = ({
- {isSuccess(opprettOppgaveStatus) ? ( + {isSuccess(flyttOppgaveResult) ? ( - Gå til oppgave + Gå til oppgave ) : ( diff --git a/apps/etterlatte-saksbehandling-ui/client/src/shared/api/gosys.ts b/apps/etterlatte-saksbehandling-ui/client/src/shared/api/gosys.ts index d4ff257c58b..8515300382d 100644 --- a/apps/etterlatte-saksbehandling-ui/client/src/shared/api/gosys.ts +++ b/apps/etterlatte-saksbehandling-ui/client/src/shared/api/gosys.ts @@ -32,6 +32,9 @@ export const redigerFristApi = async (args: { return apiClient.post(`/oppgaver/gosys/${args.oppgaveId}/endre-frist`, { ...args.redigerFristRequest }) } +export const flyttTilGjenny = async (args: { oppgaveId: number; sakId: number }): Promise> => + apiClient.post(`/oppgaver/gosys/${args.oppgaveId}/flytt-til-gjenny?sakId=${args.sakId}`, {}) + export const ferdigstilleGosysOppgave = async (args: { oppgaveId: number versjon: number