diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/DoYouPayExtraForParkingSpacesController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/DoYouPayExtraForParkingSpacesController.scala new file mode 100644 index 00000000..fe62bac6 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/DoYouPayExtraForParkingSpacesController.scala @@ -0,0 +1,86 @@ +/* + * Copyright 2025 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.gov.hmrc.ngrraldfrontend.controllers + +import play.api.i18n.I18nSupport +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import uk.gov.hmrc.http.NotFoundException +import uk.gov.hmrc.ngrraldfrontend.actions.{AuthRetrievals, DataRetrievalAction} +import uk.gov.hmrc.ngrraldfrontend.config.AppConfig +import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio.buildRadios +import uk.gov.hmrc.ngrraldfrontend.models.forms.DoYouPayExtraForParkingSpacesForm +import uk.gov.hmrc.ngrraldfrontend.models.forms.DoYouPayExtraForParkingSpacesForm.form +import uk.gov.hmrc.ngrraldfrontend.models.{Landlord, Mode, NormalMode, UserAnswers} +import uk.gov.hmrc.ngrraldfrontend.navigation.Navigator +import uk.gov.hmrc.ngrraldfrontend.pages.DoYouPayExtraForParkingSpacesPage +import uk.gov.hmrc.ngrraldfrontend.repo.SessionRepository +import uk.gov.hmrc.ngrraldfrontend.views.html.DoYouPayExtraForParkingSpacesView +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController + +import javax.inject.{Inject, Singleton} +import scala.concurrent.{ExecutionContext, Future} + +@Singleton +class DoYouPayExtraForParkingSpacesController @Inject()(doYouPayExtraForParkingSpacesView: DoYouPayExtraForParkingSpacesView, + authenticate : AuthRetrievals, + getData: DataRetrievalAction, + navigator: Navigator, + sessionRepository: SessionRepository, + mcc: MessagesControllerComponents)(implicit appConfig: AppConfig, ec: ExecutionContext) + extends FrontendController(mcc) with I18nSupport { + + def show(mode: Mode): Action[AnyContent] = { + (authenticate andThen getData).async { implicit request => + val preparedForm = request.userAnswers.getOrElse(UserAnswers(request.credId)).get(DoYouPayExtraForParkingSpacesPage) match { + case None => form + case Some(value) => form.fill(DoYouPayExtraForParkingSpacesForm(if (value) "Yes" else "No")) + } + Future.successful(Ok(doYouPayExtraForParkingSpacesView( + form = preparedForm, + radios = buildRadios(preparedForm, DoYouPayExtraForParkingSpacesForm.ngrRadio(preparedForm)), + propertyAddress = request.property.addressFull, + mode = mode + ))) + } + } + + def submit(mode: Mode): Action[AnyContent] = + (authenticate andThen getData).async { implicit request => + form.bindFromRequest().fold( + formWithErrors => { + Future.successful(BadRequest(doYouPayExtraForParkingSpacesView( + form = formWithErrors, + radios = buildRadios(formWithErrors, DoYouPayExtraForParkingSpacesForm.ngrRadio(formWithErrors)), + propertyAddress = request.property.addressFull, + mode = mode + ))) + }, + radioValue => + for { + updatedAnswers <- Future.fromTry(request.userAnswers.getOrElse(UserAnswers(request.credId)) + .set(DoYouPayExtraForParkingSpacesPage, + radioValue.radioValue match + case "Yes" => true + case _ => false + ) + ) + _ <- sessionRepository.set(updatedAnswers) + } yield Redirect(navigator.nextPage(DoYouPayExtraForParkingSpacesPage, mode, updatedAnswers)) + ) + } + +} diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/ProvideDetailsOfFirstSecondRentPeriodController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/ProvideDetailsOfFirstSecondRentPeriodController.scala index cfabed28..b4529eb8 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/ProvideDetailsOfFirstSecondRentPeriodController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/ProvideDetailsOfFirstSecondRentPeriodController.scala @@ -28,7 +28,7 @@ import uk.gov.hmrc.ngrraldfrontend.models.{Mode, NGRDate, NormalMode, ProvideDet import uk.gov.hmrc.ngrraldfrontend.models.components.* import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio.buildRadios import uk.gov.hmrc.ngrraldfrontend.models.forms.ProvideDetailsOfFirstSecondRentPeriodForm -import uk.gov.hmrc.ngrraldfrontend.models.forms.ProvideDetailsOfFirstSecondRentPeriodForm.form +import uk.gov.hmrc.ngrraldfrontend.models.forms.ProvideDetailsOfFirstSecondRentPeriodForm._ import uk.gov.hmrc.ngrraldfrontend.utils.DateKeyFinder import uk.gov.hmrc.ngrraldfrontend.navigation.Navigator import uk.gov.hmrc.ngrraldfrontend.pages.ProvideDetailsOfFirstSecondRentPeriodPage @@ -148,15 +148,7 @@ class ProvideDetailsOfFirstSecondRentPeriodController @Inject()(view: ProvideDet (authenticate andThen getData).async { implicit request => val preparedForm = request.userAnswers.getOrElse(UserAnswers(request.credId)).get(ProvideDetailsOfFirstSecondRentPeriodPage) match { case None => form - case Some(value) => form.fill(ProvideDetailsOfFirstSecondRentPeriodForm(NGRDate.fromString(value.firstDateStart),NGRDate.fromString(value.firstDateEnd),value.firstRentPeriodRadio match { - case true => "yesPayedRent" - case false => "noRentPayed" - }, value.firstRentPeriodAmount match { - case Some(value) => Some(value) - case None => None - }, - NGRDate.fromString(value.secondDateStart),NGRDate.fromString(value.secondDateEnd),BigDecimal(value.secondHowMuchIsRent))) - + case Some(value) => answerToForm(value) } Future.successful(Ok(view( selectedPropertyAddress = request.property.addressFull, @@ -204,22 +196,8 @@ class ProvideDetailsOfFirstSecondRentPeriodController @Inject()(view: ProvideDet mode ))), provideDetailsOfFirstSecondRentPeriodForm => - val provideDetailsOfFirstSecondRentPeriod: ProvideDetailsOfFirstSecondRentPeriod = ProvideDetailsOfFirstSecondRentPeriod( - provideDetailsOfFirstSecondRentPeriodForm.firstDateStartInput.makeString, - provideDetailsOfFirstSecondRentPeriodForm.firstDateEndInput.makeString, - provideDetailsOfFirstSecondRentPeriodForm.firstRentPeriodRadio match { - case "yesPayedRent" => true - case _ => false - }, - provideDetailsOfFirstSecondRentPeriodForm.firstRentPeriodAmount match { - case Some(value) => Some(value) - case None => None - }, - provideDetailsOfFirstSecondRentPeriodForm.secondDateStartInput.makeString, - provideDetailsOfFirstSecondRentPeriodForm.secondDateEndInput.makeString, - provideDetailsOfFirstSecondRentPeriodForm.secondHowMuchIsRent.toString()) for { - updatedAnswers <- Future.fromTry(request.userAnswers.getOrElse(UserAnswers(request.credId)).set(ProvideDetailsOfFirstSecondRentPeriodPage, provideDetailsOfFirstSecondRentPeriod)) + updatedAnswers <- Future.fromTry(request.userAnswers.getOrElse(UserAnswers(request.credId)).set(ProvideDetailsOfFirstSecondRentPeriodPage, formToAnswers(provideDetailsOfFirstSecondRentPeriodForm))) _ <- sessionRepository.set(updatedAnswers) } yield Redirect(navigator.nextPage(ProvideDetailsOfFirstSecondRentPeriodPage, NormalMode, updatedAnswers)) ) diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala index 868a75ce..5583b231 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala @@ -24,7 +24,7 @@ import uk.gov.hmrc.govukfrontend.views.viewmodels.table.{Table, TableRow} import uk.gov.hmrc.http.NotFoundException import uk.gov.hmrc.ngrraldfrontend.actions.{AuthRetrievals, DataRetrievalAction} import uk.gov.hmrc.ngrraldfrontend.config.AppConfig -import uk.gov.hmrc.ngrraldfrontend.models.{Mode, ProvideDetailsOfFirstSecondRentPeriod} +import uk.gov.hmrc.ngrraldfrontend.models.{Mode, NGRDate, ProvideDetailsOfFirstSecondRentPeriod} import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio.buildRadios import uk.gov.hmrc.ngrraldfrontend.models.forms.RentPeriodsForm @@ -58,7 +58,7 @@ class RentPeriodsController @Inject()(view: RentPeriodView, content = Text(messages("rentPeriods.first.startDate")) ), TableRow( - content = Text(rentPeriods.firstDateStart), + content = Text(NGRDate.formatDate(rentPeriods.firstDateStart)), attributes = Map( "id" -> "first-period-start-date-id" ) @@ -69,7 +69,7 @@ class RentPeriodsController @Inject()(view: RentPeriodView, content = Text(messages("rentPeriods.first.endDate")) ), TableRow( - content = Text(rentPeriods.firstDateEnd), + content = Text(NGRDate.formatDate(rentPeriods.firstDateEnd)), attributes = Map( "id" -> "first-period-end-date-id" ) @@ -119,7 +119,7 @@ class RentPeriodsController @Inject()(view: RentPeriodView, content = Text(messages("rentPeriods.second.startDate")) ), TableRow( - content = Text(rentPeriods.secondDateStart), + content = Text(NGRDate.formatDate(rentPeriods.secondDateStart)), attributes = Map( "id" -> "second-period-start-date-id" ) @@ -130,7 +130,7 @@ class RentPeriodsController @Inject()(view: RentPeriodView, content = Text(messages("rentPeriods.second.endDate")) ), TableRow( - content = Text(rentPeriods.secondDateEnd), + content = Text(NGRDate.formatDate(rentPeriods.secondDateEnd)), attributes = Map( "id" -> "second-period-end-date-id" ) @@ -141,7 +141,7 @@ class RentPeriodsController @Inject()(view: RentPeriodView, content = Text(messages("rentPeriods.second.rentValue")) ), TableRow( - content = Text(rentPeriods.secondHowMuchIsRent), + content = Text(formatRentValue(rentPeriods.secondHowMuchIsRent.toDouble)), attributes = Map( "id" -> "second-period-rent-value-id" ) diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/NGRMonthYear.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/NGRMonthYear.scala index 9c1b6e15..1d766eec 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/models/NGRMonthYear.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/NGRMonthYear.scala @@ -41,8 +41,8 @@ object NGRMonthYear { } def fromString(dateString: String): NGRMonthYear = { - val parts = dateString.split("-").map(_.toInt) - NGRMonthYear(parts(1).toString, parts(0).toString) + val parts = dateString.split("-") + NGRMonthYear(parts(1), parts(0)) } diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/DoYouPayExtraForParkingSpacesForm.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/DoYouPayExtraForParkingSpacesForm.scala new file mode 100644 index 00000000..b95f0e6b --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/DoYouPayExtraForParkingSpacesForm.scala @@ -0,0 +1,48 @@ +/* + * Copyright 2025 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.gov.hmrc.ngrraldfrontend.models.forms + +import play.api.data.Form +import play.api.data.Forms.mapping +import play.api.i18n.Messages +import play.api.libs.json.{Json, OFormat} +import uk.gov.hmrc.ngrraldfrontend.models.components.{NGRRadio, NGRRadioName} +import uk.gov.hmrc.ngrraldfrontend.models.forms.mappings.Mappings + +case class DoYouPayExtraForParkingSpacesForm(radioValue: String) + +object DoYouPayExtraForParkingSpacesForm extends Mappings { + + implicit val format: OFormat[DoYouPayExtraForParkingSpacesForm] = Json.format[DoYouPayExtraForParkingSpacesForm] + + private lazy val radioUnselectedError = "doYouPayExtraForParkingSpaces.required.error" + val payExtraRadio = "payExtra" + + def unapply(doYouPayExtraForParkingSpacesForm: DoYouPayExtraForParkingSpacesForm): Option[String] = Some(doYouPayExtraForParkingSpacesForm.radioValue) + + def form: Form[DoYouPayExtraForParkingSpacesForm] = { + Form( + mapping( + payExtraRadio -> radioText(radioUnselectedError) + )(DoYouPayExtraForParkingSpacesForm.apply)(DoYouPayExtraForParkingSpacesForm.unapply) + ) + } + + def ngrRadio(form: Form[DoYouPayExtraForParkingSpacesForm])(implicit messages: Messages): NGRRadio = + NGRRadio(NGRRadioName(payExtraRadio), NGRRadioButtons = Seq(NGRRadio.yesButton, NGRRadio.noButton)) + +} diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/ProvideDetailsOfFirstSecondRentPeriodForm.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/ProvideDetailsOfFirstSecondRentPeriodForm.scala index 94cfb082..1b642f33 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/ProvideDetailsOfFirstSecondRentPeriodForm.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/ProvideDetailsOfFirstSecondRentPeriodForm.scala @@ -69,7 +69,39 @@ object ProvideDetailsOfFirstSecondRentPeriodForm extends CommonFormValidators wi provideDetailsOfFirstSecondRentPeriodForm.secondDateEndInput, provideDetailsOfFirstSecondRentPeriodForm.secondHowMuchIsRent ) - + + def answerToForm(value: ProvideDetailsOfFirstSecondRentPeriod): Form[ProvideDetailsOfFirstSecondRentPeriodForm] = + form.fill( + ProvideDetailsOfFirstSecondRentPeriodForm( + NGRDate.fromString(value.firstDateStart), + NGRDate.fromString(value.firstDateEnd), + value.firstRentPeriodRadio match { + case true => "yesPayedRent" + case false => "noRentPayed" + }, + value.firstRentPeriodAmount, + NGRDate.fromString(value.secondDateStart), + NGRDate.fromString(value.secondDateEnd), + BigDecimal(value.secondHowMuchIsRent)) + ) + + def formToAnswers(provideDetailsOfFirstSecondRentPeriodForm: ProvideDetailsOfFirstSecondRentPeriodForm): ProvideDetailsOfFirstSecondRentPeriod = + ProvideDetailsOfFirstSecondRentPeriod( + provideDetailsOfFirstSecondRentPeriodForm.firstDateStartInput.makeString, + provideDetailsOfFirstSecondRentPeriodForm.firstDateEndInput.makeString, + provideDetailsOfFirstSecondRentPeriodForm.firstRentPeriodRadio match { + case "yesPayedRent" => true + case _ => false + }, + provideDetailsOfFirstSecondRentPeriodForm.firstRentPeriodAmount match { + case Some(value) if provideDetailsOfFirstSecondRentPeriodForm.firstRentPeriodRadio.equals("yesPayedRent") => Some(value) + case _ => None + }, + provideDetailsOfFirstSecondRentPeriodForm.secondDateStartInput.makeString, + provideDetailsOfFirstSecondRentPeriodForm.secondDateEndInput.makeString, + provideDetailsOfFirstSecondRentPeriodForm.secondHowMuchIsRent.toString() + ) + private def firstRentPeriodAmountValidation[A]: Constraint[A] = Constraint((input: A) => val provideDetailsOfFirstSecondRentPeriodForm = input.asInstanceOf[ProvideDetailsOfFirstSecondRentPeriodForm] diff --git a/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala b/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala index 1028e075..5c2c3fa5 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala @@ -131,17 +131,26 @@ class Navigator @Inject()() { answers.get(DoesYourRentIncludeParkingPage) match { case Some(value) => value match { case "Yes" => uk.gov.hmrc.ngrraldfrontend.controllers.routes.HowManyParkingSpacesOrGaragesIncludedInRentController.show(NormalMode) - case _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) + case _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.DoYouPayExtraForParkingSpacesController.show(NormalMode) } case None => throw new NotFoundException("Failed to find answers") } - case HowManyParkingSpacesOrGaragesIncludedInRentPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) + case HowManyParkingSpacesOrGaragesIncludedInRentPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.DoYouPayExtraForParkingSpacesController.show(NormalMode) case InterimSetByTheCourtPage => answers => answers.get(ProvideDetailsOfFirstSecondRentPeriodPage) match { case Some(_) => uk.gov.hmrc.ngrraldfrontend.controllers.routes.RentDatesAgreeController.show(NormalMode) - case None => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) + case None => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) } case RentFreePeriodPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.RentDatesAgreeStartController.show(NormalMode) + //TODO need to connect to next pages when they are done + case DoYouPayExtraForParkingSpacesPage => answers => + answers.get(DoYouPayExtraForParkingSpacesPage) match { + case Some(value) => value match { + case true => uk.gov.hmrc.ngrraldfrontend.controllers.routes.DoYouPayExtraForParkingSpacesController.show(NormalMode) + case _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.DoYouPayExtraForParkingSpacesController.show(NormalMode) + } + case None => throw new NotFoundException("Failed to find answers") + } } //TODO change to check your answers page diff --git a/app/uk/gov/hmrc/ngrraldfrontend/pages/DoYouPayExtraForParkingSpacesPage.scala b/app/uk/gov/hmrc/ngrraldfrontend/pages/DoYouPayExtraForParkingSpacesPage.scala new file mode 100644 index 00000000..613bdf69 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/pages/DoYouPayExtraForParkingSpacesPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2025 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.gov.hmrc.ngrraldfrontend.pages + +import play.api.libs.json.JsPath + +case object DoYouPayExtraForParkingSpacesPage extends QuestionPage[Boolean]{ + + override def path: JsPath = JsPath \ toString + + override def toString: String = "doYouPayExtraForParkingSpacesNotIncludedInRent" + +} diff --git a/app/uk/gov/hmrc/ngrraldfrontend/views/DoYouPayExtraForParkingSpacesView.scala.html b/app/uk/gov/hmrc/ngrraldfrontend/views/DoYouPayExtraForParkingSpacesView.scala.html new file mode 100644 index 00000000..454dadc1 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/views/DoYouPayExtraForParkingSpacesView.scala.html @@ -0,0 +1,45 @@ +@* + * Copyright 2025 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *@ + +@import uk.gov.hmrc.govukfrontend.views.html.components._ +@import uk.gov.hmrc.govukfrontend.views.Aliases._ +@import uk.gov.hmrc.ngrraldfrontend.views.html.components._ +@import uk.gov.hmrc.ngrraldfrontend.viewmodels.govuk.all._ +@import uk.gov.hmrc.ngrraldfrontend.config.AppConfig +@import uk.gov.hmrc.ngrraldfrontend.models.components.NavigationBarContent +@import uk.gov.hmrc.ngrraldfrontend.models.forms.DoYouPayExtraForParkingSpacesForm + + +@this( + layout: Layout, + govukRadios : GovukRadios, + formHelper: FormWithCSRF, + govukErrorSummary: GovukErrorSummary, + saveAndContinueButton: saveAndContinueButton) + +@(form:Form[DoYouPayExtraForParkingSpacesForm], radios: Radios, propertyAddress: String,mode: Mode)(implicit request: RequestHeader, messages: Messages, appConfig: AppConfig) + +@layout(pageTitle = Some(messages("doYouPayExtraForParkingSpaces.title")), showBackLink = true, fullWidth = false) { + @formHelper(action = uk.gov.hmrc.ngrraldfrontend.controllers.routes.DoYouPayExtraForParkingSpacesController.submit(mode), Symbol("autoComplete") -> "off") { + @if(form.errors.nonEmpty) { + @govukErrorSummary(ErrorSummaryViewModel(form)) + } + @propertyAddress +

@messages("doYouPayExtraForParkingSpaces.title")

+ @govukRadios(radios) + @saveAndContinueButton(msg = messages("service.continue"), isStartButton = false) + } +} diff --git a/conf/app.routes b/conf/app.routes index 0463d15d..864ba9cb 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -129,4 +129,10 @@ POST /interim-rent-set-by-court/change uk.gov.hmrc.n GET /rent-free-period uk.gov.hmrc.ngrraldfrontend.controllers.RentFreePeriodController.show(mode: Mode = NormalMode) POST /rent-free-period uk.gov.hmrc.ngrraldfrontend.controllers.RentFreePeriodController.submit(mode: Mode = NormalMode) GET /rent-free-period/change uk.gov.hmrc.ngrraldfrontend.controllers.RentFreePeriodController.show(mode: Mode = CheckMode) -POST /rent-free-period/change uk.gov.hmrc.ngrraldfrontend.controllers.RentFreePeriodController.submit(mode: Mode = CheckMode) \ No newline at end of file +POST /rent-free-period/change uk.gov.hmrc.ngrraldfrontend.controllers.RentFreePeriodController.submit(mode: Mode = CheckMode) + +#Do you pay extra for parking spaces or garages not included in rent +GET /do-you-pay-extra-for-parking-spaces-or-garages-not-included-in-rent uk.gov.hmrc.ngrraldfrontend.controllers.DoYouPayExtraForParkingSpacesController.show(mode: Mode = NormalMode) +POST /do-you-pay-extra-for-parking-spaces-or-garages-not-included-in-rent uk.gov.hmrc.ngrraldfrontend.controllers.DoYouPayExtraForParkingSpacesController.submit(mode: Mode = NormalMode) +GET /do-you-pay-extra-for-parking-spaces-or-garages-not-included-in-rent/change uk.gov.hmrc.ngrraldfrontend.controllers.DoYouPayExtraForParkingSpacesController.show(mode: Mode = CheckMode) +POST /do-you-pay-extra-for-parking-spaces-or-garages-not-included-in-rent/change uk.gov.hmrc.ngrraldfrontend.controllers.DoYouPayExtraForParkingSpacesController.submit(mode: Mode = CheckMode) \ No newline at end of file diff --git a/conf/messages b/conf/messages index 738b6566..3cf1aabe 100644 --- a/conf/messages +++ b/conf/messages @@ -382,4 +382,8 @@ rentFreePeriod.months.invalid.error = Rent-free period must be a number, like 6 rentFreePeriod.months.minimum.error = Rent-free period must be more more than 0 rentFreePeriod.months.maximum.error = Rent-free period must be 99 months or less rentFreePeriod.reasons.required.error = Tell us why you have a rent-free period -rentFreePeriod.reasons.length.error = What you tell us about why you have a rent-free period must be 250 characters or less \ No newline at end of file +rentFreePeriod.reasons.length.error = What you tell us about why you have a rent-free period must be 250 characters or less + +#DoYouPayExtraForParkingSpaces +doYouPayExtraForParkingSpaces.title = Do you pay extra for parking spaces or garages that are not included in your rent? +doYouPayExtraForParkingSpaces.required.error = Select yes if your rent includes extra parking spaces or garages \ No newline at end of file diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/DoYouPayExtraForParkingSpacesControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/DoYouPayExtraForParkingSpacesControllerSpec.scala new file mode 100644 index 00000000..735acdf8 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/DoYouPayExtraForParkingSpacesControllerSpec.scala @@ -0,0 +1,109 @@ +/* + * Copyright 2025 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.gov.hmrc.ngrraldfrontend.controllers + +import org.jsoup.Jsoup +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.when +import play.api.http.Status.{BAD_REQUEST, OK, SEE_OTHER} +import play.api.test.FakeRequest +import play.api.test.Helpers.{await, contentAsString, defaultAwaitTimeout, redirectLocation, status} +import uk.gov.hmrc.http.{HeaderNames, NotFoundException} +import uk.gov.hmrc.ngrraldfrontend.helpers.ControllerSpecSupport +import uk.gov.hmrc.ngrraldfrontend.models.registration.CredId +import uk.gov.hmrc.ngrraldfrontend.models.{NormalMode, UserAnswers} +import uk.gov.hmrc.ngrraldfrontend.pages.DoYouPayExtraForParkingSpacesPage +import uk.gov.hmrc.ngrraldfrontend.views.html.DoYouPayExtraForParkingSpacesView + +import scala.concurrent.Future + +class DoYouPayExtraForParkingSpacesControllerSpec extends ControllerSpecSupport{ + val pageTitle = "Do you pay extra for parking spaces or garages that are not included in your rent?" + val view: DoYouPayExtraForParkingSpacesView = inject[DoYouPayExtraForParkingSpacesView] + val controllerNoProperty : DoYouPayExtraForParkingSpacesController = new DoYouPayExtraForParkingSpacesController(view,fakeAuth, fakeData(None), mockNavigator, mockSessionRepository, mcc)(mockConfig) + val controllerProperty : Option[UserAnswers] => DoYouPayExtraForParkingSpacesController = answers => new DoYouPayExtraForParkingSpacesController(view,fakeAuth, fakeDataProperty(Some(property), answers), mockNavigator, mockSessionRepository, mcc)(mockConfig) + val doYouPayExtraForParkingSpacesAnswers: Option[UserAnswers] = UserAnswers("id").set(DoYouPayExtraForParkingSpacesPage, true).toOption + + "DoYouPayExtraForParkingSpacesController" when { + "calling show method" should { + "Return OK and the correct view" in { + val result = controllerProperty(None).show(NormalMode)(authenticatedFakeRequest) + status(result) mustBe OK + val content = contentAsString(result) + content must include(pageTitle) + } + "return OK and the correct view with prepopulated data" in { + val result = controllerProperty(doYouPayExtraForParkingSpacesAnswers).show(NormalMode)(authenticatedFakeRequest) + status(result) mustBe OK + val content = contentAsString(result) + val document = Jsoup.parse(content) + document.select("input[type=radio][name=payExtra][value=Yes]").hasAttr("checked") mustBe true + document.select("input[type=radio][name=payExtra][value=No]").hasAttr("checked") mustBe false + } + "Return Not Found Exception where no property is found in mongo" in { + when(mockNGRConnector.getLinkedProperty(any[CredId])(any())).thenReturn(Future.successful(None)) + val exception = intercept[NotFoundException] { + await(controllerNoProperty.show(NormalMode)(authenticatedFakeRequest)) + } + exception.getMessage contains "Could not find answers in backend mongo" mustBe true + } + } + "method submit" must { + "Return OK and the correct view and the answer is Yes" in { + when(mockSessionRepository.set(any())).thenReturn(Future.successful(true)) + val fakePostRequest = FakeRequest(routes.DoYouPayExtraForParkingSpacesController.submit(NormalMode)) + .withFormUrlEncodedBody("payExtra" -> "Yes") + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controllerProperty(None).submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) + status(result) mustBe SEE_OTHER + redirectLocation(result) mustBe Some(routes.DoYouPayExtraForParkingSpacesController.show(NormalMode).url) + } + "Return OK and the correct view and the answer is No" in { + when(mockSessionRepository.set(any())).thenReturn(Future.successful(true)) + val fakePostRequest = FakeRequest(routes.CheckRentFreePeriodController.submit(NormalMode)) + .withFormUrlEncodedBody("payExtra" -> "No") + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controllerProperty(None).submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) + status(result) mustBe SEE_OTHER + redirectLocation(result) mustBe Some(routes.DoYouPayExtraForParkingSpacesController.show(NormalMode).url) + } + "Return BAD_REQUEST for missing input and the correct view" in { + val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit(NormalMode)) + .withFormUrlEncodedBody("payExtra" -> "") + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controllerProperty(None).submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) + status(result) mustBe BAD_REQUEST + val content = contentAsString(result) + content must include(pageTitle) + content must include("Select yes if your rent includes extra parking spaces or garages") + } + "Return Exception if no address is in the mongo" in { + when(mockNGRConnector.getLinkedProperty(any[CredId])(any())).thenReturn(Future.successful(None)) + val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit(NormalMode)) + .withFormUrlEncodedBody("payExtra" -> "") + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + val exception = intercept[NotFoundException] { + await(controllerNoProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest))) + } + exception.getMessage contains "Could not find answers in backend mongo" mustBe true + } + } + } +} diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/DoesYourRentIncludeParkingControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/DoesYourRentIncludeParkingControllerSpec.scala index 1397ed74..8c98e3ea 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/controllers/DoesYourRentIncludeParkingControllerSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/DoesYourRentIncludeParkingControllerSpec.scala @@ -88,10 +88,10 @@ class DoesYourRentIncludeParkingControllerSpec extends ControllerSpecSupport { ) .withHeaders(HeaderNames.authorisation -> "Bearer 1"), None, None, None, Some(property), credId = Some(credId.value), None, None, nino = Nino(true, Some("")))) result.map(result => { - result.header.headers.get("Location") mustBe Some("/ngr-rald-frontend/do-you-have-a-rent-free-period") + result.header.headers.get("Location") mustBe Some("/do-you-pay-extra-for-parking-spaces-or-garages-not-included-in-rent") }) status(result) mustBe SEE_OTHER - redirectLocation(result) mustBe Some(routes.CheckRentFreePeriodController.show(NormalMode).url) + redirectLocation(result) mustBe Some(routes.DoYouPayExtraForParkingSpacesController.show(NormalMode).url) } "Return Form with Errors when no radio selection is input" in { val result = controllerProperty(None).submit(NormalMode)(AuthenticatedUserRequest(FakeRequest(routes.DoesYourRentIncludeParkingController.submit(NormalMode)) diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowManyParkingSpacesOrGaragesIncludedInRentControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowManyParkingSpacesOrGaragesIncludedInRentControllerSpec.scala index cb2494f2..4d2823a8 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowManyParkingSpacesOrGaragesIncludedInRentControllerSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowManyParkingSpacesOrGaragesIncludedInRentControllerSpec.scala @@ -93,7 +93,7 @@ class HowManyParkingSpacesOrGaragesIncludedInRentControllerSpec extends Controll val result = controllerProperty(None).submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) status(result) mustBe SEE_OTHER - redirectLocation(result) mustBe Some(routes.CheckRentFreePeriodController.show(NormalMode).url) + redirectLocation(result) mustBe Some(routes.DoYouPayExtraForParkingSpacesController.show(NormalMode).url) } "Return BAD_REQUEST for inputting 0 in all fields and the correct view" in { val fakePostRequest = FakeRequest(routes.HowManyParkingSpacesOrGaragesIncludedInRentController.submit(NormalMode)) diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtControllerSpec.scala index 7123446e..522ec21c 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtControllerSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtControllerSpec.scala @@ -88,7 +88,7 @@ class InterimRentSetByTheCourtControllerSpec extends ControllerSpecSupport { val content = contentAsString(result) val document = Jsoup.parse(content) document.select("input[name=interimAmount]").attr("value") mustBe "10000" - document.select("input[name=date.month]").attr("value") mustBe "1" + document.select("input[name=date.month]").attr("value") mustBe "01" document.select("input[name=date.year]").attr("value") mustBe "1990" } diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/forms/DoYouPayExtraForParkingSpacesFormSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/DoYouPayExtraForParkingSpacesFormSpec.scala new file mode 100644 index 00000000..3ad8de43 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/DoYouPayExtraForParkingSpacesFormSpec.scala @@ -0,0 +1,73 @@ +/* + * Copyright 2025 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.gov.hmrc.ngrraldfrontend.models.forms + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import play.api.data.FormError +import play.api.libs.json.{JsValue, Json} + +class DoYouPayExtraForParkingSpacesFormSpec extends AnyWordSpec with Matchers { + val requiredError = "doYouPayExtraForParkingSpaces.required.error" + val checkRentPeriodModel: DoYouPayExtraForParkingSpacesForm = DoYouPayExtraForParkingSpacesForm("Yes") + val checkRentPeriodJson: JsValue = Json.parse("""{"radioValue":"Yes"} + |""".stripMargin) + + "DoYouPayExtraForParkingSpacesForm" should { + "serialize into json" in { + Json.toJson(checkRentPeriodModel) shouldBe checkRentPeriodJson + } + "deserialize from json" in { + checkRentPeriodJson.as[DoYouPayExtraForParkingSpacesForm] shouldBe checkRentPeriodModel + } + "bind successfully with a valid value yes" in { + val data = Map(DoYouPayExtraForParkingSpacesForm.payExtraRadio -> "yes") + val boundForm = DoYouPayExtraForParkingSpacesForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(DoYouPayExtraForParkingSpacesForm("yes")) + } + "bind successfully with a valid value no" in { + val data = Map(DoYouPayExtraForParkingSpacesForm.payExtraRadio -> "no") + val boundForm = DoYouPayExtraForParkingSpacesForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(DoYouPayExtraForParkingSpacesForm("no")) + } + "return an error when the radio value is empty" in { + val data = Map(DoYouPayExtraForParkingSpacesForm.payExtraRadio -> "") + val boundForm = DoYouPayExtraForParkingSpacesForm.form.bind(data) + + boundForm.hasErrors shouldBe true + boundForm.errors should contain(FormError(DoYouPayExtraForParkingSpacesForm.payExtraRadio, requiredError)) + } + + "return an error when the radio value is missing" in { + val data = Map.empty[String, String] + val boundForm = DoYouPayExtraForParkingSpacesForm.form.bind(data) + + boundForm.hasErrors shouldBe true + boundForm.errors should contain(FormError(DoYouPayExtraForParkingSpacesForm.payExtraRadio, requiredError)) + } + + "unbind correctly to a data map" in { + val form = DoYouPayExtraForParkingSpacesForm.form.fill(DoYouPayExtraForParkingSpacesForm("no")) + form.data shouldBe Map(DoYouPayExtraForParkingSpacesForm.payExtraRadio -> "no") + } + } + +} diff --git a/test/uk/gov/hmrc/ngrraldfrontend/views/DoYouPayExtraForParkingSpacesViewSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/views/DoYouPayExtraForParkingSpacesViewSpec.scala new file mode 100644 index 00000000..af9f3595 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/views/DoYouPayExtraForParkingSpacesViewSpec.scala @@ -0,0 +1,114 @@ +/* + * Copyright 2025 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.gov.hmrc.ngrraldfrontend.views + +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import play.api.libs.json.Json +import uk.gov.hmrc.govukfrontend.views.viewmodels.radios.Radios +import uk.gov.hmrc.ngrraldfrontend.helpers.ViewBaseSpec +import uk.gov.hmrc.ngrraldfrontend.models.NormalMode +import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio.{buildRadios, noButton, yesButton} +import uk.gov.hmrc.ngrraldfrontend.models.components.{NGRRadio, NGRRadioName} +import uk.gov.hmrc.ngrraldfrontend.models.forms.DoYouPayExtraForParkingSpacesForm +import uk.gov.hmrc.ngrraldfrontend.views.html.DoYouPayExtraForParkingSpacesView + +class DoYouPayExtraForParkingSpacesViewSpec extends ViewBaseSpec { + lazy val view: DoYouPayExtraForParkingSpacesView = inject[DoYouPayExtraForParkingSpacesView] + + object Strings { + val heading = "Do you pay extra for parking spaces or garages that are not included in your rent?" + val radio1 = "Yes" + val radio2 = "No" + val continue = "Continue" + } + + object Selectors { + val heading = "#main-content > div > div.govuk-grid-column-two-thirds > form > h1" + val radio1 = "#main-content > div > div.govuk-grid-column-two-thirds > form > div > div > div:nth-child(1) > label" + val radio2 = "#main-content > div > div.govuk-grid-column-two-thirds > form > div > div > div:nth-child(2) > label" + val continue = "#continue" + } + + val address = "5 Brixham Marina, Berry Head Road, Brixham, Devon, TQ5 9BW" + private val ngrRadio: NGRRadio = NGRRadio(NGRRadioName(DoYouPayExtraForParkingSpacesForm.payExtraRadio), Seq(yesButton, noButton)) + val form = DoYouPayExtraForParkingSpacesForm.form.fillAndValidate(DoYouPayExtraForParkingSpacesForm("Yes")) + val radio: Radios = buildRadios(form, ngrRadio) + + "TellUsAboutYourNewAgreementView" must { + val doYouPayExtraForParkingSpacesView = view(form, radio, address, NormalMode) + lazy implicit val document: Document = Jsoup.parse(doYouPayExtraForParkingSpacesView.body) + val htmlApply = view.apply(form, radio, address, NormalMode).body + val htmlRender = view.render(form, radio, address, NormalMode, request, messages, mockConfig).body + lazy val htmlF = view.f(form, radio, address, NormalMode) + + "htmlF is not empty" in { + htmlF.toString() must not be empty + } + + "apply must be the same as render" in { + htmlApply mustBe htmlRender + } + + "render is not empty" in { + htmlRender must not be empty + } + + "show correct heading" in { + elementText(Selectors.heading) mustBe Strings.heading + } + + "show correct radio 1" in { + elementText(Selectors.radio1) mustBe Strings.radio1 + } + + "show correct radio 2" in { + elementText(Selectors.radio2) mustBe Strings.radio2 + } + + "show correct continue button" in { + elementText(Selectors.continue) mustBe Strings.continue + } + + "serialize to JSON correctly" in { + val form = DoYouPayExtraForParkingSpacesForm(radioValue = "yes") + val json = Json.toJson(form) + + json mustBe Json.obj( + "radioValue" -> "yes", + ) + } + + "deserialize from JSON correctly" in { + val json = Json.obj( + "radioValue" -> "yes", + ) + val result = json.validate[DoYouPayExtraForParkingSpacesForm] + + result.isSuccess mustBe true + result.get mustBe DoYouPayExtraForParkingSpacesForm("yes") + } + + "fail deserialization if value is missing" in { + val json = Json.obj() + val result = json.validate[DoYouPayExtraForParkingSpacesForm] + + result.isError mustBe true + } + } + +}