diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 93a5de10..958fc908 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -2,6 +2,8 @@ TooManyFunctions:MotebehovService.kt$MotebehovService + LongParameterList:MotebehovVeilederADControllerV4.kt$MotebehovVeilederADControllerV4$( private val contextHolder: TokenValidationContextHolder, private val metric: Metric, private val historikkService: HistorikkService, private val motebehovService: MotebehovService, private val pdlConsumer: PdlConsumer, private val veilederTilgangConsumer: VeilederTilgangConsumer, private val esyfovarselService: EsyfovarselService, ) + LongParameterList:MotebehovArbeidstakerControllerV4.kt$MotebehovArbeidstakerControllerV4$( private val contextHolder: TokenValidationContextHolder, private val metric: Metric, private val motebehovStatusServiceV2: MotebehovStatusServiceV2, private val motebehovOppfolgingstilfelleServiceV2: MotebehovOppfolgingstilfelleServiceV2, @Value("\${dialogmote.frontend.client.id}") val dialogmoteClientId: String, @Value("\${ditt.sykefravaer.frontend.client.id}") val dittSykefravaerClientId: String, @Value("\${esyfo-proxy.client.id}") val esyfoProxyClientId: String, ) AnnotationOnSeparateLine:MotebehovArbeidsgiverControllerV3.kt$MotebehovArbeidsgiverControllerV3$@Pattern(regexp = "^[0-9]{11}$") diff --git a/src/main/kotlin/no/nav/syfo/metric/Metric.kt b/src/main/kotlin/no/nav/syfo/metric/Metric.kt index 908b2da6..e00223bc 100644 --- a/src/main/kotlin/no/nav/syfo/metric/Metric.kt +++ b/src/main/kotlin/no/nav/syfo/metric/Metric.kt @@ -2,7 +2,7 @@ package no.nav.syfo.metric import io.micrometer.core.instrument.MeterRegistry import io.micrometer.core.instrument.Tags -import no.nav.syfo.motebehov.MotebehovSvar +import no.nav.syfo.motebehov.MotebehovFormSubmissionCombinedDTO import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType import no.nav.syfo.oppfolgingstilfelle.database.PersonOppfolgingstilfelle import org.springframework.stereotype.Controller @@ -150,28 +150,32 @@ class Metric @Inject constructor( fun tellBesvarMotebehov( activeOppfolgingstilfelle: PersonOppfolgingstilfelle, motebehovSkjemaType: MotebehovSkjemaType?, - motebehovSvar: MotebehovSvar, + formSubmission: MotebehovFormSubmissionCombinedDTO, erInnloggetBrukerArbeidstaker: Boolean ) { - val harForklaring = motebehovSvar.forklaring?.isNotBlank() ?: false + val harForklaring = formSubmission.forklaring?.isNotBlank() ?: false tellMotebehovBesvart( activeOppfolgingstilfelle, motebehovSkjemaType, - motebehovSvar.harMotebehov, + formSubmission.harMotebehov, erInnloggetBrukerArbeidstaker ) countDayInOppfolgingstilfelleMotebehovCreated( activeOppfolgingstilfelle, motebehovSkjemaType, - motebehovSvar.harMotebehov, + formSubmission.harMotebehov, harForklaring, erInnloggetBrukerArbeidstaker ) - if (!motebehovSvar.harMotebehov) { - tellMotebehovBesvartNeiAntallTegn(motebehovSvar.forklaring!!.length, erInnloggetBrukerArbeidstaker) + + if (!formSubmission.harMotebehov && formSubmission.forklaring !== null) { + tellMotebehovBesvartNeiAntallTegn(formSubmission.forklaring.length, erInnloggetBrukerArbeidstaker) } else if (harForklaring) { - tellMotebehovBesvartJaMedForklaringTegn(motebehovSvar.forklaring!!.length, erInnloggetBrukerArbeidstaker) + tellMotebehovBesvartJaMedForklaringTegn( + formSubmission.forklaring!!.length, + erInnloggetBrukerArbeidstaker + ) tellMotebehovBesvartJaMedForklaringAntall(erInnloggetBrukerArbeidstaker) } } diff --git a/src/main/kotlin/no/nav/syfo/motebehov/ConvertLegacyMotebehovSvarFieldsHelper.kt b/src/main/kotlin/no/nav/syfo/motebehov/ConvertLegacyMotebehovSvarFieldsHelper.kt deleted file mode 100644 index 6fdf547e..00000000 --- a/src/main/kotlin/no/nav/syfo/motebehov/ConvertLegacyMotebehovSvarFieldsHelper.kt +++ /dev/null @@ -1,242 +0,0 @@ -package no.nav.syfo.motebehov - -import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType -import org.springframework.stereotype.Component - -enum class MotebehovInnmelderType { - ARBEIDSGIVER, - ARBEIDSTAKER, -} - -@Component -class ConvertLegacyMotebehovSvarFieldsHelper { - private val formIdentifierArbeidsgiverSvarBehov = "motebehov-arbeidsgiver-svar" - private val formIdentifierArbeidsgiverMeldBehov = "motebehov-arbeidsgiver-meld" - private val formIdentifierArbeidsgiverUnknownSvarMeldBehov = "motebehov-arbeidsgiver-unknown" - private val formIdentifierArbeidstakerSvarBehov = "motebehov-arbeidstaker-svar" - private val formIdentifierArbeidstakerMeldBehov = "motebehov-arbeidstaker-meld" - private val formIdentifierArbeidstakerUnknownSvarMeldBehov = "motebehov-arbeidstaker-unknown" - - private val legacyFormsSemanticVersion = "0.1.0" - - private val motebehovFieldIds = mapOf( - "svarHarBehovRadioGroupField" to "harBehovRadioGroup", - "meldHarBehovLegacyCheckboxField" to "harBehovCheckbox", - "onskerSykmelderDeltarCheckboxField" to "onskerSykmelderDeltarCheckbox", - "begrunnelseTextField" to "begrunnelseText", - ) - - private val motebehovLegacyLabels = mapOf( - "svarArbeidsgiverHarBehovField" to "Har dere behov for et møte med NAV?", - "svarArbeidstakerHarBehovField" to "Har du behov for et møte med NAV og arbeidsgiveren din?", - "svarHarBehovRadioOptionYes" to "Ja, jeg mener det er behov for et møte", - "svarHarBehovRadioOptionNo" to "Nei, jeg mener det ikke er behov for et møte", - "meldArbeidsgiverOnskerMoteCheckbox" to "Jeg ønsker et møte med NAV og den ansatte", - "meldArbeidstakerOnskerMoteCheckbox" to "Jeg ønsker et møte med NAV og arbeidsgiveren min.", - "meldArbeidsgiverOnskerSykmelderDeltarCheckbox" to - "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet.", - "meldArbeidstakerOnskerSykmelderDeltarCheckbox" to - "Jeg ønsker at den som sykmelder meg, også skal delta i møtet.", - "begrunnelseTextField" to "Begrunnelse" - ) - - private val formFilloutOptionIds = mapOf( - "svarHarBehovRadioOptionYes" to "ja", - "svarHarBehovRadioOptionNo" to "nei" - ) - - data class ExtractedFromLegacyForklaring( - val actualBegrunnelse: String, - val onskerSykmelderDeltar: Boolean - ) - - fun createLegacyBegrunnelseTextField( - begrunnelseTextValue: String, - harMotebehov: Boolean, - skjemaType: MotebehovSkjemaType?, - ): FilloutTextField { - return FilloutTextField( - fieldID = motebehovFieldIds["begrunnelseTextField"] ?: "", - fieldLabel = motebehovLegacyLabels["begrunnelseTextField"] ?: "", - textValue = begrunnelseTextValue, - wasOptional = skjemaType == MotebehovSkjemaType.MELD_BEHOV || harMotebehov - ) - } - - fun createLegacySvarBehovRadioGroupField( - harMotebehov: Boolean, - motebehovInnmelderType: MotebehovInnmelderType - ): FilloutRadioGroupField { - val optionIdYes = formFilloutOptionIds["svarHarBehovRadioOptionYes"]!! - val optionIdNo = formFilloutOptionIds["svarHarBehovRadioOptionNo"]!! - - val optionLabelYes = motebehovLegacyLabels["svarHarBehovRadioOptionYes"]!! - val optionLabelNo = motebehovLegacyLabels["svarHarBehovRadioOptionNo"]!! - - val selectedOptionId = if (harMotebehov) optionIdYes else optionIdNo - val selectedOptionLabel = if (harMotebehov) optionLabelYes else optionLabelNo - - return FilloutRadioGroupField( - fieldID = motebehovFieldIds["svarHarBehovRadioGroupField"]!!, - fieldLabel = motebehovInnmelderType.let { - when (it) { - MotebehovInnmelderType.ARBEIDSGIVER -> - motebehovLegacyLabels["svarArbeidsgiverHarBehovField"]!! - - MotebehovInnmelderType.ARBEIDSTAKER -> - motebehovLegacyLabels["svarArbeidstakerHarBehovField"]!! - } - }, - selectedOptionId, - selectedOptionLabel, - options = listOf( - FormFilloutFieldOption( - optionId = optionIdYes, - optionLabel = optionLabelYes, - wasSelected = harMotebehov - ), - FormFilloutFieldOption( - optionId = optionIdNo, - optionLabel = optionLabelNo, - wasSelected = !harMotebehov - ) - ) - ) - } - - fun createLegacyMeldOnskerMoteCheckboxField( - motebehovInnmelderType: MotebehovInnmelderType - ): FilloutCheckboxField { - return FilloutCheckboxField( - fieldID = motebehovFieldIds["meldHarBehovLegacyCheckboxField"] ?: "", - fieldLabel = motebehovInnmelderType.let { - when (it) { - MotebehovInnmelderType.ARBEIDSGIVER -> - motebehovLegacyLabels["meldArbeidsgiverOnskerMoteCheckbox"] ?: "" - - MotebehovInnmelderType.ARBEIDSTAKER -> - motebehovLegacyLabels["meldArbeidstakerOnskerMoteCheckbox"] ?: "" - } - }, - wasChecked = true, - ) - } - - fun createLegacyOnskerSykmelderDeltarCheckboxField( - onskerSykmelderDeltar: Boolean, - motebehovInnmelderType: MotebehovInnmelderType, - ): FilloutCheckboxField { - return FilloutCheckboxField( - fieldID = motebehovFieldIds["onskerSykmelderDeltarCheckboxField"] ?: "", - fieldLabel = motebehovInnmelderType.let { - when (it) { - MotebehovInnmelderType.ARBEIDSGIVER -> - motebehovLegacyLabels["meldArbeidsgiverOnskerSykmelderDeltarCheckbox"] ?: "" - - MotebehovInnmelderType.ARBEIDSTAKER -> - motebehovLegacyLabels["meldArbeidstakerOnskerSykmelderDeltarCheckbox"] ?: "" - } - }, - wasChecked = onskerSykmelderDeltar, - ) - } - - // When a user checked the checkbox for onskerSykmelderDeltar in the legacy form, the text in the forklaring field - // submitted from the frontend would contain the label text for that checkbox concatenated with the text value of - // the begrunnelse text field. - fun extractActualUserBegrunnelseAndOnskerSykmelderDeltarFromLegacyForklaring( - legacyForklaring: String? - ): ExtractedFromLegacyForklaring { - if (legacyForklaring == null) return ExtractedFromLegacyForklaring("", false) - - var onskerSykmelderDeltar = false - - if (legacyForklaring.contains( - "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet" - ) || legacyForklaring.contains( - "Jeg ønsker at den som sykmelder meg, også skal delta i møtet" - ) - ) { - onskerSykmelderDeltar = true - } - - var actualBegrunnelse = legacyForklaring.replace( - "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet (valgfri).", - "" - ) - actualBegrunnelse = actualBegrunnelse.replace( - "Jeg ønsker at den som sykmelder meg, også skal delta i møtet (valgfri).", "" - ) - - // When the user didn't write anything in the begrunnelse text field, "undefined" would be appended to the - // forklaring value, at least in some cases. We remove it here. - actualBegrunnelse = actualBegrunnelse.replace("undefined", "") - - actualBegrunnelse = actualBegrunnelse.trim() - - return ExtractedFromLegacyForklaring(actualBegrunnelse, onskerSykmelderDeltar) - } - - /** - * Converts a "legacy motebehovSvar" with fields harMotebehov and forklaring to a FormFillout. - * The returned FormFillout matches what the forms look like in production at the time of writing, which is before - * an update to the frontend that will make the forms contain more fields, and that will make the frontend submit - * a FormFillout instead of individual hard-coded field values. - */ - fun convertLegacyMotebehovSvarToFormFillout( - harMotebehov: Boolean, - forklaring: String?, - skjemaType: MotebehovSkjemaType?, - motebehovInnmelderType: MotebehovInnmelderType - ): FormFillout { - val formFilloutFields = mutableListOf() - - if (skjemaType == MotebehovSkjemaType.SVAR_BEHOV) { - formFilloutFields.add( - createLegacySvarBehovRadioGroupField( - harMotebehov, - motebehovInnmelderType - ) - ) - } else if (skjemaType == MotebehovSkjemaType.MELD_BEHOV) { - formFilloutFields.add( - createLegacyMeldOnskerMoteCheckboxField( - motebehovInnmelderType - ) - ) - } - - val (actualBegrunnelse, onskerAtSykmelderDeltar) = - extractActualUserBegrunnelseAndOnskerSykmelderDeltarFromLegacyForklaring(forklaring) - - // It was only the MELD_BEHOV form that had the "onskerSykmelderDeltar" checkbox. - if (skjemaType == MotebehovSkjemaType.MELD_BEHOV || onskerAtSykmelderDeltar) { - formFilloutFields.add( - createLegacyOnskerSykmelderDeltarCheckboxField( - onskerAtSykmelderDeltar, - motebehovInnmelderType - ) - ) - } - - formFilloutFields.add(createLegacyBegrunnelseTextField(actualBegrunnelse, harMotebehov, skjemaType)) - - val formIdentifier = when (motebehovInnmelderType) { - MotebehovInnmelderType.ARBEIDSGIVER -> - when (skjemaType) { - MotebehovSkjemaType.SVAR_BEHOV -> formIdentifierArbeidsgiverSvarBehov - MotebehovSkjemaType.MELD_BEHOV -> formIdentifierArbeidsgiverMeldBehov - else -> formIdentifierArbeidsgiverUnknownSvarMeldBehov - } - - MotebehovInnmelderType.ARBEIDSTAKER -> - when (skjemaType) { - MotebehovSkjemaType.SVAR_BEHOV -> formIdentifierArbeidstakerSvarBehov - MotebehovSkjemaType.MELD_BEHOV -> formIdentifierArbeidstakerMeldBehov - else -> formIdentifierArbeidstakerUnknownSvarMeldBehov - } - } - - return FormFillout(formIdentifier, legacyFormsSemanticVersion, formFilloutFields) - } -} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/FormFillout.kt b/src/main/kotlin/no/nav/syfo/motebehov/FormFillout.kt deleted file mode 100644 index 79933f9f..00000000 --- a/src/main/kotlin/no/nav/syfo/motebehov/FormFillout.kt +++ /dev/null @@ -1,119 +0,0 @@ -package no.nav.syfo.motebehov - -import com.fasterxml.jackson.annotation.JsonProperty -import java.io.Serializable -import javax.validation.constraints.NotEmpty -import javax.validation.constraints.NotNull - -// The kdoc comments are written with regards to FormFillout being used in a general context, -// not specifically for the motebehov use case. - -/** - * FormFillout is a data class describing details of some simple form and how it was filled out in a form submission. - * - * FormFillout can be used as a DTO for transmitting a form fillout between frontend and backend, and as a serializable - * storage format. The main use case for the FormFillout format is to support storage and display of form responses for - * forms where at least some of these conditions apply: - * - The form will probably change over time (for example with labels changing, or fields being added or removed). - * - The form responses are mainly submitted in order to be displayed to humans (as opposed to mainly being read - * programmatically). - * - The users viewing the form responses are interested in seeing what the form looked like at the time of submission, - * as opposed to what the form looks like currently, i.e. in case of a label change, or in case of a more drastic - * change. - * - The users viewing the form response might be interested in seeing all the options that was available to choose from - * for a radio buttons field, as opposed to just the selected option. - * - * The contents of a form fillout might be displayed to the "form filling" user themself on a receipt screeen, or to a - * veileder in Modia. - * - * A form fillout is meant to describe what a form looked like at the time of submission, much like a paper copy of a - * filled out form. It describes which fields the form consisted of and their types, labels, etc. A stored FormFillout - * preserves this data, so that this data doesn't have to be stored elsewhere. If a form is changed, a form fillout for - * an earlier version of the form will still be valid and describe the form at the time of submission. - * - * A form fillout consists of a list of *fillout fields*. All fillout fields have an id, a label, and a type. - * The type of a field can be one of the following: - * - TEXT: A field where the user could input text. - * - CHECKBOX: A checkbox field. - * - RADIO_OPTIONS: A radio buttons field where the user could select one of multiple options. - */ -data class FormFillout( - /** An identifier or name identifying which form this is fillout is for. */ - val formIdentifier: String, - /** This version tag can be used to signify which version of a form a form fillout is for, and how much is - * changed between two versions. If a label text is changed, it might be denoted with a patch version bump. If the - * ordering of the fields are changed, or the set of options for a radioGroup field is changed, it might count as a - * minor version bump. If the set of fieldIds for a form is changed, which can happen if new fields are added or - * existing fields are removed, or if an existing fieldId is changed, it might count as a major version bump. */ - val formSemanticVersion: String, - @field:NotEmpty - val filloutFieldsList: List, -) : Serializable { - @get:JsonProperty - val fieldValues: Map - get() = filloutFieldsList.associate { filloutField -> - filloutField.fieldID to when (filloutField) { - is FilloutTextField -> filloutField.textValue - is FilloutCheckboxField -> filloutField.wasChecked - is FilloutRadioGroupField -> filloutField.selectedOptionId - else -> throw IllegalArgumentException("Unknown field type: ${filloutField.fieldType}") - } - } - - companion object { - private const val serialVersionUID: Long = 1L - } -} - -abstract class FilloutField( - @field:NotEmpty - open val fieldID: String, - @field:NotEmpty - open val fieldLabel: String, - open val fieldType: FormFilloutFieldType -) : Serializable { - companion object { - private const val serialVersionUID: Long = 1L - } -} - -data class FilloutTextField( - override val fieldID: String, - override val fieldLabel: String, - @field:NotEmpty - val textValue: String, - val wasOptional: Boolean? = false, -) : FilloutField(fieldID, fieldLabel, FormFilloutFieldType.TEXT) - -data class FilloutCheckboxField( - override val fieldID: String, - override val fieldLabel: String, - @field:NotNull - val wasChecked: Boolean, -) : FilloutField(fieldID, fieldLabel, FormFilloutFieldType.CHECKBOX) - -data class FilloutRadioGroupField( - override val fieldID: String, - override val fieldLabel: String, - @field:NotEmpty - val selectedOptionId: String, - @field:NotEmpty - val selectedOptionLabel: String, - @field:NotEmpty - val options: List, - val wasOptional: Boolean? = false, -) : FilloutField(fieldID, fieldLabel, FormFilloutFieldType.RADIO_OPTIONS) - -data class FormFilloutFieldOption( - @field:NotEmpty - val optionId: String, - @field:NotEmpty - val optionLabel: String, - val wasSelected: Boolean = false, -) - -enum class FormFilloutFieldType(val type: String) { - TEXT("text"), - CHECKBOX("checkbox"), - RADIO_OPTIONS("radioOptions") -} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/Motebehov.kt b/src/main/kotlin/no/nav/syfo/motebehov/Motebehov.kt index ae1ec094..e7e501ed 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/Motebehov.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/Motebehov.kt @@ -1,6 +1,8 @@ package no.nav.syfo.motebehov -import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTO +import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTOv3 +import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTOv4 +import no.nav.syfo.motebehov.database.PMotebehov import no.nav.syfo.motebehov.motebehovstatus.DAYS_END_SVAR_BEHOV import no.nav.syfo.motebehov.motebehovstatus.DAYS_START_SVAR_BEHOV import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType @@ -17,14 +19,14 @@ data class Motebehov( val opprettetAvFnr: String, val arbeidstakerFnr: String, val virksomhetsnummer: String, - val motebehovSvar: MotebehovSvar, + val formSubmission: MotebehovFormSubmissionCombinedDTO, val tildeltEnhet: String? = null, val behandletTidspunkt: LocalDateTime? = null, val behandletVeilederIdent: String? = null, val skjemaType: MotebehovSkjemaType? = null, ) : Serializable -data class MotebehovOutputDTO( +data class MotebehovWithLegacyMotebehovSvarOutputDTO( val id: UUID, val opprettetDato: LocalDateTime, val aktorId: String, @@ -32,18 +34,33 @@ data class MotebehovOutputDTO( val opprettetAvFnr: String, val arbeidstakerFnr: String, val virksomhetsnummer: String, - val motebehovSvar: MotebehovSvarOutputDTO, val tildeltEnhet: String? = null, val behandletTidspunkt: LocalDateTime? = null, val behandletVeilederIdent: String? = null, - val skjemaType: MotebehovSkjemaType? = null, + val skjemaType: MotebehovSkjemaType? = null, // make not nullable + val motebehovSvar: MotebehovSvarLegacyDTO, +) + +data class MotebehovWithFormValuesOutputDTO( + val id: UUID, + val opprettetDato: LocalDateTime, + val aktorId: String, + val opprettetAv: String, + val opprettetAvFnr: String, + val arbeidstakerFnr: String, + val virksomhetsnummer: String, + val tildeltEnhet: String? = null, + val behandletTidspunkt: LocalDateTime? = null, + val behandletVeilederIdent: String? = null, + val skjemaType: MotebehovSkjemaType? = null, // make not nullable + val formValues: MotebehovFormValuesOutputDTO, ) fun List.toMotebehovVeilederDTOList() = this.map { it.toMotebehovVeilederDTO() } -fun Motebehov.toMotebehovVeilederDTO(): MotebehovVeilederDTO { - return MotebehovVeilederDTO( +fun Motebehov.toMotebehovVeilederDTO(): MotebehovVeilederDTOv3 { + return MotebehovVeilederDTOv3( id = this.id, opprettetDato = this.opprettetDato, aktorId = this.aktorId, @@ -51,15 +68,34 @@ fun Motebehov.toMotebehovVeilederDTO(): MotebehovVeilederDTO { opprettetAvNavn = null, arbeidstakerFnr = this.arbeidstakerFnr, virksomhetsnummer = this.virksomhetsnummer, - motebehovSvar = this.motebehovSvar.toMotebehovSvarOutputDTO(), + motebehovSvar = MotebehovSvarLegacyDTO(this.formSubmission.harMotebehov, this.formSubmission.forklaring), tildeltEnhet = this.tildeltEnhet, behandletTidspunkt = this.behandletTidspunkt, - behandletVeilederIdent = this.behandletVeilederIdent, skjemaType = this.skjemaType, + behandletVeilederIdent = this.behandletVeilederIdent, + skjemaType = this.skjemaType, + ) +} + +fun List.toMotebehovVeilederDTOv4List() = + this.map { it.toMotebehovVeilederDTOv4() } + +fun Motebehov.toMotebehovVeilederDTOv4(): MotebehovVeilederDTOv4 { + return MotebehovVeilederDTOv4( + id = this.id, + opprettetDato = this.opprettetDato, + opprettetAv = this.opprettetAv, + opprettetAvNavn = null, + arbeidstakerFnr = this.arbeidstakerFnr, + virksomhetsnummer = this.virksomhetsnummer, + behandletTidspunkt = this.behandletTidspunkt, + behandletVeilederIdent = this.behandletVeilederIdent, + skjemaType = this.skjemaType, + formValues = this.formSubmission.toMotebehovFormValuesOutputDTO(), ) } fun Motebehov.isUbehandlet(): Boolean { - return this.motebehovSvar.harMotebehov && this.behandletVeilederIdent.isNullOrEmpty() + return this.formSubmission.harMotebehov && this.behandletVeilederIdent.isNullOrEmpty() } fun Motebehov.isCreatedInOppfolgingstilfelle(oppfolgingstilfelle: PersonOppfolgingstilfelle): Boolean { @@ -76,8 +112,26 @@ fun Motebehov.isSvarBehovForOppfolgingstilfelle(oppfolgingstilfelle: PersonOppfo createdDate.isBefore(lastDateSvarBehovAvailability.plusDays(1)) } -fun Motebehov.toMotebehovOutputDTO(): MotebehovOutputDTO { - return MotebehovOutputDTO( +fun Motebehov.toPMotebehov(): PMotebehov = + PMotebehov( + uuid = this.id, + opprettetDato = this.opprettetDato, + opprettetAv = this.opprettetAv, + aktoerId = this.aktorId, + virksomhetsnummer = this.virksomhetsnummer, + harMotebehov = this.formSubmission.harMotebehov, + forklaring = this.formSubmission.forklaring, + tildeltEnhet = this.tildeltEnhet, + behandletVeilederIdent = this.behandletVeilederIdent, + behandletTidspunkt = this.behandletTidspunkt, + skjemaType = this.skjemaType, + sykmeldtFnr = this.arbeidstakerFnr, + opprettetAvFnr = this.opprettetAvFnr, + formSnapshot = this.formSubmission.formSnapshot, + ) + +fun Motebehov.toMotebehovWithFormValuesOutputDTO(): MotebehovWithFormValuesOutputDTO = + MotebehovWithFormValuesOutputDTO( id = this.id, opprettetDato = this.opprettetDato, aktorId = this.aktorId, @@ -85,10 +139,25 @@ fun Motebehov.toMotebehovOutputDTO(): MotebehovOutputDTO { opprettetAvFnr = this.opprettetAvFnr, arbeidstakerFnr = this.arbeidstakerFnr, virksomhetsnummer = this.virksomhetsnummer, - motebehovSvar = this.motebehovSvar.toMotebehovSvarOutputDTO(), tildeltEnhet = this.tildeltEnhet, behandletTidspunkt = this.behandletTidspunkt, behandletVeilederIdent = this.behandletVeilederIdent, skjemaType = this.skjemaType, + formValues = this.formSubmission.toMotebehovFormValuesOutputDTO(), + ) + +fun Motebehov.toMotebehovWithLegacyMotebehovSvarOutputDTO(): MotebehovWithLegacyMotebehovSvarOutputDTO = + MotebehovWithLegacyMotebehovSvarOutputDTO( + id = this.id, + opprettetDato = this.opprettetDato, + aktorId = this.aktorId, + opprettetAv = this.opprettetAv, + opprettetAvFnr = this.opprettetAvFnr, + arbeidstakerFnr = this.arbeidstakerFnr, + virksomhetsnummer = this.virksomhetsnummer, + tildeltEnhet = this.tildeltEnhet, + behandletTidspunkt = this.behandletTidspunkt, + behandletVeilederIdent = this.behandletVeilederIdent, + skjemaType = this.skjemaType, + motebehovSvar = this.formSubmission.toMotebehovSvarLegacyDTO() ) -} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/MotebehovFormValuesDTOs.kt b/src/main/kotlin/no/nav/syfo/motebehov/MotebehovFormValuesDTOs.kt new file mode 100644 index 00000000..bf6ae643 --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/MotebehovFormValuesDTOs.kt @@ -0,0 +1,89 @@ +package no.nav.syfo.motebehov + +import no.nav.syfo.motebehov.formSnapshot.BEGRUNNELSE_TEXT_FIELD_ID +import no.nav.syfo.motebehov.formSnapshot.FormSnapshot +import no.nav.syfo.motebehov.formSnapshot.ONSKER_SYKMELDER_DELTAR_BEGRUNNELSE_TEXT_FIELD_ID +import no.nav.syfo.motebehov.formSnapshot.ONSKER_SYKMELDER_DELTAR_CHECKBOX_FIELD_ID +import no.nav.syfo.motebehov.formSnapshot.ONSKER_TOLK_CHECKBOX_FIELD_ID +import no.nav.syfo.motebehov.formSnapshot.TOLK_SPRAK_TEXT_FIELD_ID + +// Temporary class used in services to contain values of both legacy and new form submission DTOs below. +data class MotebehovFormSubmissionCombinedDTO( + val harMotebehov: Boolean, + val forklaring: String? = null, + val formSnapshot: FormSnapshot? +) + +// Existing input and output DTO to phase out. +data class MotebehovSvarLegacyDTO( + val harMotebehov: Boolean, + val forklaring: String? = null, +) + +data class MotebehovFormSubmissionDTO( + val harMotebehov: Boolean, + val formSnapshot: FormSnapshot, +) + +data class MotebehovFormValuesOutputDTO( + val harMotebehov: Boolean, + val formSnapshot: FormSnapshot?, + val begrunnelse: String? = null, + val onskerSykmelderDeltar: Boolean? = null, + val onskerSykmelderDeltarBegrunnelse: String? = null, + val onskerTolk: Boolean? = null, + val tolkSprak: String? = null, +) + +data class MotebehovFormValuesExtractedFromFormSnapshot( + val formIdentifier: String, + val formSemanticVersion: String, + val begrunnelse: String? = null, + val onskerSykmelderDeltar: Boolean, + val onskerSykmelderDeltarBegrunnelse: String? = null, + val onskerTolk: Boolean, + val tolkSprak: String? = null, +) + +fun extractFormValuesFromFormSnapshot(formSnapshot: FormSnapshot): MotebehovFormValuesExtractedFromFormSnapshot { + val fieldValues = formSnapshot.fieldValues + + return MotebehovFormValuesExtractedFromFormSnapshot( + formIdentifier = formSnapshot.formIdentifier, + formSemanticVersion = formSnapshot.formSemanticVersion, + begrunnelse = fieldValues[BEGRUNNELSE_TEXT_FIELD_ID] as? String, + onskerSykmelderDeltar = fieldValues[ONSKER_SYKMELDER_DELTAR_CHECKBOX_FIELD_ID] as? Boolean ?: false, + onskerSykmelderDeltarBegrunnelse = fieldValues[ONSKER_SYKMELDER_DELTAR_BEGRUNNELSE_TEXT_FIELD_ID] as? String, + onskerTolk = fieldValues[ONSKER_TOLK_CHECKBOX_FIELD_ID] as? Boolean ?: false, + tolkSprak = fieldValues[TOLK_SPRAK_TEXT_FIELD_ID] as? String, + ) +} + +fun MotebehovFormSubmissionDTO.toMotebehovFormSubmissionCombinedDTO(): MotebehovFormSubmissionCombinedDTO { + return MotebehovFormSubmissionCombinedDTO( + harMotebehov = this.harMotebehov, + forklaring = null, + formSnapshot = this.formSnapshot, + ) +} + +fun MotebehovFormSubmissionCombinedDTO.toMotebehovFormValuesOutputDTO(): MotebehovFormValuesOutputDTO { + val valuesFromFormSnapshot = this.formSnapshot?.let { extractFormValuesFromFormSnapshot(it) } + + return MotebehovFormValuesOutputDTO( + harMotebehov = this.harMotebehov, + formSnapshot = this.formSnapshot, + begrunnelse = valuesFromFormSnapshot?.begrunnelse, + onskerSykmelderDeltar = valuesFromFormSnapshot?.onskerSykmelderDeltar, + onskerSykmelderDeltarBegrunnelse = valuesFromFormSnapshot?.onskerSykmelderDeltarBegrunnelse, + onskerTolk = valuesFromFormSnapshot?.onskerTolk, + tolkSprak = valuesFromFormSnapshot?.tolkSprak, + ) +} + +fun MotebehovFormSubmissionCombinedDTO.toMotebehovSvarLegacyDTO(): MotebehovSvarLegacyDTO { + return MotebehovSvarLegacyDTO( + harMotebehov = this.harMotebehov, + forklaring = this.forklaring, + ) +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/MotebehovOppfolgingstilfelleServiceV2.kt b/src/main/kotlin/no/nav/syfo/motebehov/MotebehovOppfolgingstilfelleServiceV2.kt index 47f78e74..65cac3ee 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/MotebehovOppfolgingstilfelleServiceV2.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/MotebehovOppfolgingstilfelleServiceV2.kt @@ -45,7 +45,7 @@ class MotebehovOppfolgingstilfelleServiceV2 @Inject constructor( ) if (activeOppfolgingstilfelleExists && motebehovStatus.isMotebehovAvailableForAnswer()) { - val storedMotebehovSvar = storeNyttMotebehovForArbeidsgiver( + val storedMotebehovFormSubmission = storeNyttMotebehovForArbeidsgiver( arbeidstakerFnr, nyttMotebehov, innloggetFnr, @@ -55,7 +55,7 @@ class MotebehovOppfolgingstilfelleServiceV2 @Inject constructor( metric.tellBesvarMotebehov( activeOppfolgingstilfelle!!, motebehovStatus.skjemaType, - storedMotebehovSvar, + storedMotebehovFormSubmission, false, ) @@ -90,22 +90,18 @@ class MotebehovOppfolgingstilfelleServiceV2 @Inject constructor( nyttMotebehov: NyttMotebehovArbeidsgiverDTO, innloggetFnr: String, skjemaType: MotebehovSkjemaType?, - ): MotebehovSvar { - val motebehovSvarToStore = MotebehovSvar( - harMotebehov = nyttMotebehov.motebehovSvarInputDTO.harMotebehov, - forklaring = nyttMotebehov.motebehovSvarInputDTO.forklaring, - formFillout = nyttMotebehov.motebehovSvarInputDTO.formFillout - ) + ): MotebehovFormSubmissionCombinedDTO { + val motebehovFormSubmission = nyttMotebehov.formSubmission motebehovService.lagreMotebehov( innloggetFnr, arbeidstakerFnr, nyttMotebehov.virksomhetsnummer, - skjemaType!!, - motebehovSvarToStore, + skjemaType, + motebehovFormSubmission, ) - return motebehovSvarToStore + return motebehovFormSubmission } private fun ferdigstillVarselForSvarMotebehovForArbeidsgiver( @@ -127,7 +123,7 @@ class MotebehovOppfolgingstilfelleServiceV2 @Inject constructor( @Transactional fun createMotebehovForArbeidstaker( arbeidstakerFnr: String, - nyttMotebehovSvar: TemporaryCombinedNyttMotebehovSvar + formSubmission: MotebehovFormSubmissionCombinedDTO ) { val activeOppolgingstilfelle = oppfolgingstilfelleService.getActiveOppfolgingstilfelleForArbeidstaker(arbeidstakerFnr) @@ -148,26 +144,20 @@ class MotebehovOppfolgingstilfelleServiceV2 @Inject constructor( emptyList() } - val motebehovSvar = MotebehovSvar( - harMotebehov = nyttMotebehovSvar.harMotebehov, - forklaring = nyttMotebehovSvar.forklaring, - formFillout = nyttMotebehovSvar.formFillout - ) - if (virksomhetsnummerList.isNotEmpty()) { for (virksomhetsnummer in virksomhetsnummerList) { motebehovService.lagreMotebehov( arbeidstakerFnr, arbeidstakerFnr, virksomhetsnummer, - motebehovStatus.skjemaType!!, - motebehovSvar, + motebehovStatus.skjemaType, + formSubmission, ) } metric.tellBesvarMotebehov( activeOppolgingstilfelle, motebehovStatus.skjemaType, - motebehovSvar, + formSubmission, true, ) if (motebehovStatus.skjemaType == MotebehovSkjemaType.SVAR_BEHOV) { diff --git a/src/main/kotlin/no/nav/syfo/motebehov/MotebehovService.kt b/src/main/kotlin/no/nav/syfo/motebehov/MotebehovService.kt index a38e4482..2529ec53 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/MotebehovService.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/MotebehovService.kt @@ -5,7 +5,8 @@ import no.nav.syfo.consumer.behandlendeenhet.BehandlendeEnhetConsumer import no.nav.syfo.consumer.pdl.PdlConsumer import no.nav.syfo.metric.Metric import no.nav.syfo.motebehov.database.MotebehovDAO -import no.nav.syfo.motebehov.database.PMotebehov +import no.nav.syfo.motebehov.database.toMotebehov +import no.nav.syfo.motebehov.formSnapshot.MotebehovInnmelderType import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType import no.nav.syfo.personoppgavehendelse.PersonoppgavehendelseService import org.slf4j.LoggerFactory @@ -23,7 +24,6 @@ class MotebehovService @Inject constructor( private val behandlendeEnhetConsumer: BehandlendeEnhetConsumer, private val personoppgavehendelseService: PersonoppgavehendelseService, private val motebehovDAO: MotebehovDAO, - private val convertLegacyMotebehovSvarFieldsHelper: ConvertLegacyMotebehovSvarFieldsHelper, private val pdlConsumer: PdlConsumer, ) { @Transactional @@ -69,7 +69,7 @@ class MotebehovService @Inject constructor( val arbeidstakerAktoerId = pdlConsumer.aktorid(arbeidstakerFnr) return motebehovDAO.hentMotebehovListeForAktoer(arbeidstakerAktoerId) .stream() - .map { dbMotebehov: PMotebehov -> mapPMotebehovToMotebehov(arbeidstakerFnr, dbMotebehov, null) } + .map { it.toMotebehov(arbeidstakerFnr, null) } .collect(Collectors.toList()) } @@ -77,7 +77,7 @@ class MotebehovService @Inject constructor( return motebehovDAO .hentMotebehov(motebehovId) .stream() - .map { dbMotebehov: PMotebehov -> mapPMotebehovToMotebehov(dbMotebehov) } + .map { it.toMotebehov() } .collect(Collectors.toList()) .firstOrNull() } @@ -86,13 +86,7 @@ class MotebehovService @Inject constructor( val arbeidstakerAktoerId = pdlConsumer.aktorid(arbeidstakerFnr) return motebehovDAO.hentMotebehovListeForOgOpprettetAvArbeidstaker(arbeidstakerAktoerId) .stream() - .map { dbMotebehov: PMotebehov -> - mapPMotebehovToMotebehov( - arbeidstakerFnr, - dbMotebehov, - MotebehovInnmelderType.ARBEIDSTAKER - ) - } + .map { it.toMotebehov(arbeidstakerFnr, MotebehovInnmelderType.ARBEIDSTAKER) } .collect(Collectors.toList()) } @@ -108,13 +102,7 @@ class MotebehovService @Inject constructor( virksomhetsnummer, ) .stream() - .map { dbMotebehov: PMotebehov -> - mapPMotebehovToMotebehov( - arbeidstakerFnr, - dbMotebehov, - MotebehovInnmelderType.ARBEIDSGIVER - ) - } + .map { it.toMotebehov(arbeidstakerFnr, MotebehovInnmelderType.ARBEIDSGIVER) } .collect(Collectors.toList()) } @@ -123,121 +111,34 @@ class MotebehovService @Inject constructor( innloggetFNR: String, arbeidstakerFnr: String, virksomhetsnummer: String, - skjemaType: MotebehovSkjemaType, - motebehovSvar: MotebehovSvar, + skjemaType: MotebehovSkjemaType?, + motebehovFormSubmission: MotebehovFormSubmissionCombinedDTO, ): UUID { val innloggetBrukerAktoerId = pdlConsumer.aktorid(innloggetFNR) val arbeidstakerAktoerId = pdlConsumer.aktorid(arbeidstakerFnr) val arbeidstakerBehandlendeEnhet = behandlendeEnhetConsumer.getBehandlendeEnhet(arbeidstakerFnr, null).enhetId - val motebehov = mapNyttMotebehovToPMotebehov( - innloggetBrukerAktoerId, - arbeidstakerAktoerId, - innloggetFNR, - arbeidstakerFnr, - arbeidstakerBehandlendeEnhet, - virksomhetsnummer, - skjemaType, - motebehovSvar, - ) - val uuid = motebehovDAO.create(motebehov) - if (motebehovSvar.harMotebehov) { - personoppgavehendelseService.sendPersonoppgaveHendelseMottatt(uuid, arbeidstakerFnr) - } - return uuid - } - private fun mapNyttMotebehovToPMotebehov( - innloggetAktoerId: String, - arbeidstakerAktoerId: String, - innloggetFnr: String, - arbeidstakerFnr: String, - tildeltEnhet: String, - virksomhetsnummer: String, - skjemaType: MotebehovSkjemaType, - motebehovSvar: MotebehovSvar, - ): PMotebehov { - return PMotebehov( - uuid = UUID.randomUUID(), + val motebehov = Motebehov( + id = UUID.randomUUID(), opprettetDato = LocalDateTime.now(), - opprettetAv = innloggetAktoerId, - aktoerId = arbeidstakerAktoerId, + aktorId = arbeidstakerAktoerId, + opprettetAv = innloggetBrukerAktoerId, + opprettetAvFnr = innloggetFNR, + arbeidstakerFnr = arbeidstakerFnr, virksomhetsnummer = virksomhetsnummer, - harMotebehov = motebehovSvar.harMotebehov, - forklaring = motebehovSvar.forklaring, - tildeltEnhet = tildeltEnhet, - behandletVeilederIdent = null, - behandletTidspunkt = null, + formSubmission = motebehovFormSubmission, + tildeltEnhet = arbeidstakerBehandlendeEnhet, skjemaType = skjemaType, - sykmeldtFnr = arbeidstakerFnr, - opprettetAvFnr = innloggetFnr, ) - } - private fun mapPMotebehovToMotebehov( - arbeidstakerFnr: String, - pMotebehov: PMotebehov, - knownMotebehovInnmelderType: MotebehovInnmelderType? - ): Motebehov { - return Motebehov( - id = pMotebehov.uuid, - opprettetDato = pMotebehov.opprettetDato, - aktorId = pMotebehov.aktoerId, - opprettetAv = pMotebehov.opprettetAv, - opprettetAvFnr = pMotebehov.opprettetAvFnr!!, - arbeidstakerFnr = arbeidstakerFnr, - virksomhetsnummer = pMotebehov.virksomhetsnummer, - motebehovSvar = createMotebehovSvarFromPMotebehov(pMotebehov, knownMotebehovInnmelderType), - tildeltEnhet = pMotebehov.tildeltEnhet, - behandletTidspunkt = pMotebehov.behandletTidspunkt, - behandletVeilederIdent = pMotebehov.behandletVeilederIdent, - skjemaType = pMotebehov.skjemaType, - ) - } + val pMotebehov = motebehov.toPMotebehov() - private fun mapPMotebehovToMotebehov(pMotebehov: PMotebehov): Motebehov { - return Motebehov( - id = pMotebehov.uuid, - opprettetDato = pMotebehov.opprettetDato, - aktorId = pMotebehov.aktoerId, - opprettetAv = pMotebehov.opprettetAv, - opprettetAvFnr = pMotebehov.opprettetAvFnr!!, - arbeidstakerFnr = pMotebehov.sykmeldtFnr!!, - virksomhetsnummer = pMotebehov.virksomhetsnummer, - motebehovSvar = createMotebehovSvarFromPMotebehov(pMotebehov, null), - tildeltEnhet = pMotebehov.tildeltEnhet, - behandletTidspunkt = pMotebehov.behandletTidspunkt, - behandletVeilederIdent = pMotebehov.behandletVeilederIdent, - skjemaType = pMotebehov.skjemaType, - ) - } - - /** - * Legacy db entities will not have formFillout. In that case we create it from harMotebehov and forklaring. - */ - private fun createMotebehovSvarFromPMotebehov( - pMotebehov: PMotebehov, - knownInnmelderType: MotebehovInnmelderType? - ): MotebehovSvar { - val motebehovInnmelderType = knownInnmelderType - ?: if (pMotebehov.opprettetAv == pMotebehov.aktoerId || - pMotebehov.opprettetAvFnr == pMotebehov.sykmeldtFnr - ) { - MotebehovInnmelderType.ARBEIDSTAKER - } else { - MotebehovInnmelderType.ARBEIDSGIVER - } - - return MotebehovSvar( - harMotebehov = pMotebehov.harMotebehov, - forklaring = pMotebehov.forklaring, - formFillout = convertLegacyMotebehovSvarFieldsHelper.convertLegacyMotebehovSvarToFormFillout( - pMotebehov.harMotebehov, - pMotebehov.forklaring, - pMotebehov.skjemaType, - motebehovInnmelderType, - ) - ) + val uuid = motebehovDAO.create(pMotebehov) + if (motebehovFormSubmission.harMotebehov) { + personoppgavehendelseService.sendPersonoppgaveHendelseMottatt(uuid, arbeidstakerFnr) + } + return uuid } companion object { diff --git a/src/main/kotlin/no/nav/syfo/motebehov/MotebehovSvar.kt b/src/main/kotlin/no/nav/syfo/motebehov/MotebehovSvar.kt deleted file mode 100644 index b22500f9..00000000 --- a/src/main/kotlin/no/nav/syfo/motebehov/MotebehovSvar.kt +++ /dev/null @@ -1,55 +0,0 @@ -package no.nav.syfo.motebehov - -import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType - -data class MotebehovSvar( - val harMotebehov: Boolean, - // This forklaring field is to be phased in favor of formFillout, and eventually removed. Details in plan. - val forklaring: String? = null, - val formFillout: FormFillout? -) - -// Existing input DTO to phase out. MotebehovSvarFormFilloutInputDTO will take over. -data class MotebehovSvarInputDTO( - val harMotebehov: Boolean, - val forklaring: String? = null, -) - -data class MotebehovSvarFormFilloutInputDTO( - val harMotebehov: Boolean, - val formFillout: FormFillout, - // New fields to get on input and store, instead of having to calculate them in a probably unstable way. - val skjemaType: MotebehovSkjemaType, - val innmelderType: MotebehovInnmelderType, -) - -data class TemporaryCombinedNyttMotebehovSvar( - val harMotebehov: Boolean, - val forklaring: String? = null, - val formFillout: FormFillout?, -) - -data class MotebehovSvarOutputDTO( - val harMotebehov: Boolean, - val forklaring: String? = null, - val formFillout: FormFillout?, - val begrunnelse: String? = null, - val onskerSykmelderDeltar: Boolean? = null, - val onskerSykmelderDeltarBegrunnelse: String? = null, - val onskerTolk: Boolean? = null, - val tolkSprak: String? = null, -) - -fun MotebehovSvar.toMotebehovSvarOutputDTO(): MotebehovSvarOutputDTO { - return MotebehovSvarOutputDTO( - harMotebehov = this.harMotebehov, - forklaring = this.forklaring, - formFillout = this.formFillout, - begrunnelse = this.formFillout?.fieldValues?.get("begrunnelseText") as? String, - onskerSykmelderDeltar = this.formFillout?.fieldValues?.get("onskerSykmelderDeltarCheckbox") as? Boolean, - onskerSykmelderDeltarBegrunnelse = - this.formFillout?.fieldValues?.get("onskerSykmelderDeltarBegrunnelseText") as? String, - onskerTolk = this.formFillout?.fieldValues?.get("onskerTolkCheckbox") as? Boolean, - tolkSprak = this.formFillout?.fieldValues?.get("tolkSprakText") as? String, - ) -} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/NyttMotebehov.kt b/src/main/kotlin/no/nav/syfo/motebehov/NyttMotebehov.kt index 146dffc7..4cbbbea9 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/NyttMotebehov.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/NyttMotebehov.kt @@ -6,6 +6,6 @@ import javax.validation.constraints.NotEmpty data class NyttMotebehov( val arbeidstakerFnr: String? = null, val virksomhetsnummer: @NotEmpty String, - val motebehovSvar: MotebehovSvarInputDTO, + val motebehovSvar: MotebehovSvarLegacyDTO, val tildeltEnhet: String? = null ) : Serializable diff --git a/src/main/kotlin/no/nav/syfo/motebehov/NyttMotebehovArbeidsgiverDTO.kt b/src/main/kotlin/no/nav/syfo/motebehov/NyttMotebehovArbeidsgiverDTO.kt index 949583c3..590deb78 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/NyttMotebehovArbeidsgiverDTO.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/NyttMotebehovArbeidsgiverDTO.kt @@ -2,23 +2,23 @@ package no.nav.syfo.motebehov import javax.validation.constraints.NotEmpty -data class NyttMotebehovArbeidsgiverInputDTO( +data class NyttMotebehovArbeidsgiverLegacyDTO( val arbeidstakerFnr: String, val virksomhetsnummer: @NotEmpty String, - val motebehovSvar: MotebehovSvarInputDTO, + val motebehovSvar: MotebehovSvarLegacyDTO, val tildeltEnhet: String? = null ) -data class NyttMotebehovArbeidsgiverFormFilloutInputDTO( +data class NyttMotebehovArbeidsgiverFormSubmissionDTO( val arbeidstakerFnr: String, val virksomhetsnummer: @NotEmpty String, - val motebehovSvarInputDTO: MotebehovSvarFormFilloutInputDTO, + val formSubmission: MotebehovFormSubmissionDTO, val tildeltEnhet: String? = null ) data class NyttMotebehovArbeidsgiverDTO( val arbeidstakerFnr: String, val virksomhetsnummer: @NotEmpty String, - val motebehovSvarInputDTO: TemporaryCombinedNyttMotebehovSvar, + val formSubmission: MotebehovFormSubmissionCombinedDTO, val tildeltEnhet: String? = null ) diff --git a/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV3.kt b/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV3.kt index bc195b11..f2101392 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV3.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV3.kt @@ -7,13 +7,13 @@ import no.nav.syfo.api.auth.tokenX.TokenXUtil.TokenXIssuer import no.nav.syfo.api.auth.tokenX.TokenXUtil.fnrFromIdportenTokenX import no.nav.syfo.consumer.brukertilgang.BrukertilgangService import no.nav.syfo.metric.Metric +import no.nav.syfo.motebehov.MotebehovFormSubmissionCombinedDTO import no.nav.syfo.motebehov.MotebehovOppfolgingstilfelleServiceV2 import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverDTO -import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverFormFilloutInputDTO -import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverInputDTO -import no.nav.syfo.motebehov.TemporaryCombinedNyttMotebehovSvar -import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatus +import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverLegacyDTO import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusServiceV2 +import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusWithLegacyMotebehovDTO +import no.nav.syfo.motebehov.motebehovstatus.toMotebehovStatusWithLegacyMotebehovDTO import org.springframework.beans.factory.annotation.Value import org.springframework.http.MediaType import org.springframework.web.bind.annotation.* @@ -44,7 +44,7 @@ class MotebehovArbeidsgiverControllerV3 @Inject constructor( fun motebehovStatusArbeidsgiver( @RequestParam(name = "fnr") arbeidstakerFnr: @Pattern(regexp = "^[0-9]{11}$") String, @RequestParam(name = "virksomhetsnummer") virksomhetsnummer: String, - ): MotebehovStatus { + ): MotebehovStatusWithLegacyMotebehovDTO { metric.tellEndepunktKall("call_endpoint_motebehovstatus_arbeidsgiver") TokenXUtil.validateTokenXClaims(contextHolder, dialogmoteClientId) brukertilgangService.kastExceptionHvisIkkeTilgangTilAnsatt(arbeidstakerFnr) @@ -53,6 +53,7 @@ class MotebehovArbeidsgiverControllerV3 @Inject constructor( val isOwnLeader = arbeidsgiverFnr == arbeidstakerFnr return motebehovStatusServiceV2.motebehovStatusForArbeidsgiver(arbeidstakerFnr, isOwnLeader, virksomhetsnummer) + .toMotebehovStatusWithLegacyMotebehovDTO() } // Currently used POST-endpoint to phase out @@ -62,7 +63,7 @@ class MotebehovArbeidsgiverControllerV3 @Inject constructor( produces = [MediaType.APPLICATION_JSON_VALUE], ) fun lagreMotebehovArbeidsgiver( - @RequestBody nyttMotebehovDTO: @Valid NyttMotebehovArbeidsgiverInputDTO, + @RequestBody nyttMotebehovDTO: @Valid NyttMotebehovArbeidsgiverLegacyDTO, ) { metric.tellEndepunktKall("call_endpoint_save_motebehov_arbeidsgiver") val innloggetFnr = TokenXUtil.validateTokenXClaims(contextHolder, dialogmoteClientId) @@ -76,45 +77,10 @@ class MotebehovArbeidsgiverControllerV3 @Inject constructor( val nyttMotebehovArbeidsgiverDTO = NyttMotebehovArbeidsgiverDTO( arbeidstakerFnr = nyttMotebehovDTO.arbeidstakerFnr, virksomhetsnummer = nyttMotebehovDTO.virksomhetsnummer, - motebehovSvarInputDTO = TemporaryCombinedNyttMotebehovSvar( + formSubmission = MotebehovFormSubmissionCombinedDTO( harMotebehov = nyttMotebehovDTO.motebehovSvar.harMotebehov, forklaring = nyttMotebehovDTO.motebehovSvar.forklaring, - formFillout = null, - ), - ) - - motebehovOppfolgingstilfelleServiceV2.createMotebehovForArbeidgiver( - innloggetFnr, - ansattFnr, - isOwnLeader, - nyttMotebehovArbeidsgiverDTO, - ) - } - - @PostMapping( - value = ["/motebehov-form-fillout"], - consumes = [MediaType.APPLICATION_JSON_VALUE], - produces = [MediaType.APPLICATION_JSON_VALUE], - ) - fun lagreMotebehovArbeidsgiver( - @RequestBody nyttMotebehovDTO: @Valid NyttMotebehovArbeidsgiverFormFilloutInputDTO, - ) { - metric.tellEndepunktKall("call_endpoint_save_motebehov_arbeidsgiver") - val innloggetFnr = TokenXUtil.validateTokenXClaims(contextHolder, dialogmoteClientId) - .fnrFromIdportenTokenX() - val ansattFnr = nyttMotebehovDTO.arbeidstakerFnr - brukertilgangService.kastExceptionHvisIkkeTilgangTilAnsatt(ansattFnr) - - val arbeidsgiverFnr = fnrFromIdportenTokenX(contextHolder) - val isOwnLeader = arbeidsgiverFnr == ansattFnr - - val nyttMotebehovArbeidsgiverDTO = NyttMotebehovArbeidsgiverDTO( - arbeidstakerFnr = nyttMotebehovDTO.arbeidstakerFnr, - virksomhetsnummer = nyttMotebehovDTO.virksomhetsnummer, - motebehovSvarInputDTO = TemporaryCombinedNyttMotebehovSvar( - harMotebehov = nyttMotebehovDTO.motebehovSvarInputDTO.harMotebehov, - forklaring = null, - formFillout = nyttMotebehovDTO.motebehovSvarInputDTO.formFillout, + formSnapshot = null, ), ) diff --git a/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV4.kt b/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV4.kt new file mode 100644 index 00000000..2fb0e387 --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV4.kt @@ -0,0 +1,99 @@ +package no.nav.syfo.motebehov.api + +import no.nav.security.token.support.core.api.ProtectedWithClaims +import no.nav.security.token.support.core.context.TokenValidationContextHolder +import no.nav.syfo.api.auth.tokenX.TokenXUtil +import no.nav.syfo.api.auth.tokenX.TokenXUtil.TokenXIssuer +import no.nav.syfo.api.auth.tokenX.TokenXUtil.fnrFromIdportenTokenX +import no.nav.syfo.consumer.brukertilgang.BrukertilgangService +import no.nav.syfo.metric.Metric +import no.nav.syfo.motebehov.MotebehovOppfolgingstilfelleServiceV2 +import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverDTO +import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverFormSubmissionDTO +import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusServiceV2 +import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusWithFormValuesDTO +import no.nav.syfo.motebehov.motebehovstatus.toMotebehovStatusWithFormValuesDTO +import no.nav.syfo.motebehov.toMotebehovFormSubmissionCombinedDTO +import org.springframework.beans.factory.annotation.Value +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import javax.inject.Inject +import javax.validation.Valid +import javax.validation.constraints.Pattern + +@RestController +@ProtectedWithClaims( + issuer = TokenXIssuer.TOKENX, + claimMap = ["acr=Level4", "acr=idporten-loa-high"], + combineWithOr = true +) +@RequestMapping(value = ["/api/v4"]) +class MotebehovArbeidsgiverControllerV4 @Inject constructor( + private val contextHolder: TokenValidationContextHolder, + private val metric: Metric, + private val motebehovOppfolgingstilfelleServiceV2: MotebehovOppfolgingstilfelleServiceV2, + private val motebehovStatusServiceV2: MotebehovStatusServiceV2, + private val brukertilgangService: BrukertilgangService, + @Value("\${dialogmote.frontend.client.id}") + val dialogmoteClientId: String, +) { + @GetMapping( + value = ["/motebehov"], + produces = [MediaType.APPLICATION_JSON_VALUE], + ) + fun motebehovStatusArbeidsgiver( + @RequestParam(name = "fnr") arbeidstakerFnr: + @Pattern(regexp = "^[0-9]{11}$") + String, + @RequestParam(name = "virksomhetsnummer") virksomhetsnummer: String, + ): MotebehovStatusWithFormValuesDTO { + metric.tellEndepunktKall("call_endpoint_motebehovstatus_arbeidsgiver") + TokenXUtil.validateTokenXClaims(contextHolder, dialogmoteClientId) + brukertilgangService.kastExceptionHvisIkkeTilgangTilAnsatt(arbeidstakerFnr) + + val arbeidsgiverFnr = fnrFromIdportenTokenX(contextHolder) + val isOwnLeader = arbeidsgiverFnr == arbeidstakerFnr + + return motebehovStatusServiceV2.motebehovStatusForArbeidsgiver( + arbeidstakerFnr, + isOwnLeader, + virksomhetsnummer + ).toMotebehovStatusWithFormValuesDTO() + } + + @PostMapping( + value = ["/motebehov"], + consumes = [MediaType.APPLICATION_JSON_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE], + ) + fun lagreMotebehovArbeidsgiver( + @RequestBody nyttMotebehovDTO: @Valid NyttMotebehovArbeidsgiverFormSubmissionDTO, + ) { + metric.tellEndepunktKall("call_endpoint_save_motebehov_arbeidsgiver") + val innloggetFnr = TokenXUtil.validateTokenXClaims(contextHolder, dialogmoteClientId) + .fnrFromIdportenTokenX() + val ansattFnr = nyttMotebehovDTO.arbeidstakerFnr + brukertilgangService.kastExceptionHvisIkkeTilgangTilAnsatt(ansattFnr) + + val arbeidsgiverFnr = fnrFromIdportenTokenX(contextHolder) + val isOwnLeader = arbeidsgiverFnr == ansattFnr + + val nyttMotebehovArbeidsgiverDTO = NyttMotebehovArbeidsgiverDTO( + arbeidstakerFnr = nyttMotebehovDTO.arbeidstakerFnr, + virksomhetsnummer = nyttMotebehovDTO.virksomhetsnummer, + formSubmission = nyttMotebehovDTO.formSubmission.toMotebehovFormSubmissionCombinedDTO(), + ) + + motebehovOppfolgingstilfelleServiceV2.createMotebehovForArbeidgiver( + innloggetFnr, + ansattFnr, + isOwnLeader, + nyttMotebehovArbeidsgiverDTO, + ) + } +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV3.kt b/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV3.kt index 7c66bc0e..e54a2348 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV3.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV3.kt @@ -6,12 +6,13 @@ import no.nav.syfo.api.auth.tokenX.TokenXUtil import no.nav.syfo.api.auth.tokenX.TokenXUtil.TokenXIssuer import no.nav.syfo.api.auth.tokenX.TokenXUtil.fnrFromIdportenTokenX import no.nav.syfo.metric.Metric +import no.nav.syfo.motebehov.MotebehovFormSubmissionCombinedDTO import no.nav.syfo.motebehov.MotebehovOppfolgingstilfelleServiceV2 -import no.nav.syfo.motebehov.MotebehovSvarFormFilloutInputDTO -import no.nav.syfo.motebehov.MotebehovSvarInputDTO -import no.nav.syfo.motebehov.TemporaryCombinedNyttMotebehovSvar +import no.nav.syfo.motebehov.MotebehovSvarLegacyDTO import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatus import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusServiceV2 +import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusWithLegacyMotebehovDTO +import no.nav.syfo.motebehov.motebehovstatus.toMotebehovStatusWithLegacyMotebehovDTO import org.springframework.beans.factory.annotation.Value import org.springframework.http.MediaType import org.springframework.web.bind.annotation.* @@ -62,7 +63,7 @@ class MotebehovArbeidstakerControllerV3 @Inject constructor( value = ["/motebehov/all"], produces = [MediaType.APPLICATION_JSON_VALUE], ) - fun motebehovStatusArbeidstakerWithCodeSixUsers(): MotebehovStatus { + fun motebehovStatusArbeidstakerWithCodeSixUsers(): MotebehovStatusWithLegacyMotebehovDTO { val arbeidstakerFnr = TokenXUtil.validateTokenXClaims( contextHolder, dialogmoteClientId, @@ -73,6 +74,7 @@ class MotebehovArbeidstakerControllerV3 @Inject constructor( metric.tellEndepunktKall("call_endpoint_motebehovstatus_arbeidstaker_all") return motebehovStatusServiceV2.motebehovStatusForArbeidstaker(arbeidstakerFnr) + .toMotebehovStatusWithLegacyMotebehovDTO() } // Currently used POST-endpoint to phase out @@ -82,7 +84,7 @@ class MotebehovArbeidstakerControllerV3 @Inject constructor( produces = [MediaType.APPLICATION_JSON_VALUE], ) fun submitMotebehovArbeidstaker( - @RequestBody nyttMotebehovSvar: @Valid MotebehovSvarInputDTO, + @RequestBody nyttMotebehovSvar: @Valid MotebehovSvarLegacyDTO, ) { metric.tellEndepunktKall("call_endpoint_save_motebehov_arbeidstaker") val arbeidstakerFnr = TokenXUtil.validateTokenXClaims( @@ -92,43 +94,15 @@ class MotebehovArbeidstakerControllerV3 @Inject constructor( ) .fnrFromIdportenTokenX() - val motebehovSvar = TemporaryCombinedNyttMotebehovSvar( + val formSubmission = MotebehovFormSubmissionCombinedDTO( harMotebehov = nyttMotebehovSvar.harMotebehov, forklaring = nyttMotebehovSvar.forklaring, - formFillout = null, + formSnapshot = null, ) motebehovOppfolgingstilfelleServiceV2.createMotebehovForArbeidstaker( arbeidstakerFnr, - motebehovSvar, - ) - } - - @PostMapping( - value = ["/motebehov-form-fillout"], - consumes = [MediaType.APPLICATION_JSON_VALUE], - produces = [MediaType.APPLICATION_JSON_VALUE], - ) - fun submitMotebehovArbeidstaker( - @RequestBody nyttMotebehovSvar: @Valid MotebehovSvarFormFilloutInputDTO, - ) { - metric.tellEndepunktKall("call_endpoint_save_motebehov_arbeidstaker") - val arbeidstakerFnr = TokenXUtil.validateTokenXClaims( - contextHolder, - dialogmoteClientId, - esyfoProxyClientId - ) - .fnrFromIdportenTokenX() - - val motebehovSvar = TemporaryCombinedNyttMotebehovSvar( - harMotebehov = nyttMotebehovSvar.harMotebehov, - forklaring = null, - formFillout = nyttMotebehovSvar.formFillout, - ) - - motebehovOppfolgingstilfelleServiceV2.createMotebehovForArbeidstaker( - arbeidstakerFnr, - motebehovSvar, + formSubmission, ) } diff --git a/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV4.kt b/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV4.kt new file mode 100644 index 00000000..314da54f --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV4.kt @@ -0,0 +1,84 @@ +package no.nav.syfo.motebehov.api + +import no.nav.security.token.support.core.api.ProtectedWithClaims +import no.nav.security.token.support.core.context.TokenValidationContextHolder +import no.nav.syfo.api.auth.tokenX.TokenXUtil +import no.nav.syfo.api.auth.tokenX.TokenXUtil.TokenXIssuer +import no.nav.syfo.api.auth.tokenX.TokenXUtil.fnrFromIdportenTokenX +import no.nav.syfo.metric.Metric +import no.nav.syfo.motebehov.MotebehovFormSubmissionDTO +import no.nav.syfo.motebehov.MotebehovOppfolgingstilfelleServiceV2 +import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusServiceV2 +import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusWithFormValuesDTO +import no.nav.syfo.motebehov.motebehovstatus.toMotebehovStatusWithFormValuesDTO +import no.nav.syfo.motebehov.toMotebehovFormSubmissionCombinedDTO +import org.springframework.beans.factory.annotation.Value +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import javax.inject.Inject +import javax.validation.Valid + +@RestController +@ProtectedWithClaims( + issuer = TokenXIssuer.TOKENX, + claimMap = ["acr=Level4", "acr=idporten-loa-high"], + combineWithOr = true +) +@RequestMapping(value = ["/api/v4/arbeidstaker"]) +class MotebehovArbeidstakerControllerV4 @Inject constructor( + private val contextHolder: TokenValidationContextHolder, + private val metric: Metric, + private val motebehovStatusServiceV2: MotebehovStatusServiceV2, + private val motebehovOppfolgingstilfelleServiceV2: MotebehovOppfolgingstilfelleServiceV2, + @Value("\${dialogmote.frontend.client.id}") + val dialogmoteClientId: String, + @Value("\${ditt.sykefravaer.frontend.client.id}") + val dittSykefravaerClientId: String, + @Value("\${esyfo-proxy.client.id}") + val esyfoProxyClientId: String, +) { + @GetMapping( + value = ["/motebehov/all"], + produces = [MediaType.APPLICATION_JSON_VALUE], + ) + fun motebehovStatusArbeidstakerWithCodeSixUsers(): MotebehovStatusWithFormValuesDTO { + val arbeidstakerFnr = TokenXUtil.validateTokenXClaims( + contextHolder, + dialogmoteClientId, + esyfoProxyClientId + ) + .fnrFromIdportenTokenX() + + metric.tellEndepunktKall("call_endpoint_motebehovstatus_arbeidstaker_all") + + return motebehovStatusServiceV2.motebehovStatusForArbeidstaker( + arbeidstakerFnr + ).toMotebehovStatusWithFormValuesDTO() + } + + @PostMapping( + value = ["/motebehov"], + consumes = [MediaType.APPLICATION_JSON_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE], + ) + fun submitMotebehovArbeidstaker( + @RequestBody nyttMotebehovFormSubmission: @Valid MotebehovFormSubmissionDTO, + ) { + metric.tellEndepunktKall("call_endpoint_save_motebehov_arbeidstaker") + val arbeidstakerFnr = TokenXUtil.validateTokenXClaims( + contextHolder, + dialogmoteClientId, + esyfoProxyClientId + ) + .fnrFromIdportenTokenX() + + motebehovOppfolgingstilfelleServiceV2.createMotebehovForArbeidstaker( + arbeidstakerFnr, + nyttMotebehovFormSubmission.toMotebehovFormSubmissionCombinedDTO(), + ) + } +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/api/internad/dto/MotebehovVeilederDTO.kt b/src/main/kotlin/no/nav/syfo/motebehov/api/internad/dto/MotebehovVeilederDTO.kt index 2d89578d..af9e1410 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/api/internad/dto/MotebehovVeilederDTO.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/api/internad/dto/MotebehovVeilederDTO.kt @@ -1,12 +1,12 @@ package no.nav.syfo.motebehov.api.internad.dto -import no.nav.syfo.motebehov.MotebehovSvarOutputDTO -import no.nav.syfo.motebehov.motebehovstatus.* -import java.io.Serializable +import no.nav.syfo.motebehov.MotebehovFormValuesOutputDTO +import no.nav.syfo.motebehov.MotebehovSvarLegacyDTO +import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType import java.time.LocalDateTime import java.util.* -data class MotebehovVeilederDTO( +data class MotebehovVeilederDTOv3( val id: UUID, val opprettetDato: LocalDateTime, val aktorId: String, @@ -14,9 +14,23 @@ data class MotebehovVeilederDTO( val opprettetAvNavn: String?, val arbeidstakerFnr: String, val virksomhetsnummer: String, - val motebehovSvar: MotebehovSvarOutputDTO, val tildeltEnhet: String? = null, val behandletTidspunkt: LocalDateTime? = null, val behandletVeilederIdent: String? = null, - val skjemaType: MotebehovSkjemaType? = null -) : Serializable + val skjemaType: MotebehovSkjemaType? = null, + val motebehovSvar: MotebehovSvarLegacyDTO, +) + +data class MotebehovVeilederDTOv4( + val id: UUID, + val opprettetDato: LocalDateTime, + val opprettetAv: String, + val opprettetAvNavn: String?, + val arbeidstakerFnr: String, + val virksomhetsnummer: String, + val behandletTidspunkt: LocalDateTime? = null, + val behandletVeilederIdent: String? = null, +// val innmelderType: InnmelderType, // ARBEIDSTAKER | ARBEIDSGIVER, + val skjemaType: MotebehovSkjemaType? = null, // make not nullable + val formValues: MotebehovFormValuesOutputDTO, +) diff --git a/src/main/kotlin/no/nav/syfo/motebehov/api/internad/v3/MotebehovVeilederADControllerV3.kt b/src/main/kotlin/no/nav/syfo/motebehov/api/internad/v3/MotebehovVeilederADControllerV3.kt index 300665bb..42e617a2 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/api/internad/v3/MotebehovVeilederADControllerV3.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/api/internad/v3/MotebehovVeilederADControllerV3.kt @@ -13,7 +13,7 @@ import no.nav.syfo.consumer.veiledertilgang.VeilederTilgangConsumer import no.nav.syfo.metric.Metric import no.nav.syfo.motebehov.MotebehovService import no.nav.syfo.motebehov.MotebehovTilbakemelding -import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTO +import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTOv3 import no.nav.syfo.motebehov.historikk.Historikk import no.nav.syfo.motebehov.historikk.HistorikkService import no.nav.syfo.motebehov.toMotebehovVeilederDTOList @@ -42,7 +42,7 @@ class MotebehovVeilederADControllerV3 @Inject constructor( @GetMapping(value = ["/motebehov"], produces = [MediaType.APPLICATION_JSON_VALUE]) fun hentMotebehovListe( @RequestHeader(name = NAV_PERSONIDENT_HEADER) personident: @Pattern(regexp = "^[0-9]{11}$") String - ): List { + ): List { metric.tellEndepunktKall("veileder_hent_motebehov") kastExceptionHvisIkkeTilgang(personident) diff --git a/src/main/kotlin/no/nav/syfo/motebehov/api/internad/v4/MotebehovVeilederADControllerV4.kt b/src/main/kotlin/no/nav/syfo/motebehov/api/internad/v4/MotebehovVeilederADControllerV4.kt new file mode 100644 index 00000000..42129733 --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/api/internad/v4/MotebehovVeilederADControllerV4.kt @@ -0,0 +1,121 @@ +package no.nav.syfo.motebehov.api.internad.v4 + +import jakarta.ws.rs.BadRequestException +import jakarta.ws.rs.ForbiddenException +import jakarta.ws.rs.NotFoundException +import no.nav.security.token.support.core.api.ProtectedWithClaims +import no.nav.security.token.support.core.context.TokenValidationContextHolder +import no.nav.syfo.api.auth.OIDCIssuer.INTERN_AZUREAD_V2 +import no.nav.syfo.api.auth.getSubjectInternADV2 +import no.nav.syfo.consumer.pdl.PdlConsumer +import no.nav.syfo.consumer.pdl.fullName +import no.nav.syfo.consumer.veiledertilgang.VeilederTilgangConsumer +import no.nav.syfo.metric.Metric +import no.nav.syfo.motebehov.MotebehovService +import no.nav.syfo.motebehov.MotebehovTilbakemelding +import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTOv4 +import no.nav.syfo.motebehov.historikk.Historikk +import no.nav.syfo.motebehov.historikk.HistorikkService +import no.nav.syfo.motebehov.toMotebehovVeilederDTOv4List +import no.nav.syfo.util.NAV_PERSONIDENT_HEADER +import no.nav.syfo.varsel.esyfovarsel.EsyfovarselService +import org.jsoup.Jsoup +import org.jsoup.safety.Safelist +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestHeader +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import javax.inject.Inject +import javax.validation.Valid +import javax.validation.constraints.Pattern + +@RestController +@ProtectedWithClaims(issuer = INTERN_AZUREAD_V2) +@RequestMapping(value = ["/api/internad/v4/veileder"]) +class MotebehovVeilederADControllerV4 @Inject constructor( + private val contextHolder: TokenValidationContextHolder, + private val metric: Metric, + private val historikkService: HistorikkService, + private val motebehovService: MotebehovService, + private val pdlConsumer: PdlConsumer, + private val veilederTilgangConsumer: VeilederTilgangConsumer, + private val esyfovarselService: EsyfovarselService, +) { + @GetMapping(value = ["/motebehov"], produces = [MediaType.APPLICATION_JSON_VALUE]) + fun hentMotebehovListe( + @RequestHeader(name = NAV_PERSONIDENT_HEADER) personident: + @Pattern(regexp = "^[0-9]{11}$") + String + ): List { + metric.tellEndepunktKall("veileder_hent_motebehov") + + kastExceptionHvisIkkeTilgang(personident) + val motebehovVeilederDTOList = motebehovService.hentMotebehovListe(personident) + .toMotebehovVeilederDTOv4List() + .map { motebehovVeilederDTO -> + val opprettetAvAktorId = motebehovVeilederDTO.opprettetAv + motebehovVeilederDTO.copy( + opprettetAvNavn = pdlConsumer.person(opprettetAvAktorId)?.fullName() + ) + } + return motebehovVeilederDTOList + } + + @GetMapping(value = ["/historikk"], produces = [MediaType.APPLICATION_JSON_VALUE]) + fun hentMotebehovHistorikk( + @RequestHeader(name = NAV_PERSONIDENT_HEADER) personident: + @Pattern(regexp = "^[0-9]{11}$") + String + ): List { + metric.tellEndepunktKall("veileder_hent_motebehov_historikk") + kastExceptionHvisIkkeTilgang(personident) + return historikkService.hentHistorikkListe(personident) + } + + @PostMapping( + value = ["/motebehov/tilbakemelding"], + consumes = [MediaType.APPLICATION_JSON_VALUE], + produces = [MediaType.APPLICATION_JSON_VALUE], + ) + fun sendTilbakemelding( + @RequestBody tilbakemelding: @Valid MotebehovTilbakemelding, + ) { + metric.tellEndepunktKall("veileder_motebehov-tilbakemelding_call") + + val motebehov = motebehovService.hentMotebehov(tilbakemelding.motebehovId) + + if (motebehov === null) { + throw NotFoundException() + } + + kastExceptionHvisIkkeTilgang(motebehov.arbeidstakerFnr) + + if (!Jsoup.isValid(tilbakemelding.varseltekst, Safelist.none())) { + throw BadRequestException("Invalid input") + } + + esyfovarselService.sendTilbakemeldingsvarsel(tilbakemelding, motebehov) + metric.tellEndepunktKall("veileder_motebehov-tilbakemelding_call_success") + } + + @PostMapping(value = ["/motebehov/behandle"]) + fun behandleMotebehov( + @RequestHeader(name = NAV_PERSONIDENT_HEADER) personident: + @Pattern(regexp = "^[0-9]{11}$") + String + ) { + metric.tellEndepunktKall("veileder_behandle_motebehov_call") + kastExceptionHvisIkkeTilgang(personident) + motebehovService.behandleUbehandledeMotebehov(personident, getSubjectInternADV2(contextHolder)) + metric.tellEndepunktKall("veileder_behandle_motebehov_success") + } + + private fun kastExceptionHvisIkkeTilgang(fnr: String) { + if (!veilederTilgangConsumer.sjekkVeiledersTilgangTilPersonMedOBO(fnr)) { + throw ForbiddenException("Veilederen har ikke tilgang til denne personen") + } + } +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/database/MotebehovDAO.kt b/src/main/kotlin/no/nav/syfo/motebehov/database/MotebehovDAO.kt index 1ae49c74..473287e1 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/database/MotebehovDAO.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/database/MotebehovDAO.kt @@ -1,5 +1,8 @@ package no.nav.syfo.motebehov.database +import no.nav.syfo.motebehov.extractFormValuesFromFormSnapshot +import no.nav.syfo.motebehov.formSnapshot.convertFormSnapshotToJsonString +import no.nav.syfo.motebehov.formSnapshot.convertJsonStringToFormSnapshot import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType import no.nav.syfo.util.DbUtil.sanitizeUserInput import no.nav.syfo.util.convert @@ -30,8 +33,12 @@ class MotebehovDAO( fun hentMotebehovListeForAktoer(aktoerId: String): List { return Optional.ofNullable( jdbcTemplate.query( - "SELECT * FROM motebehov WHERE aktoer_id = ? ORDER BY opprettet_dato ASC", - innsendingRowMapper, + """ + SELECT m.*, f.* FROM motebehov m + LEFT JOIN motebehov_form_values f ON m.motebehov_uuid = f.motebehov_uuid + WHERE m.aktoer_id = ? ORDER BY m.opprettet_dato ASC + """, + motebehovRowMapper, aktoerId ) ).orElse(emptyList()) @@ -40,10 +47,13 @@ class MotebehovDAO( fun hentMotebehovListeForOgOpprettetAvArbeidstaker(arbeidstakerAktorId: String): List { return Optional.ofNullable( jdbcTemplate.query( - "SELECT * FROM motebehov " + - "WHERE aktoer_id = ? AND opprettet_av = ? AND opprettet_dato >= ? " + - "ORDER BY opprettet_dato DESC", - innsendingRowMapper, + """ + SELECT m.*, f.* FROM motebehov m + LEFT JOIN motebehov_form_values f ON m.motebehov_uuid = f.motebehov_uuid + WHERE m.aktoer_id = ? AND m.opprettet_av = ? AND m.opprettet_dato >= ? + ORDER BY m.opprettet_dato DESC + """, + motebehovRowMapper, arbeidstakerAktorId, arbeidstakerAktorId, hentTidligsteDatoForGyldigMotebehovSvar() @@ -57,38 +67,52 @@ class MotebehovDAO( virksomhetsnummer: String ): List { if (isOwnLeader) { + val query = + """ + SELECT m.*, f.* FROM motebehov m + LEFT JOIN motebehov_form_values f ON m.motebehov_uuid = f.motebehov_uuid + WHERE m.aktoer_id = ? AND m.virksomhetsnummer = ? AND m.opprettet_dato >= ? + ORDER BY m.opprettet_dato DESC + """ return Optional.ofNullable( jdbcTemplate.query( - "SELECT * FROM motebehov " + - "WHERE aktoer_id = ? AND virksomhetsnummer = ? AND opprettet_dato >= ? " + - "ORDER BY opprettet_dato DESC", - innsendingRowMapper, + query, + motebehovRowMapper, + arbeidstakerAktorId, + virksomhetsnummer, + hentTidligsteDatoForGyldigMotebehovSvar() + ) + ).orElse(emptyList()) + } else { + val query = + """ + SELECT m.*, f.* FROM motebehov m + LEFT JOIN motebehov_form_values f ON m.motebehov_uuid = f.motebehov_uuid + WHERE m.aktoer_id = ? AND m.opprettet_av != ? AND m.virksomhetsnummer = ? AND m.opprettet_dato >= ? + ORDER BY m.opprettet_dato DESC + """ + return Optional.ofNullable( + jdbcTemplate.query( + query, + motebehovRowMapper, + arbeidstakerAktorId, arbeidstakerAktorId, virksomhetsnummer, hentTidligsteDatoForGyldigMotebehovSvar() ) ).orElse(emptyList()) } - - return Optional.ofNullable( - jdbcTemplate.query( - "SELECT * FROM motebehov " + - "WHERE aktoer_id = ? AND opprettet_av != ? AND virksomhetsnummer = ? AND opprettet_dato >= ? " + - "ORDER BY opprettet_dato DESC", - innsendingRowMapper, - arbeidstakerAktorId, - arbeidstakerAktorId, - virksomhetsnummer, - hentTidligsteDatoForGyldigMotebehovSvar() - ) - ).orElse(emptyList()) } fun hentUbehandledeMotebehov(aktoerId: String): List { return Optional.ofNullable( jdbcTemplate.query( - "SELECT * FROM motebehov WHERE aktoer_id = ? AND har_motebehov AND behandlet_veileder_ident IS NULL", - innsendingRowMapper, + """ + SELECT m.*, f.* FROM motebehov m + LEFT JOIN motebehov_form_values f ON m.motebehov_uuid = f.motebehov_uuid + WHERE m.aktoer_id = ? AND m.har_motebehov AND m.behandlet_veileder_ident IS NULL + """, + motebehovRowMapper, aktoerId ) ).orElse(emptyList()) @@ -97,9 +121,12 @@ class MotebehovDAO( fun hentUbehandledeMotebehovEldreEnnDato(date: LocalDate): List { return Optional.ofNullable( jdbcTemplate.query( - "SELECT * FROM motebehov " + - "WHERE opprettet_dato < ? AND har_motebehov AND behandlet_veileder_ident IS NULL", - innsendingRowMapper, + """ + SELECT m.*, f.* FROM motebehov m + LEFT JOIN motebehov_form_values f ON m.motebehov_uuid = f.motebehov_uuid + WHERE m.opprettet_dato < ? AND m.har_motebehov AND m.behandlet_veileder_ident IS NULL + """, + motebehovRowMapper, convert(date) ) ).orElse(emptyList()) @@ -108,8 +135,12 @@ class MotebehovDAO( fun hentMotebehov(motebehovId: String): List { return Optional.ofNullable( jdbcTemplate.query( - "SELECT * FROM motebehov WHERE motebehov_uuid = ?", - innsendingRowMapper, + """ + SELECT m.*, f.* FROM motebehov m + LEFT JOIN motebehov_form_values f ON m.motebehov_uuid = f.motebehov_uuid + WHERE m.motebehov_uuid = ? + """, + motebehovRowMapper, motebehovId ) ).orElse(emptyList()) @@ -127,12 +158,16 @@ class MotebehovDAO( fun create(motebehov: PMotebehov): UUID { val uuid = UUID.randomUUID() - val lagreSql = """ - INSERT INTO motebehov (motebehov_uuid, opprettet_dato, opprettet_av, aktoer_id, virksomhetsnummer, har_motebehov, forklaring, tildelt_enhet, behandlet_tidspunkt, behandlet_veileder_ident, skjematype, sm_fnr, opprettet_av_fnr) - VALUES ( - :motebehov_uuid, :opprettet_dato, :opprettet_av, :aktoer_id, :virksomhetsnummer, :har_motebehov, :forklaring, :tildelt_enhet, :behandlet_tidspunkt, :behandlet_veileder_ident, :skjematype, :sm_fnr, :opprettet_av_fnr) + + val lagreMotebehovSql = """ + INSERT INTO motebehov (motebehov_uuid, opprettet_dato, opprettet_av, aktoer_id, virksomhetsnummer, + har_motebehov, forklaring, tildelt_enhet, behandlet_tidspunkt, behandlet_veileder_ident, skjematype, + sm_fnr, opprettet_av_fnr) + VALUES (:motebehov_uuid, :opprettet_dato, :opprettet_av, :aktoer_id, :virksomhetsnummer, + :har_motebehov, :forklaring, :tildelt_enhet, :behandlet_tidspunkt, :behandlet_veileder_ident, :skjematype, + :sm_fnr, :opprettet_av_fnr) """.trimIndent() - val mapLagreSql = MapSqlParameterSource() + val mapLagreMotebehovSql = MapSqlParameterSource() .addValue("motebehov_uuid", uuid.toString()) .addValue("opprettet_av", motebehov.opprettetAv) .addValue("opprettet_dato", convert(LocalDateTime.now())) @@ -146,44 +181,72 @@ class MotebehovDAO( .addValue("skjematype", motebehov.skjemaType?.name) .addValue("sm_fnr", motebehov.sykmeldtFnr) .addValue("opprettet_av_fnr", motebehov.opprettetAvFnr) - namedParameterJdbcTemplate.update(lagreSql, mapLagreSql) + namedParameterJdbcTemplate.update(lagreMotebehovSql, mapLagreMotebehovSql) + + motebehov.formSnapshot?.let { + val formSnapshotJSON = convertFormSnapshotToJsonString(motebehov.formSnapshot) + val formValues = extractFormValuesFromFormSnapshot(motebehov.formSnapshot) + + val lagreMotebehovFormValuesSql = """ + INSERT INTO motebehov_form_values (motebehov_uuid, form_snapshot, form_identifier, form_semantic_version, + begrunnelse, onsker_sykmelder_deltar, onsker_sykmelder_deltar_begrunnelse, + onsker_tolk, tolk_sprak) + VALUES (:motebehov_uuid, :form_snapshot, :form_identifier, :form_semantic_version, + :begrunnelse, :onsker_sykmelder_deltar, :onsker_sykmelder_deltar_begrunnelse, + :onsker_tolk, :tolk_sprak) + """.trimIndent() + val mapLagreMotebehovFormValuesSql = MapSqlParameterSource() + .addValue("motebehov_uuid", uuid.toString()) + .addValue("form_snapshot", formSnapshotJSON, Types.OTHER) + // The columns below store copies of values inside form_snapshot, and are only used for + // debugging and data monitoring purposes. They are not read out again in this application. + .addValue("form_identifier", formValues.formIdentifier) + .addValue("form_semantic_version", formValues.formSemanticVersion) + .addValue("begrunnelse", formValues.begrunnelse) + .addValue("onsker_sykmelder_deltar", formValues.onskerSykmelderDeltar) + .addValue("onsker_sykmelder_deltar_begrunnelse", formValues.onskerSykmelderDeltarBegrunnelse) + .addValue("onsker_tolk", formValues.onskerTolk) + .addValue("tolk_sprak", formValues.tolkSprak) + namedParameterJdbcTemplate.update(lagreMotebehovFormValuesSql, mapLagreMotebehovFormValuesSql) + } return uuid } fun nullstillMotebehov(aktoerId: String): Int { - return if (hentMotebehovListeForAktoer(aktoerId).isNotEmpty()) { - val motebehovIder: List = hentMotebehovListeForAktoer(aktoerId).map { - it.uuid.toString() - } + val motebehovListe = hentMotebehovListeForAktoer(aktoerId) + if (motebehovListe.isNotEmpty()) { + val motebehovIder: List = motebehovListe.map { it.uuid.toString() } + namedParameterJdbcTemplate.update( "DELETE FROM motebehov WHERE motebehov_uuid IN (:motebehovIder)", - MapSqlParameterSource() - .addValue("motebehovIder", motebehovIder), + MapSqlParameterSource().addValue("motebehovIder", motebehovIder) ) + + return motebehovIder.size } else { - 0 + return 0 } } companion object { - private val innsendingRowMapper: RowMapper - get() = RowMapper { rs: ResultSet, _: Int -> - PMotebehov( - uuid = UUID.fromString(rs.getString("motebehov_uuid")), - opprettetDato = rs.getTimestamp("opprettet_dato").toLocalDateTime(), - opprettetAv = rs.getString("opprettet_av"), - aktoerId = rs.getString("aktoer_id"), - virksomhetsnummer = rs.getString("virksomhetsnummer"), - harMotebehov = rs.getBoolean("har_motebehov"), - forklaring = rs.getString("forklaring"), - tildeltEnhet = rs.getString("tildelt_enhet"), - behandletTidspunkt = convertNullable(rs.getTimestamp("behandlet_tidspunkt")), - behandletVeilederIdent = rs.getString("behandlet_veileder_ident"), - skjemaType = rs.getString("skjematype")?.let { MotebehovSkjemaType.valueOf(it) }, - sykmeldtFnr = rs.getString("sm_fnr"), - opprettetAvFnr = rs.getString("opprettet_av_fnr"), - ) - } + val motebehovRowMapper: RowMapper = RowMapper { rs: ResultSet, _: Int -> + PMotebehov( + uuid = UUID.fromString(rs.getString("motebehov_uuid")), + opprettetDato = rs.getTimestamp("opprettet_dato").toLocalDateTime(), + opprettetAv = rs.getString("opprettet_av"), + aktoerId = rs.getString("aktoer_id"), + virksomhetsnummer = rs.getString("virksomhetsnummer"), + harMotebehov = rs.getBoolean("har_motebehov"), + forklaring = rs.getString("forklaring"), + tildeltEnhet = rs.getString("tildelt_enhet"), + behandletTidspunkt = convertNullable(rs.getTimestamp("behandlet_tidspunkt")), + behandletVeilederIdent = rs.getString("behandlet_veileder_ident"), + skjemaType = rs.getString("skjematype")?.let { MotebehovSkjemaType.valueOf(it) }, + sykmeldtFnr = rs.getString("sm_fnr"), + opprettetAvFnr = rs.getString("opprettet_av_fnr"), + formSnapshot = rs.getString("form_snapshot")?.let { convertJsonStringToFormSnapshot(it) }, + ) + } } } diff --git a/src/main/kotlin/no/nav/syfo/motebehov/database/PMotebehov.kt b/src/main/kotlin/no/nav/syfo/motebehov/database/PMotebehov.kt index a4327000..048f0ca6 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/database/PMotebehov.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/database/PMotebehov.kt @@ -1,5 +1,10 @@ package no.nav.syfo.motebehov.database +import no.nav.syfo.motebehov.Motebehov +import no.nav.syfo.motebehov.MotebehovFormSubmissionCombinedDTO +import no.nav.syfo.motebehov.formSnapshot.FormSnapshot +import no.nav.syfo.motebehov.formSnapshot.LegacyMotebehovToFormSnapshotHelper +import no.nav.syfo.motebehov.formSnapshot.MotebehovInnmelderType import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType import java.io.Serializable import java.time.LocalDateTime @@ -18,5 +23,61 @@ data class PMotebehov( val behandletVeilederIdent: String? = null, val skjemaType: MotebehovSkjemaType? = null, val sykmeldtFnr: String? = null, - val opprettetAvFnr: String? = null + val opprettetAvFnr: String? = null, + // For old "legacy motebehov", this field will be null. + val formSnapshot: FormSnapshot? = null, ) : Serializable + +fun PMotebehov.toMotebehov( + arbeidstakerFnr: String? = null, + knownInnmelderType: MotebehovInnmelderType? = null +): Motebehov { + return Motebehov( + id = this.uuid, + opprettetDato = this.opprettetDato, + aktorId = this.aktoerId, + opprettetAv = this.opprettetAv, + opprettetAvFnr = this.opprettetAvFnr!!, + arbeidstakerFnr = arbeidstakerFnr ?: this.sykmeldtFnr!!, + virksomhetsnummer = this.virksomhetsnummer, + tildeltEnhet = this.tildeltEnhet, + behandletTidspunkt = this.behandletTidspunkt, + behandletVeilederIdent = this.behandletVeilederIdent, + skjemaType = this.skjemaType, + formSubmission = createMotebehovFormSubmissionFromPMotebehov(this, knownInnmelderType), + ) +} + +private fun createMotebehovFormSubmissionFromPMotebehov( + pMotebehov: PMotebehov, + knownInnmelderType: MotebehovInnmelderType? +): MotebehovFormSubmissionCombinedDTO { + val motebehovInnmelderType = knownInnmelderType + ?: if (pMotebehov.opprettetAv == pMotebehov.aktoerId || + pMotebehov.opprettetAvFnr == pMotebehov.sykmeldtFnr + ) { + MotebehovInnmelderType.ARBEIDSTAKER + } else { + MotebehovInnmelderType.ARBEIDSGIVER + } + + val isLegacyMotebehov = pMotebehov.formSnapshot == null + + val formSnapshot = if (isLegacyMotebehov) { + val helper = LegacyMotebehovToFormSnapshotHelper() + helper.createFormSnapshotFromLegacyMotebehovValues( + pMotebehov.harMotebehov, + pMotebehov.forklaring, + pMotebehov.skjemaType, + motebehovInnmelderType, + ) + } else { + pMotebehov.formSnapshot + } + + return MotebehovFormSubmissionCombinedDTO( + harMotebehov = pMotebehov.harMotebehov, + forklaring = pMotebehov.forklaring, + formSnapshot = formSnapshot + ) +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FomSnapshotFieldIds.kt b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FomSnapshotFieldIds.kt new file mode 100644 index 00000000..09e87838 --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FomSnapshotFieldIds.kt @@ -0,0 +1,10 @@ +package no.nav.syfo.motebehov.formSnapshot + +const val SVAR_HAR_BEHOV_RADIO_GROUP_FIELD_ID = "harBehovRadioGroup" +const val BEGRUNNELSE_TEXT_FIELD_ID = "begrunnelseText" +const val ONSKER_SYKMELDER_DELTAR_CHECKBOX_FIELD_ID = "onskerSykmelderDeltarCheckbox" +const val ONSKER_SYKMELDER_DELTAR_BEGRUNNELSE_TEXT_FIELD_ID = "onskerSykmelderDeltarBegrunnelseText" +const val ONSKER_TOLK_CHECKBOX_FIELD_ID = "onskerTolkCheckbox" +const val TOLK_SPRAK_TEXT_FIELD_ID = "tolkSprakText" + +const val MELD_HAR_BEHOV_LEGACY_CHECKBOX_FIELD_ID = "harBehovCheckbox" diff --git a/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshot.kt b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshot.kt new file mode 100644 index 00000000..97f3a8b5 --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshot.kt @@ -0,0 +1,123 @@ +package no.nav.syfo.motebehov.formSnapshot + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import java.io.Serializable +import javax.validation.constraints.NotEmpty +import javax.validation.constraints.NotNull + +// The kdoc comments are written with regards to FormSnapshot being used in a general context, +// not specifically for the motebehov use case. + +/** + * FormSnapshot is a data class describing details of some simple form and how it was filled out in a form submission. + * + * FormSnapshot can be used as a DTO for transmitting a form snapshot between frontend and backend, and as a + * serializable storage format. The main use case for the FormSnapshot format is to support storage and display of form + * responses for forms where at least some of these conditions apply: + * - The form will probably change over time (for example with labels changing, or fields being added or removed). + * - The form responses are mainly submitted in order to be displayed to humans (as opposed to mainly being read + * programmatically). + * - The users viewing the form responses are interested in seeing what the form looked like at the time of submission, + * as opposed to what the form looks like currently, i.e. in case of a label change, or in case of a more drastic + * change. + * - The users viewing the form response might be interested in seeing all the options that was available to choose from + * for a radio buttons field, as opposed to just the selected option. + * + * The contents of a form snapshot might be displayed to the "form filling" user themself on a receipt screeen, or to a + * veileder in Modia. + * + * A form snapshot is meant to describe what a form looked like at the time of submission, much like a paper copy of a + * filled out form. It describes which fields the form consisted of and their types, labels, etc. A stored FormSnapshot + * preserves this data, so that this data doesn't have to be stored elsewhere. If a form is changed, a form snapshot for + * an earlier version of the form will still be valid and describe the form at the time of submission. + * + * A form snapshot consists of a list of *snapshot fields*. All snapshot fields have an id, a label, and a type. + * The type of a field can be one of the following: + * - TEXT: A field where the user could input text. + * - CHECKBOX: A checkbox field. + * - RADIO_OPTIONS: A radio buttons field where the user could select one of multiple options. + */ +data class FormSnapshot( + /** An identifier or name identifying which form this is snapshot is for. */ + val formIdentifier: String, + /** This version tag can be used to signify which version of a form a FormSnapshot is for, and how much is + * changed between two versions. If a label text is changed, it might be denoted with a patch version bump. If the + * ordering of the fields are changed, or the set of options for a radioGroup field is changed, it might count as a + * minor version bump. If the set of fieldIds for a form is changed, which can happen if new fields are added or + * existing fields are removed, or if an existing fieldId is changed, it might count as a major version bump. */ + val formSemanticVersion: String, + @field:NotEmpty + // For info: This configures deserialization both for POST-handlers in controllers and for the object mapper used + // when reading from the database in FormSnapshotJSONConversion.kt. + @JsonDeserialize(contentUsing = FieldSnapshotDeserializer::class) + val fieldSnapshots: List, +) { + @get:JsonIgnore + val fieldValues: Map + get() = fieldSnapshots.associate { fieldSnapshot -> + fieldSnapshot.fieldId to when (fieldSnapshot) { + is TextFieldSnapshot -> fieldSnapshot.value + is SingleCheckboxFieldSnapshot -> fieldSnapshot.value + is RadioGroupFieldSnapshot -> fieldSnapshot.selectedOptionId + else -> throw IllegalArgumentException("Unknown field type: ${fieldSnapshot.fieldType}") + } + } +} + +abstract class FieldSnapshot( + @field:NotEmpty + open val fieldId: String, + @field:NotEmpty + open val fieldLabel: String, + open val description: String? = null, + open val fieldType: FormSnapshotFieldType +) : Serializable { + companion object { + private const val serialVersionUID: Long = 1L + } +} + +data class TextFieldSnapshot( + override val fieldId: String, + override val fieldLabel: String, + override val description: String? = null, + @field:NotEmpty + val value: String, + val wasRequired: Boolean? = true, +) : FieldSnapshot(fieldId, fieldLabel, description, FormSnapshotFieldType.TEXT) + +data class SingleCheckboxFieldSnapshot( + override val fieldId: String, + override val fieldLabel: String, + override val description: String? = null, + @field:NotNull + val value: Boolean, +) : FieldSnapshot(fieldId, fieldLabel, description = null, FormSnapshotFieldType.CHECKBOX_SINGLE) + +data class RadioGroupFieldSnapshot( + override val fieldId: String, + override val fieldLabel: String, + @field:NotEmpty + override val description: String? = null, + val selectedOptionId: String, + @field:NotEmpty + val selectedOptionLabel: String, + @field:NotEmpty + val options: List, + val wasRequired: Boolean? = true, +) : FieldSnapshot(fieldId, fieldLabel, description, FormSnapshotFieldType.RADIO_GROUP) + +data class FormSnapshotFieldOption( + @field:NotEmpty + val optionId: String, + @field:NotEmpty + val optionLabel: String, + val wasSelected: Boolean = false, +) + +enum class FormSnapshotFieldType(val type: String) { + TEXT("text"), + CHECKBOX_SINGLE("checkboxSingle"), + RADIO_GROUP("radioGroup") +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotFormIdentifiers.kt b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotFormIdentifiers.kt new file mode 100644 index 00000000..a02a99d2 --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotFormIdentifiers.kt @@ -0,0 +1,8 @@ +package no.nav.syfo.motebehov.formSnapshot + +const val FORM_IDENTIFIER_ARBEIDSGIVER_SVAR = "motebehov-arbeidsgiver-svar" +const val FORM_IDENTIFIER_ARBEIDSGIVER_MELD = "motebehov-arbeidsgiver-meld" +const val FORM_IDENTIFIER_ARBEIDSGIVER_UNKNOWN = "motebehov-arbeidsgiver-unknown" +const val FORM_IDENTIFIER_ARBEIDSTAKER_SVAR = "motebehov-arbeidstaker-svar" +const val FORM_IDENTIFIER_ARBEIDSTAKER_MELD = "motebehov-arbeidstaker-meld" +const val FORM_IDENTIFIER_ARBEIDSTAKER_UNKNOWN = "motebehov-arbeidstaker-unknown" diff --git a/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotJSONConversion.kt b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotJSONConversion.kt new file mode 100644 index 00000000..1e94a016 --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotJSONConversion.kt @@ -0,0 +1,60 @@ +package no.nav.syfo.motebehov.formSnapshot + +import com.fasterxml.jackson.core.JsonParseException +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.JsonProcessingException +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonMappingException +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import org.slf4j.LoggerFactory + +val log = LoggerFactory.getLogger("FormSnapshotJSONConversion") + +val objectMapper = jacksonObjectMapper() + +fun convertFormSnapshotToJsonString(formSnapshot: FormSnapshot): String { + val value = objectMapper.writeValueAsString(formSnapshot) + + return value +} + +fun convertJsonStringToFormSnapshot(json: String): FormSnapshot? { + return try { + objectMapper.readValue(json) + } catch (e: JsonParseException) { + log.error("Failed to parse JSON: ${e.message}") + null + } catch (e: JsonMappingException) { + log.error("Failed to map JSON to FormSnapshot: ${e.message}") + null + } catch (e: JsonProcessingException) { + log.error("Something went wrong with processing JSON and mapping to FormSnapshot: ${e.message}") + null + } +} + +class FieldSnapshotDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): FieldSnapshot { + val node: JsonNode = p.codec.readTree(p) + val fieldType = node.get("fieldType").asText() + + return when (fieldType) { + FormSnapshotFieldType.TEXT.name -> objectMapper.treeToValue( + node, + TextFieldSnapshot::class.java + ) + FormSnapshotFieldType.CHECKBOX_SINGLE.name -> objectMapper.treeToValue( + node, + SingleCheckboxFieldSnapshot::class.java + ) + FormSnapshotFieldType.RADIO_GROUP.name -> objectMapper.treeToValue( + node, + RadioGroupFieldSnapshot::class.java + ) + else -> throw IllegalArgumentException("Unknown field type: $fieldType") + } + } +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/LegacyMotebehovToFormSnapshotHelper.kt b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/LegacyMotebehovToFormSnapshotHelper.kt new file mode 100644 index 00000000..87b722d2 --- /dev/null +++ b/src/main/kotlin/no/nav/syfo/motebehov/formSnapshot/LegacyMotebehovToFormSnapshotHelper.kt @@ -0,0 +1,230 @@ +package no.nav.syfo.motebehov.formSnapshot + +import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType +import org.springframework.stereotype.Component + +enum class MotebehovInnmelderType { + ARBEIDSGIVER, + ARBEIDSTAKER, +} + +/** + * Used for creating a FormSnapshot from a stored "legacy" motebehov. + */ +@Component +class LegacyMotebehovToFormSnapshotHelper { + + private val legacyFormsSemanticVersion = "0.1.0" + + enum class MotebehovLegacyFormLabel(val label: String) { + SVAR_ARBEIDSGIVER_HAR_BEHOV_FIELD("Har dere behov for et møte med NAV?"), + SVAR_ARBEIDSTAKER_HAR_BEHOV_FIELD("Har du behov for et møte med NAV og arbeidsgiveren din?"), + SVAR_HAR_BEHOV_RADIO_OPTION_YES("Ja, jeg mener det er behov for et møte"), + SVAR_HAR_BEHOV_RADIO_OPTION_NO("Nei, jeg mener det ikke er behov for et møte"), + MELD_ARBEIDSGIVER_ONSKER_MOTE_CHECKBOX("Jeg ønsker et møte med NAV og den ansatte"), + MELD_ARBEIDSTAKER_ONSKER_MOTE_CHECKBOX("Jeg ønsker et møte med NAV og arbeidsgiveren min."), + MELD_ARBEIDSGIVER_ONSKER_SYKMELDER_DELTAR_CHECKBOX( + "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet." + ), + MELD_ARBEIDSTAKER_ONSKER_SYKMELDER_DELTAR_CHECKBOX( + "Jeg ønsker at den som sykmelder meg, også skal delta i møtet." + ), + BEGRUNNELSE_TEXT_FIELD("Begrunnelse") + } + + private val formSnapshotOptionIds = mapOf( + "svarHarBehovRadioOptionYes" to "ja", + "svarHarBehovRadioOptionNo" to "nei" + ) + + data class ExtractedFromLegacyForklaring( + val actualBegrunnelse: String, + val onskerSykmelderDeltar: Boolean + ) + + /** + * Creates a FormSnapshot from legacy motebehov form field values harMotebehov and forklaring. + * The returned FormSnapshot matches what the "legacy" motebehov forms looked like, which was before an update to + * the frontend that makes the forms contain more fields, and that makes the frontend submit a FormSnapshot instead + * of values for specific fields. + */ + fun createFormSnapshotFromLegacyMotebehovValues( + harMotebehov: Boolean, + forklaring: String?, + skjemaType: MotebehovSkjemaType?, + motebehovInnmelderType: MotebehovInnmelderType + ): FormSnapshot { + val fieldSnapshots = mutableListOf() + + if (skjemaType == MotebehovSkjemaType.SVAR_BEHOV) { + fieldSnapshots.add( + createLegacySvarBehovRadioGroupField( + harMotebehov, + motebehovInnmelderType + ) + ) + } else if (skjemaType == MotebehovSkjemaType.MELD_BEHOV) { + fieldSnapshots.add( + createLegacyMeldOnskerMoteCheckboxField( + motebehovInnmelderType + ) + ) + } + + val (actualBegrunnelse, onskerAtSykmelderDeltar) = + extractActualUserBegrunnelseAndOnskerSykmelderDeltarFromLegacyForklaring(forklaring) + + // It was only the MELD_BEHOV form that had the "onskerSykmelderDeltar" checkbox. + if (skjemaType == MotebehovSkjemaType.MELD_BEHOV || onskerAtSykmelderDeltar) { + fieldSnapshots.add( + createLegacyOnskerSykmelderDeltarCheckboxField( + onskerAtSykmelderDeltar, + motebehovInnmelderType + ) + ) + } + + fieldSnapshots.add(createLegacyBegrunnelseTextField(actualBegrunnelse, harMotebehov, skjemaType)) + + val formIdentifier = when (motebehovInnmelderType) { + MotebehovInnmelderType.ARBEIDSGIVER -> + when (skjemaType) { + MotebehovSkjemaType.SVAR_BEHOV -> FORM_IDENTIFIER_ARBEIDSGIVER_SVAR + MotebehovSkjemaType.MELD_BEHOV -> FORM_IDENTIFIER_ARBEIDSGIVER_MELD + else -> FORM_IDENTIFIER_ARBEIDSGIVER_UNKNOWN + } + + MotebehovInnmelderType.ARBEIDSTAKER -> + when (skjemaType) { + MotebehovSkjemaType.SVAR_BEHOV -> FORM_IDENTIFIER_ARBEIDSTAKER_SVAR + MotebehovSkjemaType.MELD_BEHOV -> FORM_IDENTIFIER_ARBEIDSTAKER_MELD + else -> FORM_IDENTIFIER_ARBEIDSTAKER_UNKNOWN + } + } + + return FormSnapshot(formIdentifier, legacyFormsSemanticVersion, fieldSnapshots) + } + + private fun createLegacyBegrunnelseTextField( + begrunnelseTextValue: String, + harMotebehov: Boolean, + skjemaType: MotebehovSkjemaType?, + ): TextFieldSnapshot = TextFieldSnapshot( + fieldId = BEGRUNNELSE_TEXT_FIELD_ID, + fieldLabel = MotebehovLegacyFormLabel.BEGRUNNELSE_TEXT_FIELD.label, + null, + value = begrunnelseTextValue, + wasRequired = skjemaType == MotebehovSkjemaType.SVAR_BEHOV && !harMotebehov + ) + + private fun createLegacySvarBehovRadioGroupField( + harMotebehov: Boolean, + motebehovInnmelderType: MotebehovInnmelderType + ): RadioGroupFieldSnapshot { + val optionIdYes = formSnapshotOptionIds["svarHarBehovRadioOptionYes"]!! + val optionIdNo = formSnapshotOptionIds["svarHarBehovRadioOptionNo"]!! + + val optionLabelYes = MotebehovLegacyFormLabel.SVAR_HAR_BEHOV_RADIO_OPTION_YES.label + val optionLabelNo = MotebehovLegacyFormLabel.SVAR_HAR_BEHOV_RADIO_OPTION_NO.label + + val selectedOptionId = if (harMotebehov) optionIdYes else optionIdNo + val selectedOptionLabel = if (harMotebehov) optionLabelYes else optionLabelNo + + return RadioGroupFieldSnapshot( + fieldId = SVAR_HAR_BEHOV_RADIO_GROUP_FIELD_ID, + fieldLabel = motebehovInnmelderType.let { + when (it) { + MotebehovInnmelderType.ARBEIDSGIVER -> + MotebehovLegacyFormLabel.SVAR_ARBEIDSGIVER_HAR_BEHOV_FIELD.label + MotebehovInnmelderType.ARBEIDSTAKER -> + MotebehovLegacyFormLabel.SVAR_ARBEIDSTAKER_HAR_BEHOV_FIELD.label + } + }, + null, + selectedOptionId, + selectedOptionLabel, + options = listOf( + FormSnapshotFieldOption( + optionId = optionIdYes, + optionLabel = optionLabelYes, + wasSelected = harMotebehov + ), + FormSnapshotFieldOption( + optionId = optionIdNo, + optionLabel = optionLabelNo, + wasSelected = !harMotebehov + ) + ) + ) + } + + private fun createLegacyMeldOnskerMoteCheckboxField( + motebehovInnmelderType: MotebehovInnmelderType + ): SingleCheckboxFieldSnapshot = SingleCheckboxFieldSnapshot( + fieldId = MELD_HAR_BEHOV_LEGACY_CHECKBOX_FIELD_ID, + fieldLabel = motebehovInnmelderType.let { + when (it) { + MotebehovInnmelderType.ARBEIDSGIVER -> + MotebehovLegacyFormLabel.MELD_ARBEIDSGIVER_ONSKER_MOTE_CHECKBOX.label + MotebehovInnmelderType.ARBEIDSTAKER -> + MotebehovLegacyFormLabel.MELD_ARBEIDSTAKER_ONSKER_MOTE_CHECKBOX.label + } + }, + null, + value = true, + ) + + private fun createLegacyOnskerSykmelderDeltarCheckboxField( + onskerSykmelderDeltar: Boolean, + motebehovInnmelderType: MotebehovInnmelderType, + ): SingleCheckboxFieldSnapshot = SingleCheckboxFieldSnapshot( + fieldId = ONSKER_SYKMELDER_DELTAR_CHECKBOX_FIELD_ID, + fieldLabel = motebehovInnmelderType.let { + when (it) { + MotebehovInnmelderType.ARBEIDSGIVER -> + MotebehovLegacyFormLabel.MELD_ARBEIDSGIVER_ONSKER_SYKMELDER_DELTAR_CHECKBOX.label + + MotebehovInnmelderType.ARBEIDSTAKER -> + MotebehovLegacyFormLabel.MELD_ARBEIDSTAKER_ONSKER_SYKMELDER_DELTAR_CHECKBOX.label + } + }, + null, + value = onskerSykmelderDeltar, + ) + + // When a user checked the checkbox for onskerSykmelderDeltar in the legacy form, the text in the forklaring field + // submitted from the frontend would contain the label text for that checkbox concatenated with the text value of + // the begrunnelse text field. + private fun extractActualUserBegrunnelseAndOnskerSykmelderDeltarFromLegacyForklaring( + legacyForklaring: String? + ): ExtractedFromLegacyForklaring { + if (legacyForklaring == null) return ExtractedFromLegacyForklaring("", false) + + var onskerSykmelderDeltar = false + + if (legacyForklaring.contains( + "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet" + ) || legacyForklaring.contains( + "Jeg ønsker at den som sykmelder meg, også skal delta i møtet" + ) + ) { + onskerSykmelderDeltar = true + } + + var actualBegrunnelse = legacyForklaring.replace( + "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet (valgfri).", + "" + ) + actualBegrunnelse = actualBegrunnelse.replace( + "Jeg ønsker at den som sykmelder meg, også skal delta i møtet (valgfri).", "" + ) + + // When the user didn't write anything in the begrunnelse text field, "undefined" would be appended to the + // forklaring value, at least in some cases. We remove it here. + actualBegrunnelse = actualBegrunnelse.replace("undefined", "") + + actualBegrunnelse = actualBegrunnelse.trim() + + return ExtractedFromLegacyForklaring(actualBegrunnelse, onskerSykmelderDeltar) + } +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/motebehovstatus/MotebehovStatus.kt b/src/main/kotlin/no/nav/syfo/motebehov/motebehovstatus/MotebehovStatus.kt index daaef3b1..86a58810 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/motebehovstatus/MotebehovStatus.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/motebehovstatus/MotebehovStatus.kt @@ -1,14 +1,17 @@ package no.nav.syfo.motebehov.motebehovstatus import no.nav.syfo.motebehov.Motebehov -import no.nav.syfo.motebehov.MotebehovOutputDTO +import no.nav.syfo.motebehov.MotebehovWithFormValuesOutputDTO +import no.nav.syfo.motebehov.MotebehovWithLegacyMotebehovSvarOutputDTO import no.nav.syfo.motebehov.isUbehandlet +import no.nav.syfo.motebehov.toMotebehovWithFormValuesOutputDTO +import no.nav.syfo.motebehov.toMotebehovWithLegacyMotebehovSvarOutputDTO import java.io.Serializable data class MotebehovStatus( val visMotebehov: Boolean, val skjemaType: MotebehovSkjemaType? = null, - val motebehov: MotebehovOutputDTO? = null + val motebehov: Motebehov? = null ) : Serializable fun MotebehovStatus.isSvarBehovVarselAvailable(): Boolean { @@ -29,3 +32,31 @@ fun MotebehovStatus.isMotebehovAvailableForAnswer(): Boolean { this.skjemaType != null && this.motebehov == null } + +data class MotebehovStatusWithLegacyMotebehovDTO( + val visMotebehov: Boolean, + val skjemaType: MotebehovSkjemaType? = null, + val motebehov: MotebehovWithLegacyMotebehovSvarOutputDTO? = null, +) + +fun MotebehovStatus.toMotebehovStatusWithLegacyMotebehovDTO(): MotebehovStatusWithLegacyMotebehovDTO { + return MotebehovStatusWithLegacyMotebehovDTO( + visMotebehov = this.visMotebehov, + skjemaType = this.skjemaType, + motebehov = this.motebehov?.toMotebehovWithLegacyMotebehovSvarOutputDTO() + ) +} + +data class MotebehovStatusWithFormValuesDTO( + val visMotebehov: Boolean, + val skjemaType: MotebehovSkjemaType? = null, + val motebehovWithFormValues: MotebehovWithFormValuesOutputDTO? = null, +) + +fun MotebehovStatus.toMotebehovStatusWithFormValuesDTO(): MotebehovStatusWithFormValuesDTO { + return MotebehovStatusWithFormValuesDTO( + visMotebehov = this.visMotebehov, + skjemaType = this.skjemaType, + motebehovWithFormValues = this.motebehov?.toMotebehovWithFormValuesOutputDTO() + ) +} diff --git a/src/main/kotlin/no/nav/syfo/motebehov/motebehovstatus/MotebehovStatusHelper.kt b/src/main/kotlin/no/nav/syfo/motebehov/motebehovstatus/MotebehovStatusHelper.kt index ff936c5b..7e429c2a 100644 --- a/src/main/kotlin/no/nav/syfo/motebehov/motebehovstatus/MotebehovStatusHelper.kt +++ b/src/main/kotlin/no/nav/syfo/motebehov/motebehovstatus/MotebehovStatusHelper.kt @@ -4,7 +4,6 @@ import no.nav.syfo.motebehov.Motebehov import no.nav.syfo.motebehov.isCreatedInOppfolgingstilfelle import no.nav.syfo.motebehov.isSvarBehovForOppfolgingstilfelle import no.nav.syfo.motebehov.isUbehandlet -import no.nav.syfo.motebehov.toMotebehovOutputDTO import no.nav.syfo.oppfolgingstilfelle.database.PersonOppfolgingstilfelle import no.nav.syfo.oppfolgingstilfelle.database.isSykmeldtNow import org.springframework.stereotype.Component @@ -36,7 +35,7 @@ class MotebehovStatusHelper { return MotebehovStatus( true, MotebehovSkjemaType.SVAR_BEHOV, - newestSvarBehovInOppfolgingstilfelle?.toMotebehovOutputDTO(), + newestSvarBehovInOppfolgingstilfelle, ) } else { val newestMeldBehovInOppfolgingstilfelle = @@ -45,7 +44,7 @@ class MotebehovStatusHelper { return MotebehovStatus( true, MotebehovSkjemaType.MELD_BEHOV, - newestMeldBehovInOppfolgingstilfelle?.toMotebehovOutputDTO(), + newestMeldBehovInOppfolgingstilfelle, ) } } diff --git a/src/main/resources/db/migration/V1_12__motebehovFormValues.sql b/src/main/resources/db/migration/V1_12__motebehovFormValues.sql new file mode 100644 index 00000000..b38a14d5 --- /dev/null +++ b/src/main/resources/db/migration/V1_12__motebehovFormValues.sql @@ -0,0 +1,27 @@ +DROP TABLE IF EXISTS MOTEBEHOV_FORM_VALUES; + +CREATE TABLE MOTEBEHOV_FORM_VALUES +( + motebehov_uuid VARCHAR(36) NOT NULL, + form_snapshot JSONB NOT NULL, + form_identifier VARCHAR(50) NOT NULL CHECK (form_identifier IN ( + 'motebehov-arbeidsgiver-svar', + 'motebehov-arbeidsgiver-meld', + 'motebehov-arbeidstaker-svar', + 'motebehov-arbeidstaker-meld' + )), + form_semantic_version VARCHAR(20) NOT NULL, + begrunnelse TEXT, + onsker_sykmelder_deltar BOOLEAN NOT NULL, + onsker_sykmelder_deltar_begrunnelse TEXT, + onsker_tolk BOOLEAN NOT NULL, + tolk_sprak TEXT +); + +CREATE INDEX idx_motebehov_form_values_motebehov_uuid ON MOTEBEHOV_FORM_VALUES (motebehov_uuid); + +ALTER TABLE MOTEBEHOV_FORM_VALUES + ADD CONSTRAINT fk_motebehov + FOREIGN KEY (motebehov_uuid) + REFERENCES MOTEBEHOV (motebehov_uuid) + ON DELETE CASCADE; \ No newline at end of file diff --git a/src/test/kotlin/no/nav/syfo/motebehov/ConvertLegacyMotebehovSvarFieldsHelperTest.kt b/src/test/kotlin/no/nav/syfo/motebehov/ConvertLegacyMotebehovSvarFieldsHelperTest.kt deleted file mode 100644 index 81a338dc..00000000 --- a/src/test/kotlin/no/nav/syfo/motebehov/ConvertLegacyMotebehovSvarFieldsHelperTest.kt +++ /dev/null @@ -1,183 +0,0 @@ -package no.nav.syfo.motebehov - -import io.kotest.core.spec.style.DescribeSpec -import io.kotest.matchers.collections.shouldContainExactly -import io.kotest.matchers.shouldBe -import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType - -class ConvertLegacyMotebehovSvarFieldsHelperTest : DescribeSpec({ - - val convertLegacyMotebehovSvarFieldsHelper = ConvertLegacyMotebehovSvarFieldsHelper() - - describe("ConvertLegacyMotebehovSvarFieldsHelper") { - it("should convert legacy motebehovSvar from arbeidstaker of type svar ja correctly") { - val harMotebehov = true - val forklaring = "Jeg ønkser å snakke om bedre tilrettelegging" - val skjemaType = MotebehovSkjemaType.SVAR_BEHOV - val motebehovInnmelderType = MotebehovInnmelderType.ARBEIDSTAKER - - val formFillout = convertLegacyMotebehovSvarFieldsHelper.convertLegacyMotebehovSvarToFormFillout( - harMotebehov, - forklaring, - skjemaType, - motebehovInnmelderType - ) - - "motebehov-arbeidstaker-svar" shouldBe formFillout.formIdentifier - formFillout.formSemanticVersion shouldBe "0.1.0" - - formFillout.filloutFieldsList shouldContainExactly listOf( - FilloutRadioGroupField( - "harBehovRadioGroup", - "Har du behov for et møte med NAV og arbeidsgiveren din?", - "ja", - "Ja, jeg mener det er behov for et møte", - listOf( - FormFilloutFieldOption("ja", "Ja, jeg mener det er behov for et møte", true), - FormFilloutFieldOption("nei", "Nei, jeg mener det ikke er behov for et møte") - ) - ), - FilloutTextField( - "begrunnelseText", - "Begrunnelse", - forklaring, - true - ) - ) - - formFillout.fieldValues shouldBe - mapOf( - "harBehovRadioGroup" to "ja", - "begrunnelseText" to forklaring - ) - } - - it("should convert legacy motebehovSvar from arbeidsgiver of type svar nei correctly") { - val harMotebehov = false - val forklaring = "Vi trenger et møte" - val skjemaType = MotebehovSkjemaType.SVAR_BEHOV - val motebehovInnmelderType = MotebehovInnmelderType.ARBEIDSGIVER - - val formFillout = convertLegacyMotebehovSvarFieldsHelper.convertLegacyMotebehovSvarToFormFillout( - harMotebehov, - forklaring, - skjemaType, - motebehovInnmelderType - ) - - formFillout.formIdentifier shouldBe "motebehov-arbeidsgiver-svar" - formFillout.formSemanticVersion shouldBe "0.1.0" - - formFillout.filloutFieldsList shouldContainExactly listOf( - FilloutRadioGroupField( - "harBehovRadioGroup", - "Har dere behov for et møte med NAV?", - "nei", - "Nei, jeg mener det ikke er behov for et møte", - listOf( - FormFilloutFieldOption("ja", "Ja, jeg mener det er behov for et møte"), - FormFilloutFieldOption("nei", "Nei, jeg mener det ikke er behov for et møte", true) - ) - ), - FilloutTextField( - "begrunnelseText", - "Begrunnelse", - forklaring, - false - ), - ) - - formFillout.fieldValues shouldBe - mapOf( - "harBehovRadioGroup" to "nei", - "begrunnelseText" to forklaring - ) - } - - it("should convert legacy motebehovSvar from arbeidstaker of type meld correctly") { - val harMotebehov = true - val forklaring = "Dette er tekst i begrunnelsesfeltet" - val skjemaType = MotebehovSkjemaType.MELD_BEHOV - val motebehovInnmelderType = MotebehovInnmelderType.ARBEIDSTAKER - - val formFillout = convertLegacyMotebehovSvarFieldsHelper.convertLegacyMotebehovSvarToFormFillout( - harMotebehov, - forklaring, - skjemaType, - motebehovInnmelderType - ) - - formFillout.formIdentifier shouldBe "motebehov-arbeidstaker-meld" - formFillout.formSemanticVersion shouldBe "0.1.0" - - formFillout.filloutFieldsList shouldContainExactly listOf( - FilloutCheckboxField( - "harBehovCheckbox", - "Jeg ønsker et møte med NAV og arbeidsgiveren min.", - true, - ), - FilloutCheckboxField( - "onskerSykmelderDeltarCheckbox", - "Jeg ønsker at den som sykmelder meg, også skal delta i møtet.", - false, - ), - FilloutTextField( - "begrunnelseText", - "Begrunnelse", - forklaring, - true - ), - ) - - formFillout.fieldValues shouldBe - mapOf( - "harBehovCheckbox" to true, - "onskerSykmelderDeltarCheckbox" to false, - "begrunnelseText" to forklaring - ) - } - - it("should convert legacy motebehovSvar from arbeidsgiver of type meld and onsker sykmelder correctly") { - val harMotebehov = true - val forklaring = "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet (valgfri). " + - "Vi trenger å ha et møte med NAV." - val skjemaType = MotebehovSkjemaType.MELD_BEHOV - val motebehovInnmelderType = MotebehovInnmelderType.ARBEIDSGIVER - - val formFillout = convertLegacyMotebehovSvarFieldsHelper.convertLegacyMotebehovSvarToFormFillout( - harMotebehov, - forklaring, - skjemaType, - motebehovInnmelderType - ) - - formFillout.formIdentifier shouldBe "motebehov-arbeidsgiver-meld" - formFillout.formSemanticVersion shouldBe "0.1.0" - - formFillout.filloutFieldsList shouldContainExactly listOf( - FilloutCheckboxField( - "harBehovCheckbox", - "Jeg ønsker et møte med NAV og den ansatte", - true, - ), - FilloutCheckboxField( - "onskerSykmelderDeltarCheckbox", - "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet.", - true, - ), - FilloutTextField( - "begrunnelseText", - "Begrunnelse", - "Vi trenger å ha et møte med NAV.", - true - ), - ) - - formFillout.fieldValues shouldBe mapOf( - "harBehovCheckbox" to true, - "onskerSykmelderDeltarCheckbox" to true, - "begrunnelseText" to "Vi trenger å ha et møte med NAV." - ) - } - } -}) diff --git a/src/test/kotlin/no/nav/syfo/motebehov/MotebehovServiceTest.kt b/src/test/kotlin/no/nav/syfo/motebehov/MotebehovServiceTest.kt index 62ce34a5..5982f822 100644 --- a/src/test/kotlin/no/nav/syfo/motebehov/MotebehovServiceTest.kt +++ b/src/test/kotlin/no/nav/syfo/motebehov/MotebehovServiceTest.kt @@ -5,20 +5,39 @@ import io.kotest.extensions.spring.SpringExtension import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe +import io.mockk.every import no.nav.syfo.IntegrationTest import no.nav.syfo.LocalApplication +import no.nav.syfo.consumer.pdl.PdlConsumer import no.nav.syfo.motebehov.database.MotebehovDAO +import no.nav.syfo.motebehov.formSnapshot.mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot +import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType import no.nav.syfo.personoppgavehendelse.PersonoppgavehendelseService +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_AKTORID +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_FNR +import no.nav.syfo.testhelper.UserConstants.LEDER_AKTORID +import no.nav.syfo.testhelper.UserConstants.LEDER_FNR +import no.nav.syfo.testhelper.UserConstants.VIRKSOMHETSNUMMER import no.nav.syfo.testhelper.generator.MotebehovGenerator +import no.nav.syfo.testhelper.mockAndExpectBehandlendeEnhetRequest import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.TestConfiguration import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.test.web.client.MockRestServiceServer +import org.springframework.web.client.RestTemplate import java.time.LocalDate @TestConfiguration @SpringBootTest(classes = [LocalApplication::class]) class MotebehovServiceTest : IntegrationTest() { + @Value("\${azure.openid.config.token.endpoint}") + private lateinit var azureTokenEndpoint: String + + @Value("\${syfobehandlendeenhet.url}") + private lateinit var behandlendeenhetUrl: String @MockkBean(relaxed = true) private lateinit var personoppgavehendelseService: PersonoppgavehendelseService @@ -34,6 +53,20 @@ class MotebehovServiceTest : IntegrationTest() { private val veilederIdent = "testVeileder" + @Autowired + private lateinit var restTemplate: RestTemplate + + @MockkBean + private lateinit var pdlConsumer: PdlConsumer + + private lateinit var mockRestServiceServerAzureAD: MockRestServiceServer + + @Autowired + @Qualifier("AzureAD") + private lateinit var restTemplateAzureAD: RestTemplate + + private lateinit var mockRestServiceServer: MockRestServiceServer + init { extensions(SpringExtension) beforeTest { @@ -67,6 +100,53 @@ class MotebehovServiceTest : IntegrationTest() { motebehov.first().behandletVeilederIdent.shouldBeNull() motebehov.first().behandletTidspunkt.shouldBeNull() } + + it("should store motebehov and retrieve it with same values") { + // Arrange + mockRestServiceServer = MockRestServiceServer.bindTo(restTemplate).build() + mockRestServiceServerAzureAD = MockRestServiceServer.bindTo(restTemplateAzureAD).build() + + mockAndExpectBehandlendeEnhetRequest( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + ARBEIDSTAKER_FNR, + ) + + every { pdlConsumer.aktorid(ARBEIDSTAKER_FNR) } returns ARBEIDSTAKER_AKTORID + every { pdlConsumer.aktorid(LEDER_FNR) } returns LEDER_AKTORID + + // Act + val uuid = motebehovService.lagreMotebehov( + LEDER_FNR, + ARBEIDSTAKER_FNR, + VIRKSOMHETSNUMMER, + MotebehovSkjemaType.SVAR_BEHOV, + MotebehovFormSubmissionCombinedDTO( + true, + null, + mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot + ) + ) + + val retrievedMotebehov = motebehovService.hentMotebehov(uuid.toString()) + + // Assert + retrievedMotebehov.shouldNotBeNull() + retrievedMotebehov.id shouldBe uuid + retrievedMotebehov.opprettetAvFnr shouldBe LEDER_FNR + retrievedMotebehov.arbeidstakerFnr shouldBe ARBEIDSTAKER_FNR + retrievedMotebehov.opprettetAv shouldBe LEDER_AKTORID + retrievedMotebehov.aktorId shouldBe ARBEIDSTAKER_AKTORID + retrievedMotebehov.virksomhetsnummer shouldBe VIRKSOMHETSNUMMER + retrievedMotebehov.behandletTidspunkt.shouldBeNull() + retrievedMotebehov.behandletVeilederIdent.shouldBeNull() + retrievedMotebehov.skjemaType shouldBe MotebehovSkjemaType.SVAR_BEHOV + + val retrievedformSnapshot = retrievedMotebehov.formSubmission.formSnapshot + retrievedformSnapshot shouldBe mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot + } } } } diff --git a/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV3Test.kt b/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV3Test.kt index fc939d1a..627e6432 100644 --- a/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV3Test.kt +++ b/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV3Test.kt @@ -11,15 +11,13 @@ import no.nav.syfo.consumer.brukertilgang.BrukertilgangConsumer import no.nav.syfo.consumer.pdl.PdlConsumer import no.nav.syfo.dialogmotekandidat.database.DialogmotekandidatDAO import no.nav.syfo.dialogmotekandidat.database.DialogmotekandidatEndringArsak -import no.nav.syfo.motebehov.MotebehovInnmelderType -import no.nav.syfo.motebehov.MotebehovSvarInputDTO -import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverInputDTO +import no.nav.syfo.motebehov.MotebehovSvarLegacyDTO +import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverLegacyDTO import no.nav.syfo.motebehov.api.internad.v3.MotebehovVeilederADControllerV3 import no.nav.syfo.motebehov.database.MotebehovDAO import no.nav.syfo.motebehov.motebehovstatus.DAYS_END_SVAR_BEHOV import no.nav.syfo.motebehov.motebehovstatus.DAYS_START_SVAR_BEHOV import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType -import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatus import no.nav.syfo.oppfolgingstilfelle.database.OppfolgingstilfelleDAO import no.nav.syfo.personoppgavehendelse.PersonoppgavehendelseProducer import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_AKTORID @@ -346,7 +344,7 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { ), ) - val motebehovSvar = motebehovGenerator.lagMotebehovSvarInputDTO(true) + val motebehovSvar = motebehovGenerator.lagMotebehovSvarLegacyDTO(true) submitMotebehovAndSendOversikthendelse(motebehovSvar) resetMockRestServers() @@ -373,20 +371,14 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { ), ) - val motebehovSvarInputDTO = motebehovGenerator.lagMotebehovSvarInputDTO(true) - val motebehovSvarOutputDTO = - motebehovGenerator.lagMotebehovSvarOutputDTOThatShouldBeCreatedFromInputDTO( - motebehovSvarInputDTO, - MotebehovSkjemaType.MELD_BEHOV, - MotebehovInnmelderType.ARBEIDSGIVER - ) + val motebehovSvarLegacyDTO = motebehovGenerator.lagMotebehovSvarLegacyDTO(true) - submitMotebehovAndSendOversikthendelse(motebehovSvarInputDTO) + submitMotebehovAndSendOversikthendelse(motebehovSvarLegacyDTO) mockRestServiceServer.reset() motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) - .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, motebehovSvarOutputDTO) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, motebehovSvarLegacyDTO) } it("getMotebehovStatusWithTodayInsideOppfolgingstilfelleBeforeSvarBehovStartDate") { @@ -445,22 +437,16 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { ), ) - val motebehovSvarInputDTO = motebehovGenerator.lagMotebehovSvarInputDTO(true) - val motebehovSvarOutputDTO = - motebehovGenerator.lagMotebehovSvarOutputDTOThatShouldBeCreatedFromInputDTO( - motebehovSvarInputDTO, - MotebehovSkjemaType.SVAR_BEHOV, - MotebehovInnmelderType.ARBEIDSGIVER - ) + val motebehovSvarLegacyDTO = motebehovGenerator.lagMotebehovSvarLegacyDTO(true) - submitMotebehovAndSendOversikthendelse(motebehovSvarInputDTO) + submitMotebehovAndSendOversikthendelse(motebehovSvarLegacyDTO) verify { esyfovarselService.ferdigstillSvarMotebehovForArbeidsgiver(any(), ARBEIDSTAKER_FNR, any()) } verify(exactly = 0) { esyfovarselService.ferdigstillSvarMotebehovForArbeidstaker(ARBEIDSTAKER_FNR) } mockRestServiceServer.reset() motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) - .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, motebehovSvarOutputDTO) + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, motebehovSvarLegacyDTO) } it("getMotebehovStatusWithNoMotebehovAndNoMoteInsideSvarBehovLowerLimit") { @@ -498,12 +484,12 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { oppfolgingstilfelleDAO, generateOppfolgingstilfellePerson(), ) - val motebehov = motebehovGenerator.lagNyttMotebehovArbeidsgiverInput().copy( - motebehovSvar = MotebehovSvarInputDTO(harMotebehov = true, forklaring = ""), + val motebehov = motebehovGenerator.lagNyttMotebehovArbeidsgiverLegacyInput().copy( + motebehovSvar = MotebehovSvarLegacyDTO(harMotebehov = true, forklaring = ""), ) lagreMotebehov(motebehov) - verifyMotebehovStatus(motebehov.motebehovSvar, MotebehovSkjemaType.SVAR_BEHOV) + verifyMotebehovStatus(motebehov.motebehovSvar) } it("getMotebehovStatusAndSendOversikthendelseWithMotebehovHarBehovFalse") { @@ -513,12 +499,12 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { generateOppfolgingstilfellePerson(), ) - val motebehov = motebehovGenerator.lagNyttMotebehovArbeidsgiverInput().copy( - motebehovSvar = MotebehovSvarInputDTO(harMotebehov = false, forklaring = ""), + val motebehov = motebehovGenerator.lagNyttMotebehovArbeidsgiverLegacyInput().copy( + motebehovSvar = MotebehovSvarLegacyDTO(harMotebehov = false, forklaring = ""), ) lagreMotebehov(motebehov) - verifyMotebehovStatus(motebehov.motebehovSvar, MotebehovSkjemaType.SVAR_BEHOV) + verifyMotebehovStatus(motebehov.motebehovSvar) } it("innsendtMotebehovForEgenLederFerdigstillerOgsaaSykmeldtVarsel") { @@ -530,8 +516,8 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { personIdentNumber = LEDER_FNR, ), ) - val motebehov = motebehovGenerator.lagNyttMotebehovArbeidsgiverInput().copy( - motebehovSvar = MotebehovSvarInputDTO(harMotebehov = true, forklaring = ""), + val motebehov = motebehovGenerator.lagNyttMotebehovArbeidsgiverLegacyInput().copy( + motebehovSvar = MotebehovSvarLegacyDTO(harMotebehov = true, forklaring = ""), arbeidstakerFnr = LEDER_FNR ) @@ -549,7 +535,7 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { } } - private fun submitMotebehovAndSendOversikthendelse(motebehovSvarInputDTO: MotebehovSvarInputDTO) { + private fun submitMotebehovAndSendOversikthendelse(motebehovSvarInputDTO: MotebehovSvarLegacyDTO) { mockAndExpectBehandlendeEnhetRequest( azureTokenEndpoint, mockRestServiceServerAzureAD, @@ -559,7 +545,7 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { ) motebehovArbeidsgiverController.lagreMotebehovArbeidsgiver( - motebehovGenerator.lagNyttMotebehovArbeidsgiverInput().copy( + motebehovGenerator.lagNyttMotebehovArbeidsgiverLegacyInput().copy( motebehovSvar = motebehovSvarInputDTO, ), ) @@ -570,7 +556,7 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { } } - private fun lagreMotebehov(innsendtMotebehov: NyttMotebehovArbeidsgiverInputDTO) { + private fun lagreMotebehov(innsendtMotebehov: NyttMotebehovArbeidsgiverLegacyDTO) { mockAndExpectBehandlendeEnhetRequest( azureTokenEndpoint, mockRestServiceServerAzureAD, @@ -591,19 +577,12 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { } private fun verifyMotebehovStatus( - innsendtMotebehovSvar: MotebehovSvarInputDTO, - innsendtSkjemaType: MotebehovSkjemaType, + innsendtMotebehovSvar: MotebehovSvarLegacyDTO, ) { - val motebehovStatus: MotebehovStatus = motebehovArbeidsgiverController.motebehovStatusArbeidsgiver( + val motebehovStatus = motebehovArbeidsgiverController.motebehovStatusArbeidsgiver( ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER, ) - val motebehovSvarOutputDTOThatShouldBeCreated = motebehovGenerator - .lagMotebehovSvarOutputDTOThatShouldBeCreatedFromInputDTO( - innsendtMotebehovSvar, - innsendtSkjemaType, - MotebehovInnmelderType.ARBEIDSGIVER - ) assertTrue(motebehovStatus.visMotebehov) assertEquals(MotebehovSkjemaType.SVAR_BEHOV, motebehovStatus.skjemaType) @@ -614,7 +593,7 @@ class MotebehovArbeidsgiverControllerV3Test : IntegrationTest() { assertThat(motebehov.virksomhetsnummer).isEqualTo(VIRKSOMHETSNUMMER) assertThat(motebehov.skjemaType).isEqualTo(motebehovStatus.skjemaType) assertThat(motebehov.motebehovSvar).usingRecursiveComparison() - .isEqualTo(motebehovSvarOutputDTOThatShouldBeCreated) + .isEqualTo(innsendtMotebehovSvar) if (innsendtMotebehovSvar.harMotebehov) { verify { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } } else { diff --git a/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV4Test.kt b/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV4Test.kt new file mode 100644 index 00000000..fe333ddb --- /dev/null +++ b/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidsgiverControllerV4Test.kt @@ -0,0 +1,659 @@ +package no.nav.syfo.motebehov.api + +import com.ninjasquad.springmockk.MockkBean +import io.kotest.extensions.spring.SpringExtension +import io.mockk.every +import io.mockk.verify +import no.nav.syfo.IntegrationTest +import no.nav.syfo.LocalApplication +import no.nav.syfo.consumer.azuread.v2.AzureAdV2TokenConsumer +import no.nav.syfo.consumer.brukertilgang.BrukertilgangConsumer +import no.nav.syfo.consumer.pdl.PdlConsumer +import no.nav.syfo.dialogmotekandidat.database.DialogmotekandidatDAO +import no.nav.syfo.dialogmotekandidat.database.DialogmotekandidatEndringArsak +import no.nav.syfo.motebehov.MotebehovFormSubmissionDTO +import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverFormSubmissionDTO +import no.nav.syfo.motebehov.api.internad.v4.MotebehovVeilederADControllerV4 +import no.nav.syfo.motebehov.database.MotebehovDAO +import no.nav.syfo.motebehov.formSnapshot.mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot +import no.nav.syfo.motebehov.motebehovstatus.DAYS_END_SVAR_BEHOV +import no.nav.syfo.motebehov.motebehovstatus.DAYS_START_SVAR_BEHOV +import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType +import no.nav.syfo.motebehov.toMotebehovFormSubmissionCombinedDTO +import no.nav.syfo.motebehov.toMotebehovFormValuesOutputDTO +import no.nav.syfo.oppfolgingstilfelle.database.OppfolgingstilfelleDAO +import no.nav.syfo.personoppgavehendelse.PersonoppgavehendelseProducer +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_AKTORID +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_FNR +import no.nav.syfo.testhelper.UserConstants.LEDER_AKTORID +import no.nav.syfo.testhelper.UserConstants.LEDER_FNR +import no.nav.syfo.testhelper.UserConstants.VEILEDER_ID +import no.nav.syfo.testhelper.UserConstants.VIRKSOMHETSNUMMER +import no.nav.syfo.testhelper.UserConstants.VIRKSOMHETSNUMMER_2 +import no.nav.syfo.testhelper.assertion.assertMotebehovStatus +import no.nav.syfo.testhelper.clearCache +import no.nav.syfo.testhelper.generator.MotebehovGenerator +import no.nav.syfo.testhelper.generator.generateOppfolgingstilfellePerson +import no.nav.syfo.testhelper.generator.generatePdlHentPerson +import no.nav.syfo.testhelper.mockAndExpectBehandlendeEnhetRequest +import no.nav.syfo.testhelper.mockAndExpectBehandlendeEnhetRequestWithTilgangskontroll +import no.nav.syfo.util.TokenValidationUtil +import no.nav.syfo.varsel.esyfovarsel.EsyfovarselService +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.TestInstance +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.cache.CacheManager +import org.springframework.test.web.client.MockRestServiceServer +import org.springframework.web.client.RestTemplate +import java.time.LocalDate +import java.time.LocalDateTime +import java.util.* +import java.util.function.Consumer + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestConfiguration +@SpringBootTest(classes = [LocalApplication::class]) +class MotebehovArbeidsgiverControllerV4Test : IntegrationTest() { + @Value("\${azure.openid.config.token.endpoint}") + private lateinit var azureTokenEndpoint: String + + @Value("\${syfobehandlendeenhet.url}") + private lateinit var behandlendeenhetUrl: String + + @Value("\${istilgangskontroll.url}") + private lateinit var tilgangskontrollUrl: String + + @Autowired + private lateinit var motebehovArbeidsgiverController: MotebehovArbeidsgiverControllerV4 + + @Autowired + private lateinit var motebehovVeilederController: MotebehovVeilederADControllerV4 + + @Autowired + private lateinit var motebehovDAO: MotebehovDAO + + @Autowired + private lateinit var cacheManager: CacheManager + + @Autowired + private lateinit var oppfolgingstilfelleDAO: OppfolgingstilfelleDAO + + @Autowired + private lateinit var dialogmotekandidatDAO: DialogmotekandidatDAO + + @Autowired + @Qualifier("AzureAD") + private lateinit var restTemplateAzureAD: RestTemplate + + @Autowired + private lateinit var restTemplate: RestTemplate + + @Autowired + private lateinit var tokenValidationUtil: TokenValidationUtil + + @MockkBean(relaxed = true) + private lateinit var esyfovarselService: EsyfovarselService + + @MockkBean(relaxed = true) + private lateinit var pdlConsumer: PdlConsumer + + @MockkBean + private lateinit var brukertilgangConsumer: BrukertilgangConsumer + + @MockkBean(relaxed = true) + private lateinit var personoppgavehendelseProducer: PersonoppgavehendelseProducer + + private lateinit var mockRestServiceServerAzureAD: MockRestServiceServer + private lateinit var mockRestServiceServer: MockRestServiceServer + + private val motebehovGenerator = MotebehovGenerator() + + init { + extensions(SpringExtension) + beforeTest { + + every { brukertilgangConsumer.hasAccessToAnsatt(ARBEIDSTAKER_FNR) } returns true + every { brukertilgangConsumer.hasAccessToAnsatt(LEDER_FNR) } returns true + every { pdlConsumer.person(ARBEIDSTAKER_FNR) } returns generatePdlHentPerson(null, null) + every { pdlConsumer.aktorid(ARBEIDSTAKER_FNR) } returns ARBEIDSTAKER_AKTORID + every { pdlConsumer.aktorid(LEDER_FNR) } returns LEDER_AKTORID + + every { pdlConsumer.isKode6(ARBEIDSTAKER_FNR) } returns false + + mockRestServiceServer = MockRestServiceServer.bindTo(restTemplate).build() + mockRestServiceServerAzureAD = MockRestServiceServer.bindTo(restTemplateAzureAD).build() + cleanDB() + } + afterEach { + resetMockRestServers() + cacheManager.cacheNames + .forEach( + Consumer { cacheName: String -> + val cache = cacheManager.getCache(cacheName) + cache?.clear() + }, + ) + cleanDB() + AzureAdV2TokenConsumer.Companion.clearCache() + } + afterTest { + tokenValidationUtil.resetAll() + } + + describe("MotebehovArbeidsgiverControllerV4") { + it("getMotebehovStatusWithNoOppfolgingstilfelle") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(false, null, null) + } + + it("getMotebehovStatusWithTodayOutsideOppfolgingstilfelleStart") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().plusDays(1), + end = LocalDate.now().plusDays(10), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(false, null, null) + } + + it("getMotebehovStatusWithTodayOutsideOppfolgingstilfelleEnd") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(17), + end = LocalDate.now().minusDays(16), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(false, null, null) + } + + it( + "getMotebehovStatusWithTodayInsideOppfolgingstilfelleMergedByActiveAndExpired" + + "OppfolgingstilfelleNoOverlapVirksomhetWithoutActiveOppfolgingstilfelle" + ) { + val activeOppfolgingstilfelleStartDate = + LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV).plusDays(1) + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate.minusDays(2), + end = activeOppfolgingstilfelleStartDate.minusDays(1), + ), + ) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate, + end = LocalDate.now().plusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER_2), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(false, null, null) + } + + it( + "getMotebehovStatusWithTodayInsideOppfolgingstilfelleMergedByActiveAndExpired" + + "OppfolgingstilfelleNoOverlap" + ) { + val activeOppfolgingstilfelleStartDate = LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV).plusDays(1) + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate.minusDays(2), + end = activeOppfolgingstilfelleStartDate.minusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER_2), + ), + ) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate, + end = LocalDate.now().plusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it( + "getMotebehovStatusWithTodayInsideOppfolgingstilfelleMergedByActiveAndExpired" + + "OppfolgingstilfelleWithOverlap" + ) { + val activeOppfolgingstilfelleStartDate = + LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV).plusDays(1) + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate.minusDays(2), + end = activeOppfolgingstilfelleStartDate, + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER_2), + ), + ) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate, + end = LocalDate.now().plusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER), + ), + ) + + createKandidatInDB(ARBEIDSTAKER_FNR) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, null) + } + + it("getMotebehovStatusWithTodayInsideOppfolgingstilfelleMergedBy2Oppfolgingstilfeller") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).minusDays(1), + end = LocalDate.now(), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER), + ), + ) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(2), + end = LocalDate.now().plusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER_2), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("getMotebehovStatusWithTodayInsideOppfolgingstilfelleDay1") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now(), + end = LocalDate.now(), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("getMotebehovStatusWithTodayInsideOppfolgingstilfelleLastDay") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).minusDays(1), + end = LocalDate.now(), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("getMotebehovStatusWithTodayInsideOppfolgingstilfelleMedBehov") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now(), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("getMotebehovStatusWithTodayInsideOppfolgingstilfelleMeldBehovSubmittedAndBehandlet") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now(), + end = LocalDate.now().plusDays(1), + ), + ) + + val arbeidsgiverFormSubmissionMeld = motebehovGenerator.lagNyArbeidsgiverFormSubmissionMeld() + submitMotebehovAndSendOversikthendelse(arbeidsgiverFormSubmissionMeld) + + resetMockRestServers() + + mockBehandlendEnhetWithTilgangskontroll(ARBEIDSTAKER_FNR) + tokenValidationUtil.logInAsNavCounselor(VEILEDER_ID) + motebehovVeilederController.behandleMotebehov(ARBEIDSTAKER_FNR) + + resetMockRestServers() + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it( + "getMotebehovStatusWithTodayInsideOppfolgingstilfelleMeldBehovMoteplanleggerActiveMeldBehovSubmitted" + ) { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now(), + end = LocalDate.now().plusDays(1), + ), + ) + + val arbeidsgiverFormSubmissionMeld = motebehovGenerator.lagNyArbeidsgiverFormSubmissionMeld() + val formValuesOutputDTOThatShouldBeCreated = arbeidsgiverFormSubmissionMeld.formSubmission + .toMotebehovFormSubmissionCombinedDTO() + .toMotebehovFormValuesOutputDTO() + + submitMotebehovAndSendOversikthendelse(arbeidsgiverFormSubmissionMeld) + + mockRestServiceServer.reset() + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus( + true, + MotebehovSkjemaType.MELD_BEHOV, + formValuesOutputDTOThatShouldBeCreated + ) + } + + it("getMotebehovStatusWithTodayInsideOppfolgingstilfelleBeforeSvarBehovStartDate") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV).plusDays(1), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("getMotebehovStatusWithTodayInsideOppfolgingstilfelleAfterSvarBehovEndDate") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("getMotebehovStatusWithNoMotebehovInsideSvarBehovUpperLimit") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + createKandidatInDB(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).plusDays(1), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, null) + } + + it("getMotebehovStatusWithSvarBehovAndMoteCreated") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + createKandidatInDB(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).plusDays(1), + end = LocalDate.now().plusDays(1), + ), + ) + + val arbeidsgiverFormSubmissionSvarJa = motebehovGenerator.lagNyArbeidsgiverFormSubmissionSvarJa() + val formValuesOutputDTOThatShouldBeCreated = arbeidsgiverFormSubmissionSvarJa.formSubmission + .toMotebehovFormSubmissionCombinedDTO() + .toMotebehovFormValuesOutputDTO() + + submitMotebehovAndSendOversikthendelse(arbeidsgiverFormSubmissionSvarJa) + verify { esyfovarselService.ferdigstillSvarMotebehovForArbeidsgiver(any(), ARBEIDSTAKER_FNR, any()) } + verify(exactly = 0) { esyfovarselService.ferdigstillSvarMotebehovForArbeidstaker(ARBEIDSTAKER_FNR) } + + mockRestServiceServer.reset() + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus( + true, + MotebehovSkjemaType.SVAR_BEHOV, + formValuesOutputDTOThatShouldBeCreated + ) + } + + it("getMotebehovStatusWithNoMotebehovAndNoMoteInsideSvarBehovLowerLimit") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + createKandidatInDB(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, null) + } + + it("getMotebehovStatusWithNoMotebehovAndNoMote") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson(), + ) + + createKandidatInDB(ARBEIDSTAKER_FNR) + + motebehovArbeidsgiverController.motebehovStatusArbeidsgiver(ARBEIDSTAKER_FNR, VIRKSOMHETSNUMMER) + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, null) + } + + it("getMotebehovStatusAndSendOversikthendelseWithMotebehovHarBehovTrue") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson(), + ) + val motebehov = motebehovGenerator.lagNyArbeidsgiverFormSubmissionSvarJa() + + lagreMotebehov(motebehov) + verifyMotebehovStatus(motebehov.formSubmission) + } + + it("getMotebehovStatusAndSendOversikthendelseWithMotebehovHarBehovFalse") { + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson(), + ) + + val motebehov = motebehovGenerator.lagNyArbeidsgiverFormSubmissionSvarNei() + + lagreMotebehov(motebehov) + verifyMotebehovStatus(motebehov.formSubmission) + } + + it("innsendtMotebehovForEgenLederFerdigstillerOgsaaSykmeldtVarsel") { + cleanDB() + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson(virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER)).copy( + personIdentNumber = LEDER_FNR, + ), + ) + val motebehov = NyttMotebehovArbeidsgiverFormSubmissionDTO( + arbeidstakerFnr = LEDER_FNR, + virksomhetsnummer = VIRKSOMHETSNUMMER, + formSubmission = MotebehovFormSubmissionDTO( + harMotebehov = true, + formSnapshot = mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot + ) + + ) + + lagreMotebehov(motebehov) + + verify(exactly = 1) { + esyfovarselService.ferdigstillSvarMotebehovForArbeidsgiver( + LEDER_FNR, + LEDER_FNR, + VIRKSOMHETSNUMMER + ) + } + verify(exactly = 1) { esyfovarselService.ferdigstillSvarMotebehovForArbeidstaker(LEDER_FNR) } + } + } + } + + private fun submitMotebehovAndSendOversikthendelse( + arbeidsgiverFormSubmissionInputDTO: NyttMotebehovArbeidsgiverFormSubmissionDTO + ) { + mockAndExpectBehandlendeEnhetRequest( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + ARBEIDSTAKER_FNR, + ) + + motebehovArbeidsgiverController.lagreMotebehovArbeidsgiver(arbeidsgiverFormSubmissionInputDTO) + if (arbeidsgiverFormSubmissionInputDTO.formSubmission.harMotebehov) { + verify { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } else { + verify(exactly = 0) { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } + } + + private fun lagreMotebehov(innsendtMotebehov: NyttMotebehovArbeidsgiverFormSubmissionDTO) { + mockAndExpectBehandlendeEnhetRequest( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + innsendtMotebehov.arbeidstakerFnr, + ) + + createKandidatInDB(innsendtMotebehov.arbeidstakerFnr) + + motebehovArbeidsgiverController.lagreMotebehovArbeidsgiver( + innsendtMotebehov + ) + + if (!innsendtMotebehov.formSubmission.harMotebehov) { + mockRestServiceServer.reset() + } + } + + private fun verifyMotebehovStatus(innsendtFormSubmission: MotebehovFormSubmissionDTO) { + val motebehovStatus = motebehovArbeidsgiverController.motebehovStatusArbeidsgiver( + ARBEIDSTAKER_FNR, + VIRKSOMHETSNUMMER, + ) + val formValuesOutputDTOThatShouldBeCreated = innsendtFormSubmission + .toMotebehovFormSubmissionCombinedDTO() + .toMotebehovFormValuesOutputDTO() + + assertTrue(motebehovStatus.visMotebehov) + assertEquals(MotebehovSkjemaType.SVAR_BEHOV, motebehovStatus.skjemaType) + val motebehov = motebehovStatus.motebehovWithFormValues!! + assertNotNull(motebehov) + assertThat(motebehov.opprettetAv).isEqualTo(LEDER_AKTORID) + assertThat(motebehov.arbeidstakerFnr).isEqualTo(ARBEIDSTAKER_FNR) + assertThat(motebehov.virksomhetsnummer).isEqualTo(VIRKSOMHETSNUMMER) + assertThat(motebehov.skjemaType).isEqualTo(motebehovStatus.skjemaType) + + assertThat(motebehov.formValues).usingRecursiveComparison() + .isEqualTo(formValuesOutputDTOThatShouldBeCreated) + + if (innsendtFormSubmission.harMotebehov) { + verify { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } else { + verify(exactly = 0) { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } + verify { esyfovarselService.ferdigstillSvarMotebehovForArbeidsgiver(any(), any(), any()) } + verify(exactly = 0) { esyfovarselService.ferdigstillSvarMotebehovForArbeidstaker(motebehov.arbeidstakerFnr) } + } + + private fun resetMockRestServers() { + mockRestServiceServer.reset() + mockRestServiceServerAzureAD.reset() + } + + private fun cleanDB() { + motebehovDAO.nullstillMotebehov(ARBEIDSTAKER_AKTORID) + motebehovDAO.nullstillMotebehov(LEDER_AKTORID) + oppfolgingstilfelleDAO.nullstillOppfolgingstilfeller(ARBEIDSTAKER_FNR) + oppfolgingstilfelleDAO.nullstillOppfolgingstilfeller(LEDER_AKTORID) + dialogmotekandidatDAO.delete(ARBEIDSTAKER_FNR) + dialogmotekandidatDAO.delete(LEDER_FNR) + } + + private fun mockBehandlendEnhetWithTilgangskontroll(fnr: String) { + mockAndExpectBehandlendeEnhetRequestWithTilgangskontroll( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + tilgangskontrollUrl, + fnr, + ) + } + + private fun createKandidatInDB(fnr: String) { + dialogmotekandidatDAO.create( + dialogmotekandidatExternalUUID = UUID.randomUUID().toString(), + createdAt = LocalDateTime.now().minusDays(DAYS_START_SVAR_BEHOV), + fnr = fnr, + kandidat = true, + arsak = DialogmotekandidatEndringArsak.STOPPUNKT.name, + ) + } +} diff --git a/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV3Test.kt b/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV3Test.kt index bf217f9a..e5704140 100644 --- a/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV3Test.kt +++ b/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV3Test.kt @@ -10,14 +10,12 @@ import no.nav.syfo.consumer.azuread.v2.AzureAdV2TokenConsumer import no.nav.syfo.consumer.pdl.PdlConsumer import no.nav.syfo.dialogmotekandidat.database.DialogmotekandidatDAO import no.nav.syfo.dialogmotekandidat.database.DialogmotekandidatEndringArsak -import no.nav.syfo.motebehov.MotebehovInnmelderType -import no.nav.syfo.motebehov.MotebehovSvarInputDTO +import no.nav.syfo.motebehov.MotebehovSvarLegacyDTO import no.nav.syfo.motebehov.api.internad.v3.MotebehovVeilederADControllerV3 import no.nav.syfo.motebehov.database.MotebehovDAO import no.nav.syfo.motebehov.motebehovstatus.DAYS_END_SVAR_BEHOV import no.nav.syfo.motebehov.motebehovstatus.DAYS_START_SVAR_BEHOV import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType -import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatus import no.nav.syfo.oppfolgingstilfelle.database.OppfolgingstilfelleDAO import no.nav.syfo.personoppgavehendelse.PersonoppgavehendelseProducer import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_AKTORID @@ -301,7 +299,7 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { ), ) - val motebehovSvarInputDTO = motebehovGenerator.lagMotebehovSvarInputDTO(true) + val motebehovSvarInputDTO = motebehovGenerator.lagMotebehovSvarLegacyDTO(true) submitMotebehovAndSendOversikthendelse(motebehovSvarInputDTO) @@ -334,15 +332,9 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { ), ) - val motebehovSvarInputDTO = motebehovGenerator.lagMotebehovSvarInputDTO(true) - val motebehovSvarOutputDTOThatShouldBeCreated = motebehovGenerator - .lagMotebehovSvarOutputDTOThatShouldBeCreatedFromInputDTO( - motebehovSvarInputDTO, - MotebehovSkjemaType.MELD_BEHOV, - MotebehovInnmelderType.ARBEIDSTAKER - ) + val legacyMotebehovSvar = motebehovGenerator.lagMotebehovSvarLegacyDTO(true) - submitMotebehovAndSendOversikthendelse(motebehovSvarInputDTO) + submitMotebehovAndSendOversikthendelse(legacyMotebehovSvar) mockRestServiceServer.reset() @@ -350,7 +342,7 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { .assertMotebehovStatus( true, MotebehovSkjemaType.MELD_BEHOV, - motebehovSvarOutputDTOThatShouldBeCreated + legacyMotebehovSvar ) } @@ -410,15 +402,9 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { ), ) - val motebehovSvarInputDTO = motebehovGenerator.lagMotebehovSvarInputDTO(true) - val motebehovSvarOutputDTOThatShouldBeCreated = motebehovGenerator - .lagMotebehovSvarOutputDTOThatShouldBeCreatedFromInputDTO( - motebehovSvarInputDTO, - MotebehovSkjemaType.SVAR_BEHOV, - MotebehovInnmelderType.ARBEIDSTAKER - ) + val legacyMotebehovSvar = motebehovGenerator.lagMotebehovSvarLegacyDTO(true) - submitMotebehovAndSendOversikthendelse(motebehovSvarInputDTO) + submitMotebehovAndSendOversikthendelse(legacyMotebehovSvar) verify { esyfovarselService.ferdigstillSvarMotebehovForArbeidstaker(ARBEIDSTAKER_FNR) } verify(exactly = 0) { esyfovarselService.ferdigstillSvarMotebehovForArbeidsgiver(any(), any(), any()) } @@ -428,7 +414,7 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { .assertMotebehovStatus( true, MotebehovSkjemaType.SVAR_BEHOV, - motebehovSvarOutputDTOThatShouldBeCreated + legacyMotebehovSvar ) } @@ -524,7 +510,7 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { ARBEIDSTAKER_FNR, ) - val motebehovSvar = motebehovGenerator.lagMotebehovSvarInputDTO(true) + val motebehovSvar = motebehovGenerator.lagMotebehovSvarLegacyDTO(true) motebehovArbeidstakerController.submitMotebehovArbeidstaker(motebehovSvar) val motebehovList = motebehovDAO.hentMotebehovListeForOgOpprettetAvArbeidstaker(ARBEIDSTAKER_AKTORID) @@ -535,7 +521,7 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { } } - private fun submitMotebehovAndSendOversikthendelse(motebehovSvar: MotebehovSvarInputDTO) { + private fun submitMotebehovAndSendOversikthendelse(motebehovSvar: MotebehovSvarLegacyDTO) { mockAndExpectBehandlendeEnhetRequest( azureTokenEndpoint, mockRestServiceServerAzureAD, @@ -561,15 +547,15 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { ARBEIDSTAKER_FNR, ) - val motebehovSvarInput = motebehovGenerator.lagMotebehovSvarInputDTO(harBehov) + val legacyMotebehovSvar = motebehovGenerator.lagMotebehovSvarLegacyDTO(harBehov) - motebehovArbeidstakerController.submitMotebehovArbeidstaker(motebehovSvarInput) + motebehovArbeidstakerController.submitMotebehovArbeidstaker(legacyMotebehovSvar) if (!harBehov) { mockRestServiceServer.reset() } - val motebehovStatus: MotebehovStatus = + val motebehovStatus = motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() assertTrue(motebehovStatus.visMotebehov) @@ -580,8 +566,8 @@ class MotebehovArbeidstakerControllerV3Test : IntegrationTest() { assertThat(motebehov.arbeidstakerFnr).isEqualTo(ARBEIDSTAKER_FNR) assertThat(motebehov.virksomhetsnummer).isEqualTo(VIRKSOMHETSNUMMER) assertThat(motebehov.skjemaType).isEqualTo(motebehovStatus.skjemaType) - assertThat(motebehov.motebehovSvar.harMotebehov).isEqualTo(motebehovSvarInput.harMotebehov) - assertThat(motebehov.motebehovSvar.forklaring).isEqualTo(motebehovSvarInput.forklaring) + assertThat(motebehov.motebehovSvar.harMotebehov).isEqualTo(legacyMotebehovSvar.harMotebehov) + assertThat(motebehov.motebehovSvar.forklaring).isEqualTo(legacyMotebehovSvar.forklaring) if (harBehov) { verify { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } } else { diff --git a/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV4Test.kt b/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV4Test.kt new file mode 100644 index 00000000..ce2b9612 --- /dev/null +++ b/src/test/kotlin/no/nav/syfo/motebehov/api/MotebehovArbeidstakerControllerV4Test.kt @@ -0,0 +1,627 @@ +package no.nav.syfo.motebehov.api + +import com.ninjasquad.springmockk.MockkBean +import io.kotest.extensions.spring.SpringExtension +import io.mockk.every +import io.mockk.verify +import no.nav.syfo.IntegrationTest +import no.nav.syfo.LocalApplication +import no.nav.syfo.consumer.azuread.v2.AzureAdV2TokenConsumer +import no.nav.syfo.consumer.pdl.PdlConsumer +import no.nav.syfo.dialogmotekandidat.database.DialogmotekandidatDAO +import no.nav.syfo.dialogmotekandidat.database.DialogmotekandidatEndringArsak +import no.nav.syfo.motebehov.MotebehovFormSubmissionDTO +import no.nav.syfo.motebehov.api.internad.v4.MotebehovVeilederADControllerV4 +import no.nav.syfo.motebehov.database.MotebehovDAO +import no.nav.syfo.motebehov.motebehovstatus.DAYS_END_SVAR_BEHOV +import no.nav.syfo.motebehov.motebehovstatus.DAYS_START_SVAR_BEHOV +import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType +import no.nav.syfo.motebehov.toMotebehovFormSubmissionCombinedDTO +import no.nav.syfo.motebehov.toMotebehovFormValuesOutputDTO +import no.nav.syfo.oppfolgingstilfelle.database.OppfolgingstilfelleDAO +import no.nav.syfo.personoppgavehendelse.PersonoppgavehendelseProducer +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_AKTORID +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_FNR +import no.nav.syfo.testhelper.UserConstants.VEILEDER_ID +import no.nav.syfo.testhelper.UserConstants.VIRKSOMHETSNUMMER +import no.nav.syfo.testhelper.UserConstants.VIRKSOMHETSNUMMER_2 +import no.nav.syfo.testhelper.assertion.assertMotebehovStatus +import no.nav.syfo.testhelper.clearCache +import no.nav.syfo.testhelper.generator.MotebehovGenerator +import no.nav.syfo.testhelper.generator.generateOppfolgingstilfellePerson +import no.nav.syfo.testhelper.generator.generatePdlHentPerson +import no.nav.syfo.testhelper.mockAndExpectBehandlendeEnhetRequest +import no.nav.syfo.testhelper.mockAndExpectBehandlendeEnhetRequestWithTilgangskontroll +import no.nav.syfo.util.TokenValidationUtil +import no.nav.syfo.varsel.esyfovarsel.EsyfovarselService +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.cache.CacheManager +import org.springframework.test.web.client.MockRestServiceServer +import org.springframework.web.client.RestTemplate +import java.time.LocalDate +import java.time.LocalDateTime +import java.util.* +import java.util.function.Consumer + +@TestConfiguration +@SpringBootTest(classes = [LocalApplication::class]) +class MotebehovArbeidstakerControllerV4Test : IntegrationTest() { + @Value("\${azure.openid.config.token.endpoint}") + private lateinit var azureTokenEndpoint: String + + @Value("\${syfobehandlendeenhet.url}") + private lateinit var behandlendeenhetUrl: String + + @Value("\${istilgangskontroll.url}") + private lateinit var tilgangskontrollUrl: String + + @Autowired + private lateinit var motebehovArbeidstakerController: MotebehovArbeidstakerControllerV4 + + @Autowired + private lateinit var motebehovVeilederController: MotebehovVeilederADControllerV4 + + @Autowired + private lateinit var motebehovDAO: MotebehovDAO + + @Autowired + private lateinit var cacheManager: CacheManager + + @Autowired + private lateinit var oppfolgingstilfelleDAO: OppfolgingstilfelleDAO + + @Autowired + private lateinit var dialogmotekandidatDAO: DialogmotekandidatDAO + + @Autowired + @Qualifier("AzureAD") + private lateinit var restTemplateAzureAD: RestTemplate + + @Autowired + private lateinit var restTemplate: RestTemplate + + @Autowired + private lateinit var tokenValidationUtil: TokenValidationUtil + + @MockkBean(relaxed = true) + private lateinit var esyfovarselService: EsyfovarselService + + @MockkBean + private lateinit var pdlConsumer: PdlConsumer + + @MockkBean(relaxed = true) + private lateinit var personoppgavehendelseProducer: PersonoppgavehendelseProducer + + private lateinit var mockRestServiceServerAzureAD: MockRestServiceServer + private lateinit var mockRestServiceServer: MockRestServiceServer + + private val motebehovGenerator = MotebehovGenerator() + + init { + extensions(SpringExtension) + beforeTest { + every { pdlConsumer.person(ARBEIDSTAKER_FNR) } returns generatePdlHentPerson(null, null) + every { pdlConsumer.aktorid(any()) } returns ARBEIDSTAKER_AKTORID + every { pdlConsumer.fnr(any()) } returns ARBEIDSTAKER_FNR + every { pdlConsumer.isKode6(ARBEIDSTAKER_FNR) } returns false + + mockRestServiceServer = MockRestServiceServer.bindTo(restTemplate).build() + mockRestServiceServerAzureAD = MockRestServiceServer.bindTo(restTemplateAzureAD).build() + cleanDB() + } + + afterEach { + resetMockRestServers() + cacheManager.cacheNames + .forEach( + Consumer { cacheName: String -> + val cache = cacheManager.getCache(cacheName) + cache?.clear() + }, + ) + AzureAdV2TokenConsumer.Companion.clearCache() + cleanDB() + } + + describe("Møtebehov arbeidstaker controller V4") { + it("get MotebehovStatus With No Oppfolgingstilfelle") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(false, null, null) + } + + it("get MotebehovStatus With Today Outside OppfolgingstilfelleStart") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().plusDays(1), + end = LocalDate.now().plusDays(10), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(false, null, null) + } + + it("get MotebehovStatus With Today Outside OppfolgingstilfelleEnd") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(17), + end = LocalDate.now().minusDays(16), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(false, null, null) + } + + it( + "get MotebehovStatus With Today Inside Oppfolgingstilfelle Merged By Active And" + + " Expired Oppfolgingstilfelle No Overlap" + ) { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + val activeOppfolgingstilfelleStartDate = LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV).plusDays(1) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate.minusDays(2), + end = activeOppfolgingstilfelleStartDate.minusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER), + ), + ) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate, + end = LocalDate.now().plusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER_2), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it( + "getMotebehovStatus With Today Inside Oppfolgingstilfelle Merged By Active And" + + " Expired Oppfolgingstilfelle With Overlap" + ) { + createKandidatInDB() + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + + val activeOppfolgingstilfelleStartDate = LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV).plusDays(1) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate.minusDays(2), + end = activeOppfolgingstilfelleStartDate, + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER), + ), + ) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = activeOppfolgingstilfelleStartDate, + end = LocalDate.now().plusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER_2), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, null) + } + + it("get MotebehovStatus With Today Inside Oppfolgingstilfelle Merged By 2 Oppfolgingstilfeller") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).minusDays(1), + end = LocalDate.now(), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER), + ), + ) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(2), + end = LocalDate.now().plusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER_2), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("get MotebehovStatus With Today Inside Oppfolgingstilfelle Day1") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now(), + end = LocalDate.now(), + ), + ) + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("get MotebehovStatus With Today Inside Oppfolgingstilfelle LastDay") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).minusDays(1), + end = LocalDate.now(), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("get MotebehovStatus With Today Inside Oppfolgingstilfelle") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now(), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("get MotebehovStatus With Today Inside Oppfolgingstilfelle, MeldBehov Submitted And Behandlet") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now(), + end = LocalDate.now().plusDays(1), + ), + ) + + val motebehovFormSubmissionDTO = motebehovGenerator.lagFormSubmissionArbeidstakerMeldDTO() + + submitMotebehovAndSendOversikthendelse(motebehovFormSubmissionDTO) + + resetMockRestServers() + mockBehandlendEnhetWithTilgangskontroll(ARBEIDSTAKER_FNR) + tokenValidationUtil.logInAsNavCounselor(VEILEDER_ID) + motebehovVeilederController.behandleMotebehov(ARBEIDSTAKER_FNR) + + resetMockRestServers() + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus( + true, + MotebehovSkjemaType.MELD_BEHOV, + null + ) + } + + it( + "get MotebehovStatus With Today Inside Oppfolgingstilfelle MeldBehov," + + " Moteplanlegger Active, MeldBehov Submitted" + ) { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now(), + end = LocalDate.now().plusDays(1), + ), + ) + + val motebehovFormSubmissionDTO = motebehovGenerator.lagFormSubmissionArbeidstakerMeldDTO() + + submitMotebehovAndSendOversikthendelse(motebehovFormSubmissionDTO) + + mockRestServiceServer.reset() + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus( + true, + MotebehovSkjemaType.MELD_BEHOV, + motebehovFormSubmissionDTO + .toMotebehovFormSubmissionCombinedDTO() + .toMotebehovFormValuesOutputDTO() + ) + } + + it("get MotebehovStatus With Today Inside Oppfolgingstilfelle Before SvarBehov StartDate") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV).plusDays(1), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("get MotebehovStatus With Today Inside Oppfolgingstilfelle After SvarBehov EndDate") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.MELD_BEHOV, null) + } + + it("get MotebehovStatus With No Motebehov And Mote Inside SvarBehov Upper Limit") { + createKandidatInDB() + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).plusDays(1), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, null) + } + + it("get MotebehovStatus with SvarBehov and Mote created") { + createKandidatInDB() + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).plusDays(1), + end = LocalDate.now().plusDays(1), + ), + ) + + val motebehovFormSubmissionDTO = motebehovGenerator.lagFormSubmissionArbeidstakerSvarJaDTO() + + submitMotebehovAndSendOversikthendelse(motebehovFormSubmissionDTO) + verify { esyfovarselService.ferdigstillSvarMotebehovForArbeidstaker(ARBEIDSTAKER_FNR) } + verify(exactly = 0) { esyfovarselService.ferdigstillSvarMotebehovForArbeidsgiver(any(), any(), any()) } + + mockRestServiceServer.reset() + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus( + true, + MotebehovSkjemaType.SVAR_BEHOV, + motebehovFormSubmissionDTO + .toMotebehovFormSubmissionCombinedDTO() + .toMotebehovFormValuesOutputDTO() + ) + } + + it("get MotebehovStatus with no Motebehov and no Mote inside SvarBehov lower limit") { + createKandidatInDB() + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_START_SVAR_BEHOV), + end = LocalDate.now().plusDays(1), + ), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, null) + } + + it("get MotebehovStatus with no Motebehov and no Mote") { + createKandidatInDB() + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson(), + ) + + motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + .assertMotebehovStatus(true, MotebehovSkjemaType.SVAR_BEHOV, null) + } + + it("get MotebehovStatus and sendOversikthendelse with Motebehov harBehov=true") { + createKandidatInDB() + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson(virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER)).copy( + personIdentNumber = ARBEIDSTAKER_FNR, + ), + ) + + lagreOgHentMotebehovOgSendOversikthendelse(harBehov = true) + } + + it("get MotebehovStatus and SendOversikthendelse with Motebehov harBehov=false") { + createKandidatInDB() + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson(virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER)).copy( + personIdentNumber = ARBEIDSTAKER_FNR, + ), + ) + + lagreOgHentMotebehovOgSendOversikthendelse(harBehov = false) + } + + it("submitMotebehov multiple active Oppfolgingstilfeller") { + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(DAYS_END_SVAR_BEHOV).minusDays(1), + end = LocalDate.now(), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER), + ), + ) + + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson( + start = LocalDate.now().minusDays(2), + end = LocalDate.now().plusDays(1), + virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER_2), + ), + ) + + mockAndExpectBehandlendeEnhetRequest( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + ARBEIDSTAKER_FNR, + ) + mockAndExpectBehandlendeEnhetRequest( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + ARBEIDSTAKER_FNR, + ) + + val formSubmission = motebehovGenerator.lagFormSubmissionArbeidstakerSvarJaDTO() + + motebehovArbeidstakerController.submitMotebehovArbeidstaker(formSubmission) + + val motebehovList = motebehovDAO.hentMotebehovListeForOgOpprettetAvArbeidstaker(ARBEIDSTAKER_AKTORID) + + assertEquals(2, motebehovList.size) + verify(exactly = 2) { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } + } + } + + private fun submitMotebehovAndSendOversikthendelse(motebehovFormSubmission: MotebehovFormSubmissionDTO) { + mockAndExpectBehandlendeEnhetRequest( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + ARBEIDSTAKER_FNR, + ) + + motebehovArbeidstakerController.submitMotebehovArbeidstaker(motebehovFormSubmission) + if (motebehovFormSubmission.harMotebehov) { + verify { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } else { + verify(exactly = 0) { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } + } + + private fun lagreOgHentMotebehovOgSendOversikthendelse(harBehov: Boolean) { + mockAndExpectBehandlendeEnhetRequest( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + ARBEIDSTAKER_FNR, + ) + + val formSubmission = + if (harBehov) { + motebehovGenerator.lagFormSubmissionArbeidstakerSvarJaDTO() + } else { + motebehovGenerator.lagFormSubmissionArbeidstakerSvarNeiDTO() + } + + motebehovArbeidstakerController.submitMotebehovArbeidstaker(formSubmission) + + if (!harBehov) { + mockRestServiceServer.reset() + } + + val motebehovStatus = motebehovArbeidstakerController.motebehovStatusArbeidstakerWithCodeSixUsers() + + assertTrue(motebehovStatus.visMotebehov) + assertEquals(MotebehovSkjemaType.SVAR_BEHOV, motebehovStatus.skjemaType) + val motebehov = motebehovStatus.motebehovWithFormValues + assertNotNull(motebehov) + assertThat(motebehov?.opprettetAv).isEqualTo(ARBEIDSTAKER_AKTORID) + assertThat(motebehov?.arbeidstakerFnr).isEqualTo(ARBEIDSTAKER_FNR) + assertThat(motebehov?.virksomhetsnummer).isEqualTo(VIRKSOMHETSNUMMER) + assertThat(motebehov?.skjemaType).isEqualTo(motebehovStatus.skjemaType) + assertNotNull(motebehov?.formValues) + assertThat(motebehov?.formValues?.harMotebehov).isEqualTo(formSubmission.harMotebehov) + assertThat(motebehov?.formValues?.formSnapshot).isEqualTo(formSubmission.formSnapshot) + if (harBehov) { + verify { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } else { + verify(exactly = 0) { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } + verify { + if (motebehov != null) { + esyfovarselService.ferdigstillSvarMotebehovForArbeidstaker(motebehov.arbeidstakerFnr) + } + } + verify(exactly = 0) { esyfovarselService.ferdigstillSvarMotebehovForArbeidsgiver(any(), any(), any()) } + } + + private fun mockBehandlendEnhetWithTilgangskontroll(fnr: String) { + mockAndExpectBehandlendeEnhetRequestWithTilgangskontroll( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + tilgangskontrollUrl, + fnr, + ) + } + + private fun createKandidatInDB() { + dialogmotekandidatDAO.create( + dialogmotekandidatExternalUUID = UUID.randomUUID().toString(), + createdAt = LocalDateTime.now().minusDays(DAYS_START_SVAR_BEHOV), + fnr = ARBEIDSTAKER_FNR, + kandidat = true, + arsak = DialogmotekandidatEndringArsak.STOPPUNKT.name, + ) + } + + private fun resetMockRestServers() { + mockRestServiceServer.reset() + mockRestServiceServerAzureAD.reset() + } + + private fun cleanDB() { + motebehovDAO.nullstillMotebehov(ARBEIDSTAKER_AKTORID) + oppfolgingstilfelleDAO.nullstillOppfolgingstilfeller(ARBEIDSTAKER_FNR) + dialogmotekandidatDAO.delete(ARBEIDSTAKER_FNR) + } +} diff --git a/src/test/kotlin/no/nav/syfo/motebehov/api/internad/v3/MotebehovVeilederADControllerV3Test.kt b/src/test/kotlin/no/nav/syfo/motebehov/api/internad/v3/MotebehovVeilederADControllerV3Test.kt index 6b6ff660..268ca360 100644 --- a/src/test/kotlin/no/nav/syfo/motebehov/api/internad/v3/MotebehovVeilederADControllerV3Test.kt +++ b/src/test/kotlin/no/nav/syfo/motebehov/api/internad/v3/MotebehovVeilederADControllerV3Test.kt @@ -13,12 +13,12 @@ import no.nav.syfo.LocalApplication import no.nav.syfo.consumer.azuread.v2.AzureAdV2TokenConsumer import no.nav.syfo.consumer.brukertilgang.BrukertilgangConsumer import no.nav.syfo.consumer.pdl.PdlConsumer -import no.nav.syfo.motebehov.MotebehovSvarInputDTO -import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverInputDTO +import no.nav.syfo.motebehov.MotebehovSvarLegacyDTO +import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverLegacyDTO import no.nav.syfo.motebehov.api.MotebehovArbeidsgiverControllerV3 import no.nav.syfo.motebehov.api.MotebehovArbeidstakerControllerV3 import no.nav.syfo.motebehov.api.dbCreateOppfolgingstilfelle -import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTO +import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTOv3 import no.nav.syfo.motebehov.database.MotebehovDAO import no.nav.syfo.motebehov.historikk.Historikk import no.nav.syfo.motebehov.historikk.HistorikkService @@ -285,12 +285,12 @@ class MotebehovVeilederADControllerV3Test : IntegrationTest() { } } - private fun arbeidsgiverLoggerInnOgLagrerMotebehov(): NyttMotebehovArbeidsgiverInputDTO { - val motebehovSvarInputDTO = MotebehovSvarInputDTO( + private fun arbeidsgiverLoggerInnOgLagrerMotebehov(): NyttMotebehovArbeidsgiverLegacyDTO { + val motebehovSvarInputDTO = MotebehovSvarLegacyDTO( harMotebehov = true, forklaring = "", ) - val nyttMotebehovInputDTO = NyttMotebehovArbeidsgiverInputDTO( + val nyttMotebehovInputDTO = NyttMotebehovArbeidsgiverLegacyDTO( arbeidstakerFnr = ARBEIDSTAKER_FNR, virksomhetsnummer = VIRKSOMHETSNUMMER, motebehovSvarInputDTO, @@ -303,8 +303,8 @@ class MotebehovVeilederADControllerV3Test : IntegrationTest() { private fun sykmeldtLoggerInnOgLagrerMotebehov( harBehov: Boolean, - ): MotebehovSvarInputDTO { - val motebehovSvar = MotebehovSvarInputDTO( + ): MotebehovSvarLegacyDTO { + val motebehovSvar = MotebehovSvarLegacyDTO( harMotebehov = harBehov, forklaring = "", ) @@ -326,7 +326,7 @@ class MotebehovVeilederADControllerV3Test : IntegrationTest() { motebehovVeilederController.behandleMotebehov(fnr) } - private fun loggInnOgKallHentMotebehovListe(fnr: String, veileder: String): List { + private fun loggInnOgKallHentMotebehovListe(fnr: String, veileder: String): List { tokenValidationUtil.logInAsNavCounselor(veileder) return motebehovVeilederController.hentMotebehovListe(fnr) } diff --git a/src/test/kotlin/no/nav/syfo/motebehov/api/internad/v4/MotebehovVeilederADControllerV4Test.kt b/src/test/kotlin/no/nav/syfo/motebehov/api/internad/v4/MotebehovVeilederADControllerV4Test.kt new file mode 100644 index 00000000..22fe69de --- /dev/null +++ b/src/test/kotlin/no/nav/syfo/motebehov/api/internad/v4/MotebehovVeilederADControllerV4Test.kt @@ -0,0 +1,394 @@ +package no.nav.syfo.motebehov.api.internad.v4 + +import com.ninjasquad.springmockk.MockkBean +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.extensions.spring.SpringExtension +import io.kotest.matchers.date.shouldBeWithin +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.verify +import no.nav.syfo.IntegrationTest +import no.nav.syfo.LocalApplication +import no.nav.syfo.consumer.azuread.v2.AzureAdV2TokenConsumer +import no.nav.syfo.consumer.brukertilgang.BrukertilgangConsumer +import no.nav.syfo.consumer.pdl.PdlConsumer +import no.nav.syfo.motebehov.MotebehovFormSubmissionDTO +import no.nav.syfo.motebehov.NyttMotebehovArbeidsgiverFormSubmissionDTO +import no.nav.syfo.motebehov.api.MotebehovArbeidsgiverControllerV4 +import no.nav.syfo.motebehov.api.MotebehovArbeidstakerControllerV4 +import no.nav.syfo.motebehov.api.dbCreateOppfolgingstilfelle +import no.nav.syfo.motebehov.api.internad.dto.MotebehovVeilederDTOv4 +import no.nav.syfo.motebehov.database.MotebehovDAO +import no.nav.syfo.motebehov.formSnapshot.mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot +import no.nav.syfo.motebehov.formSnapshot.mockArbeidstakerSvarJaFormSnapshot +import no.nav.syfo.motebehov.formSnapshot.mockArbeidstakerSvarNeiFormSnapshot +import no.nav.syfo.motebehov.historikk.Historikk +import no.nav.syfo.motebehov.historikk.HistorikkService +import no.nav.syfo.motebehov.toMotebehovFormSubmissionCombinedDTO +import no.nav.syfo.motebehov.toMotebehovFormValuesOutputDTO +import no.nav.syfo.oppfolgingstilfelle.database.OppfolgingstilfelleDAO +import no.nav.syfo.personoppgavehendelse.PersonoppgavehendelseProducer +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_AKTORID +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_FNR +import no.nav.syfo.testhelper.UserConstants.LEDER_AKTORID +import no.nav.syfo.testhelper.UserConstants.LEDER_FNR +import no.nav.syfo.testhelper.UserConstants.PERSON_FULL_NAME +import no.nav.syfo.testhelper.UserConstants.VEILEDER_2_ID +import no.nav.syfo.testhelper.UserConstants.VEILEDER_ID +import no.nav.syfo.testhelper.UserConstants.VIRKSOMHETSNUMMER +import no.nav.syfo.testhelper.clearCache +import no.nav.syfo.testhelper.generator.generateOppfolgingstilfellePerson +import no.nav.syfo.testhelper.generator.generatePdlHentPerson +import no.nav.syfo.testhelper.mockAndExpectBehandlendeEnhetRequest +import no.nav.syfo.testhelper.mockSvarFraIstilgangskontrollTilgangTilBruker +import no.nav.syfo.util.TokenValidationUtil +import org.junit.jupiter.api.TestInstance +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.cache.CacheManager +import org.springframework.http.HttpStatus +import org.springframework.test.web.client.MockRestServiceServer +import org.springframework.web.client.RestTemplate +import java.time.Duration +import java.time.LocalDateTime +import java.util.function.Consumer + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestConfiguration +@SpringBootTest(classes = [LocalApplication::class]) +class MotebehovVeilederADControllerV4Test : IntegrationTest() { + + @Value("\${azure.openid.config.token.endpoint}") + private lateinit var azureTokenEndpoint: String + + @Value("\${istilgangskontroll.url}") + private lateinit var tilgangskontrollUrl: String + + @Value("\${syfobehandlendeenhet.url}") + private lateinit var behandlendeenhetUrl: String + + @Autowired + private lateinit var motebehovArbeidstakerControllerV4: MotebehovArbeidstakerControllerV4 + + @Autowired + private lateinit var motebehovArbeidsgiverControllerV4: MotebehovArbeidsgiverControllerV4 + + @Autowired + private lateinit var oppfolgingstilfelleDAO: OppfolgingstilfelleDAO + + @Autowired + private lateinit var motebehovVeilederController: MotebehovVeilederADControllerV4 + + @Autowired + private lateinit var motebehovDAO: MotebehovDAO + + @Autowired + private lateinit var cacheManager: CacheManager + + @Autowired + @Qualifier("AzureAD") + private lateinit var restTemplateAzureAD: RestTemplate + + @Autowired + private lateinit var restTemplate: RestTemplate + + @Autowired + private lateinit var tokenValidationUtil: TokenValidationUtil + + @MockkBean + private lateinit var brukertilgangConsumer: BrukertilgangConsumer + + @MockkBean(relaxed = true) + private lateinit var pdlConsumer: PdlConsumer + + @MockkBean(relaxed = true) + private lateinit var personoppgavehendelseProducer: PersonoppgavehendelseProducer + + private lateinit var mockRestServiceServerAzureAD: MockRestServiceServer + private lateinit var mockRestServiceServer: MockRestServiceServer + + init { + extensions(SpringExtension) + beforeTest { + cleanDB() + + mockRestServiceServer = MockRestServiceServer.bindTo(restTemplate).build() + mockRestServiceServerAzureAD = MockRestServiceServer.bindTo(restTemplateAzureAD).build() + + every { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } returns Unit + every { brukertilgangConsumer.hasAccessToAnsatt(ARBEIDSTAKER_FNR) } returns true + + every { pdlConsumer.aktorid(ARBEIDSTAKER_FNR) } returns ARBEIDSTAKER_AKTORID + every { pdlConsumer.aktorid(LEDER_FNR) } returns LEDER_AKTORID + every { pdlConsumer.fnr(ARBEIDSTAKER_AKTORID) } returns ARBEIDSTAKER_FNR + every { pdlConsumer.fnr(LEDER_AKTORID) } returns LEDER_FNR + every { pdlConsumer.person(ARBEIDSTAKER_FNR) } returns generatePdlHentPerson(null, null) + every { pdlConsumer.person(LEDER_FNR) } returns generatePdlHentPerson(null, null) + + createOppfolgingstilfelle() + } + + afterTest { + mockRestServiceServer.verify() + mockRestServiceServerAzureAD.verify() + resetMockRestServers() + cacheManager.cacheNames + .forEach( + Consumer { cacheName: String -> + val cache = cacheManager.getCache(cacheName) + cache?.clear() + }, + ) + cleanDB() + AzureAdV2TokenConsumer.Companion.clearCache() + tokenValidationUtil.resetAll() + } + + describe("MotebehovVeilederADControllerV4") { + it("arbeidsgiver lagrer Motebehov og Veileder henter Motebehov") { + // Arbeidsgiver lagrer nytt motebehov + mockBehandlendEnhet(ARBEIDSTAKER_FNR) + val submitted = arbeidsgiverLoggerInnOgLagrerMotebehov() + + // Veileder henter møtebehov + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + val motebehovListe = loggInnOgKallHentMotebehovListe(ARBEIDSTAKER_FNR, VEILEDER_ID) + motebehovListe.size shouldBe 1 + val motebehov = motebehovListe[0] + motebehov.opprettetAv shouldBe LEDER_AKTORID + motebehov.arbeidstakerFnr shouldBe ARBEIDSTAKER_FNR + motebehov.virksomhetsnummer shouldBe VIRKSOMHETSNUMMER + + motebehov.formValues.harMotebehov shouldBe submitted.formSubmission.harMotebehov + motebehov.formValues shouldBe submitted.formSubmission + .toMotebehovFormSubmissionCombinedDTO() + .toMotebehovFormValuesOutputDTO() + } + + it("arbeidstaker lagrer Motebehov og Veileder henter Motebehov") { + // Arbeidstaker lagrer nytt motebehov + mockBehandlendEnhet(ARBEIDSTAKER_FNR) + val submitted = sykmeldtLoggerInnOgLagrerMotebehov(true) + + // Veileder henter møtebehov + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + val motebehovListe = loggInnOgKallHentMotebehovListe(ARBEIDSTAKER_FNR, VEILEDER_ID) + motebehovListe.size shouldBe 1 + val motebehov = motebehovListe[0] + motebehov.opprettetAv shouldBe ARBEIDSTAKER_AKTORID + motebehov.arbeidstakerFnr shouldBe ARBEIDSTAKER_FNR + motebehov.virksomhetsnummer shouldBe VIRKSOMHETSNUMMER + + motebehov.formValues.harMotebehov shouldBe submitted.harMotebehov + motebehov.formValues shouldBe submitted + .toMotebehovFormSubmissionCombinedDTO() + .toMotebehovFormValuesOutputDTO() + } + + it("hent Historikk") { + // Arbeidsgiver lagrer motebehov + mockBehandlendEnhet(ARBEIDSTAKER_FNR) + arbeidsgiverLoggerInnOgLagrerMotebehov() + + // Veileder henter motebehovliste + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + val motebehovListe = loggInnOgKallHentMotebehovListe(ARBEIDSTAKER_FNR, VEILEDER_ID) + val motebehov = motebehovListe[0] + resetMockRestServers() + + // Veileder behandler motebehov + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + loggInnOgKallBehandleMotebehov(ARBEIDSTAKER_FNR, VEILEDER_ID) + + // Veileder leser motebehov historikk + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + val historikkListe = loggInnOgKallHentMotebehovHistorikk(ARBEIDSTAKER_FNR, VEILEDER_ID) + historikkListe.size shouldBe 2 + val (opprettetAv, tekst, tidspunkt) = historikkListe[0] + opprettetAv shouldBe LEDER_AKTORID + tekst shouldBe PERSON_FULL_NAME + HistorikkService.HAR_SVART_PAA_MOTEBEHOV + tidspunkt shouldBe motebehov.opprettetDato + val (_, tekst1, tidspunkt1) = historikkListe[1] + tekst1 shouldBe HistorikkService.MOTEBEHOVET_BLE_LEST_AV + VEILEDER_ID + val today = LocalDateTime.now() + tidspunkt1.shouldBeWithin(Duration.ofSeconds(1), today) + } + + it("hent ubehandlede Motebehov") { + mockBehandlendEnhet(ARBEIDSTAKER_FNR) + sykmeldtLoggerInnOgLagrerMotebehov(true) + resetMockRestServers() + arbeidsgiverLoggerInnOgLagrerMotebehov() + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + tokenValidationUtil.logInAsNavCounselor(VEILEDER_ID) + val motebehovListe = loggInnOgKallHentMotebehovListe(ARBEIDSTAKER_FNR, VEILEDER_ID) + + motebehovListe.forEach( + Consumer { motebehovVeilederDTO -> + motebehovVeilederDTO.behandletTidspunkt shouldBe null + motebehovVeilederDTO.behandletVeilederIdent shouldBe null + }, + ) + } + + it("behandle kun motebehov med Motebehov") { + // AT og AG lagrer møtebehov + mockBehandlendEnhet(ARBEIDSTAKER_FNR) + sykmeldtLoggerInnOgLagrerMotebehov(false) + resetMockRestServers() + arbeidsgiverLoggerInnOgLagrerMotebehov() + + // Veileder behandler møtebehov med behov satt til 'true' + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + loggInnOgKallBehandleMotebehov(ARBEIDSTAKER_FNR, VEILEDER_ID) + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + val motebehovListe = loggInnOgKallHentMotebehovListe(ARBEIDSTAKER_FNR, VEILEDER_ID) + motebehovListe[0].behandletTidspunkt shouldBe null + motebehovListe[0].behandletVeilederIdent shouldBe null + motebehovListe[1].behandletTidspunkt.shouldNotBeNull() + motebehovListe[1].behandletVeilederIdent shouldBe VEILEDER_ID + verify(exactly = 2) { personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) } + } + + it("behandle Motebehov og ulik Veileder behandler") { + mockBehandlendEnhet(ARBEIDSTAKER_FNR) + sykmeldtLoggerInnOgLagrerMotebehov(true) + behandleMotebehov(ARBEIDSTAKER_AKTORID, VEILEDER_ID) + resetMockRestServers() + arbeidsgiverLoggerInnOgLagrerMotebehov() + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + loggInnOgKallBehandleMotebehov(ARBEIDSTAKER_FNR, VEILEDER_2_ID) + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + val motebehovListe1 = loggInnOgKallHentMotebehovListe(ARBEIDSTAKER_FNR, VEILEDER_ID) + motebehovListe1[0].behandletTidspunkt.shouldNotBeNull() + motebehovListe1[0].behandletVeilederIdent shouldBe VEILEDER_ID + motebehovListe1[1].behandletTidspunkt.shouldNotBeNull() + motebehovListe1[1].behandletVeilederIdent shouldBe VEILEDER_2_ID + verify(exactly = 3) { + personoppgavehendelseProducer.sendPersonoppgavehendelse(any(), any()) + } + } + + it("behandle ikkeeksisterende Motebehov") { + mockBehandlendEnhet(ARBEIDSTAKER_FNR) + sykmeldtLoggerInnOgLagrerMotebehov(true) + behandleMotebehov(ARBEIDSTAKER_AKTORID, VEILEDER_ID) + resetMockRestServers() + mockSvarFraIstilgangskontroll(ARBEIDSTAKER_FNR, HttpStatus.OK) + + shouldThrow { loggInnOgKallBehandleMotebehov(ARBEIDSTAKER_FNR, VEILEDER_2_ID) } + } + } + } + + private fun arbeidsgiverLoggerInnOgLagrerMotebehov(): NyttMotebehovArbeidsgiverFormSubmissionDTO { + val formSubmission = MotebehovFormSubmissionDTO( + harMotebehov = true, + formSnapshot = mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot + ) + val arbeidsgiverFormSubmission = NyttMotebehovArbeidsgiverFormSubmissionDTO( + arbeidstakerFnr = ARBEIDSTAKER_FNR, + virksomhetsnummer = VIRKSOMHETSNUMMER, + formSubmission, + ) + tokenValidationUtil.logInAsDialogmoteUser(LEDER_FNR) + motebehovArbeidsgiverControllerV4.lagreMotebehovArbeidsgiver(arbeidsgiverFormSubmission) + + return arbeidsgiverFormSubmission + } + + private fun sykmeldtLoggerInnOgLagrerMotebehov( + harBehov: Boolean, + ): MotebehovFormSubmissionDTO { + val motebehovFormSubmission = MotebehovFormSubmissionDTO( + harMotebehov = harBehov, + formSnapshot = if (harBehov) { + mockArbeidstakerSvarJaFormSnapshot + } else { + mockArbeidstakerSvarNeiFormSnapshot + }, + ) + tokenValidationUtil.logInAsDialogmoteUser(ARBEIDSTAKER_FNR) + motebehovArbeidstakerControllerV4.submitMotebehovArbeidstaker(motebehovFormSubmission) + + return motebehovFormSubmission + } + + private fun behandleMotebehov(aktoerId: String, veileder: String) { + val ubehandledeMotebehov = motebehovDAO.hentUbehandledeMotebehov(aktoerId) + ubehandledeMotebehov.forEach { + motebehovDAO.oppdaterUbehandledeMotebehovTilBehandlet(it.uuid, veileder) + } + } + + private fun loggInnOgKallBehandleMotebehov(fnr: String, veileder: String) { + tokenValidationUtil.logInAsNavCounselor(veileder) + motebehovVeilederController.behandleMotebehov(fnr) + } + + private fun loggInnOgKallHentMotebehovListe(fnr: String, veileder: String): List { + tokenValidationUtil.logInAsNavCounselor(veileder) + return motebehovVeilederController.hentMotebehovListe(fnr) + } + + private fun loggInnOgKallHentMotebehovHistorikk(fnr: String, veileder: String): List { + tokenValidationUtil.logInAsNavCounselor(veileder) + return motebehovVeilederController.hentMotebehovHistorikk(fnr) + } + + private fun mockSvarFraIstilgangskontroll( + fnr: String, + status: HttpStatus, + ) { + mockSvarFraIstilgangskontrollTilgangTilBruker( + azureTokenEndpoint = azureTokenEndpoint, + tilgangskontrollUrl = tilgangskontrollUrl, + mockRestServiceServer = mockRestServiceServer, + mockRestServiceServerAzureAD = mockRestServiceServerAzureAD, + status = status, + fnr = fnr, + ) + } + + private fun mockBehandlendEnhet(fnr: String) { + mockAndExpectBehandlendeEnhetRequest( + azureTokenEndpoint, + mockRestServiceServerAzureAD, + mockRestServiceServer, + behandlendeenhetUrl, + fnr, + ) + } + + private fun createOppfolgingstilfelle() { + dbCreateOppfolgingstilfelle( + oppfolgingstilfelleDAO, + generateOppfolgingstilfellePerson(virksomhetsnummerList = listOf(VIRKSOMHETSNUMMER)).copy( + personIdentNumber = ARBEIDSTAKER_FNR, + ), + ) + } + + private fun resetMockRestServers() { + mockRestServiceServer.reset() + mockRestServiceServerAzureAD.reset() + } + + private fun cleanDB() { + motebehovDAO.nullstillMotebehov(ARBEIDSTAKER_AKTORID) + oppfolgingstilfelleDAO.nullstillOppfolgingstilfeller(ARBEIDSTAKER_FNR) + } +} diff --git a/src/test/kotlin/no/nav/syfo/motebehov/database/MotebehovDAOTest.kt b/src/test/kotlin/no/nav/syfo/motebehov/database/MotebehovDAOTest.kt index ff54d0f3..b09ffdb5 100644 --- a/src/test/kotlin/no/nav/syfo/motebehov/database/MotebehovDAOTest.kt +++ b/src/test/kotlin/no/nav/syfo/motebehov/database/MotebehovDAOTest.kt @@ -1,10 +1,20 @@ package no.nav.syfo.motebehov.database import io.kotest.extensions.spring.SpringExtension +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import no.nav.syfo.IntegrationTest import no.nav.syfo.LocalApplication +import no.nav.syfo.motebehov.extractFormValuesFromFormSnapshot +import no.nav.syfo.motebehov.formSnapshot.FORM_IDENTIFIER_ARBEIDSTAKER_SVAR +import no.nav.syfo.motebehov.formSnapshot.MOCK_ARBEIDSTAKER_SVAR_SPRAK +import no.nav.syfo.motebehov.formSnapshot.MOCK_ARRBEIDSTAKER_SVAR_BEGRUNNELSE +import no.nav.syfo.motebehov.formSnapshot.MOCK_SNAPSHOTS_FORM_SEMANTIC_VERSION +import no.nav.syfo.motebehov.formSnapshot.convertFormSnapshotToJsonString +import no.nav.syfo.motebehov.formSnapshot.mockArbeidstakerSvarJaFormSnapshot import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_AKTORID +import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_FNR import no.nav.syfo.testhelper.UserConstants.LEDER_AKTORID import no.nav.syfo.testhelper.UserConstants.VIRKSOMHETSNUMMER import no.nav.syfo.testhelper.generator.MotebehovGenerator @@ -13,7 +23,9 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.TestConfiguration import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.jdbc.core.RowMapper import org.springframework.test.context.jdbc.Sql +import java.sql.ResultSet import java.time.temporal.ChronoUnit @TestConfiguration @@ -52,6 +64,7 @@ class MotebehovDAOTest : IntegrationTest() { motebehovFraDb.forklaring shouldBe pMotebehov.forklaring motebehovFraDb.tildeltEnhet shouldBe pMotebehov.tildeltEnhet motebehovFraDb.skjemaType shouldBe pMotebehov.skjemaType + motebehovFraDb.formSnapshot shouldBe pMotebehov.formSnapshot } it("hentMotebehovListeForOgOpprettetAvArbeidstakerIkkeGyldig") { @@ -140,18 +153,145 @@ class MotebehovDAOTest : IntegrationTest() { motebehovFraDb.aktoerId shouldBe pMotebehov.aktoerId } - it("lagre møtebehov") { - val uuid = motebehovDAO.create(motebehovGenerator.generatePmotebehov()) + it("should create møtebehov and retrieve it back with same values") { + val motebehovToStore = motebehovGenerator.generatePmotebehov() + val uuid = motebehovDAO.create(motebehovToStore) val motebehovListe = motebehovDAO.hentMotebehovListeForAktoer(ARBEIDSTAKER_AKTORID) motebehovListe.size shouldBe 1 - motebehovListe[0].uuid shouldBe uuid + + val retrievedMotebehov = motebehovListe[0] + + retrievedMotebehov.uuid shouldBe uuid + retrievedMotebehov.harMotebehov shouldBe true + retrievedMotebehov.aktoerId shouldBe ARBEIDSTAKER_AKTORID + retrievedMotebehov.sykmeldtFnr shouldBe ARBEIDSTAKER_FNR + + retrievedMotebehov.formSnapshot.shouldNotBeNull() + retrievedMotebehov.formSnapshot shouldBe mockArbeidstakerSvarJaFormSnapshot + } + + it( + "should store the correct values in motebehov_form_values when creating a motebehov " + + "with a formSnapshot" + ) { + val motebehovToStore = motebehovGenerator.generatePmotebehov() + val motebehovToStoreFormSnapshotConvertedToJSON = motebehovToStore.formSnapshot?.let { + convertFormSnapshotToJsonString( + it + ) + } + + val uuid = motebehovDAO.create(motebehovToStore) + val motebehovFormValuesFromDb = readMotebehovFormValuesFromDb(uuid.toString()) + + motebehovFormValuesFromDb.shouldNotBeNull() + + areStringsEqualAsSqlJsonbValues( + motebehovFormValuesFromDb.formSnapshotJSON, + motebehovToStoreFormSnapshotConvertedToJSON ?: "" + ) shouldBe true + + motebehovFormValuesFromDb.formIdentifier shouldBe FORM_IDENTIFIER_ARBEIDSTAKER_SVAR + motebehovFormValuesFromDb.formSemanticVersion shouldBe MOCK_SNAPSHOTS_FORM_SEMANTIC_VERSION + motebehovFormValuesFromDb.begrunnelse shouldBe MOCK_ARRBEIDSTAKER_SVAR_BEGRUNNELSE + motebehovFormValuesFromDb.onskerSykmelderDeltar shouldBe false + motebehovFormValuesFromDb.onskerSykmelderDeltarBegrunnelse.shouldBeNull() + motebehovFormValuesFromDb.onskerTolk shouldBe true + motebehovFormValuesFromDb.tolkSprak shouldBe MOCK_ARBEIDSTAKER_SVAR_SPRAK } } } private fun insertPMotebehov(motebehov: PMotebehov) { - val sqlInsert = - "INSERT INTO MOTEBEHOV VALUES(DEFAULT, 'bae778f2-a085-11e8-98d0-529269fb1459', '" + motebehov.opprettetDato + "', '" + motebehov.opprettetAv + "', '" + motebehov.aktoerId + "', '" + motebehov.virksomhetsnummer + "', '" + '1' + "', '" + motebehov.forklaring + "', '" + motebehov.tildeltEnhet + "', null, null, null, null, null)" - jdbcTemplate.update(sqlInsert) + val motebehovId = "bae778f2-a085-11e8-98d0-529269fb1459" + + val sqlMotebehovInsert = """ + INSERT INTO MOTEBEHOV (id, motebehov_uuid, opprettet_dato, opprettet_av, aktoer_id, virksomhetsnummer, + har_motebehov, forklaring, tildelt_enhet, behandlet_tidspunkt, behandlet_veileder_ident, skjematype, + sm_fnr, opprettet_av_fnr) + VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?) + """.trimIndent() + jdbcTemplate.update( + sqlMotebehovInsert, + motebehovId, + motebehov.opprettetDato, + motebehov.opprettetAv, + motebehov.aktoerId, + motebehov.virksomhetsnummer, + motebehov.harMotebehov, + motebehov.forklaring, + motebehov.tildeltEnhet, + motebehov.behandletTidspunkt, + motebehov.behandletVeilederIdent, + motebehov.skjemaType, + motebehov.sykmeldtFnr, + motebehov.opprettetAvFnr + ) + + val formSnapshot = motebehov.formSnapshot + + formSnapshot?.let { + val formSnapshotJSON = convertFormSnapshotToJsonString(formSnapshot) + val formValues = extractFormValuesFromFormSnapshot(formSnapshot) + + val sqlFormValuesInsert = """ + INSERT INTO motebehov_form_values (motebehov_uuid, form_identifier, form_semantic_version, + form_snapshot, begrunnelse, onsker_sykmelder_deltar, onsker_sykmelder_deltar_begrunnelse, + onsker_tolk, tolk_sprak) + VALUES (?, ?, ?, ?::jsonb, ?, ?, ?, ?, ?) + """.trimIndent() + jdbcTemplate.update( + sqlFormValuesInsert, + motebehovId, + formValues.formIdentifier, + formValues.formSemanticVersion, + formSnapshotJSON, + formValues.begrunnelse, + formValues.onskerSykmelderDeltar, + formValues.onskerSykmelderDeltarBegrunnelse, + formValues.onskerTolk, + formValues.tolkSprak + ) + } + } + + private fun readMotebehovFormValuesFromDb(motebehovId: String): PMotebehovFormValues? { + val motebehovFormValuesRowMapper: RowMapper = RowMapper { rs: ResultSet, _: Int -> + PMotebehovFormValues( + formIdentifier = rs.getString("form_identifier"), + formSemanticVersion = rs.getString("form_semantic_version"), + formSnapshotJSON = rs.getString("form_snapshot"), + begrunnelse = rs.getString("begrunnelse")?.takeIf { it.isNotEmpty() }, + onskerSykmelderDeltar = rs.getBoolean("onsker_sykmelder_deltar"), + onskerSykmelderDeltarBegrunnelse = rs.getString( + "onsker_sykmelder_deltar_begrunnelse" + )?.takeIf { it.isNotEmpty() }, + onskerTolk = rs.getBoolean("onsker_tolk"), + tolkSprak = rs.getString("tolk_sprak")?.takeIf { it.isNotEmpty() }, + ) + } + + val sql = "SELECT * FROM motebehov_form_values WHERE motebehov_uuid = ?" + return jdbcTemplate.queryForObject(sql, motebehovFormValuesRowMapper, motebehovId) + } + + fun areStringsEqualAsSqlJsonbValues(jsonb1: String, jsonb2: String): Boolean { + val sql = "SELECT ?::jsonb = ?::jsonb" + return jdbcTemplate.queryForObject(sql, Boolean::class.java, jsonb1, jsonb2) ?: false + } + + companion object { + // Beside formSnapshotJSON, these fields are not read out of the database (in this application). + // That is why this data class is only defined inside of this test class. + data class PMotebehovFormValues( + val formSnapshotJSON: String, + val formIdentifier: String, + val formSemanticVersion: String, + val begrunnelse: String?, + val onskerSykmelderDeltar: Boolean, + val onskerSykmelderDeltarBegrunnelse: String?, + val onskerTolk: Boolean, + val tolkSprak: String?, + ) } } diff --git a/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/ConvertLegacyMotebehovSvarFieldsHelperTest.kt b/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/ConvertLegacyMotebehovSvarFieldsHelperTest.kt new file mode 100644 index 00000000..5af78325 --- /dev/null +++ b/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/ConvertLegacyMotebehovSvarFieldsHelperTest.kt @@ -0,0 +1,205 @@ +package no.nav.syfo.motebehov.formSnapshot + +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.shouldBe +import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType + +class ConvertLegacyMotebehovSvarFieldsHelperTest : DescribeSpec({ + + val legacyMotebehovToFormSnapshotHelper = LegacyMotebehovToFormSnapshotHelper() + + describe("ConvertLegacyMotebehovSvarFieldsHelper") { + it( + "should create a form snapshot matching the legacy version of the form 'motebehov-arbeidstaker-svar' " + + "form filled in with ja and certain begrunnelse" + ) { + val harMotebehov = true + val begrunnelse = "Jeg ønkser å snakke om bedre tilrettelegging" + val skjemaType = MotebehovSkjemaType.SVAR_BEHOV + val motebehovInnmelderType = MotebehovInnmelderType.ARBEIDSTAKER + + val formSnapshot = legacyMotebehovToFormSnapshotHelper.createFormSnapshotFromLegacyMotebehovValues( + harMotebehov, + begrunnelse, + skjemaType, + motebehovInnmelderType + ) + + formSnapshot.formIdentifier shouldBe "motebehov-arbeidstaker-svar" + formSnapshot.formSemanticVersion shouldBe "0.1.0" + + formSnapshot.fieldSnapshots shouldContainExactly listOf( + RadioGroupFieldSnapshot( + "harBehovRadioGroup", + "Har du behov for et møte med NAV og arbeidsgiveren din?", + null, + "ja", + "Ja, jeg mener det er behov for et møte", + listOf( + FormSnapshotFieldOption("ja", "Ja, jeg mener det er behov for et møte", true), + FormSnapshotFieldOption("nei", "Nei, jeg mener det ikke er behov for et møte") + ) + ), + TextFieldSnapshot( + "begrunnelseText", + "Begrunnelse", + null, + begrunnelse, + false + ) + ) + + formSnapshot.fieldValues shouldBe + mapOf( + "harBehovRadioGroup" to "ja", + "begrunnelseText" to begrunnelse + ) + } + + it( + "should create a form snapshot matching the legacy version of the 'motebehov-arbeidsgiver-svar' " + + "form filled in with nei and certain begrunnelse" + ) { + val harMotebehov = false + val begrunnelse = "Vi har avtalt det vi trenger" + val skjemaType = MotebehovSkjemaType.SVAR_BEHOV + val motebehovInnmelderType = MotebehovInnmelderType.ARBEIDSGIVER + + val formSnapshot = legacyMotebehovToFormSnapshotHelper.createFormSnapshotFromLegacyMotebehovValues( + harMotebehov, + begrunnelse, + skjemaType, + motebehovInnmelderType + ) + + formSnapshot.formIdentifier shouldBe "motebehov-arbeidsgiver-svar" + formSnapshot.formSemanticVersion shouldBe "0.1.0" + + formSnapshot.fieldSnapshots shouldContainExactly listOf( + RadioGroupFieldSnapshot( + "harBehovRadioGroup", + "Har dere behov for et møte med NAV?", + null, + "nei", + "Nei, jeg mener det ikke er behov for et møte", + listOf( + FormSnapshotFieldOption("ja", "Ja, jeg mener det er behov for et møte"), + FormSnapshotFieldOption("nei", "Nei, jeg mener det ikke er behov for et møte", true) + ) + ), + TextFieldSnapshot( + "begrunnelseText", + "Begrunnelse", + null, + begrunnelse, + true + ), + ) + + formSnapshot.fieldValues shouldBe + mapOf( + "harBehovRadioGroup" to "nei", + "begrunnelseText" to begrunnelse + ) + } + + it( + "should create a form snapshot matching the legacy version of the 'motebehov-arbeidstaker-meld' " + + "form filled in with certain begrunnelse" + ) { + val harMotebehov = true + val begrunnelse = "Dette er tekst i begrunnelsesfeltet" + val skjemaType = MotebehovSkjemaType.MELD_BEHOV + val motebehovInnmelderType = MotebehovInnmelderType.ARBEIDSTAKER + + val formSnapshot = legacyMotebehovToFormSnapshotHelper.createFormSnapshotFromLegacyMotebehovValues( + harMotebehov, + begrunnelse, + skjemaType, + motebehovInnmelderType + ) + + formSnapshot.formIdentifier shouldBe "motebehov-arbeidstaker-meld" + formSnapshot.formSemanticVersion shouldBe "0.1.0" + + formSnapshot.fieldSnapshots shouldContainExactly listOf( + SingleCheckboxFieldSnapshot( + "harBehovCheckbox", + "Jeg ønsker et møte med NAV og arbeidsgiveren min.", + null, + true, + ), + SingleCheckboxFieldSnapshot( + "onskerSykmelderDeltarCheckbox", + "Jeg ønsker at den som sykmelder meg, også skal delta i møtet.", + null, + false, + ), + TextFieldSnapshot( + "begrunnelseText", + "Begrunnelse", + null, + begrunnelse, + false + ), + ) + + formSnapshot.fieldValues shouldBe + mapOf( + "harBehovCheckbox" to true, + "onskerSykmelderDeltarCheckbox" to false, + "begrunnelseText" to begrunnelse + ) + } + + it( + "should create a form snapshot matching the legacy version of the 'motebehov-arbeidsgiver-meld' " + + "form filled in with certain begrunnelse" + ) { + val harMotebehov = true + val begrunnelse = "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet (valgfri). " + + "Vi trenger å ha et møte med NAV." + val skjemaType = MotebehovSkjemaType.MELD_BEHOV + val motebehovInnmelderType = MotebehovInnmelderType.ARBEIDSGIVER + + val formSnapshot = legacyMotebehovToFormSnapshotHelper.createFormSnapshotFromLegacyMotebehovValues( + harMotebehov, + begrunnelse, + skjemaType, + motebehovInnmelderType + ) + + formSnapshot.formIdentifier shouldBe "motebehov-arbeidsgiver-meld" + formSnapshot.formSemanticVersion shouldBe "0.1.0" + + formSnapshot.fieldSnapshots shouldContainExactly listOf( + SingleCheckboxFieldSnapshot( + "harBehovCheckbox", + "Jeg ønsker et møte med NAV og den ansatte", + null, + true, + ), + SingleCheckboxFieldSnapshot( + "onskerSykmelderDeltarCheckbox", + "Jeg ønsker at den som sykmelder arbeidstakeren, også skal delta i møtet.", + null, + true, + ), + TextFieldSnapshot( + "begrunnelseText", + "Begrunnelse", + null, + "Vi trenger å ha et møte med NAV.", + false + ), + ) + + formSnapshot.fieldValues shouldBe mapOf( + "harBehovCheckbox" to true, + "onskerSykmelderDeltarCheckbox" to true, + "begrunnelseText" to "Vi trenger å ha et møte med NAV." + ) + } + } +}) diff --git a/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotJSONConversionTest.kt b/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotJSONConversionTest.kt new file mode 100644 index 00000000..eccba6a0 --- /dev/null +++ b/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/FormSnapshotJSONConversionTest.kt @@ -0,0 +1,21 @@ +package no.nav.syfo.motebehov.formSnapshot + +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe + +class FormSnapshotJSONConversionTest : DescribeSpec({ + + describe("FormSnapshotJSONConversion") { + + mockFormSnapshots.forEach { (formSnapshotName, formSnapshotToConvert) -> + + it("should get the same FormSnapshot back after converting to json and back for $formSnapshotName") { + val json = convertFormSnapshotToJsonString(formSnapshotToConvert) + + val formSnapshotConvertedBackFromJson = convertJsonStringToFormSnapshot(json) + + formSnapshotConvertedBackFromJson shouldBe formSnapshotToConvert + } + } + } +}) diff --git a/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/MockFormSnapshots.kt b/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/MockFormSnapshots.kt new file mode 100644 index 00000000..f60c0c26 --- /dev/null +++ b/src/test/kotlin/no/nav/syfo/motebehov/formSnapshot/MockFormSnapshots.kt @@ -0,0 +1,241 @@ +package no.nav.syfo.motebehov.formSnapshot + +const val MOCK_ARRBEIDSTAKER_SVAR_BEGRUNNELSE = "Ønsker å snakke om mine behov for tilrettelegging" +const val MOCK_ARBEIDSTAKER_SVAR_SPRAK = "tegnspråk" + +const val MOCK_SNAPSHOTS_FORM_SEMANTIC_VERSION = "1.0.0" + +val mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot = FormSnapshot( + FORM_IDENTIFIER_ARBEIDSGIVER_SVAR, + MOCK_SNAPSHOTS_FORM_SEMANTIC_VERSION, + listOf( + RadioGroupFieldSnapshot( + "harBehovRadioGroup", + "Har dere behov for et dialogmøte med NAV?", + "Du svarer på vegne av arbeidsgiver. Den ansatte har fått det samme spørsmålet og svarer på " + + "vegne av seg selv.", + "ja", + "Ja, vi har behov for et dialogmøte.", + listOf( + FormSnapshotFieldOption("ja", "Ja, vi har behov for et dialogmøte.", true), + FormSnapshotFieldOption("nei", "Nei, vi har ikke behov for et dialogmøte nå.") + ) + ), + TextFieldSnapshot( + "begrunnelseText", + "Begrunnelse (må fylles ut)", + "Hva ønsker du å ta opp i møtet? Hva tenker du at NAV kan bistå med? Ikke skriv sensitiv " + + "informasjon, for eksempel detaljerte opplysninger om helse.", + "Vi trenger litt hjelp med videre tiltak", + ), + SingleCheckboxFieldSnapshot( + "onskerSykmelderDeltarCheckbox", + "Jeg ønsker at sykmelder (lege/behandler) også deltar i møtet.", + null, + true + ), + TextFieldSnapshot( + "onskerSykmelderDeltarBegrunnelseText", + "Hvorfor ønsker du at lege/behandler deltar i møtet? (Må fylles ut)", + null, + "Ønsker å høre leges tanker rundt mulige tiltak for tilrettelegging", + ), + SingleCheckboxFieldSnapshot( + "onskerTolkCheckbox", + "Vi har behov for tolk.", + null, + false + ) + ) +) + +val mockArbeidsgiverSvarNeiFormSnapshot = FormSnapshot( + FORM_IDENTIFIER_ARBEIDSGIVER_SVAR, + MOCK_SNAPSHOTS_FORM_SEMANTIC_VERSION, + listOf( + RadioGroupFieldSnapshot( + "harBehovRadioGroup", + "Har dere behov for et dialogmøte med NAV?", + "Du svarer på vegne av arbeidsgiver. Den ansatte har fått det samme spørsmålet og svarer på " + + "vegne av seg selv.", + "nei", + "Nei, vi har ikke behov for et dialogmøte nå.", + listOf( + FormSnapshotFieldOption("ja", "Ja, vi har behov for et dialogmøte."), + FormSnapshotFieldOption("nei", "Nei, vi har ikke behov for et dialogmøte nå.", true) + ) + ), + TextFieldSnapshot( + "begrunnelseText", + "Begrunnelse (må fylles ut)", + "Hvorfor mener du det ikke er behov for et dialogmøte? Ikke skriv sensitiv informasjon, " + + "for eksempel detaljerte opplysninger om helse.", + "Vi har allerede god dialog", + ), + SingleCheckboxFieldSnapshot( + "onskerSykmelderDeltarCheckbox", + "Jeg ønsker at sykmelder (lege/behandler) også deltar i møtet.", + null, + false + ), + SingleCheckboxFieldSnapshot( + "onskerTolkCheckbox", + "Vi har behov for tolk.", + null, + false + ) + ) +) + +val mockArbeidsgiverMeldOnskerSykmelderOgTolkFormSnapshot = FormSnapshot( + FORM_IDENTIFIER_ARBEIDSGIVER_MELD, + "1.0.0", + listOf( + TextFieldSnapshot( + "begrunnelseText", + "Hvorfor ønsker du et dialogmøte? (Må fylles ut)", + "Hva ønsker du å ta opp i møtet? Hva tenker du at NAV kan bistå med? Ikke skriv sensitiv " + + "informasjon, for eksempel detaljerte opplysninger om helse.", + "Vi trenger å ta en fot i bakken.", + ), + SingleCheckboxFieldSnapshot( + "onskerSykmelderDeltarCheckbox", + "Jeg ønsker at sykmelder (lege/behandler) også deltar i møtet.", + null, + true + ), + TextFieldSnapshot( + "onskerSykmelderDeltarBegrunnelseText", + "Hvorfor ønsker du at lege/behandler deltar i møtet? (Må fylles ut)", + null, + "Ønsker å høre leges tanker rundt mulige tiltak for tilrettelegging", + ), + SingleCheckboxFieldSnapshot( + "onskerTolkCheckbox", + "Vi har behov for tolk.", + null, + true + ), + TextFieldSnapshot( + "tolkSprakText", + "Hva slags tolk har dere behov for? (Må fylles ut)", + "Oppgi for eksempel et språk eller tegnspråktolk.", + "Tegnspråk", + ) + ) +) + +val mockArbeidstakerSvarJaFormSnapshot = FormSnapshot( + FORM_IDENTIFIER_ARBEIDSTAKER_SVAR, + "1.0.0", + listOf( + RadioGroupFieldSnapshot( + "harBehovRadioGroup", + "Ønsker du et dialogmøte med NAV og arbeidsgiveren din?", + null, + "ja", + "Ja, jeg ønsker et dialogmøte.", + listOf( + FormSnapshotFieldOption("ja", "Ja, jeg ønsker et dialogmøte.", true), + FormSnapshotFieldOption("nei", "Nei, jeg mener det ikke er behov for et dialogmøte.") + ) + ), + TextFieldSnapshot( + "begrunnelseText", + "Begrunnelse (valgfri)", + "Hva ønsker du å ta opp i møtet? Ikke skriv sensitiv informasjon, for eksempel detaljerte " + + "opplysninger om helse.", + MOCK_ARRBEIDSTAKER_SVAR_BEGRUNNELSE, + false + ), + SingleCheckboxFieldSnapshot( + "onskerSykmelderDeltarCheckbox", + " Jeg ønsker at den som har sykmeldt meg (lege/behandler) også deltar i møtet.", + null, + false + ), + SingleCheckboxFieldSnapshot( + "onskerTolkCheckbox", + " Jeg har behov for tolk.", + null, + true + ), + TextFieldSnapshot( + "tolkSprakText", + "Hva slags tolk har du behov for? (Må fylles ut)", + "Oppgi for eksempel et språk eller tegnspråktolk.", + MOCK_ARBEIDSTAKER_SVAR_SPRAK + ) + ) +) + +val mockArbeidstakerSvarNeiFormSnapshot = FormSnapshot( + FORM_IDENTIFIER_ARBEIDSTAKER_SVAR, + "1.0.0", + listOf( + RadioGroupFieldSnapshot( + "harBehovRadioGroup", + "Ønsker du et dialogmøte med NAV og arbeidsgiveren din?", + null, + "nei", + "Nei, jeg mener det ikke er behov for et dialogmøte.", + listOf( + FormSnapshotFieldOption("ja", "Ja, jeg ønsker et dialogmøte."), + FormSnapshotFieldOption("nei", "Nei, jeg mener det ikke er behov for et dialogmøte.", true) + ) + ), + TextFieldSnapshot( + "begrunnelseText", + "Begrunnelse (må fylles ut)", + "Hvorfor mener du det ikke er behov for et dialogmøte? Ikke skriv sensitiv informasjon, " + + "for eksempel detaljerte opplysninger om helse.", + "Jeg tror ikke det er behov for et dialogmøte.", + ), + SingleCheckboxFieldSnapshot( + "onskerSykmelderDeltarCheckbox", + " Jeg ønsker at den som har sykmeldt meg (lege/behandler) også deltar i møtet.", + null, + false + ), + SingleCheckboxFieldSnapshot( + "onskerTolkCheckbox", + " Jeg har behov for tolk.", + null, + false + ), + ) +) + +val mockArbeidstakerMeldSnapshot = FormSnapshot( + FORM_IDENTIFIER_ARBEIDSGIVER_MELD, + "1.0.0", + listOf( + TextFieldSnapshot( + "begrunnelseText", + "Hvorfor ønsker du et dialogmøte? (Må fylles ut)", + "Ikke skriv sensitiv informasjon, for eksempel detaljerte opplysninger om helse.", + "Det er noen ting jeg vil ta opp med NAV", + ), + SingleCheckboxFieldSnapshot( + "onskerSykmelderDeltarCheckbox", + "Jeg ønsker at den som har sykmeldt meg (lege/behandler) også deltar i møtet.", + null, + false + ), + SingleCheckboxFieldSnapshot( + "onskerTolkCheckbox", + "Jeg har behov for tolk.", + null, + false + ) + ) +) + +val mockFormSnapshots = mapOf( + "mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot" to mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot, + "mockArbeidsgiverSvarNeiFormSnapshot" to mockArbeidsgiverSvarNeiFormSnapshot, + "mockArbeidsgiverMeldOnskerSykmelderOgTolkFormSnapshot" to mockArbeidsgiverMeldOnskerSykmelderOgTolkFormSnapshot, + "mockArbeidstakerSvarJaFormSnapshot" to mockArbeidstakerSvarJaFormSnapshot, + "mockArbeidstakerSvarNeiFormSnapshot" to mockArbeidstakerSvarNeiFormSnapshot, + "mockArbeidstakerMeldSnapshot" to mockArbeidstakerMeldSnapshot +) diff --git a/src/test/kotlin/no/nav/syfo/testhelper/assertion/MotebehovStatusAssertion.kt b/src/test/kotlin/no/nav/syfo/testhelper/assertion/MotebehovStatusAssertion.kt index 074f4727..3fef6f09 100644 --- a/src/test/kotlin/no/nav/syfo/testhelper/assertion/MotebehovStatusAssertion.kt +++ b/src/test/kotlin/no/nav/syfo/testhelper/assertion/MotebehovStatusAssertion.kt @@ -1,21 +1,39 @@ package no.nav.syfo.testhelper.assertion +import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe -import no.nav.syfo.motebehov.MotebehovSvarOutputDTO +import no.nav.syfo.motebehov.MotebehovFormValuesOutputDTO +import no.nav.syfo.motebehov.MotebehovSvarLegacyDTO import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType -import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatus +import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusWithFormValuesDTO +import no.nav.syfo.motebehov.motebehovstatus.MotebehovStatusWithLegacyMotebehovDTO -fun MotebehovStatus.assertMotebehovStatus( +fun MotebehovStatusWithLegacyMotebehovDTO.assertMotebehovStatus( expVisMotebehov: Boolean, expSkjemaType: MotebehovSkjemaType?, - expMotebehovSvarOutputDTO: MotebehovSvarOutputDTO? + expLegacyMotebehovSvar: MotebehovSvarLegacyDTO? ) { expVisMotebehov shouldBe this.visMotebehov expSkjemaType shouldBe this.skjemaType - if (expMotebehovSvarOutputDTO != null) { - expMotebehovSvarOutputDTO shouldBe this.motebehov!!.motebehovSvar + if (expLegacyMotebehovSvar != null) { + this.motebehov.shouldNotBeNull() + expLegacyMotebehovSvar.harMotebehov shouldBe this.motebehov!!.motebehovSvar.harMotebehov + expLegacyMotebehovSvar.forklaring shouldBe this.motebehov!!.motebehovSvar.forklaring expSkjemaType shouldBe this.motebehov!!.skjemaType + } +} + +fun MotebehovStatusWithFormValuesDTO.assertMotebehovStatus( + expVisMotebehov: Boolean, + expSkjemaType: MotebehovSkjemaType?, + expMotebehovFormValues: MotebehovFormValuesOutputDTO? +) { + expVisMotebehov shouldBe this.visMotebehov + expSkjemaType shouldBe this.skjemaType + if (expMotebehovFormValues != null) { + expMotebehovFormValues shouldBe this.motebehovWithFormValues!!.formValues + expSkjemaType shouldBe this.motebehovWithFormValues!!.skjemaType } else { - expMotebehovSvarOutputDTO shouldBe this.motebehov + expMotebehovFormValues shouldBe this.motebehovWithFormValues } } diff --git a/src/test/kotlin/no/nav/syfo/testhelper/generator/MotebehovGenerator.kt b/src/test/kotlin/no/nav/syfo/testhelper/generator/MotebehovGenerator.kt index a8bb3b2d..a813dd2d 100644 --- a/src/test/kotlin/no/nav/syfo/testhelper/generator/MotebehovGenerator.kt +++ b/src/test/kotlin/no/nav/syfo/testhelper/generator/MotebehovGenerator.kt @@ -2,7 +2,12 @@ package no.nav.syfo.testhelper.generator import no.nav.syfo.motebehov.* import no.nav.syfo.motebehov.database.PMotebehov -import no.nav.syfo.motebehov.motebehovstatus.MotebehovSkjemaType +import no.nav.syfo.motebehov.formSnapshot.mockArbeidsgiverMeldOnskerSykmelderOgTolkFormSnapshot +import no.nav.syfo.motebehov.formSnapshot.mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot +import no.nav.syfo.motebehov.formSnapshot.mockArbeidsgiverSvarNeiFormSnapshot +import no.nav.syfo.motebehov.formSnapshot.mockArbeidstakerMeldSnapshot +import no.nav.syfo.motebehov.formSnapshot.mockArbeidstakerSvarJaFormSnapshot +import no.nav.syfo.motebehov.formSnapshot.mockArbeidstakerSvarNeiFormSnapshot import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_AKTORID import no.nav.syfo.testhelper.UserConstants.ARBEIDSTAKER_FNR import no.nav.syfo.testhelper.UserConstants.LEDER_AKTORID @@ -15,7 +20,7 @@ import java.time.LocalDateTime import java.util.* class MotebehovGenerator { - private val motebehovSvarInputDTO = MotebehovSvarInputDTO( + private val motebehovSvarLegacyInputDTO = MotebehovSvarLegacyDTO( harMotebehov = true, forklaring = "", ) @@ -27,10 +32,10 @@ class MotebehovGenerator { virksomhetsnummer = VIRKSOMHETSNUMMER, opprettetAv = LEDER_AKTORID, opprettetDato = LocalDateTime.now().minusMinutes(2L), - motebehovSvar = lagMotebehovSvarThatShouldBeCreatedFromInputDTO( - motebehovSvarInputDTO, - MotebehovSkjemaType.SVAR_BEHOV, - MotebehovInnmelderType.ARBEIDSGIVER + formSubmission = MotebehovFormSubmissionCombinedDTO( + harMotebehov = true, + forklaring = "", + formSnapshot = mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot ), tildeltEnhet = NAV_ENHET, behandletVeilederIdent = VEILEDER_ID, @@ -38,59 +43,79 @@ class MotebehovGenerator { opprettetAvFnr = LEDER_FNR, ) - private val nyttMotebehovArbeidsgiverInput = NyttMotebehovArbeidsgiverInputDTO( + private val nyttMotebehovArbeidsgiverLegacyInput = NyttMotebehovArbeidsgiverLegacyDTO( arbeidstakerFnr = ARBEIDSTAKER_FNR, virksomhetsnummer = VIRKSOMHETSNUMMER, - motebehovSvar = motebehovSvarInputDTO, + motebehovSvar = motebehovSvarLegacyInputDTO, tildeltEnhet = NAV_ENHET, ) - fun lagMotebehovSvarInputDTO(harBehov: Boolean): MotebehovSvarInputDTO { - return motebehovSvarInputDTO.copy( + private val nyttMotebehovArbeidsgiverFormSubmissionInput = NyttMotebehovArbeidsgiverFormSubmissionDTO( + arbeidstakerFnr = ARBEIDSTAKER_FNR, + virksomhetsnummer = VIRKSOMHETSNUMMER, + formSubmission = MotebehovFormSubmissionDTO( + harMotebehov = true, + formSnapshot = mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot, + ), + tildeltEnhet = NAV_ENHET, + ) + + fun lagMotebehovSvarLegacyDTO(harBehov: Boolean): MotebehovSvarLegacyDTO { + return motebehovSvarLegacyInputDTO.copy( harMotebehov = harBehov, ) } - fun lagNyttMotebehovArbeidsgiverInput(): NyttMotebehovArbeidsgiverInputDTO { - return nyttMotebehovArbeidsgiverInput.copy() + fun lagFormSubmissionArbeidstakerSvarJaDTO(): MotebehovFormSubmissionDTO { + return MotebehovFormSubmissionDTO( + harMotebehov = true, + formSnapshot = mockArbeidstakerSvarJaFormSnapshot, + ) + } + + fun lagFormSubmissionArbeidstakerSvarNeiDTO(): MotebehovFormSubmissionDTO { + return MotebehovFormSubmissionDTO( + harMotebehov = false, + formSnapshot = mockArbeidstakerSvarNeiFormSnapshot, + ) + } + + fun lagFormSubmissionArbeidstakerMeldDTO(): MotebehovFormSubmissionDTO { + return MotebehovFormSubmissionDTO( + harMotebehov = true, + formSnapshot = mockArbeidstakerMeldSnapshot, + ) + } + + fun lagNyttMotebehovArbeidsgiverLegacyInput(): NyttMotebehovArbeidsgiverLegacyDTO { + return nyttMotebehovArbeidsgiverLegacyInput.copy() } - fun lagMotebehovSvarThatShouldBeCreatedFromInputDTO( - inputDTO: MotebehovSvarInputDTO, - skjemaType: MotebehovSkjemaType, - innmelderType: MotebehovInnmelderType - ): MotebehovSvar { - val legacyFieldsToFormFilloutHelper = ConvertLegacyMotebehovSvarFieldsHelper() - - return MotebehovSvar( - harMotebehov = inputDTO.harMotebehov, - forklaring = inputDTO.forklaring, - formFillout = legacyFieldsToFormFilloutHelper.convertLegacyMotebehovSvarToFormFillout( - inputDTO.harMotebehov, - inputDTO.forklaring, - skjemaType, - innmelderType + fun lagNyArbeidsgiverFormSubmissionSvarJa(): NyttMotebehovArbeidsgiverFormSubmissionDTO { + return nyttMotebehovArbeidsgiverFormSubmissionInput.copy( + formSubmission = MotebehovFormSubmissionDTO( + harMotebehov = true, + formSnapshot = mockArbeidsgiverSvarJaOnskerSykmelderFormSnapshot, ) ) } - fun lagMotebehovSvarOutputDTOThatShouldBeCreatedFromInputDTO( - inputDTO: MotebehovSvarInputDTO, - skjemaType: MotebehovSkjemaType, - innmelderType: MotebehovInnmelderType - ): MotebehovSvarOutputDTO { - val legacyFieldsToFormFilloutHelper = ConvertLegacyMotebehovSvarFieldsHelper() - - return MotebehovSvar( - harMotebehov = inputDTO.harMotebehov, - forklaring = inputDTO.forklaring, - formFillout = legacyFieldsToFormFilloutHelper.convertLegacyMotebehovSvarToFormFillout( - inputDTO.harMotebehov, - inputDTO.forklaring, - skjemaType, - innmelderType + fun lagNyArbeidsgiverFormSubmissionSvarNei(): NyttMotebehovArbeidsgiverFormSubmissionDTO { + return nyttMotebehovArbeidsgiverFormSubmissionInput.copy( + formSubmission = MotebehovFormSubmissionDTO( + harMotebehov = false, + formSnapshot = mockArbeidsgiverSvarNeiFormSnapshot, ) - ).toMotebehovSvarOutputDTO() + ) + } + + fun lagNyArbeidsgiverFormSubmissionMeld(): NyttMotebehovArbeidsgiverFormSubmissionDTO { + return nyttMotebehovArbeidsgiverFormSubmissionInput.copy( + formSubmission = MotebehovFormSubmissionDTO( + harMotebehov = true, + formSnapshot = mockArbeidsgiverMeldOnskerSykmelderOgTolkFormSnapshot, + ) + ) } private val nyttPMotebehovArbeidstaker = PMotebehov( @@ -99,10 +124,11 @@ class MotebehovGenerator { opprettetAv = LEDER_AKTORID, aktoerId = ARBEIDSTAKER_AKTORID, virksomhetsnummer = VIRKSOMHETSNUMMER, - forklaring = "Megling", + forklaring = null, harMotebehov = true, tildeltEnhet = NAV_ENHET, sykmeldtFnr = ARBEIDSTAKER_FNR, + formSnapshot = mockArbeidstakerSvarJaFormSnapshot, ) fun getOpprettetDato(erGyldig: Boolean): LocalDateTime { @@ -122,7 +148,7 @@ class MotebehovGenerator { return motebehov.copy() } - fun generateMotebehovOutputDTO(): MotebehovOutputDTO { - return motebehov.toMotebehovOutputDTO() + fun generateMotebehovOutputDTO(): MotebehovWithFormValuesOutputDTO { + return motebehov.toMotebehovWithFormValuesOutputDTO() } } diff --git a/src/test/kotlin/no/nav/syfo/testhelper/generator/MotebehovStatusGenerator.kt b/src/test/kotlin/no/nav/syfo/testhelper/generator/MotebehovStatusGenerator.kt index 15a9e242..73d3c5f1 100644 --- a/src/test/kotlin/no/nav/syfo/testhelper/generator/MotebehovStatusGenerator.kt +++ b/src/test/kotlin/no/nav/syfo/testhelper/generator/MotebehovStatusGenerator.kt @@ -8,5 +8,5 @@ private val motebehovGenerator = MotebehovGenerator() val generateMotebehovStatus = MotebehovStatus( visMotebehov = true, skjemaType = MotebehovSkjemaType.SVAR_BEHOV, - motebehov = motebehovGenerator.generateMotebehovOutputDTO() + motebehov = motebehovGenerator.generateMotebehov() ).copy()