diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/AgreementController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/AgreementController.scala index 742ceed4..43915ef9 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/AgreementController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/AgreementController.scala @@ -36,9 +36,10 @@ import uk.gov.hmrc.ngrraldfrontend.views.html.AgreementView import uk.gov.hmrc.ngrraldfrontend.views.html.components.{DateTextFields, NGRCharacterCountComponent} import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController -import javax.inject.Inject +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} +@Singleton class AgreementController @Inject()(view: AgreementView, authenticate: AuthRetrievals, dateTextFields: DateTextFields, diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/AgreementVerbalController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/AgreementVerbalController.scala index 7cc49612..9143ceb1 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/AgreementVerbalController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/AgreementVerbalController.scala @@ -37,9 +37,10 @@ import uk.gov.hmrc.ngrraldfrontend.views.html.AgreementVerbalView import uk.gov.hmrc.ngrraldfrontend.views.html.components.DateTextFields import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController -import javax.inject.Inject +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} +@Singleton class AgreementVerbalController @Inject()(view: AgreementVerbalView, authenticate: AuthRetrievals, hasLinkedProperties: PropertyLinkingAction, diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/CheckRentFreePeriodController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/CheckRentFreePeriodController.scala index 013bc56c..33139e8a 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/CheckRentFreePeriodController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/CheckRentFreePeriodController.scala @@ -30,9 +30,10 @@ import uk.gov.hmrc.ngrraldfrontend.repo.RaldRepo import uk.gov.hmrc.ngrraldfrontend.views.html.CheckRentFreePeriodView import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController -import javax.inject.Inject +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} +@Singleton class CheckRentFreePeriodController @Inject()(checkRentFreePeriodView: CheckRentFreePeriodView, authenticate : AuthRetrievals, hasLinkedProperties: PropertyLinkingAction, diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchIsTotalAnnualRentController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchIsTotalAnnualRentController.scala index a95ce3e6..9ff1263b 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchIsTotalAnnualRentController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchIsTotalAnnualRentController.scala @@ -29,9 +29,10 @@ import uk.gov.hmrc.ngrraldfrontend.repo.RaldRepo import uk.gov.hmrc.ngrraldfrontend.views.html.HowMuchIsTotalAnnualRentView import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController -import javax.inject.Inject +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} +@Singleton class HowMuchIsTotalAnnualRentController @Inject()(howMuchIsTotalAnnualRentView: HowMuchIsTotalAnnualRentView, authenticate: AuthRetrievals, hasLinkedProperties: PropertyLinkingAction, diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/LandlordController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/LandlordController.scala index 4b63f136..9c21fb5e 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/LandlordController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/LandlordController.scala @@ -34,9 +34,10 @@ import uk.gov.hmrc.ngrraldfrontend.views.html.LandlordView import uk.gov.hmrc.ngrraldfrontend.views.html.components.NGRCharacterCountComponent import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController -import javax.inject.Inject +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} +@Singleton class LandlordController @Inject()(view: LandlordView, authenticate: AuthRetrievals, hasLinkedProperties: PropertyLinkingAction, diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/ProvideDetailsOfFirstSecondRentPeriodController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/ProvideDetailsOfFirstSecondRentPeriodController.scala index c80aa212..05f15ac6 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/ProvideDetailsOfFirstSecondRentPeriodController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/ProvideDetailsOfFirstSecondRentPeriodController.scala @@ -36,9 +36,10 @@ import uk.gov.hmrc.ngrraldfrontend.views.html.ProvideDetailsOfFirstSecondRentPer import uk.gov.hmrc.ngrraldfrontend.views.html.components.InputText import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController -import javax.inject.Inject +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} +@Singleton class ProvideDetailsOfFirstSecondRentPeriodController @Inject()(view: ProvideDetailsOfFirstSecondRentPeriodView, authenticate: AuthRetrievals, inputText: InputText, diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala new file mode 100644 index 00000000..e21dd8d4 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala @@ -0,0 +1,185 @@ +/* + * 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, Messages} +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import uk.gov.hmrc.govukfrontend.views.Aliases.Text +import uk.gov.hmrc.govukfrontend.views.viewmodels.* +import uk.gov.hmrc.govukfrontend.views.viewmodels.table.{Table, TableRow} +import uk.gov.hmrc.http.NotFoundException +import uk.gov.hmrc.ngrraldfrontend.actions.{AuthRetrievals, PropertyLinkingAction} +import uk.gov.hmrc.ngrraldfrontend.config.AppConfig +import uk.gov.hmrc.ngrraldfrontend.models.RaldUserAnswers +import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio +import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio.buildRadios +import uk.gov.hmrc.ngrraldfrontend.models.components.NavBarPageContents.createDefaultNavBar +import uk.gov.hmrc.ngrraldfrontend.models.forms.RentPeriodsForm +import uk.gov.hmrc.ngrraldfrontend.models.forms.RentPeriodsForm.form +import uk.gov.hmrc.ngrraldfrontend.models.registration.CredId +import uk.gov.hmrc.ngrraldfrontend.repo.RaldRepo +import uk.gov.hmrc.ngrraldfrontend.views.html.RentPeriodView +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController + +import javax.inject.{Inject, Singleton} +import scala.concurrent.{ExecutionContext, Future} + +@Singleton +class RentPeriodsController @Inject()(view: RentPeriodView, + authenticate: AuthRetrievals, + hasLinkedProperties: PropertyLinkingAction, + raldRepo: RaldRepo, + mcc: MessagesControllerComponents + )(implicit appConfig: AppConfig, ec:ExecutionContext) extends FrontendController(mcc) with I18nSupport { + + def firstTable(userAnswers: RaldUserAnswers)(implicit messages:Messages): Table = + Table( + rows = Seq( + Seq( + TableRow( + content = Text(messages("rentPeriods.first.startDate")) + ), + TableRow( + content = Text(userAnswers.provideDetailsOfFirstSecondRentPeriod.map{ dates => + dates.firstDateStart + }.getOrElse("")) + ) + ), + Seq( + TableRow( + content = Text(messages("rentPeriods.first.endDate")) + ), + TableRow( + content = Text(userAnswers.provideDetailsOfFirstSecondRentPeriod.map{ dates => + dates.firstDateEnd + }.getOrElse("")) + ) + ), + if(userAnswers.provideDetailsOfFirstSecondRentPeriod.nonEmpty){ + Seq( + TableRow( + content = Text(messages("rentPeriods.first.rentValue")) + ), + TableRow( + content = Text(userAnswers.provideDetailsOfFirstSecondRentPeriod.map { dates => + dates.firstRentPeriodAmount.get + }.getOrElse("")) + ) + ) + }else(Seq()), + Seq( + TableRow( + content = Text(messages("rentPeriods.first.doYouPay")) + ), + TableRow( + content = Text(userAnswers.provideDetailsOfFirstSecondRentPeriod.map{ dates => + if(dates.firstRentPeriodRadio == true){ + "Yes" + }else{"False"} + }.getOrElse("")) + ) + ) + ), + head = None, + caption = Some(Messages("rentPeriods.first.subheading")), + captionClasses = "govuk-table__caption--m", + firstCellIsHeader = true + ) + + def secondTable(userAnswers: RaldUserAnswers)(implicit messages: Messages): Table = Table( + rows = Seq( + Seq( + TableRow( + content = Text(messages("rentPeriods.second.startDate")) + ), + TableRow( + content = Text(userAnswers.provideDetailsOfFirstSecondRentPeriod.map{ dates => + dates.secondDateStart + }.getOrElse("")) + ) + ), + Seq( + TableRow( + content = Text(messages("rentPeriods.second.endDate")) + ), + TableRow( + content = Text(userAnswers.provideDetailsOfFirstSecondRentPeriod.map { dates => + dates.secondDateEnd + }.getOrElse("")) + ) + ), + Seq( + TableRow( + content = Text(messages("rentPeriods.second.rentValue")) + ), + TableRow( + content = Text(userAnswers.provideDetailsOfFirstSecondRentPeriod.map { dates => + dates.firstRentPeriodAmount.get + }.getOrElse("")) + ) + ) + ), + head = None, + caption = Some(Messages("rentPeriods.first.subheading")), + captionClasses = "govuk-table__caption--m", + firstCellIsHeader = true + ) + + def show: Action[AnyContent] = { + (authenticate andThen hasLinkedProperties).async { implicit request => + raldRepo.findByCredId(CredId(request.credId.getOrElse(""))).flatMap { + case Some(answers: RaldUserAnswers) => + Future.successful(Ok(view( + navigationBarContent = createDefaultNavBar, + selectedPropertyAddress = answers.selectedProperty.addressFull, + form, + firstTable = firstTable(answers), + secondTable = secondTable(answers), + ngrRadio = buildRadios(form, RentPeriodsForm.ngrRadio(form))))) + case None => + throw new NotFoundException("Couldn't find user Answers") + } + } + } + + def submit: Action[AnyContent] = { + (authenticate andThen hasLinkedProperties).async { implicit request => + form + .bindFromRequest() + .fold( + formWithErrors => + raldRepo.findByCredId(CredId(request.credId.getOrElse(""))).flatMap { + case Some(answers: RaldUserAnswers) => + Future.successful(BadRequest(view( + navigationBarContent = createDefaultNavBar, + selectedPropertyAddress = answers.selectedProperty.addressFull, + formWithErrors, + firstTable = firstTable(answers), + secondTable = secondTable(answers), + buildRadios(formWithErrors, RentPeriodsForm.ngrRadio(formWithErrors))))) + case None => throw new NotFoundException("Couldn't find user Answers") + }, + rentPeriodsForm => + raldRepo.insertRentPeriod( + CredId(request.credId.getOrElse("")), + rentPeriodsForm.radioValue + ) + Future.successful(Redirect(routes.WhatTypeOfAgreementController.show.url)) + ) + } + } +} diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/WhatIsYourRentBasedOnController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/WhatIsYourRentBasedOnController.scala index 4efa961b..aac45d1d 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/WhatIsYourRentBasedOnController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/WhatIsYourRentBasedOnController.scala @@ -34,9 +34,10 @@ import uk.gov.hmrc.ngrraldfrontend.views.html.WhatIsYourRentBasedOnView import uk.gov.hmrc.ngrraldfrontend.views.html.components.NGRCharacterCountComponent import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController -import javax.inject.Inject +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} +@Singleton class WhatIsYourRentBasedOnController @Inject()(view: WhatIsYourRentBasedOnView, authenticate: AuthRetrievals, hasLinkedProperties: PropertyLinkingAction, diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/RentPeriodsForm.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/RentPeriodsForm.scala new file mode 100644 index 00000000..a86fd36b --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/RentPeriodsForm.scala @@ -0,0 +1,67 @@ +/* + * 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 +/* + * 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 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.govukfrontend.views.Aliases.{Legend, Text} +import uk.gov.hmrc.ngrraldfrontend.models.components.* +import uk.gov.hmrc.ngrraldfrontend.models.forms.mappings.Mappings + +final case class RentPeriodsForm(radioValue: String) + +object RentPeriodsForm extends Mappings { + implicit val format: OFormat[RentPeriodsForm] = Json.format[RentPeriodsForm] + + private lazy val radioUnselectedError = "rentPeriods.error.required" + private val rentPeriodsRadio = "rent-periods-radio" + + def unapply(rentPeriodsForm: RentPeriodsForm): Option[String] = Some(rentPeriodsForm.radioValue) + + def form: Form[RentPeriodsForm] = { + Form( + mapping( + rentPeriodsRadio -> text(radioUnselectedError) + )(RentPeriodsForm.apply)(RentPeriodsForm.unapply) + ) + } + + private val yes: NGRRadioButtons = NGRRadioButtons(radioContent = "rentPeriods.yes", radioValue = No) + private val no: NGRRadioButtons = NGRRadioButtons(radioContent = "rentPeriods.no", radioValue = Yes) + + def ngrRadio(form: Form[RentPeriodsForm])(implicit messages: Messages): NGRRadio = + NGRRadio(ngrTitle = Some(Legend(content = Text(messages("rentPeriods.radio.heading")), classes = "govuk-fieldset__legend--m", isPageHeading = true)), radioGroupName = NGRRadioName("rent-periods-radio"), NGRRadioButtons = Seq(yes, no)) + +} + diff --git a/app/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepo.scala b/app/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepo.scala index 45a1a2f6..38f76aa2 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepo.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepo.scala @@ -125,30 +125,33 @@ case class RaldRepo @Inject()(mongo: MongoComponent, } def insertProvideDetailsOfFirstSecondRentPeriod( - credId: CredId, - firstDateStart: String, - firstDateEnd: String, - firstRentPeriodRadio: String, - firstRentPeriodAmount: Option[BigDecimal], - secondDateStart: String, - secondDateEnd: String, - secondHowMuchIsRent: BigDecimal - ): Future[Option[RaldUserAnswers]] = { - val firstDateStartVal = Updates.set("ProvideDetailsOfFirstSecondRentPeriod.firstDateStart", firstDateStart) - val firstDateEndVal = Updates.set("ProvideDetailsOfFirstSecondRentPeriod.firstDateEnd", firstDateEnd) - val firstRentPeriodRadioVal = Updates.set("ProvideDetailsOfFirstSecondRentPeriod.firstRentPeriodRadio", firstRentPeriodRadio match { - case answer if (answer == "yesPayedRent") => true - case _ => false - }) - val firstRentPeriodAmountVal = Updates.set("ProvideDetailsOfFirstSecondRentPeriod.firstRentPeriodAmount", firstRentPeriodAmount match { - case Some(value) => value.toString() - case _ => null - }) - val secondDateStartVal = Updates.set("ProvideDetailsOfFirstSecondRentPeriod.secondDateStart", secondDateStart) - val secondDateEndVal = Updates.set("ProvideDetailsOfFirstSecondRentPeriod.secondDateEnd", secondDateEnd) - val secondHowMuchIsRentVal = Updates.set("ProvideDetailsOfFirstSecondRentPeriod.secondHowMuchIsRent", secondHowMuchIsRent.toString) - val answers = Seq(firstDateStartVal, firstDateEndVal, firstRentPeriodRadioVal, firstRentPeriodAmountVal, secondDateStartVal, secondDateEndVal, secondHowMuchIsRentVal) - findAndUpdateByCredId(credId = credId, answers: _*) + credId: CredId, + firstDateStart: String, + firstDateEnd: String, + firstRentPeriodRadio: String, + firstRentPeriodAmount: Option[BigDecimal], + secondDateStart: String, + secondDateEnd: String, + secondHowMuchIsRent: BigDecimal + ): Future[Option[RaldUserAnswers]] = { + + val updates = Seq( + Updates.set("ProvideDetailsOfFirstSecondRentPeriod.firstDateStart", firstDateStart), + Updates.set("ProvideDetailsOfFirstSecondRentPeriod.firstDateEnd", firstDateEnd), + Updates.set("ProvideDetailsOfFirstSecondRentPeriod.firstRentPeriodRadio", firstRentPeriodRadio match { + case answer if (answer == "yesPayedRent") => true + case _ => false + }), + Updates.set("ProvideDetailsOfFirstSecondRentPeriod.firstRentPeriodAmount", firstRentPeriodAmount match { + case Some(value) => value.toString() + case _ => null + }), + Updates.set("ProvideDetailsOfFirstSecondRentPeriod.secondDateStart", secondDateStart), + Updates.set("ProvideDetailsOfFirstSecondRentPeriod.secondDateEnd", secondDateEnd), + Updates.set("ProvideDetailsOfFirstSecondRentPeriod.secondHowMuchIsRent", secondHowMuchIsRent.toString) + ) + + findAndUpdateByCredId(credId, updates: _*) } def insertRentBased(credId: CredId, rentBased: String, rentBasedOtherText:Option[String]): Future[Option[RaldUserAnswers]] = { @@ -189,6 +192,14 @@ case class RaldRepo @Inject()(mongo: MongoComponent, } + def insertRentPeriod(credId: CredId, hasAnotherRentPeriod: String): Future[Option[RaldUserAnswers]] = { + hasAnotherRentPeriod match { + case "Yes" => findAndUpdateByCredId(credId, Updates.set("hasAnotherRentPeriod", true)) + case _ => findAndUpdateByCredId(credId, Updates.set("hasAnotherRentPeriod", false)) + } + + } + def findByCredId(credId: CredId): Future[Option[RaldUserAnswers]] = { collection.find( equal("credId.value", credId.value) diff --git a/app/uk/gov/hmrc/ngrraldfrontend/views/RentPeriodView.scala.html b/app/uk/gov/hmrc/ngrraldfrontend/views/RentPeriodView.scala.html new file mode 100644 index 00000000..b39d991b --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/views/RentPeriodView.scala.html @@ -0,0 +1,50 @@ +@* + * 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.ngrraldfrontend.config.AppConfig +@import uk.gov.hmrc.ngrraldfrontend.models.components.NavigationBarContent +@import uk.gov.hmrc.ngrraldfrontend.viewmodels.govuk.all._ +@import uk.gov.hmrc.govukfrontend.views.html.components._ +@import uk.gov.hmrc.ngrraldfrontend.views.html.components._ +@import uk.gov.hmrc.ngrraldfrontend.models.forms.RentPeriodsForm +@import uk.gov.hmrc.govukfrontend.views.html.components._ + +@this( + layout: Layout, + govukErrorSummary: GovukErrorSummary, + govukRadios : GovukRadios, + saveAndContinueButton: saveAndContinueButton, + formHelper: FormWithCSRF, + govukTable : GovukTable +) + +@(navigationBarContent: NavigationBarContent, selectedPropertyAddress: String, form: Form[RentPeriodsForm], firstTable: Table, secondTable: Table, ngrRadio: Radios)(implicit request: RequestHeader, messages: Messages, appConfig: AppConfig) + +@layout(pageTitle = Some(messages("rentPeriods.title")), showBackLink = true, fullWidth = false, navigationBarContent = Some(navigationBarContent)) { + + @formHelper(action = uk.gov.hmrc.ngrraldfrontend.controllers.routes.RentPeriodsController.submit, Symbol("autoComplete") -> "off") { + @if(form.errors.nonEmpty) { + @govukErrorSummary(ErrorSummaryViewModel(form)) + } + @selectedPropertyAddress +

@messages("rentPeriods.title")

+ + @govukTable(firstTable) + @govukTable(secondTable) + @govukRadios(ngrRadio) + @saveAndContinueButton(msg = messages("service.saveAndContinue"), isStartButton = false) + } +} \ No newline at end of file diff --git a/conf/app.routes b/conf/app.routes index 818022fc..8c8ea28c 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -29,4 +29,6 @@ POST /agreement uk.gov.hmrc.ngrraldfront GET /did-you-agree-rent-with-landlord uk.gov.hmrc.ngrraldfrontend.controllers.DidYouAgreeRentWithLandlordController.show POST /did-you-agree-rent-with-landlord uk.gov.hmrc.ngrraldfrontend.controllers.DidYouAgreeRentWithLandlordController.submit GET /provide-details-of-first-second-rent-period uk.gov.hmrc.ngrraldfrontend.controllers.ProvideDetailsOfFirstSecondRentPeriodController.show -POST /provide-details-of-first-second-rent-period uk.gov.hmrc.ngrraldfrontend.controllers.ProvideDetailsOfFirstSecondRentPeriodController.submit \ No newline at end of file +POST /provide-details-of-first-second-rent-period uk.gov.hmrc.ngrraldfrontend.controllers.ProvideDetailsOfFirstSecondRentPeriodController.submit +GET /rent-periods uk.gov.hmrc.ngrraldfrontend.controllers.RentPeriodsController.show +POST /rent-periods uk.gov.hmrc.ngrraldfrontend.controllers.RentPeriodsController.submit \ No newline at end of file diff --git a/conf/messages b/conf/messages index 0b5de10c..ebda3ca0 100644 --- a/conf/messages +++ b/conf/messages @@ -199,7 +199,6 @@ provideDetailsOfFirstSecondRentPeriod.secondPeriod.end.date.label = End date provideDetailsOfFirstSecondRentPeriod.secondPeriod.end.date.hint = For example, 27 6 2026 provideDetailsOfFirstSecondRentPeriod.secondPeriod.subheading.2 = How much is the rent for this period (excluding VAT)? provideDetailsOfFirstSecondRentPeriod.secondPeriod.hint = Enter the amount you pay each year (excluding VAT) even if the period is for more or less than a year - provideDetailsOfFirstSecondRentPeriod.first.startDate.required.error = Date you will start paying rent must be a real date. provideDetailsOfFirstSecondRentPeriod.first.startDate.day.month.required.error = Date you will start paying rent must be a real date. provideDetailsOfFirstSecondRentPeriod.first.startDate.day.year.required.error = Date you will start paying rent must be a real date. @@ -233,7 +232,22 @@ provideDetailsOfFirstSecondRentPeriod.second.endDate.day.required.error = Date y provideDetailsOfFirstSecondRentPeriod.second.endDate.month.required.error = Date you will start paying rent must be a real date. provideDetailsOfFirstSecondRentPeriod.second.endDate.year.required.error = Date you will start paying rent must be a real date. provideDetailsOfFirstSecondRentPeriod.second.endDate.format.error = Date you will start paying rent must be a real date. - provideDetailsOfFirstSecondRentPeriod.firstPeriod.radio.error.required = Select yes if you pay rent in this period. provideDetailsOfFirstSecondRentPeriod.firstPeriod.amount.error.required = Enter the rent for the first rent period, in pounds. -provideDetailsOfFirstSecondRentPeriod.secondPeriod.amount.error.required = Enter the rent for the second rent period, in pounds. \ No newline at end of file +provideDetailsOfFirstSecondRentPeriod.secondPeriod.amount.error.required = Enter the rent for the second rent period, in pounds. + +#RentPeriods +rentPeriods.title = Rent periods +rentPeriods.first.subheading = First rent period +rentPeriods.first.startDate = Start date +rentPeriods.first.endDate = End date +rentPeriods.first.rentValue = Rent for this period(excluding VAT) +rentPeriods.first.doYouPay = Do you pay rent in this period? +rentPeriods.second.subheading = Second rent period +rentPeriods.second.startDate = Start date +rentPeriods.second.endDate = End date +rentPeriods.second.rentValue = Rent for this period(excluding VAT) +rentPeriods.radio.heading = Do you need to add another rent period? +rentPeriods.error.required = Select yes if you need to add another rent period. +rentPeriods.yes = Yes +rentPeriods.no = No \ No newline at end of file diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/AgreedRentChangeControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/AgreedRentChangeControllerSpec.scala index 9d22bc08..30fd95e6 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/controllers/AgreedRentChangeControllerSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/AgreedRentChangeControllerSpec.scala @@ -46,7 +46,7 @@ class AgreedRentChangeControllerSpec extends ControllerSpecSupport { } } "method submit" must { - "Return OK and the correct view" in { + "Return OK and the correct view after submitting Yes" in { val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit) .withFormUrlEncodedBody((AgreedRentChangeForm.agreedRentChangeRadio, "Yes")) .withHeaders(HeaderNames.authorisation -> "Bearer 1") @@ -55,6 +55,15 @@ class AgreedRentChangeControllerSpec extends ControllerSpecSupport { status(result) mustBe SEE_OTHER redirectLocation(result) mustBe Some(routes.ProvideDetailsOfFirstSecondRentPeriodController.show.url) } + "Return OK and the correct view after submitting No" in { + val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit) + .withFormUrlEncodedBody((AgreedRentChangeForm.agreedRentChangeRadio, "No")) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controller.submit()(authenticatedFakeRequest(fakePostRequest)) + status(result) mustBe SEE_OTHER + redirectLocation(result) mustBe Some(routes.WhatTypeOfLeaseRenewalController.show.url) + } "Return BAD_REQUEST for missing input and the correct view" in { mockRequest() val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit) diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsControllerSpec.scala new file mode 100644 index 00000000..f2ab645b --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsControllerSpec.scala @@ -0,0 +1,118 @@ +/* + * 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.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.{contentAsString, defaultAwaitTimeout, redirectLocation, status} +import uk.gov.hmrc.auth.core.Nino +import uk.gov.hmrc.http.HeaderNames +import uk.gov.hmrc.ngrraldfrontend.helpers.ControllerSpecSupport +import uk.gov.hmrc.ngrraldfrontend.models.AgreementType.NewAgreement +import uk.gov.hmrc.ngrraldfrontend.models.registration.CredId +import uk.gov.hmrc.ngrraldfrontend.models.{AuthenticatedUserRequest, ProvideDetailsOfFirstSecondRentPeriod, RaldUserAnswers} +import uk.gov.hmrc.ngrraldfrontend.views.html.RentPeriodView + +import scala.concurrent.Future + +class RentPeriodsControllerSpec extends ControllerSpecSupport { + val pageTitle = "Rent periods" + val view: RentPeriodView = inject[RentPeriodView] + val controller: RentPeriodsController = new RentPeriodsController(view, mockAuthJourney, mockPropertyLinkingAction, mockRaldRepo, mcc)(mockConfig, ec) + + "Tell us about your new agreement controller" must { + "method show" must { + "Return OK and the correct view" in { + when(mockRaldRepo.findByCredId(any())) thenReturn (Future.successful(Some(RaldUserAnswers(credId = CredId(null), NewAgreement, selectedProperty = property)))) + val result = controller.show()(authenticatedFakeRequest()) + status(result) mustBe OK + val content = contentAsString(result) + content must include(pageTitle) + } + } + + "method show" must { + "Return OK and the correct view when the user has said yes to having paid rent for the first period" in { + when(mockRaldRepo.findByCredId(any())) thenReturn Future.successful(Some(RaldUserAnswers( + credId = CredId(null), + agreementType = NewAgreement, + selectedProperty = property, + provideDetailsOfFirstSecondRentPeriod = + Some(ProvideDetailsOfFirstSecondRentPeriod( + firstDateStart = "", + firstDateEnd = "", + firstRentPeriodRadio = true, + firstRentPeriodAmount = Some(""), + secondDateStart = "", + secondDateEnd = "", + secondHowMuchIsRent = ""))))) + val result = controller.show()(authenticatedFakeRequest()) + status(result) mustBe OK + val content = contentAsString(result) + content must include(pageTitle) + } + } + + "method submit" must { + "Return OK and the correct view after submitting yes" in { + when(mockRaldRepo.findByCredId(any())) thenReturn (Future.successful(Some(RaldUserAnswers(credId = CredId(null), NewAgreement, selectedProperty = property)))) + mockRequest(hasCredId = true) + val result = controller.submit()(AuthenticatedUserRequest(FakeRequest(routes.RentPeriodsController.submit) + .withFormUrlEncodedBody( + "rent-periods-radio" -> "Yes" + ) + .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/landlord") + }) + status(result) mustBe SEE_OTHER + redirectLocation(result) mustBe Some(routes.WhatTypeOfAgreementController.show.url) + } + "Return OK and the correct view after submitting no" in { + when(mockRaldRepo.findByCredId(any())) thenReturn (Future.successful(Some(RaldUserAnswers(credId = CredId(null), NewAgreement, selectedProperty = property)))) + mockRequest(hasCredId = true) + val result = controller.submit()(AuthenticatedUserRequest(FakeRequest(routes.LandlordController.submit) + .withFormUrlEncodedBody( + "rent-periods-radio" -> "No" + ) + .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/landlord") + }) + status(result) mustBe SEE_OTHER + redirectLocation(result) mustBe Some(routes.WhatTypeOfAgreementController.show.url) + } + "Return Form with Errors when no name is input" in { + mockRequest(hasCredId = true) + val result = controller.submit()(AuthenticatedUserRequest(FakeRequest(routes.LandlordController.submit) + .withFormUrlEncodedBody( + "rent-periods-radio" -> "" + ) + .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/landlord") + }) + status(result) mustBe BAD_REQUEST + val content = contentAsString(result) + content must include(pageTitle) + } + } + } +} + diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/AgreementSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/AgreementSpec.scala new file mode 100644 index 00000000..db389f36 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/AgreementSpec.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 + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import play.api.libs.json.* + +class AgreementSpec extends AnyFlatSpec with Matchers { + + "Agreement" should "serialize to JSON correctly" in { + val agreement = Agreement( + agreementStart = "2025-01-01", + isOpenEnded = true, + openEndedDate = Some("2026-01-01"), + haveBreakClause = true, + breakClauseInfo = Some("6-month notice") + ) + + val json = Json.toJson(agreement) + (json \ "agreementStart").as[String] shouldBe "2025-01-01" + (json \ "openEndedDate").asOpt[String] shouldBe Some("2026-01-01") + (json \ "breakClauseInfo").asOpt[String] shouldBe Some("6-month notice") + } + + it should "deserialize from JSON correctly" in { + val json = Json.parse( + """ + |{ + | "agreementStart": "2025-01-01", + | "isOpenEnded": true, + | "openEndedDate": "2026-01-01", + | "haveBreakClause": true, + | "breakClauseInfo": "6-month notice" + |} + |""".stripMargin) + + val result = json.as[Agreement] + result.agreementStart shouldBe "2025-01-01" + result.openEndedDate shouldBe Some("2026-01-01") + result.breakClauseInfo shouldBe Some("6-month notice") + } + + it should "handle missing optional fields gracefully" in { + val json = Json.parse( + """ + |{ + | "agreementStart": "2025-01-01", + | "isOpenEnded": false, + | "haveBreakClause": false + |} + |""".stripMargin) + + val result = json.as[Agreement] + result.openEndedDate shouldBe None + result.breakClauseInfo shouldBe None + } +} + diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/ProvideDetailsOfFirstSecondRentPeriodSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/ProvideDetailsOfFirstSecondRentPeriodSpec.scala new file mode 100644 index 00000000..1e59c1f3 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/ProvideDetailsOfFirstSecondRentPeriodSpec.scala @@ -0,0 +1,76 @@ +/* + * 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 + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import play.api.libs.json._ + +class ProvideDetailsOfFirstSecondRentPeriodSpec extends AnyFlatSpec with Matchers { + + "ProvideDetailsOfFirstSecondRentPeriod" should "serialize to JSON correctly" in { + val rentDetails = ProvideDetailsOfFirstSecondRentPeriod( + firstDateStart = "2025-01-01", + firstDateEnd = "2025-01-31", + firstRentPeriodRadio = true, + firstRentPeriodAmount = Some("1000"), + secondDateStart = "2025-02-01", + secondDateEnd = "2025-02-28", + secondHowMuchIsRent = "1200" + ) + + val json = Json.toJson(rentDetails) + (json \ "firstDateStart").as[String] shouldBe "2025-01-01" + (json \ "firstRentPeriodAmount").asOpt[String] shouldBe Some("1000") + } + + it should "deserialize from JSON correctly" in { + val json = Json.parse( + """ + |{ + | "firstDateStart": "2025-01-01", + | "firstDateEnd": "2025-01-31", + | "firstRentPeriodRadio": true, + | "firstRentPeriodAmount": "1000", + | "secondDateStart": "2025-02-01", + | "secondDateEnd": "2025-02-28", + | "secondHowMuchIsRent": "1200" + |} + |""".stripMargin) + + val result = json.as[ProvideDetailsOfFirstSecondRentPeriod] + result.firstDateStart shouldBe "2025-01-01" + result.firstRentPeriodAmount shouldBe Some("1000") + } + + it should "handle missing optional field gracefully" in { + val json = Json.parse( + """ + |{ + | "firstDateStart": "2025-01-01", + | "firstDateEnd": "2025-01-31", + | "firstRentPeriodRadio": false, + | "secondDateStart": "2025-02-01", + | "secondDateEnd": "2025-02-28", + | "secondHowMuchIsRent": "1200" + |} + |""".stripMargin) + + val result = json.as[ProvideDetailsOfFirstSecondRentPeriod] + result.firstRentPeriodAmount shouldBe None + } +} \ No newline at end of file diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/forms/RentPeriodsFormSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/RentPeriodsFormSpec.scala new file mode 100644 index 00000000..f39d8260 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/RentPeriodsFormSpec.scala @@ -0,0 +1,51 @@ +/* + * 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.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import play.api.data.FormError +import play.api.libs.json.Json + +class RentPeriodsFormSpec extends AnyFlatSpec with Matchers { + + val validData = Map( + "rent-periods-radio" -> "Yes" + ) + + "RentPeriodsForm" should "bind valid data successfully" in { + val boundForm = RentPeriodsForm.form.bind(validData) + + boundForm.errors shouldBe empty + boundForm.value shouldBe Some(RentPeriodsForm("Yes")) + } + + it should "fail when radio input is missing" in { + val data = validData - "rent-periods-radio" + val boundForm = RentPeriodsForm.form.bind(data) + + boundForm.errors shouldBe List(FormError("rent-periods-radio", List("rentPeriods.error.required"), List())) + } + + "RentPeriods.unapply" should "extract fields correctly" in { + val form = RentPeriodsForm("Yes") + val result = RentPeriodsForm.unapply(form) + result shouldBe Some("Yes") + } + + +}