diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/constants/enums/LicensingType.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/constants/enums/LicensingType.kt index 80a8308042..ae6104ad46 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/constants/enums/LicensingType.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/constants/enums/LicensingType.kt @@ -5,9 +5,10 @@ enum class LicensingType { HMO_MANDATORY_LICENCE, HMO_ADDITIONAL_LICENCE, NO_LICENSING, + PROVIDE_LATER, ; companion object { - val licencedEntries = entries.minus(NO_LICENSING) + val licencedEntries = entries.minus(NO_LICENSING).minus(PROVIDE_LATER) } } diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/helpers/converters/MessageKeyConverter.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/helpers/converters/MessageKeyConverter.kt index ae01b4a5e4..cf54e25e0f 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/helpers/converters/MessageKeyConverter.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/helpers/converters/MessageKeyConverter.kt @@ -83,6 +83,7 @@ class MessageKeyConverter { LicensingType.HMO_MANDATORY_LICENCE -> "forms.licensingType.radios.option.hmoMandatory.label" LicensingType.HMO_ADDITIONAL_LICENCE -> "forms.licensingType.radios.option.hmoAdditional.label" LicensingType.NO_LICENSING -> "forms.checkPropertyAnswers.propertyDetails.noLicensing" + LicensingType.PROVIDE_LATER -> "forms.checkPropertyAnswers.propertyDetails.licensingProvideLater" } private fun convertOwnershipType(ownershipType: OwnershipType): String = diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/PropertyRegistrationJourneyFactory.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/PropertyRegistrationJourneyFactory.kt index 20743f5add..8e09ed1890 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/PropertyRegistrationJourneyFactory.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/PropertyRegistrationJourneyFactory.kt @@ -86,6 +86,7 @@ import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.Prope import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.ProvideElectricalCertLaterStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.ProvideEpcLaterStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.ProvideGasCertLaterStep +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.ProvideLicensingLaterStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.RemoveElectricalCertUploadStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.RemoveGasCertUploadStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.RentAmountStep @@ -605,6 +606,7 @@ class PropertyRegistrationJourney( override val selectiveLicenceStep: SelectiveLicenceStep, override val hmoMandatoryLicenceStep: HmoMandatoryLicenceStep, override val hmoAdditionalLicenceStep: HmoAdditionalLicenceStep, + override val provideLicensingLaterStep: ProvideLicensingLaterStep, // Occupation steps override val occupied: OccupiedStep, // Nested households and tenants task @@ -748,6 +750,7 @@ class PropertyRegistrationJourney( override var backUrlKey: Int? by delegateProvider.nullableDelegate("backUrlKey") override val allowProvideCertificateLaterRoute: Boolean = true + override val allowProvideLicensingLaterRoute: Boolean = true override fun generateJourneyId(seed: Any?): String { val user = seed as? Principal diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/states/LicensingState.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/states/LicensingState.kt index 3f0ed67350..5b25edd49c 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/states/LicensingState.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/states/LicensingState.kt @@ -4,14 +4,20 @@ import uk.gov.communities.prsdb.webapp.constants.enums.LicensingType import uk.gov.communities.prsdb.webapp.journeys.JourneyState import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.HmoAdditionalLicenceStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.HmoMandatoryLicenceStep +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.LicensingTypeMode import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.LicensingTypeStep +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.ProvideLicensingLaterStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.SelectiveLicenceStep interface LicensingState : JourneyState { + val allowProvideLicensingLaterRoute: Boolean + val isOccupied: Boolean? + val licensingTypeStep: LicensingTypeStep val selectiveLicenceStep: SelectiveLicenceStep val hmoMandatoryLicenceStep: HmoMandatoryLicenceStep val hmoAdditionalLicenceStep: HmoAdditionalLicenceStep + val provideLicensingLaterStep: ProvideLicensingLaterStep fun getLicenceNumberOrNull(): String? = when (licensingTypeStep.formModelOrNull?.licensingType) { @@ -23,4 +29,14 @@ interface LicensingState : JourneyState { fun getLicenceNumber(): String = getLicenceNumberOrNull() ?: throw IllegalStateException("Licence number is not available for the current licensing type") + + fun getLicensingType(): LicensingType = + when (val outcome = licensingTypeStep.outcome) { + LicensingTypeMode.SELECTIVE_LICENCE -> LicensingType.SELECTIVE_LICENCE + LicensingTypeMode.HMO_MANDATORY_LICENCE -> LicensingType.HMO_MANDATORY_LICENCE + LicensingTypeMode.HMO_ADDITIONAL_LICENCE -> LicensingType.HMO_ADDITIONAL_LICENCE + LicensingTypeMode.NO_LICENSING -> LicensingType.NO_LICENSING + LicensingTypeMode.PROVIDE_LATER -> LicensingType.PROVIDE_LATER + null -> throw IllegalStateException("Licensing type has not been provided") + } } diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/LicensingTypeStepConfig.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/LicensingTypeStepConfig.kt index 827906b0a0..e3f5e631a4 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/LicensingTypeStepConfig.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/LicensingTypeStepConfig.kt @@ -2,10 +2,14 @@ package uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps import uk.gov.communities.prsdb.webapp.annotations.webAnnotations.JourneyFrameworkComponent import uk.gov.communities.prsdb.webapp.config.managers.FeatureFlagManager +import uk.gov.communities.prsdb.webapp.constants.CONTINUE_BUTTON_ACTION_NAME +import uk.gov.communities.prsdb.webapp.constants.PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING +import uk.gov.communities.prsdb.webapp.constants.PROVIDE_THIS_LATER_BUTTON_ACTION_NAME import uk.gov.communities.prsdb.webapp.constants.enums.LicensingType import uk.gov.communities.prsdb.webapp.journeys.AbstractRequestableStepConfig -import uk.gov.communities.prsdb.webapp.journeys.JourneyState import uk.gov.communities.prsdb.webapp.journeys.JourneyStep.RequestableStep +import uk.gov.communities.prsdb.webapp.journeys.UnrecoverableJourneyStateException +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.states.LicensingState import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.LicensingTypeFormModel import uk.gov.communities.prsdb.webapp.models.viewModels.formModels.RadiosButtonViewModel import uk.gov.communities.prsdb.webapp.models.viewModels.formModels.RadiosDividerViewModel @@ -13,14 +17,21 @@ import uk.gov.communities.prsdb.webapp.models.viewModels.formModels.RadiosDivide @JourneyFrameworkComponent class LicensingTypeStepConfig( private val featureFlagManager: FeatureFlagManager, -) : AbstractRequestableStepConfig() { +) : AbstractRequestableStepConfig() { override val formModelClass = LicensingTypeFormModel::class - override fun getStepSpecificContent(state: JourneyState): Map { - // TODO(PDJB-990): add a 'Provide this later' button to the licensing page behind the FF: pdjb-939-property-registration-restructure-and-skipping/PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING + override fun getStepSpecificContent(state: LicensingState): Map { + val showProvideThisLater = + state.allowProvideLicensingLaterRoute && + featureFlagManager.checkFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING) return mapOf( "fieldSetHeading" to "forms.licensingType.fieldSetHeading", "fieldSetHint" to "forms.licensingType.fieldSetHint", + "submitButtonText" to "forms.buttons.saveAndContinue", + "showSecondarySubmitButton" to showProvideThisLater, + "submitButtonAction" to CONTINUE_BUTTON_ACTION_NAME, + "secondarySubmitButtonText" to "forms.buttons.provideThisLater", + "secondarySubmitButtonAction" to PROVIDE_THIS_LATER_BUTTON_ACTION_NAME, "radioOptions" to listOf( RadiosButtonViewModel( @@ -47,15 +58,31 @@ class LicensingTypeStepConfig( ) } - override fun chooseTemplate(state: JourneyState): String = "forms/licensingTypeForm" + override fun chooseTemplate(state: LicensingState): String = "forms/licensingTypeForm" - override fun mode(state: JourneyState) = - getFormModelFromStateOrNull(state)?.licensingType?.let { licensingType -> - when (licensingType) { - LicensingType.SELECTIVE_LICENCE -> LicensingTypeMode.SELECTIVE_LICENCE - LicensingType.HMO_MANDATORY_LICENCE -> LicensingTypeMode.HMO_MANDATORY_LICENCE - LicensingType.HMO_ADDITIONAL_LICENCE -> LicensingTypeMode.HMO_ADDITIONAL_LICENCE - LicensingType.NO_LICENSING -> LicensingTypeMode.NO_LICENSING + override fun mode(state: LicensingState) = + getFormModelFromStateOrNull(state)?.let { formModel -> + if (formModel.action == PROVIDE_THIS_LATER_BUTTON_ACTION_NAME) { + if (state.allowProvideLicensingLaterRoute && + featureFlagManager.checkFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING) + ) { + LicensingTypeMode.PROVIDE_LATER + } else { + throw UnrecoverableJourneyStateException( + state.journeyId, + "The 'Provide this later' route is not available for this journey", + ) + } + } else { + formModel.licensingType?.let { licensingType -> + when (licensingType) { + LicensingType.SELECTIVE_LICENCE -> LicensingTypeMode.SELECTIVE_LICENCE + LicensingType.HMO_MANDATORY_LICENCE -> LicensingTypeMode.HMO_MANDATORY_LICENCE + LicensingType.HMO_ADDITIONAL_LICENCE -> LicensingTypeMode.HMO_ADDITIONAL_LICENCE + LicensingType.NO_LICENSING -> LicensingTypeMode.NO_LICENSING + LicensingType.PROVIDE_LATER -> LicensingTypeMode.PROVIDE_LATER + } + } } } } @@ -63,7 +90,7 @@ class LicensingTypeStepConfig( @JourneyFrameworkComponent final class LicensingTypeStep( stepConfig: LicensingTypeStepConfig, -) : RequestableStep(stepConfig) { +) : RequestableStep(stepConfig) { companion object { const val ROUTE_SEGMENT = "licensing-type" } @@ -74,4 +101,5 @@ enum class LicensingTypeMode { HMO_MANDATORY_LICENCE, HMO_ADDITIONAL_LICENCE, NO_LICENSING, + PROVIDE_LATER, } diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/ProvideLicensingLaterStepConfig.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/ProvideLicensingLaterStepConfig.kt new file mode 100644 index 0000000000..5eca652240 --- /dev/null +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/ProvideLicensingLaterStepConfig.kt @@ -0,0 +1,34 @@ +package uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps + +import uk.gov.communities.prsdb.webapp.annotations.webAnnotations.JourneyFrameworkComponent +import uk.gov.communities.prsdb.webapp.journeys.AbstractRequestableStepConfig +import uk.gov.communities.prsdb.webapp.journeys.JourneyStep.RequestableStep +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.states.LicensingState +import uk.gov.communities.prsdb.webapp.journeys.shared.Complete +import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.NoInputFormModel + +@JourneyFrameworkComponent +class ProvideLicensingLaterStepConfig : AbstractRequestableStepConfig() { + override val formModelClass = NoInputFormModel::class + + override fun getStepSpecificContent(state: LicensingState) = + mapOf( + "submitButtonText" to if (state.isOccupied == true) "forms.buttons.continue" else "forms.buttons.saveAndContinue", + ) + + override fun chooseTemplate(state: LicensingState): String = + state.isOccupied?.let { isOccupied -> + if (isOccupied) "forms/provideLicensingLaterOccupiedForm" else "forms/provideLicensingLaterUnoccupiedForm" + } ?: throw IllegalStateException("ProvideLicensingLaterStep should not be reachable before isOccupied is set") + + override fun mode(state: LicensingState) = getFormModelFromStateOrNull(state)?.let { Complete.COMPLETE } +} + +@JourneyFrameworkComponent +final class ProvideLicensingLaterStep( + stepConfig: ProvideLicensingLaterStepConfig, +) : RequestableStep(stepConfig) { + companion object { + const val ROUTE_SEGMENT = "provide-licensing-later" + } +} diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/SavePropertyRegistrationDataStepConfig.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/SavePropertyRegistrationDataStepConfig.kt index bebeeb9504..b4602a8e8c 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/SavePropertyRegistrationDataStepConfig.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/SavePropertyRegistrationDataStepConfig.kt @@ -14,7 +14,6 @@ import uk.gov.communities.prsdb.webapp.journeys.JourneyStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.PropertyRegistrationJourneyState import uk.gov.communities.prsdb.webapp.journeys.shared.Complete import uk.gov.communities.prsdb.webapp.journeys.shared.YesOrNo -import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.LicensingTypeFormModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.NewNumberOfPeopleFormModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.NumberOfBedroomsFormModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.NumberOfHouseholdsFormModel @@ -71,7 +70,7 @@ class SavePropertyRegistrationDataStepConfig( } else { null }, - licenseType = state.licensingTypeStep.formModel.notNullValue(LicensingTypeFormModel::licensingType), + licenseType = state.getLicensingType(), licenceNumber = state.getLicenceNumberOrNull() ?: "", ownershipType = state.ownershipTypeStep.formModel.notNullValue(OwnershipTypeFormModel::ownershipType), isOccupied = isOccupied, @@ -141,6 +140,7 @@ class SavePropertyRegistrationDataStepConfig( .formModelIfReachableOrNull ?.exemptionReason, epcProvideLater = state.hasEpcStep.outcome == HasEpcMode.PROVIDE_LATER, + licenseProvideLater = state.licensingTypeStep.outcome == LicensingTypeMode.PROVIDE_LATER, ) } } diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/tasks/LicensingTask.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/tasks/LicensingTask.kt index 447914e7d0..1d69017501 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/tasks/LicensingTask.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/tasks/LicensingTask.kt @@ -1,7 +1,6 @@ package uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.tasks import uk.gov.communities.prsdb.webapp.annotations.webAnnotations.JourneyFrameworkComponent -import uk.gov.communities.prsdb.webapp.config.managers.FeatureFlagManager import uk.gov.communities.prsdb.webapp.journeys.OrParents import uk.gov.communities.prsdb.webapp.journeys.Task import uk.gov.communities.prsdb.webapp.journeys.hasOutcome @@ -11,15 +10,13 @@ import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.HmoAd import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.HmoMandatoryLicenceStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.LicensingTypeMode import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.LicensingTypeStep +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.ProvideLicensingLaterStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.SelectiveLicenceStep @JourneyFrameworkComponent -class LicensingTask( - private val featureFlagManager: FeatureFlagManager, -) : Task() { +class LicensingTask : Task() { override fun makeSubJourney(state: LicensingState) = subJourney(state) { - // TODO(PDJB-990): route to the 'provide details about licensing later' page when 'Provide this later' is selected behind the FF: pdjb-939-property-registration-restructure-and-skipping/PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING step(journey.licensingTypeStep) { routeSegment(LicensingTypeStep.ROUTE_SEGMENT) nextStep { mode -> @@ -28,6 +25,7 @@ class LicensingTask( LicensingTypeMode.HMO_MANDATORY_LICENCE -> journey.hmoMandatoryLicenceStep LicensingTypeMode.HMO_ADDITIONAL_LICENCE -> journey.hmoAdditionalLicenceStep LicensingTypeMode.NO_LICENSING -> exitStep + LicensingTypeMode.PROVIDE_LATER -> journey.provideLicensingLaterStep } } } @@ -46,6 +44,12 @@ class LicensingTask( parents { journey.licensingTypeStep.hasOutcome(LicensingTypeMode.HMO_ADDITIONAL_LICENCE) } nextStep { exitStep } } + step(journey.provideLicensingLaterStep) { + routeSegment(ProvideLicensingLaterStep.ROUTE_SEGMENT) + parents { journey.licensingTypeStep.hasOutcome(LicensingTypeMode.PROVIDE_LATER) } + nextStep { exitStep } + savable() + } exitStep { parents { OrParents( @@ -53,6 +57,7 @@ class LicensingTask( journey.selectiveLicenceStep.isComplete(), journey.hmoMandatoryLicenceStep.isComplete(), journey.hmoAdditionalLicenceStep.isComplete(), + journey.provideLicensingLaterStep.isComplete(), ) } } diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/update/updateLicensing/UpdatePropertyLicensingJourneyFactory.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/update/updateLicensing/UpdatePropertyLicensingJourneyFactory.kt index e32c3819aa..0385b32d59 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/update/updateLicensing/UpdatePropertyLicensingJourneyFactory.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/update/updateLicensing/UpdatePropertyLicensingJourneyFactory.kt @@ -17,6 +17,7 @@ import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.Finis import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.HmoAdditionalLicenceStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.HmoMandatoryLicenceStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.LicensingTypeStep +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.ProvideLicensingLaterStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.SelectiveLicenceStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.tasks.LicensingTask import uk.gov.communities.prsdb.webapp.journeys.shared.states.CheckYourAnswersJourneyState @@ -110,6 +111,7 @@ class UpdateLicensingJourney( override val selectiveLicenceStep: SelectiveLicenceStep, override val hmoMandatoryLicenceStep: HmoMandatoryLicenceStep, override val hmoAdditionalLicenceStep: HmoAdditionalLicenceStep, + override val provideLicensingLaterStep: ProvideLicensingLaterStep, // Check your answers step override val cyaStep: UpdateLicensingCyaStep, override val finishCyaStep: FinishCyaJourneyStep, @@ -126,6 +128,9 @@ class UpdateLicensingJourney( override var originalJourneyUpdated: Instant? by delegateProvider.nullableDelegate("originalJourneyUpdated") override var cyaUrlPath: String? by delegateProvider.nullableDelegate("cyaRouteSegment") + + override val allowProvideLicensingLaterRoute: Boolean = false + override val isOccupied: Boolean? = null } interface UpdateLicensingJourneyState : diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/shared/helpers/LicensingDetailsHelper.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/shared/helpers/LicensingDetailsHelper.kt index d0acf2de54..950812f438 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/shared/helpers/LicensingDetailsHelper.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/journeys/shared/helpers/LicensingDetailsHelper.kt @@ -2,10 +2,12 @@ package uk.gov.communities.prsdb.webapp.journeys.shared.helpers import uk.gov.communities.prsdb.webapp.annotations.webAnnotations.PrsdbWebService import uk.gov.communities.prsdb.webapp.config.managers.FeatureFlagManager +import uk.gov.communities.prsdb.webapp.constants.PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING import uk.gov.communities.prsdb.webapp.constants.enums.LicensingType import uk.gov.communities.prsdb.webapp.exceptions.NotNullFormModelValueIsNullException.Companion.notNullValue import uk.gov.communities.prsdb.webapp.journeys.Destination import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.states.LicensingState +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.LicensingTypeMode import uk.gov.communities.prsdb.webapp.journeys.shared.states.CheckYourAnswersJourneyState import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.LicensingTypeFormModel import uk.gov.communities.prsdb.webapp.models.viewModels.summaryModels.SummaryListRowViewModel @@ -17,7 +19,18 @@ class LicensingDetailsHelper( fun getCheckYourAnswersSummaryList( state: T, ): List where T : LicensingState, T : CheckYourAnswersJourneyState { - // TODO(PDJB-990): show 'Provide this later' in the licensing CYA row (property registration only) behind the FF: pdjb-939-property-registration-restructure-and-skipping/PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING + if (featureFlagManager.checkFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING) && + state.licensingTypeStep.outcome == LicensingTypeMode.PROVIDE_LATER + ) { + return listOf( + SummaryListRowViewModel.forCheckYourAnswersPage( + "forms.checkPropertyAnswers.propertyDetails.licensingType", + "forms.checkPropertyAnswers.propertyDetails.licensingProvideLater", + Destination.VisitableStep(state.licensingTypeStep, state.getCyaJourneyId(state.licensingTypeStep)), + ), + ) + } + return state.licensingTypeStep.formModel.notNullValue(LicensingTypeFormModel::licensingType).let { licensingType -> listOfNotNull( SummaryListRowViewModel.forCheckYourAnswersPage( diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/models/requestModels/formModels/LicensingTypeFormModel.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/models/requestModels/formModels/LicensingTypeFormModel.kt index 9c45aedc6e..d48fe3a1fb 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/models/requestModels/formModels/LicensingTypeFormModel.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/models/requestModels/formModels/LicensingTypeFormModel.kt @@ -1,15 +1,30 @@ package uk.gov.communities.prsdb.webapp.models.requestModels.formModels -import jakarta.validation.constraints.NotNull +import uk.gov.communities.prsdb.webapp.constants.PROVIDE_THIS_LATER_BUTTON_ACTION_NAME import uk.gov.communities.prsdb.webapp.constants.enums.LicensingType import uk.gov.communities.prsdb.webapp.database.entity.PropertyOwnership +import uk.gov.communities.prsdb.webapp.validation.ConstraintDescriptor +import uk.gov.communities.prsdb.webapp.validation.DelegatedPropertyConstraintValidator import uk.gov.communities.prsdb.webapp.validation.IsValidPrioritised +import uk.gov.communities.prsdb.webapp.validation.ValidatedBy @IsValidPrioritised class LicensingTypeFormModel : FormModel { - @NotNull(message = "forms.licensingType.radios.error.missing") + @ValidatedBy( + constraints = [ + ConstraintDescriptor( + messageKey = "forms.licensingType.radios.error.missing", + validatorType = DelegatedPropertyConstraintValidator::class, + targetMethod = "licensingTypeIsValidForAction", + ), + ], + ) var licensingType: LicensingType? = null + var action: String? = null + + fun licensingTypeIsValidForAction(): Boolean = action == PROVIDE_THIS_LATER_BUTTON_ACTION_NAME || licensingType != null + companion object { fun fromPropertyOwnership(propertyOwnership: PropertyOwnership): LicensingTypeFormModel = LicensingTypeFormModel().apply { diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/LicenseService.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/LicenseService.kt index ec50f995d1..3d927aa5d7 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/LicenseService.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/LicenseService.kt @@ -25,7 +25,7 @@ class LicenseService( updateLicenceType: LicensingType?, updateLicenceNumber: String?, ): License? = - if (updateLicenceType == LicensingType.NO_LICENSING) { + if (!licenceShouldBeStored(updateLicenceType ?: LicensingType.NO_LICENSING)) { license?.let { licenseRepository.delete(license) } null } else if (license == null) { @@ -36,4 +36,12 @@ class LicenseService( licenseRepository.save(license) license } + + // A licence is only stored when the landlord selects a licence type other than NO_LICENSING or PROVIDE_LATER. + // NO_LICENSING and PROVIDE_LATER are both represented by a null licence, but are distinguished by the + // licenseProvideLater flag on the property ownership. + companion object { + fun licenceShouldBeStored(licenceType: LicensingType): Boolean = + licenceType != LicensingType.NO_LICENSING && licenceType != LicensingType.PROVIDE_LATER + } } diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyOwnershipService.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyOwnershipService.kt index 751ed95a3c..e45b48aa0f 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyOwnershipService.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyOwnershipService.kt @@ -61,6 +61,7 @@ class PropertyOwnershipService( rentAmount: BigDecimal?, customPropertyType: String?, markedJointLandlord: Boolean = false, + licenseProvideLater: Boolean? = null, ): PropertyOwnership { val registrationNumber = registrationNumberService.createRegistrationNumber(RegistrationNumberType.PROPERTY) @@ -85,6 +86,7 @@ class PropertyOwnershipService( customRentFrequency = customRentFrequency, rentAmount = rentAmount, markedJointLandlord = markedJointLandlord, + licenseProvideLater = licenseProvideLater, ).apply { if (isOccupied) lastOccupiedDate = LocalDate.now() }, diff --git a/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyRegistrationService.kt b/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyRegistrationService.kt index 4b104cc4a9..05dd62f27b 100644 --- a/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyRegistrationService.kt +++ b/src/main/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyRegistrationService.kt @@ -40,7 +40,7 @@ class PropertyRegistrationService( fun registerProperty( addressModel: AddressDataModel, propertyType: PropertyType, - licenseType: LicensingType, + licenseType: LicensingType?, licenceNumber: String, ownershipType: OwnershipType, isOccupied: Boolean, @@ -72,6 +72,7 @@ class PropertyRegistrationService( epcExemptionReason: EpcExemptionReason? = null, epcMeesExemptionReason: MeesExemptionReason? = null, epcProvideLater: Boolean? = null, + licenseProvideLater: Boolean = false, ) { val landlord = individualLandlordRepository.findByBaseUser_Id(baseUserId) @@ -98,6 +99,7 @@ class PropertyRegistrationService( customPropertyType, markedJointLandlord, mutableSetOf(landlord), + licenseProvideLater = licenseProvideLater, ) propertyComplianceService.saveRegistrationComplianceData( @@ -127,7 +129,7 @@ class PropertyRegistrationService( private fun createPropertyOwnershipAndRelatedEntities( addressModel: AddressDataModel, propertyType: PropertyType, - licenseType: LicensingType, + licenseType: LicensingType?, licenceNumber: String, ownershipType: OwnershipType, isOccupied: Boolean, @@ -143,6 +145,7 @@ class PropertyRegistrationService( customPropertyType: String?, markedJointLandlord: Boolean, landlords: MutableSet, + licenseProvideLater: Boolean = false, ): PropertyOwnership { if (addressModel.uprn != null && propertyOwnershipRepository.existsByIsActiveTrueAndAddress_Uprn(addressModel.uprn)) { throw EntityExistsException("Address already registered") @@ -151,7 +154,7 @@ class PropertyRegistrationService( val address = addressService.findOrCreateAddress(addressModel) val license = - if (licenseType != LicensingType.NO_LICENSING) { + if (licenseType != null && LicenseService.licenceShouldBeStored(licenseType)) { licenseService.createLicense(licenseType, licenceNumber) } else { null @@ -175,6 +178,7 @@ class PropertyRegistrationService( markedJointLandlord = markedJointLandlord, address = address, license = license, + licenseProvideLater = licenseProvideLater, ) } diff --git a/src/main/resources/messages/forms.yml b/src/main/resources/messages/forms.yml index 9cdf32f975..3b6d67fdae 100644 --- a/src/main/resources/messages/forms.yml +++ b/src/main/resources/messages/forms.yml @@ -661,6 +661,7 @@ checkPropertyAnswers: ownership: Ownership type licensingType: Licensing type noLicensing: None + licensingProvideLater: Provide this later tenancyDetails: heading: Tenancy and rental information occupied: Occupied by tenants diff --git a/src/main/resources/messages/registerProperty.yml b/src/main/resources/messages/registerProperty.yml index df973b0aae..7e7c5f2b0e 100644 --- a/src/main/resources/messages/registerProperty.yml +++ b/src/main/resources/messages/registerProperty.yml @@ -124,3 +124,26 @@ confirmMissingCompliance: error: missing: Select whether you want to submit this registration button: Confirm and submit +provideLicensingLater: + occupied: + heading: Provide details about property licensing later + paragraph: + one: You can continue and submit your registration now, but you’ll need to return later and provide details about this property’s license. + whenToAdd: + heading: When you must do this by + insetText: To keep your property registered, you must provide its licensing details within 28 days. + paragraph: This is because you’ve told us this property’s occupied by tenants. + unoccupied: + heading: Provide details about property licensing later + paragraph: + one: If you’re unsure, check with the local council if your property needs a license to be rented out. + whenToAdd: + heading: When you’ll need to provide these property licensing details + paragraph: + one: You can continue to finish your registration and get a Property Registration Number. + two: 'Once your property’s occupied, you have 28 days to return and tell us:' + weWillNeedToKnow: + intro: 'We’ll need to know:' + bullet: + one: if your property needs a licence or not + two: details about the licence (if your property needs one) diff --git a/src/main/resources/templates/forms/licensingTypeForm.html b/src/main/resources/templates/forms/licensingTypeForm.html index 6a83a728a8..4af3e4ecde 100644 --- a/src/main/resources/templates/forms/licensingTypeForm.html +++ b/src/main/resources/templates/forms/licensingTypeForm.html @@ -4,6 +4,11 @@ + + + + + @@ -11,6 +16,14 @@ th:replace="~{fragments/forms/fieldSet :: fieldSet(~{::#fieldset-content/content()}, 'licensingType', #{${fieldSetHeading}}, #{${fieldSetHint}})}"> - + +
+ + +
+
+ + +
diff --git a/src/main/resources/templates/forms/provideLicensingLaterOccupiedForm.html b/src/main/resources/templates/forms/provideLicensingLaterOccupiedForm.html new file mode 100644 index 0000000000..38b1a3234f --- /dev/null +++ b/src/main/resources/templates/forms/provideLicensingLaterOccupiedForm.html @@ -0,0 +1,27 @@ + + + + + + + + +
+

registerProperty.provideLicensingLater.occupied.heading

+
+
+

registerProperty.provideLicensingLater.occupied.paragraph.one

+

registerProperty.provideLicensingLater.occupied.whenToAdd.heading

+
registerProperty.provideLicensingLater.occupied.whenToAdd.insetText
+

registerProperty.provideLicensingLater.occupied.whenToAdd.paragraph

+

registerProperty.provideLicensingLater.weWillNeedToKnow.intro

+
    +
  • registerProperty.provideLicensingLater.weWillNeedToKnow.bullet.one
  • +
  • registerProperty.provideLicensingLater.weWillNeedToKnow.bullet.two
  • +
+
+
+ + + + diff --git a/src/main/resources/templates/forms/provideLicensingLaterUnoccupiedForm.html b/src/main/resources/templates/forms/provideLicensingLaterUnoccupiedForm.html new file mode 100644 index 0000000000..e66ef059d4 --- /dev/null +++ b/src/main/resources/templates/forms/provideLicensingLaterUnoccupiedForm.html @@ -0,0 +1,26 @@ + + + + + + + + +
+

registerProperty.provideLicensingLater.unoccupied.heading

+
+
+

registerProperty.provideLicensingLater.unoccupied.paragraph.one

+

registerProperty.provideLicensingLater.unoccupied.whenToAdd.heading

+

registerProperty.provideLicensingLater.unoccupied.whenToAdd.paragraph.one

+

registerProperty.provideLicensingLater.unoccupied.whenToAdd.paragraph.two

+
    +
  • registerProperty.provideLicensingLater.weWillNeedToKnow.bullet.one
  • +
  • registerProperty.provideLicensingLater.weWillNeedToKnow.bullet.two
  • +
+
+
+ + + + diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyDetailsUpdateJourneyTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyDetailsUpdateJourneyTests.kt index 242545f442..e2ae0d2b8e 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyDetailsUpdateJourneyTests.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyDetailsUpdateJourneyTests.kt @@ -190,6 +190,17 @@ class PropertyDetailsUpdateJourneyTests : IntegrationTestWithMutableData("data-l assertThat(propertyDetailsUpdatePage.propertyDetailsSummaryList.licensingTypeRow.value).containsText("None") } + @Test + fun `Update licensing flow does not show provide this later action`(page: Page) { + featureFlagManager.enableFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING) + + val propertyDetailsUpdatePage = navigator.goToPropertyDetailsLandlordView(propertyOwnershipId) + propertyDetailsUpdatePage.propertyDetailsSummaryList.licensingTypeRow.clickFirstActionLinkAndWait() + val updateLicensingTypePage = assertPageIs(page, LicensingTypeFormPagePropertyDetailsUpdate::class, urlArguments) + + assertThat(updateLicensingTypePage.provideThisLaterButton).isHidden() + } + @Test fun `A property can have its licensing number updated again from the check licensing answers page`(page: Page) { val firstNewLicenceNumber = "SL456" diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyRegistrationJourneyTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyRegistrationJourneyTests.kt index 8d0fbf81d0..60b6fec5a6 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyRegistrationJourneyTests.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyRegistrationJourneyTests.kt @@ -88,6 +88,7 @@ import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyReg import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.ProvideElectricalCertLaterFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.ProvideEpcLaterFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.ProvideGasCertLaterFormPagePropertyRegistration +import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.ProvideLicensingLaterFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.RegisterPropertyStartPage import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.RemoveElectricalCertUploadFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.RemoveGasCertUploadFormPagePropertyRegistration @@ -1622,6 +1623,84 @@ class PropertyRegistrationJourneyTests : IntegrationTestWithMutableData("data-lo assertTrue(checkAndSubmitTask.hasLink) } + @Test + fun `restructured occupied journey supports provide licensing later and shows it on check answers`(page: Page) { + featureFlagManager.enableFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING) + + val registerPropertyStartPage = navigator.goToPropertyRegistrationStartPage() + registerPropertyStartPage.startButton.clickAndWait() + var taskListPage = assertPageIs(page, TaskListPagePropertyRegistration::class) + + taskListPage.clickAboutYourPropertyTaskWithName("Property details") + val addressLookupPage = assertPageIs(page, LookupAddressFormPagePropertyRegistration::class) + addressLookupPage.submitPostcodeAndBuildingNameOrNumber("FA1 1AA", "1") + val selectAddressPage = assertPageIs(page, SelectAddressFormPagePropertyRegistration::class) + selectAddressPage.selectAddressAndSubmit("1 Fictional Road, FA1 1AA") + val propertyTypePage = assertPageIs(page, PropertyTypeFormPagePropertyRegistration::class) + propertyTypePage.submitPropertyType(PropertyType.DETACHED_HOUSE) + val bedroomsPage = assertPageIs(page, NumberOfBedroomsFormPagePropertyRegistration::class) + bedroomsPage.submitNumOfBedrooms(3) + val ownershipTypePage = assertPageIs(page, OwnershipTypeFormPagePropertyRegistration::class) + ownershipTypePage.submitOwnershipType(OwnershipType.FREEHOLD) + + val hasJointLandlordsPage = assertPageIs(page, HasJointLandlordsFormBasePagePropertyRegistration::class) + hasJointLandlordsPage.submitHasNoJointLandlords() + + val occupancyPage = assertPageIs(page, OccupancyFormPagePropertyRegistration::class) + occupancyPage.submitIsOccupied() + val licensingTypePage = assertPageIs(page, LicensingTypeFormPagePropertyRegistration::class) + licensingTypePage.submitProvideThisLater() + val provideLicensingLaterPage = assertPageIs(page, ProvideLicensingLaterFormPagePropertyRegistration::class) + assertThat(provideLicensingLaterPage.insetText).isVisible() + provideLicensingLaterPage.form.submit() + + val hasGasSupplyPage = assertPageIs(page, HasGasSupplyFormPagePropertyRegistration::class) + hasGasSupplyPage.submitHasNoGasSupply() + val checkGasSafetyAnswersPage = assertPageIs(page, CheckGasSafetyAnswersFormPagePropertyRegistration::class) + checkGasSafetyAnswersPage.form.submit() + + taskListPage = assertPageIs(page, TaskListPagePropertyRegistration::class) + taskListPage.clickRentedOutTaskWithName("Electrical safety certificate") + val hasElectricalCertPage = assertPageIs(page, HasElectricalCertFormPagePropertyRegistration::class) + hasElectricalCertPage.submitProvideThisLater() + val provideElectricalCertLaterPage = assertPageIs(page, ProvideElectricalCertLaterFormPagePropertyRegistration::class) + provideElectricalCertLaterPage.form.submit() + val checkElectricalSafetyAnswersPage = assertPageIs(page, CheckElectricalSafetyAnswersFormPagePropertyRegistration::class) + whenever(epcRegisterClient.getByUprn(uprnForSelectedAddress)).thenReturn(MockEpcData.epcRegisterClientEpcNotFoundResponse) + checkElectricalSafetyAnswersPage.form.submit() + + taskListPage = assertPageIs(page, TaskListPagePropertyRegistration::class) + taskListPage.clickRentedOutTaskWithName("Energy performance certificate (EPC)") + val hasEpcPage = assertPageIs(page, HasEpcFormPagePropertyRegistration::class) + hasEpcPage.submitProvideThisLater() + val provideEpcLaterPage = assertPageIs(page, ProvideEpcLaterFormPagePropertyRegistration::class) + provideEpcLaterPage.form.submit() + val checkEpcAnswersPage = assertPageIs(page, CheckEpcAnswersFormPagePropertyRegistration::class) + checkEpcAnswersPage.form.submit() + + taskListPage = assertPageIs(page, TaskListPagePropertyRegistration::class) + taskListPage.clickRentedOutTaskWithName("Tenancy details") + val householdsPage = assertPageIs(page, NumberOfHouseholdsFormPagePropertyRegistration::class) + householdsPage.submitNumberOfHouseholds(2) + val peoplePage = assertPageIs(page, NumberOfPeopleFormPagePropertyRegistration::class) + peoplePage.submitNumOfPeople(2) + val rentIncludesBillsPage = assertPageIs(page, RentIncludesBillsFormPagePropertyRegistration::class) + rentIncludesBillsPage.submitIsNotIncluded() + val furnishedPage = assertPageIs(page, FurnishedStatusFormPagePropertyRegistration::class) + furnishedPage.submitFurnishedStatus(FurnishedStatus.FURNISHED) + val rentFrequencyPage = assertPageIs(page, RentFrequencyFormPagePropertyRegistration::class) + rentFrequencyPage.selectRentFrequency(RentFrequency.MONTHLY) + rentFrequencyPage.form.submit() + val rentAmountPage = assertPageIs(page, RentAmountFormPagePropertyRegistration::class) + rentAmountPage.submitRentAmount("400") + + taskListPage = assertPageIs(page, TaskListPagePropertyRegistration::class) + taskListPage.clickSubmitYourRegistrationTaskWithName("Check and submit your answers") + val checkAnswersPage = assertPageIs(page, CheckAnswersPagePropertyRegistration::class) + + assertThat(checkAnswersPage.summaryList.licensingRow.value).containsText("Provide this later") + } + @Test fun `numeric values with leading zeros are displayed without leading zeros on the CYA page`(page: Page) { val checkAnswersPage = diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyRegistrationSinglePageTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyRegistrationSinglePageTests.kt index 490c995cd0..1050a7f4de 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyRegistrationSinglePageTests.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/PropertyRegistrationSinglePageTests.kt @@ -50,6 +50,7 @@ import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyReg import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.NumberOfPeopleFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.OccupancyFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.OwnershipTypeFormPagePropertyRegistration +import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.ProvideLicensingLaterFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.RemoveJointLandlordAreYouSureFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages.SelectiveLicenceFormPagePropertyRegistration import uk.gov.communities.prsdb.webapp.models.dataModels.AddressDataModel @@ -231,7 +232,7 @@ class PropertyRegistrationSinglePageTests : IntegrationTestWithImmutableData("da @Test fun `Submitting with no licensingType selected returns an error`(page: Page) { val licensingTypePage = navigator.skipToPropertyRegistrationOccupiedLicensingTypePage() - licensingTypePage.form.submit() + licensingTypePage.form.submitPrimaryButton() assertThat(licensingTypePage.form.getErrorMessage()).containsText("Select the type of licensing for the property") } @@ -254,6 +255,58 @@ class PropertyRegistrationSinglePageTests : IntegrationTestWithImmutableData("da .assertThat(licenseNumberPage.form.sectionHeader) .containsText("Tell us if your property needs a license") } + + @Test + fun `Submitting provide this later on licensing routes to occupied provide licensing later page`(page: Page) { + featureFlagManager.enableFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING) + + val taskListPage = + navigator.goToRestructuredPropertyRegistrationTaskList( + PropertyStateSessionBuilder + .beforePropertyRegistrationOwnershipType() + .withBedrooms() + .withOwnershipType() + .withHasNoJointLandlords() + .withOccupancyStatus(true), + ) + taskListPage.clickRentedOutTaskWithName("Tell us if your property needs a license") + val licensingTypePage = assertPageIs(page, LicensingTypeFormPagePropertyRegistration::class) + assertThat(licensingTypePage.provideThisLaterButton).isVisible() + + licensingTypePage.submitProvideThisLater() + val provideLicensingLaterPage = assertPageIs(page, ProvideLicensingLaterFormPagePropertyRegistration::class) + + BaseComponent + .assertThat(provideLicensingLaterPage.heading) + .containsText("Provide details about property licensing later") + BaseComponent.assertThat(provideLicensingLaterPage.insetText).isVisible() + } + + @Test + fun `Submitting provide this later on licensing routes to unoccupied provide licensing later page`(page: Page) { + featureFlagManager.enableFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING) + + val taskListPage = + navigator.goToRestructuredPropertyRegistrationTaskList( + PropertyStateSessionBuilder + .beforePropertyRegistrationOwnershipType() + .withBedrooms() + .withOwnershipType() + .withHasNoJointLandlords() + .withOccupancyStatus(false), + ) + taskListPage.clickRentedOutTaskWithName("Tell us if your property needs a license") + val licensingTypePage = assertPageIs(page, LicensingTypeFormPagePropertyRegistration::class) + assertThat(licensingTypePage.provideThisLaterButton).isVisible() + + licensingTypePage.submitProvideThisLater() + val provideLicensingLaterPage = assertPageIs(page, ProvideLicensingLaterFormPagePropertyRegistration::class) + + BaseComponent + .assertThat(provideLicensingLaterPage.heading) + .containsText("Provide details about property licensing later") + BaseComponent.assertThat(provideLicensingLaterPage.insetText).isHidden() + } } @Nested diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/pageObjects/pages/basePages/LicensingTypeFormPage.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/pageObjects/pages/basePages/LicensingTypeFormPage.kt index ed65f0e103..29efac6ef4 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/pageObjects/pages/basePages/LicensingTypeFormPage.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/pageObjects/pages/basePages/LicensingTypeFormPage.kt @@ -1,6 +1,8 @@ package uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.basePages import com.microsoft.playwright.Page +import uk.gov.communities.prsdb.webapp.constants.CONTINUE_BUTTON_ACTION_NAME +import uk.gov.communities.prsdb.webapp.constants.PROVIDE_THIS_LATER_BUTTON_ACTION_NAME import uk.gov.communities.prsdb.webapp.constants.enums.LicensingType import uk.gov.communities.prsdb.webapp.integration.pageObjects.components.FormWithSectionHeader import uk.gov.communities.prsdb.webapp.integration.pageObjects.components.Radios @@ -16,12 +18,21 @@ abstract class LicensingTypeFormPage( fun submitLicensingType(licensingType: LicensingType) { form.licensingTypeRadios.selectValue(licensingType) - form.submit() + form.submitPrimaryButton() } + fun submitProvideThisLater() = form.submitSecondaryButton() + + val provideThisLaterButton = form.provideThisLaterButton + class LicensingTypeForm( page: Page, ) : FormWithSectionHeader(page) { val licensingTypeRadios = Radios(locator) + val provideThisLaterButton = locator.locator("button[type='submit'][value='$PROVIDE_THIS_LATER_BUTTON_ACTION_NAME']") + + fun submitPrimaryButton(buttonAction: String = CONTINUE_BUTTON_ACTION_NAME) = submitSelectedButton(buttonAction) + + fun submitSecondaryButton(buttonAction: String = PROVIDE_THIS_LATER_BUTTON_ACTION_NAME) = submitSelectedButton(buttonAction) } } diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/pageObjects/pages/propertyRegistrationJourneyPages/ProvideLicensingLaterFormPagePropertyRegistration.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/pageObjects/pages/propertyRegistrationJourneyPages/ProvideLicensingLaterFormPagePropertyRegistration.kt new file mode 100644 index 0000000000..d7e9971742 --- /dev/null +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/integration/pageObjects/pages/propertyRegistrationJourneyPages/ProvideLicensingLaterFormPagePropertyRegistration.kt @@ -0,0 +1,21 @@ +package uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.propertyRegistrationJourneyPages + +import com.microsoft.playwright.Page +import uk.gov.communities.prsdb.webapp.controllers.RegisterPropertyController +import uk.gov.communities.prsdb.webapp.integration.pageObjects.components.FormWithSectionHeader.SectionHeader +import uk.gov.communities.prsdb.webapp.integration.pageObjects.components.Heading +import uk.gov.communities.prsdb.webapp.integration.pageObjects.components.InsetText +import uk.gov.communities.prsdb.webapp.integration.pageObjects.components.PostForm +import uk.gov.communities.prsdb.webapp.integration.pageObjects.pages.basePages.BasePage +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.ProvideLicensingLaterStep + +class ProvideLicensingLaterFormPagePropertyRegistration( + page: Page, +) : BasePage(page, "${RegisterPropertyController.PROPERTY_REGISTRATION_ROUTE}/${ProvideLicensingLaterStep.ROUTE_SEGMENT}") { + val heading = Heading(page.locator("h1")) + val form = PostForm(page) + val sectionHeader = SectionHeader(page.locator("main")) + + // Only present on the occupied variant + val insetText = InsetText(page) +} diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/LicensingTypeStepConfigTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/LicensingTypeStepConfigTests.kt new file mode 100644 index 0000000000..53c098209b --- /dev/null +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/LicensingTypeStepConfigTests.kt @@ -0,0 +1,103 @@ +package uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.whenever +import uk.gov.communities.prsdb.webapp.config.managers.FeatureFlagManager +import uk.gov.communities.prsdb.webapp.constants.CONTINUE_BUTTON_ACTION_NAME +import uk.gov.communities.prsdb.webapp.constants.PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING +import uk.gov.communities.prsdb.webapp.constants.PROVIDE_THIS_LATER_BUTTON_ACTION_NAME +import uk.gov.communities.prsdb.webapp.journeys.UnrecoverableJourneyStateException +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.states.LicensingState +import uk.gov.communities.prsdb.webapp.testHelpers.mockObjects.AlwaysTrueValidator + +@ExtendWith(MockitoExtension::class) +class LicensingTypeStepConfigTests { + @Mock + lateinit var mockState: LicensingState + + @Mock + lateinit var featureFlagManager: FeatureFlagManager + + val routeSegment = LicensingTypeStep.ROUTE_SEGMENT + + @Test + fun `mode returns null when form model is not present`() { + val stepConfig = setupStepConfig() + whenever(mockState.getStepData(routeSegment)).thenReturn(null) + + val result = stepConfig.mode(mockState) + + assertNull(result) + } + + @Test + fun `mode returns null when licensingType is null and action is not provideThisLater`() { + val stepConfig = setupStepConfig() + whenever(mockState.getStepData(routeSegment)) + .thenReturn(mapOf("licensingType" to null, "action" to CONTINUE_BUTTON_ACTION_NAME)) + + val result = stepConfig.mode(mockState) + + assertNull(result) + } + + @Test + fun `mode returns SELECTIVE_LICENCE when licensingType is SELECTIVE_LICENCE`() { + val stepConfig = setupStepConfig() + whenever(mockState.getStepData(routeSegment)) + .thenReturn(mapOf("licensingType" to "SELECTIVE_LICENCE", "action" to CONTINUE_BUTTON_ACTION_NAME)) + + val result = stepConfig.mode(mockState) + + assertEquals(LicensingTypeMode.SELECTIVE_LICENCE, result) + } + + @Test + fun `mode returns PROVIDE_LATER when action is provideThisLater and route is allowed and FF is on`() { + val stepConfig = setupStepConfig() + whenever(mockState.allowProvideLicensingLaterRoute).thenReturn(true) + whenever(featureFlagManager.checkFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING)).thenReturn(true) + whenever(mockState.getStepData(routeSegment)) + .thenReturn(mapOf("licensingType" to null, "action" to PROVIDE_THIS_LATER_BUTTON_ACTION_NAME)) + + val result = stepConfig.mode(mockState) + + assertEquals(LicensingTypeMode.PROVIDE_LATER, result) + } + + @Test + fun `mode throws UnrecoverableJourneyStateException when action is provideThisLater but allowProvideLicensingLaterRoute is false`() { + val stepConfig = setupStepConfig() + whenever(mockState.allowProvideLicensingLaterRoute).thenReturn(false) + whenever(mockState.journeyId).thenReturn("test-journey-id") + whenever(mockState.getStepData(routeSegment)) + .thenReturn(mapOf("licensingType" to null, "action" to PROVIDE_THIS_LATER_BUTTON_ACTION_NAME)) + + assertThrows { stepConfig.mode(mockState) } + } + + @Test + fun `mode throws UnrecoverableJourneyStateException when action is provideThisLater but FF is off`() { + val stepConfig = setupStepConfig() + whenever(mockState.allowProvideLicensingLaterRoute).thenReturn(true) + whenever(featureFlagManager.checkFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING)).thenReturn(false) + whenever(mockState.journeyId).thenReturn("test-journey-id") + whenever(mockState.getStepData(routeSegment)) + .thenReturn(mapOf("licensingType" to null, "action" to PROVIDE_THIS_LATER_BUTTON_ACTION_NAME)) + + assertThrows { stepConfig.mode(mockState) } + } + + private fun setupStepConfig(): LicensingTypeStepConfig { + val stepConfig = LicensingTypeStepConfig(featureFlagManager) + stepConfig.routeSegment = routeSegment + stepConfig.validator = AlwaysTrueValidator() + return stepConfig + } +} diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/ProvideLicensingLaterStepConfigTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/ProvideLicensingLaterStepConfigTests.kt new file mode 100644 index 0000000000..f7b4d4cc41 --- /dev/null +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/ProvideLicensingLaterStepConfigTests.kt @@ -0,0 +1,52 @@ +package uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.whenever +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.states.LicensingState +import uk.gov.communities.prsdb.webapp.testHelpers.mockObjects.AlwaysTrueValidator + +@ExtendWith(MockitoExtension::class) +class ProvideLicensingLaterStepConfigTests { + @Mock + lateinit var mockState: LicensingState + + @Test + fun `chooseTemplate returns occupied template when isOccupied is true`() { + val stepConfig = setupStepConfig() + whenever(mockState.isOccupied).thenReturn(true) + + val result = stepConfig.chooseTemplate(mockState) + + assertEquals("forms/provideLicensingLaterOccupiedForm", result) + } + + @Test + fun `chooseTemplate returns unoccupied template when isOccupied is false`() { + val stepConfig = setupStepConfig() + whenever(mockState.isOccupied).thenReturn(false) + + val result = stepConfig.chooseTemplate(mockState) + + assertEquals("forms/provideLicensingLaterUnoccupiedForm", result) + } + + @Test + fun `chooseTemplate throws IllegalStateException when isOccupied is null`() { + val stepConfig = setupStepConfig() + whenever(mockState.isOccupied).thenReturn(null) + + assertThrows { stepConfig.chooseTemplate(mockState) } + } + + private fun setupStepConfig(): ProvideLicensingLaterStepConfig { + val stepConfig = ProvideLicensingLaterStepConfig() + stepConfig.routeSegment = ProvideLicensingLaterStep.ROUTE_SEGMENT + stepConfig.validator = AlwaysTrueValidator() + return stepConfig + } +} diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/SavePropertyRegistrationDataStepConfigTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/SavePropertyRegistrationDataStepConfigTests.kt index 9f1bbebea7..951c5476cb 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/SavePropertyRegistrationDataStepConfigTests.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/propertyRegistration/steps/SavePropertyRegistrationDataStepConfigTests.kt @@ -36,7 +36,6 @@ import uk.gov.communities.prsdb.webapp.models.dataModels.EpcDataModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.EpcExemptionFormModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.EpcInDateAtStartOfTenancyCheckFormModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.HasJointLandlordsFormModel -import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.LicensingTypeFormModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.MeesExemptionReasonFormModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.OccupancyFormModel import uk.gov.communities.prsdb.webapp.models.requestModels.formModels.OwnershipTypeFormModel @@ -160,6 +159,58 @@ class SavePropertyRegistrationDataStepConfigTests { epcExemptionReason = eq(epcExemptionReason), epcMeesExemptionReason = eq(meesExemptionReason), epcProvideLater = eq(false), + licenseProvideLater = eq(false), + ) + } + + @Test + fun `afterStepIsReached passes licenseProvideLater as true when the user provides licensing later`() { + // Arrange + setupStateForPropertyRegistration() + setupStateForComplianceDataWithNullValues() + whenever(mockState.licensingTypeStep.outcome).thenReturn(LicensingTypeMode.PROVIDE_LATER) + whenever(mockState.getLicensingType()).thenReturn(LicensingType.PROVIDE_LATER) + + // Act + stepConfig.afterStepIsReached(mockState) + + // Assert + verify(mockPropertyRegistrationService).registerProperty( + addressModel = any(), + propertyType = any(), + licenseType = anyOrNull(), + licenceNumber = any(), + ownershipType = any(), + isOccupied = any(), + numberOfHouseholds = any(), + numberOfPeople = any(), + baseUserId = any(), + numBedrooms = anyOrNull(), + billsIncludedList = anyOrNull(), + customBillsIncluded = anyOrNull(), + furnishedStatus = anyOrNull(), + rentFrequency = anyOrNull(), + customRentFrequency = anyOrNull(), + rentAmount = anyOrNull(), + customPropertyType = anyOrNull(), + jointLandlordEmails = anyOrNull(), + markedJointLandlord = any(), + hasGasSupply = anyOrNull(), + gasSafetyCertIssueDate = anyOrNull(), + gasSafetyFileUploadIds = any(), + gasSafetyCertProvideLater = anyOrNull(), + electricalSafetyFileUploadIds = any(), + electricalSafetyExpiryDate = anyOrNull(), + electricalCertType = anyOrNull(), + electricalSafetyCertProvideLater = anyOrNull(), + epcCertificateUrl = anyOrNull(), + epcExpiryDate = anyOrNull(), + epcEnergyRating = anyOrNull(), + tenancyStartedBeforeEpcExpiry = anyOrNull(), + epcExemptionReason = anyOrNull(), + epcMeesExemptionReason = anyOrNull(), + epcProvideLater = anyOrNull(), + licenseProvideLater = eq(true), ) } @@ -207,6 +258,7 @@ class SavePropertyRegistrationDataStepConfigTests { epcExemptionReason = anyOrNull(), epcMeesExemptionReason = anyOrNull(), epcProvideLater = anyOrNull(), + licenseProvideLater = anyOrNull(), ), ).thenThrow(EntityExistsException("Address already registered")) @@ -264,6 +316,7 @@ class SavePropertyRegistrationDataStepConfigTests { epcExemptionReason = isNull(), epcMeesExemptionReason = isNull(), epcProvideLater = anyOrNull(), + licenseProvideLater = anyOrNull(), ) } @@ -318,11 +371,11 @@ class SavePropertyRegistrationDataStepConfigTests { whenever(mockPropertyTypeStep.formModel).thenReturn(propertyTypeFormModel) val mockLicensingTypeStep = mock() - val licensingTypeFormModel = LicensingTypeFormModel().apply { licensingType = LicensingType.SELECTIVE_LICENCE } whenever(mockState.licensingTypeStep).thenReturn(mockLicensingTypeStep) - whenever(mockLicensingTypeStep.formModel).thenReturn(licensingTypeFormModel) + whenever(mockLicensingTypeStep.outcome).thenReturn(LicensingTypeMode.SELECTIVE_LICENCE) whenever(mockState.getLicenceNumberOrNull()).thenReturn(null) + whenever(mockState.getLicensingType()).thenReturn(LicensingType.SELECTIVE_LICENCE) val mockOwnershipTypeStep = mock() val ownershipTypeFormModel = OwnershipTypeFormModel().apply { ownershipType = OwnershipType.FREEHOLD } diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/shared/helpers/LicensingDetailsHelperTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/shared/helpers/LicensingDetailsHelperTests.kt index 945feb7003..a905b30a7b 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/shared/helpers/LicensingDetailsHelperTests.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/journeys/shared/helpers/LicensingDetailsHelperTests.kt @@ -5,11 +5,13 @@ import org.mockito.Mockito.mock import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.whenever import uk.gov.communities.prsdb.webapp.config.managers.FeatureFlagManager +import uk.gov.communities.prsdb.webapp.constants.PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING import uk.gov.communities.prsdb.webapp.constants.enums.LicensingType import uk.gov.communities.prsdb.webapp.journeys.JourneyState import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.states.LicensingState import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.HmoAdditionalLicenceStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.HmoMandatoryLicenceStep +import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.LicensingTypeMode import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.LicensingTypeStep import uk.gov.communities.prsdb.webapp.journeys.propertyRegistration.steps.SelectiveLicenceStep import uk.gov.communities.prsdb.webapp.journeys.shared.states.CheckYourAnswersJourneyState @@ -74,6 +76,26 @@ class LicensingDetailsHelperTests { } } + @Test + fun `When licensing was skipped and FF is on, getCheckYourAnswersSummaryList returns a single provide-this-later row`() { + // Arrange + whenever(featureFlagManager.checkFeature(PROPERTY_REGISTRATION_RESTRUCTURE_AND_SKIPPING)).thenReturn(true) + val state = createMockLicensingStateWithSkip() + + // Act + val summaryList = licensingDetailsHelper.getCheckYourAnswersSummaryList(state) + + // Assert + summaryList.single().let { row -> + assertEquals("forms.checkPropertyAnswers.propertyDetails.licensingType", row.fieldHeading) + assertEquals("forms.checkPropertyAnswers.propertyDetails.licensingProvideLater", row.fieldValue) + assertEquals( + listOf(SummaryListRowActionsViewModel("forms.links.change", "licensing-type?journeyId=$childJourneyId")), + row.actions, + ) + } + } + interface TestableLicensingState : CheckYourAnswersJourneyState, LicensingState, @@ -129,4 +151,19 @@ class LicensingDetailsHelperTests { return stateMock } + + fun createMockLicensingStateWithSkip(): TestableLicensingState { + val stateMock = mock() + + val typeStepMock = + mock().apply { + whenever(this.outcome).thenReturn(LicensingTypeMode.PROVIDE_LATER) + whenever(this.routeSegment).thenReturn("licensing-type") + whenever(this.isStepReachable).thenReturn(true) + } + whenever(stateMock.licensingTypeStep).thenReturn(typeStepMock) + whenever(stateMock.getCyaJourneyId(anyOrNull())).thenReturn(childJourneyId) + + return stateMock + } } diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/services/LicenseServiceTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/services/LicenseServiceTests.kt index 93efd80f23..7d8dd51ad2 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/services/LicenseServiceTests.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/services/LicenseServiceTests.kt @@ -1,6 +1,7 @@ package uk.gov.communities.prsdb.webapp.services import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -11,6 +12,7 @@ import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.internal.matchers.apachecommons.ReflectionEquals import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import uk.gov.communities.prsdb.webapp.constants.enums.LicensingType @@ -78,4 +80,29 @@ class LicenseServiceTests { assertNull(updatedLicence) } + + @Test + fun `updateLicence returns null and does not delete when there is no existing licence and the new licenceType is NO_LICENSING`() { + val updatedLicence = licenseService.updateLicence(null, LicensingType.NO_LICENSING, null) + + verify(mockLicenseRepository, never()).delete(any(License::class.java)) + verify(mockLicenseRepository, never()).save(any(License::class.java)) + + assertNull(updatedLicence) + } + + @Test + fun `licenceShouldBeStored returns true for a licence type other than NO_LICENSING or PROVIDE_LATER`() { + assertTrue(LicenseService.licenceShouldBeStored(LicensingType.SELECTIVE_LICENCE)) + } + + @Test + fun `licenceShouldBeStored returns false for NO_LICENSING`() { + assertFalse(LicenseService.licenceShouldBeStored(LicensingType.NO_LICENSING)) + } + + @Test + fun `licenceShouldBeStored returns false for PROVIDE_LATER`() { + assertFalse(LicenseService.licenceShouldBeStored(LicensingType.PROVIDE_LATER)) + } } diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyRegistrationServiceTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyRegistrationServiceTests.kt index 2111577062..1fb1d2c68b 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyRegistrationServiceTests.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/services/PropertyRegistrationServiceTests.kt @@ -13,6 +13,7 @@ import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argThat import org.mockito.kotlin.eq +import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import uk.gov.communities.prsdb.webapp.constants.enums.EpcExemptionReason @@ -205,6 +206,7 @@ class PropertyRegistrationServiceTests { rentFrequency = rentFrequency, customRentFrequency = customRentFrequency, rentAmount = rentAmount, + licenseProvideLater = false, ), ).thenReturn(expectedPropertyOwnership) whenever(mockAbsoluteUrlProvider.buildLandlordDashboardUri()).thenReturn(URI("https:gov.uk")) @@ -248,6 +250,7 @@ class PropertyRegistrationServiceTests { rentFrequency = rentFrequency, customRentFrequency = customRentFrequency, rentAmount = rentAmount, + licenseProvideLater = false, ) verify(mockPropertyComplianceService).saveRegistrationComplianceData( registrationNumberValue = registrationNumber.number, @@ -298,6 +301,7 @@ class PropertyRegistrationServiceTests { rentAmount = anyOrNull(), customPropertyType = anyOrNull(), markedJointLandlord = any(), + licenseProvideLater = anyOrNull(), ), ).thenReturn(expectedPropertyOwnership) whenever(mockAbsoluteUrlProvider.buildLandlordDashboardUri()).thenReturn(URI("https:gov.uk")) @@ -388,6 +392,7 @@ class PropertyRegistrationServiceTests { rentAmount = anyOrNull(), customPropertyType = anyOrNull(), markedJointLandlord = any(), + licenseProvideLater = anyOrNull(), ), ).thenReturn(expectedPropertyOwnership) @@ -489,6 +494,7 @@ class PropertyRegistrationServiceTests { rentFrequency = rentFrequency, customRentFrequency = customRentFrequency, rentAmount = rentAmount, + licenseProvideLater = false, ), ).thenReturn(expectedPropertyOwnership) whenever(mockAbsoluteUrlProvider.buildLandlordDashboardUri()).thenReturn(URI("https:gov.uk")) @@ -513,6 +519,7 @@ class PropertyRegistrationServiceTests { customPropertyType, ) + verify(mockLicenseService, never()).createLicense(any(), any()) verify(mockPropertyOwnershipService).createPropertyOwnership( ownershipType = ownershipType, isOccupied = true, @@ -530,6 +537,116 @@ class PropertyRegistrationServiceTests { rentFrequency = rentFrequency, customRentFrequency = customRentFrequency, rentAmount = rentAmount, + licenseProvideLater = false, + ) + } + + @Test + fun `registerProperty does not create a license and sets licenseProvideLater when the user provides licensing later`() { + // Arrange + val ownershipType = OwnershipType.FREEHOLD + val numberOfHouseholds = 1 + val numberOfPeople = 2 + val landlord = MockLandlordData.createLandlord() + val propertyType = PropertyType.DETACHED_HOUSE + val customPropertyType = "End terrace" + val addressDataModel = AddressDataModel("1 Example Road, EG1 2AB") + val address = Address(addressDataModel) + val registrationNumber = RegistrationNumber(RegistrationNumberType.PROPERTY, 1233456) + val numberOfBedrooms = 1 + val billsIncludedList = "Electricity, Water" + val customBillsIncluded = "Internet" + val furnishedStatus = FurnishedStatus.FURNISHED + val rentFrequency = RentFrequency.OTHER + val customRentFrequency = "Fortnightly" + val rentAmount = 123.toBigDecimal() + + val expectedPropertyOwnership = + MockLandlordData.createPropertyOwnership( + ownershipType = ownershipType, + currentNumHouseholds = numberOfHouseholds, + currentNumTenants = numberOfPeople, + landlords = mutableSetOf(landlord), + propertyBuildType = propertyType, + address = address, + license = null, + registrationNumber = registrationNumber, + numberOfBedrooms = numberOfBedrooms, + billsIncludedList = billsIncludedList, + customBillsIncluded = customBillsIncluded, + furnishedStatus = furnishedStatus, + rentFrequency = rentFrequency, + customRentFrequency = customRentFrequency, + rentAmount = rentAmount, + ) + + whenever(mockAddressService.findOrCreateAddress(addressDataModel)).thenReturn(address) + whenever(mockIndividualLandlordRepository.findByBaseUser_Id(landlord.baseUser.id)).thenReturn(landlord) + whenever( + mockPropertyOwnershipService.createPropertyOwnership( + ownershipType = ownershipType, + isOccupied = true, + numberOfHouseholds = numberOfHouseholds, + numberOfPeople = numberOfPeople, + landlords = mutableSetOf(landlord), + propertyBuildType = propertyType, + customPropertyType = customPropertyType, + address = address, + license = null, + numBedrooms = numberOfBedrooms, + billsIncludedList = billsIncludedList, + customBillsIncluded = customBillsIncluded, + furnishedStatus = furnishedStatus, + rentFrequency = rentFrequency, + customRentFrequency = customRentFrequency, + rentAmount = rentAmount, + licenseProvideLater = true, + ), + ).thenReturn(expectedPropertyOwnership) + whenever(mockAbsoluteUrlProvider.buildLandlordDashboardUri()).thenReturn(URI("https:gov.uk")) + + // Act + propertyRegistrationService.registerProperty( + addressDataModel, + propertyType, + null, + "", + ownershipType, + true, + numberOfHouseholds, + numberOfPeople, + landlord.baseUser.id, + numberOfBedrooms, + billsIncludedList, + customBillsIncluded, + furnishedStatus, + rentFrequency, + customRentFrequency, + rentAmount, + customPropertyType, + licenseProvideLater = true, + ) + + // Assert + verify(mockLicenseService, never()).createLicense(any(), any()) + verify(mockPropertyOwnershipService).createPropertyOwnership( + ownershipType = ownershipType, + isOccupied = true, + numberOfHouseholds = numberOfHouseholds, + numberOfPeople = numberOfPeople, + landlords = mutableSetOf(landlord), + propertyBuildType = propertyType, + customPropertyType = customPropertyType, + address = address, + license = null, + numBedrooms = numberOfBedrooms, + billsIncludedList = billsIncludedList, + customBillsIncluded = customBillsIncluded, + furnishedStatus = furnishedStatus, + rentFrequency = rentFrequency, + customRentFrequency = customRentFrequency, + rentAmount = rentAmount, + licenseProvideLater = true, ) } @@ -583,6 +700,7 @@ class PropertyRegistrationServiceTests { rentFrequency = RentFrequency.MONTHLY, customRentFrequency = null, rentAmount = 123.toBigDecimal(), + licenseProvideLater = false, ), ).thenReturn(expectedPropertyOwnership) whenever(mockAbsoluteUrlProvider.buildLandlordDashboardUri()).thenReturn(URI("https:gov.uk")) @@ -663,6 +781,7 @@ class PropertyRegistrationServiceTests { rentFrequency = RentFrequency.MONTHLY, customRentFrequency = null, rentAmount = 123.toBigDecimal(), + licenseProvideLater = false, ), ).thenReturn(expectedPropertyOwnership) whenever(mockAbsoluteUrlProvider.buildLandlordDashboardUri()).thenReturn(URI("https:gov.uk")) @@ -740,6 +859,7 @@ class PropertyRegistrationServiceTests { rentFrequency = RentFrequency.MONTHLY, customRentFrequency = null, rentAmount = 123.toBigDecimal(), + licenseProvideLater = false, ), ).thenReturn(expectedPropertyOwnership) whenever(mockAbsoluteUrlProvider.buildLandlordDashboardUri()).thenReturn(URI("https:gov.uk")) @@ -807,6 +927,7 @@ class PropertyRegistrationServiceTests { rentAmount = anyOrNull(), customPropertyType = anyOrNull(), markedJointLandlord = any(), + licenseProvideLater = anyOrNull(), ), ).thenReturn(expectedPropertyOwnership) whenever(mockAbsoluteUrlProvider.buildLandlordDashboardUri()).thenReturn(URI("https:gov.uk")) @@ -853,6 +974,7 @@ class PropertyRegistrationServiceTests { rentAmount = anyOrNull(), customPropertyType = anyOrNull(), markedJointLandlord = eq(true), + licenseProvideLater = anyOrNull(), ) } } diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/testHelpers/builders/LicensingStateBuilder.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/testHelpers/builders/LicensingStateBuilder.kt index 32a1d6cb83..f54bb8d26e 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/testHelpers/builders/LicensingStateBuilder.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/testHelpers/builders/LicensingStateBuilder.kt @@ -34,6 +34,7 @@ interface LicensingStateBuilder> { LicensingType.HMO_MANDATORY_LICENCE -> withLicenceNumber("hmo-mandatory-licence", licenseNumber) LicensingType.HMO_ADDITIONAL_LICENCE -> withLicenceNumber("hmo-additional-licence", licenseNumber) LicensingType.NO_LICENSING -> {} + LicensingType.PROVIDE_LATER -> {} } return self() } diff --git a/src/test/kotlin/uk/gov/communities/prsdb/webapp/urlProviders/LandlordDashboardUrlTests.kt b/src/test/kotlin/uk/gov/communities/prsdb/webapp/urlProviders/LandlordDashboardUrlTests.kt index 8752af26d7..03825c5e41 100644 --- a/src/test/kotlin/uk/gov/communities/prsdb/webapp/urlProviders/LandlordDashboardUrlTests.kt +++ b/src/test/kotlin/uk/gov/communities/prsdb/webapp/urlProviders/LandlordDashboardUrlTests.kt @@ -201,6 +201,7 @@ class LandlordDashboardUrlTests( anyOrNull(), anyOrNull(), anyOrNull(), + anyOrNull(), ), ).thenReturn(propertyOwnership)