diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowManyParkingSpacesOrGaragesIncludedInRentController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowManyParkingSpacesOrGaragesIncludedInRentController.scala index 2a8d9ad6..a3fa14c3 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowManyParkingSpacesOrGaragesIncludedInRentController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowManyParkingSpacesOrGaragesIncludedInRentController.scala @@ -16,7 +16,7 @@ package uk.gov.hmrc.ngrraldfrontend.controllers -import play.api.data.{Form, FormError, Forms} +import play.api.data.{Form, FormError} import play.api.i18n.{I18nSupport, Messages} import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} import play.twirl.api.HtmlFormat diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtController.scala new file mode 100644 index 00000000..03e60268 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtController.scala @@ -0,0 +1,102 @@ +/* + * 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.data.Form +import play.api.i18n.{I18nSupport, Messages} +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import play.twirl.api.HtmlFormat +import uk.gov.hmrc.govukfrontend.views.Aliases.{Hint, PrefixOrSuffix, Text} +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.forms.InterimRentSetByTheCourtForm +import uk.gov.hmrc.ngrraldfrontend.models.forms.InterimRentSetByTheCourtForm.form +import uk.gov.hmrc.ngrraldfrontend.models.registration.CredId +import uk.gov.hmrc.ngrraldfrontend.repo.RaldRepo +import uk.gov.hmrc.ngrraldfrontend.views.html.InterimRentSetByTheCourtView +import uk.gov.hmrc.ngrraldfrontend.views.html.components.InputText +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController + +import javax.inject.Inject +import scala.concurrent.{ExecutionContext, Future} + +class InterimRentSetByTheCourtController @Inject()(interimRentSetByTheCourtView: InterimRentSetByTheCourtView, + authenticate: AuthRetrievals, + hasLinkedProperties: PropertyLinkingAction, + inputText: InputText, + raldRepo: RaldRepo, + mcc: MessagesControllerComponents)(implicit appConfig: AppConfig, ec: ExecutionContext) + extends FrontendController(mcc) with I18nSupport { + + def generateInputText(form: Form[InterimRentSetByTheCourtForm], inputFieldName: String)(implicit messages: Messages): HtmlFormat.Appendable = { + inputText( + form = form, + id = inputFieldName, + name = inputFieldName, + label = messages(s"interimRentSetByTheCourt.label.1"), + headingMessageArgs = Seq("govuk-fieldset__legend govuk-fieldset__legend--s"), + isPageHeading = true, + isVisible = true, + classes = Some("govuk-input govuk-input--width-5"), + prefix = Some(PrefixOrSuffix(content = Text("£"))) + ) + } + + def show: Action[AnyContent] = { + (authenticate andThen hasLinkedProperties).async { implicit request => + request.propertyLinking.map(property => + Future.successful(Ok(interimRentSetByTheCourtView( + form = form, + propertyAddress = property.addressFull, + interimAmount = generateInputText(form, "interimAmount") + )))).getOrElse(throw new NotFoundException("Couldn't find property in mongo")) + } + } + + def submit: Action[AnyContent] = + (authenticate andThen hasLinkedProperties).async { implicit request => + form.bindFromRequest().fold( + formWithErrors => { + val correctedFormErrors = formWithErrors.errors.map { formError => + (formError.key, formError.messages) match + case (key, messages) if messages.contains("interimRentSetByTheCourt.startDate.before.1900.error") || + messages.contains("interimRentSetByTheCourt.year.required.error") => + formError.copy(key = "date.year") + case ("date", messages) => + formError.copy(key = "date.month") + case _ => + formError + } + val formWithCorrectedErrors = formWithErrors.copy(errors = correctedFormErrors) + request.propertyLinking.map(property => + Future.successful(BadRequest(interimRentSetByTheCourtView( + form = formWithCorrectedErrors, + propertyAddress = property.addressFull, + interimAmount = generateInputText(formWithCorrectedErrors, "interimAmount") + )))).getOrElse(throw new NotFoundException("Couldn't find property in mongo")) + }, + interimRent => + raldRepo.insertInterimRentSetByTheCourt( + credId = CredId(request.credId.getOrElse("")), + amount = interimRent.amount, + date = interimRent.date.makeString + ) + Future.successful(Redirect(routes.CheckRentFreePeriodController.show.url)) + ) + } + } diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentInterimController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentInterimController.scala index e3d64b64..f5110a62 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentInterimController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentInterimController.scala @@ -68,7 +68,7 @@ class RentInterimController @Inject()(rentInterimView: RentInterimView, agreedRentChange = radioValue.radioValue ) if (radioValue.radioValue == "Yes") { - Future.successful(Redirect(routes.ProvideDetailsOfFirstSecondRentPeriodController.show.url)) + Future.successful(Redirect(routes.InterimRentSetByTheCourtController.show.url)) } else { Future.successful(Redirect(routes.CheckRentFreePeriodController.show.url)) } diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/InterimRentSetByTheCourt.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/InterimRentSetByTheCourt.scala new file mode 100644 index 00000000..6f0950f3 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/InterimRentSetByTheCourt.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.models + +import play.api.libs.json.{Json, OFormat} + +case class InterimRentSetByTheCourt(amount: String, + date: String + ) + +object InterimRentSetByTheCourt { + implicit val format: OFormat[InterimRentSetByTheCourt] = Json.format[InterimRentSetByTheCourt] +} \ No newline at end of file diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/NGRMonthYear.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/NGRMonthYear.scala new file mode 100644 index 00000000..4cae8fd2 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/NGRMonthYear.scala @@ -0,0 +1,55 @@ +/* + * 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 play.api.data.Forms.{mapping, text} +import play.api.data.Mapping +import play.api.libs.json.{Json, OFormat} + +import java.time.LocalDate +import java.time.format.{DateTimeFormatter, TextStyle} +import java.util.Locale + +final case class NGRMonthYear(month: String, year: String) { + def makeString: String = { + val monthStr = f"${month.toIntOption.getOrElse(0)}%02d" + s"$year-$monthStr" + } +} + +object NGRMonthYear { + implicit val format: OFormat[NGRMonthYear] = Json.format[NGRMonthYear] + + def formatDate(dateString: String): String = { + val date = LocalDate.parse(dateString) + val outputFormatter = DateTimeFormatter.ofPattern("MMMM yyyy", Locale.UK) + date.format(outputFormatter) + } + + + def unapply(ngrMonthYear: NGRMonthYear): Option[(String, String)] = + Some(ngrMonthYear.month, ngrMonthYear.year) +} + +trait MonthYearMappings { + def monthYearMapping: Mapping[NGRMonthYear] = { + mapping( + "month" -> text().transform(_.strip(), identity), + "year" -> text().transform(_.strip(), identity), + )(NGRMonthYear.apply)(NGRMonthYear.unapply) + } +} \ No newline at end of file diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/RaldUserAnswers.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/RaldUserAnswers.scala index f8887264..8d39f6fe 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/models/RaldUserAnswers.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/RaldUserAnswers.scala @@ -40,7 +40,8 @@ final case class RaldUserAnswers( hasAnotherRentPeriod: Option[Boolean] = None, whatYourRentIncludes: Option[WhatYourRentIncludes] = None, doesYourRentIncludeParking: Option[Boolean] = None, - howManyParkingSpacesOrGaragesIncludedInRent: Option[HowManyParkingSpacesOrGarages] = None + howManyParkingSpacesOrGaragesIncludedInRent: Option[HowManyParkingSpacesOrGarages] = None , + interimRentSetByTheCourt: Option[InterimRentSetByTheCourt] = None ) diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/CommonFormValidators.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/CommonFormValidators.scala index 033bb475..ee6bba3b 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/CommonFormValidators.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/CommonFormValidators.scala @@ -68,6 +68,23 @@ trait CommonFormValidators { } } + protected def isMonthYearEmpty[A](errorKeys: Map[DateErrorKeys, String]): Constraint[A] = + Constraint((input: A) => + monthYearEmptyValidation(input.asInstanceOf[NGRMonthYear], errorKeys) + ) + + protected def isMonthYearValid[A](errorKey: String): Constraint[A] = + Constraint((input: A) => + val date = input.asInstanceOf[NGRMonthYear] + monthYearValidation(date, errorKey) + ) + + protected def isMonthYearAfter1900[A](errorKey: String): Constraint[A] = + Constraint((input: A) => + val date = input.asInstanceOf[NGRMonthYear] + monthYearAfter1900Validation(date, errorKey) + ) + protected def isDateEmpty[A](errorKeys: Map[DateErrorKeys, String]): Constraint[A] = Constraint((input: A) => dateEmptyValidation(input.asInstanceOf[NGRDate], errorKeys) @@ -96,15 +113,45 @@ trait CommonFormValidators { case (false, false, true) => Invalid(errorKeys.get(Year).getOrElse("")) case (_, _, _) => Valid + protected def monthYearEmptyValidation(date: NGRMonthYear, errorKeys: Map[DateErrorKeys, String]): ValidationResult = + (date.month.isEmpty, date.year.isEmpty) match + case (true, true) => Invalid(errorKeys.get(Required).getOrElse("")) + case (true, false) => Invalid(errorKeys.get(Month).getOrElse("")) + case (false, true) => Invalid(errorKeys.get(Year).getOrElse("")) + case (_, _) => Valid + protected def dateValidation(date: NGRDate, errorKey: String) = if (Try(date.ngrDate).isFailure || (Try(date.ngrDate).isSuccess && date.year.length > 4)) Invalid(errorKey) else Valid - + + + private def monthYearAfter1900Validation(date: NGRMonthYear, errorKey: String) = + val maybeYear = date.year.toIntOption + maybeYear match { + case Some(year) => + if (year < 1900) + Invalid (errorKey) + else + Valid + case None => Invalid(errorKey) + } + private def dateAfter1900Validation(date: NGRDate, errorKey: String) = if (Try(date.ngrDate).isSuccess && date.year.toInt < 1900) Invalid(errorKey) else Valid + + protected def monthYearValidation(date: NGRMonthYear, errorKey: String) = { + val maybeMonth = date.month.toIntOption + val maybeYear = date.year.toIntOption + (maybeMonth, maybeYear) match { + case (Some(month), Some(year)) if month > 0 && month <= 12 && (month + year != 0) => + Valid + case _ => + Invalid(errorKey) + } + } } diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchIsTotalAnnualRentForm.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchIsTotalAnnualRentForm.scala index 1d946975..592bf17e 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchIsTotalAnnualRentForm.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchIsTotalAnnualRentForm.scala @@ -41,7 +41,7 @@ object HowMuchIsTotalAnnualRentForm extends CommonFormValidators { val form: Form[HowMuchIsTotalAnnualRentForm] = Form( mapping( annualRent -> text() - .transform[String](_.strip(), identity) + .transform[String](_.strip().replaceAll("[£|,|\\s]", ""), identity) .verifying( firstError( isNotEmpty(annualRent, annualRentEmptyError), diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/InterimRentSetByTheCourtForm.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/InterimRentSetByTheCourtForm.scala new file mode 100644 index 00000000..5daa5d7e --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/InterimRentSetByTheCourtForm.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 play.api.data.* +import play.api.data.Forms.* +import play.api.data.format.Formatter +import play.api.libs.json.{Json, OFormat} +import uk.gov.hmrc.ngrraldfrontend.models.* + +import scala.math.BigDecimal.RoundingMode +import scala.util.Try + + +final case class InterimRentSetByTheCourtForm(amount: BigDecimal, date: NGRMonthYear) + +object InterimRentSetByTheCourtForm extends CommonFormValidators with MonthYearMappings { + implicit val format: OFormat[InterimRentSetByTheCourtForm] = Json.format[InterimRentSetByTheCourtForm] + + private lazy val howMuchEmptyError = "interimRentSetByTheCourt.interimAmount.required.error" + private lazy val howMuchMaxError = "interimRentSetByTheCourt.interimAmount.tooLarge.error" + private lazy val howMuchFormatError = "interimRentSetByTheCourt.interimAmount.format.error" + + def unapply(interimRentSetByTheCourtForm: InterimRentSetByTheCourtForm): Option[(BigDecimal, NGRMonthYear)] = Some(interimRentSetByTheCourtForm.amount, interimRentSetByTheCourtForm.date) + + private def errorKeys(whichDate: String): Map[DateErrorKeys, String] = Map( + Required -> s"$whichDate.required.error", + Month -> s"$whichDate.month.required.error", + Year -> s"$whichDate.year.required.error" + ) + + val form: Form[InterimRentSetByTheCourtForm] = Form( + mapping( + "interimAmount" -> text() + .transform[String](_.strip().replaceAll("[£|,|\\s]", ""), identity) + .verifying( + firstError( + isNotEmpty("interimAmount", howMuchEmptyError), + regexp(amountRegex.pattern(), howMuchFormatError) + ) + ) + .transform[BigDecimal](BigDecimal(_).setScale(2, RoundingMode.HALF_UP), _.toString) + .verifying( + maximumValue[BigDecimal](BigDecimal("9999999.99"), howMuchMaxError) + ), + "date" -> monthYearMapping + .verifying( + firstError( + isMonthYearEmpty(errorKeys("interimRentSetByTheCourt")), + isMonthYearValid("interimRentSetByTheCourt.monthYear.format.error"), + isMonthYearAfter1900("interimRentSetByTheCourt.startDate.before.1900.error") + ) + ), + )(InterimRentSetByTheCourtForm.apply)(InterimRentSetByTheCourtForm.unapply) + ) + +} + + diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/ProvideDetailsOfFirstSecondRentPeriodForm.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/ProvideDetailsOfFirstSecondRentPeriodForm.scala index 68823ba3..94cfb082 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/ProvideDetailsOfFirstSecondRentPeriodForm.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/ProvideDetailsOfFirstSecondRentPeriodForm.scala @@ -110,7 +110,7 @@ object ProvideDetailsOfFirstSecondRentPeriodForm extends CommonFormValidators wi firstRentPeriodRadio -> radioText(radioFirstPeriodRequiredError), RentPeriodAmount -> optional( text() - .transform[String](_.strip(), identity) + .transform[String](_.strip().replaceAll("[£|,|\\s]", ""), identity) ), secondDateStartInput -> dateMapping .verifying( @@ -129,7 +129,7 @@ object ProvideDetailsOfFirstSecondRentPeriodForm extends CommonFormValidators wi ) ), SecondRentPeriodAmount -> text() - .transform[String](_.strip(), identity) + .transform[String](_.strip().replaceAll("[£|,|\\s]", ""), identity) .verifying( firstError( isNotEmpty(SecondRentPeriodAmount, annualRentEmptyError), diff --git a/app/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepo.scala b/app/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepo.scala index b2f3f11a..a58bd9a4 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepo.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepo.scala @@ -254,6 +254,13 @@ case class RaldRepo @Inject()(mongo: MongoComponent, findAndUpdateByCredId(credId, updates: _*) } + def insertInterimRentSetByTheCourt(credId: CredId, amount: BigDecimal, date: String) = { + val updates = Seq( + Updates.set("interimRentSetByTheCourt.amount", amount.toString()), + Updates.set("interimRentSetByTheCourt.date", date), + ) + findAndUpdateByCredId(credId, updates: _*) + } def findByCredId(credId: CredId): Future[Option[RaldUserAnswers]] = { collection.find( diff --git a/app/uk/gov/hmrc/ngrraldfrontend/views/InterimRentSetByTheCourtView.scala.html b/app/uk/gov/hmrc/ngrraldfrontend/views/InterimRentSetByTheCourtView.scala.html new file mode 100644 index 00000000..3dbd14fd --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/views/InterimRentSetByTheCourtView.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.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.forms.InterimRentSetByTheCourtForm + +@this( + layout: Layout, + formHelper: FormWithCSRF, + inputDateForMonthYear: components.InputDateForMonthYear, + govukErrorSummary: GovukErrorSummary, + saveAndContinueButton: saveAndContinueButton +) + +@(form:Form[InterimRentSetByTheCourtForm], propertyAddress: String, interimAmount: Html)(implicit request: RequestHeader, messages: Messages, appConfig: AppConfig) + +@layout(pageTitle = Some(messages("interimRentSetByTheCourt.title")), showBackLink = true, fullWidth = false) { + @formHelper(action = uk.gov.hmrc.ngrraldfrontend.controllers.routes.InterimRentSetByTheCourtController.submit, Symbol("autoComplete") -> "off") { + @if(form.errors.nonEmpty) { + @govukErrorSummary(ErrorSummaryViewModel(form)) + } + @propertyAddress +

@messages("interimRentSetByTheCourt.title")

+ @interimAmount + @inputDateForMonthYear( + form, + legendContent = messages("interimRentSetByTheCourt.label.2"), + legendAsPageHeading = false, + hintText = Some(messages("interimRentSetByTheCourt.hint.2")) + ) + @saveAndContinueButton(msg = messages("service.continue"), isStartButton = false) + } +} \ No newline at end of file diff --git a/app/uk/gov/hmrc/ngrraldfrontend/views/components/InputDateForMonthYear.scala.html b/app/uk/gov/hmrc/ngrraldfrontend/views/components/InputDateForMonthYear.scala.html new file mode 100644 index 00000000..33e5af54 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/views/components/InputDateForMonthYear.scala.html @@ -0,0 +1,70 @@ +@* + * 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._ + +@this(govukDateInput: GovukDateInput) + +@( + form: Form[_], + legendContent: String, + legendClasses: String = "govuk-fieldset__legend--xl", + id: String = "date", + hintText: Option[String] = None, + legendAsPageHeading: Boolean = true)(implicit messages: Messages) + +@govukDateInput(DateInput( + fieldset = Some(Fieldset( + legend = Some( + Legend( + content = Text(messages(legendContent)), + isPageHeading = legendAsPageHeading, + classes = if(legendAsPageHeading){legendClasses}else{"govuk-fieldset__legend--m"} + ) + ) + )), + id = id, + items = Seq( + InputItem( + id = s"$id.month", + classes = s"govuk-input--width-2${if( + form(s"$id.month").hasErrors + || form.errors.exists(_.key.contains("date")) + || form.errors.exists(_.args.contains("month")))" govuk-input--error" else ""}", + name = s"$id.month", + label = Some(messages("date.month")), + value = form(s"$id.month").value + ), + InputItem( + id = s"$id.year", + classes = s"govuk-input--width-4${if( + form(s"$id.year").hasErrors + || form.errors.exists(_.key.contains("date")) + || form.errors.exists(_.args.contains("year")))" govuk-input--error" else ""}", + name = s"$id.year", + label = Some(messages("date.year")), + value = form(s"$id.year").value + ) + ), + hint = hintText.map(hint => Hint(content = Text(messages(hint)))), + errorMessage = form.errors.find(err => + err.key == id || err.key == s"$id.month" || err.key == s"$id.year" || err.key == "date" + ).map { err => + ErrorMessage.errorMessageWithDefaultStringsTranslated( + content = Text(messages(err.message, err.args: _*)) + ) + } +)) \ No newline at end of file diff --git a/conf/app.routes b/conf/app.routes index 9b65aefd..273b4444 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -1,46 +1,48 @@ # microservice specific routes --> /hmrc-frontend hmrcfrontend.Routes -GET /assets/*file controllers.Assets.versioned(path = "/public", file: Asset) -GET /tell-us-about-your-new-agreement uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutYourNewAgreementController.show -POST /tell-us-about-your-new-agreement uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutYourNewAgreementController.submit -GET /tell-us-about-your-renewed-agreement uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutYourRenewedAgreementController.show -POST /tell-us-about-your-renewed-agreement uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutYourRenewedAgreementController.submit -GET /tell-us-about-rent-review uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutRentController.show -POST /tell-us-about-rent-review uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutRentController.submit -GET /what-type-of-lease-renewal-is-it uk.gov.hmrc.ngrraldfrontend.controllers.WhatTypeOfLeaseRenewalController.show -POST /what-type-of-lease-renewal-is-it uk.gov.hmrc.ngrraldfrontend.controllers.WhatTypeOfLeaseRenewalController.submit -GET /what-type-of-agreement-do-you-have uk.gov.hmrc.ngrraldfrontend.controllers.WhatTypeOfAgreementController.show -POST /what-type-of-agreement-do-you-have uk.gov.hmrc.ngrraldfrontend.controllers.WhatTypeOfAgreementController.submit -GET /landlord uk.gov.hmrc.ngrraldfrontend.controllers.LandlordController.show -POST /landlord uk.gov.hmrc.ngrraldfrontend.controllers.LandlordController.submit -GET /what-is-your-rent-based-on uk.gov.hmrc.ngrraldfrontend.controllers.WhatIsYourRentBasedOnController.show -POST /what-is-your-rent-based-on uk.gov.hmrc.ngrraldfrontend.controllers.WhatIsYourRentBasedOnController.submit -GET /how-much-is-total-annual-rent uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchIsTotalAnnualRentController.show -POST /how-much-is-total-annual-rent uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchIsTotalAnnualRentController.submit -GET /have-you-agreed-rent-changes-with-landlord uk.gov.hmrc.ngrraldfrontend.controllers.AgreedRentChangeController.show -POST /have-you-agreed-rent-changes-with-landlord uk.gov.hmrc.ngrraldfrontend.controllers.AgreedRentChangeController.submit -GET /agreement-verbal uk.gov.hmrc.ngrraldfrontend.controllers.AgreementVerbalController.show -POST /agreement-verbal uk.gov.hmrc.ngrraldfrontend.controllers.AgreementVerbalController.submit -GET /do-you-have-a-rent-free-period uk.gov.hmrc.ngrraldfrontend.controllers.CheckRentFreePeriodController.show -POST /do-you-have-a-rent-free-period uk.gov.hmrc.ngrraldfrontend.controllers.CheckRentFreePeriodController.submit -GET /agreement uk.gov.hmrc.ngrraldfrontend.controllers.AgreementController.show -POST /agreement uk.gov.hmrc.ngrraldfrontend.controllers.AgreementController.submit -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 -GET /rent-periods uk.gov.hmrc.ngrraldfrontend.controllers.RentPeriodsController.show -POST /rent-periods uk.gov.hmrc.ngrraldfrontend.controllers.RentPeriodsController.submit -GET /did-the-court-set-an-interim-rent uk.gov.hmrc.ngrraldfrontend.controllers.RentInterimController.show -POST /did-the-court-set-an-interim-rent uk.gov.hmrc.ngrraldfrontend.controllers.RentInterimController.submit -GET /rent-dates-agree uk.gov.hmrc.ngrraldfrontend.controllers.RentDatesAgreeController.show -POST /rent-dates-agree uk.gov.hmrc.ngrraldfrontend.controllers.RentDatesAgreeController.submit -GET /rent-dates-agree-start uk.gov.hmrc.ngrraldfrontend.controllers.RentDatesAgreeStartController.show -POST /rent-dates-agree-start uk.gov.hmrc.ngrraldfrontend.controllers.RentDatesAgreeStartController.submit -GET /what-rent-includes uk.gov.hmrc.ngrraldfrontend.controllers.WhatYourRentIncludesController.show -POST /what-rent-includes uk.gov.hmrc.ngrraldfrontend.controllers.WhatYourRentIncludesController.submit -GET /does-rent-include-parking-spaces-or-garages uk.gov.hmrc.ngrraldfrontend.controllers.DoesYourRentIncludeParkingController.show -POST /does-rent-include-parking-spaces-or-garages uk.gov.hmrc.ngrraldfrontend.controllers.DoesYourRentIncludeParkingController.submit +-> /hmrc-frontend hmrcfrontend.Routes +GET /assets/*file controllers.Assets.versioned(path = "/public", file: Asset) +GET /tell-us-about-your-new-agreement uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutYourNewAgreementController.show +POST /tell-us-about-your-new-agreement uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutYourNewAgreementController.submit +GET /tell-us-about-your-renewed-agreement uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutYourRenewedAgreementController.show +POST /tell-us-about-your-renewed-agreement uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutYourRenewedAgreementController.submit +GET /tell-us-about-rent-review uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutRentController.show +POST /tell-us-about-rent-review uk.gov.hmrc.ngrraldfrontend.controllers.TellUsAboutRentController.submit +GET /what-type-of-lease-renewal-is-it uk.gov.hmrc.ngrraldfrontend.controllers.WhatTypeOfLeaseRenewalController.show +POST /what-type-of-lease-renewal-is-it uk.gov.hmrc.ngrraldfrontend.controllers.WhatTypeOfLeaseRenewalController.submit +GET /what-type-of-agreement-do-you-have uk.gov.hmrc.ngrraldfrontend.controllers.WhatTypeOfAgreementController.show +POST /what-type-of-agreement-do-you-have uk.gov.hmrc.ngrraldfrontend.controllers.WhatTypeOfAgreementController.submit +GET /landlord uk.gov.hmrc.ngrraldfrontend.controllers.LandlordController.show +POST /landlord uk.gov.hmrc.ngrraldfrontend.controllers.LandlordController.submit +GET /what-is-your-rent-based-on uk.gov.hmrc.ngrraldfrontend.controllers.WhatIsYourRentBasedOnController.show +POST /what-is-your-rent-based-on uk.gov.hmrc.ngrraldfrontend.controllers.WhatIsYourRentBasedOnController.submit +GET /how-much-is-total-annual-rent uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchIsTotalAnnualRentController.show +POST /how-much-is-total-annual-rent uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchIsTotalAnnualRentController.submit +GET /have-you-agreed-rent-changes-with-landlord uk.gov.hmrc.ngrraldfrontend.controllers.AgreedRentChangeController.show +POST /have-you-agreed-rent-changes-with-landlord uk.gov.hmrc.ngrraldfrontend.controllers.AgreedRentChangeController.submit +GET /agreement-verbal uk.gov.hmrc.ngrraldfrontend.controllers.AgreementVerbalController.show +POST /agreement-verbal uk.gov.hmrc.ngrraldfrontend.controllers.AgreementVerbalController.submit +GET /do-you-have-a-rent-free-period uk.gov.hmrc.ngrraldfrontend.controllers.CheckRentFreePeriodController.show +POST /do-you-have-a-rent-free-period uk.gov.hmrc.ngrraldfrontend.controllers.CheckRentFreePeriodController.submit +GET /agreement uk.gov.hmrc.ngrraldfrontend.controllers.AgreementController.show +POST /agreement uk.gov.hmrc.ngrraldfrontend.controllers.AgreementController.submit +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 +GET /rent-periods uk.gov.hmrc.ngrraldfrontend.controllers.RentPeriodsController.show +POST /rent-periods uk.gov.hmrc.ngrraldfrontend.controllers.RentPeriodsController.submit +GET /did-the-court-set-an-interim-rent uk.gov.hmrc.ngrraldfrontend.controllers.RentInterimController.show +POST /did-the-court-set-an-interim-rent uk.gov.hmrc.ngrraldfrontend.controllers.RentInterimController.submit +GET /rent-dates-agree uk.gov.hmrc.ngrraldfrontend.controllers.RentDatesAgreeController.show +POST /rent-dates-agree uk.gov.hmrc.ngrraldfrontend.controllers.RentDatesAgreeController.submit +GET /rent-dates-agree-start uk.gov.hmrc.ngrraldfrontend.controllers.RentDatesAgreeStartController.show +POST /rent-dates-agree-start uk.gov.hmrc.ngrraldfrontend.controllers.RentDatesAgreeStartController.submit +GET /what-rent-includes uk.gov.hmrc.ngrraldfrontend.controllers.WhatYourRentIncludesController.show +POST /what-rent-includes uk.gov.hmrc.ngrraldfrontend.controllers.WhatYourRentIncludesController.submit +GET /does-rent-include-parking-spaces-or-garages uk.gov.hmrc.ngrraldfrontend.controllers.DoesYourRentIncludeParkingController.show +POST /does-rent-include-parking-spaces-or-garages uk.gov.hmrc.ngrraldfrontend.controllers.DoesYourRentIncludeParkingController.submit GET /how-many-parking-spaces-or-garages-included-in-rent uk.gov.hmrc.ngrraldfrontend.controllers.HowManyParkingSpacesOrGaragesIncludedInRentController.show -POST /how-many-parking-spaces-or-garages-included-in-rent uk.gov.hmrc.ngrraldfrontend.controllers.HowManyParkingSpacesOrGaragesIncludedInRentController.submit \ No newline at end of file +POST /how-many-parking-spaces-or-garages-included-in-rent uk.gov.hmrc.ngrraldfrontend.controllers.HowManyParkingSpacesOrGaragesIncludedInRentController.submit +GET /interim-rent-set-by-court uk.gov.hmrc.ngrraldfrontend.controllers.InterimRentSetByTheCourtController.show +POST /interim-rent-set-by-court uk.gov.hmrc.ngrraldfrontend.controllers.InterimRentSetByTheCourtController.submit \ No newline at end of file diff --git a/conf/messages b/conf/messages index 456567b9..bdc13cf4 100644 --- a/conf/messages +++ b/conf/messages @@ -343,7 +343,6 @@ howManyParkingSpacesOrGaragesIncludedInRent.hint = If spaces are in communal car howManyParkingSpacesOrGaragesIncludedInRent.uncoveredSpaces.label = Uncovered spaces howManyParkingSpacesOrGaragesIncludedInRent.coveredSpaces.label = Covered spaces howManyParkingSpacesOrGaragesIncludedInRent.garages.label = Garages - howManyParkingSpacesOrGaragesIncludedInRent.error.required = Total number of uncovered spaces, covered spaces and garages included in your rent must be more than 0 howManyParkingSpacesOrGaragesIncludedInRent.allFields.error.required = Enter how many parking spaces or garages are included in your rent howManyParkingSpacesOrGaragesIncludedInRent.uncoveredSpaces.wholeNum.error = Number of uncovered parking spaces included in your rent must be a number, like 9 @@ -352,3 +351,20 @@ howManyParkingSpacesOrGaragesIncludedInRent.garages.wholeNum.error = Number of g howManyParkingSpacesOrGaragesIncludedInRent.uncoveredSpaces.tooHigh.error = Number of uncovered parking spaces included in your rent must be 9,999 or less howManyParkingSpacesOrGaragesIncludedInRent.coveredSpaces.tooHigh.error = Number of covered parking spaces included in your rent must be 9,999 or less howManyParkingSpacesOrGaragesIncludedInRent.garages.tooHigh.error = Number of garages included in your rent must be 9,999 or less + +#InterimRentSetByTheCourt +interimRentSetByTheCourt.title = Interim rent set by the court +interimRentSetByTheCourt.label.1 = How much was the interim rent? +interimRentSetByTheCourt.label.2 = When did you start paying the interim rent? +interimRentSetByTheCourt.hint.2 = For example, 6 2026 +date.month = Month +date.year = Year + +interimRentSetByTheCourt.interimAmount.required.error = Enter how much the interim rent was, in pounds +interimRentSetByTheCourt.interimAmount.tooLarge.error = Interim rent must be £9,999,999.99 or less +interimRentSetByTheCourt.interimAmount.format.error = Interim rent must be a number, like 50,000 +interimRentSetByTheCourt.monthYear.format.error = Date your interim rent started must be a real date +interimRentSetByTheCourt.required.error = Enter the date you started paying interim rent +interimRentSetByTheCourt.month.required.error = Date you started paying the interim rent must include a month +interimRentSetByTheCourt.year.required.error = Date you started paying the interim rent must include a year +interimRentSetByTheCourt.startDate.before.1900.error = The date you started paying interim rent must be 1900 or after \ No newline at end of file diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtControllerSpec.scala new file mode 100644 index 00000000..284a8f3c --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/InterimRentSetByTheCourtControllerSpec.scala @@ -0,0 +1,145 @@ +/* + * 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 + + + +/* + * 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.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.views.html.InterimRentSetByTheCourtView +import uk.gov.hmrc.ngrraldfrontend.views.html.components.InputText + +class InterimRentSetByTheCourtControllerSpec extends ControllerSpecSupport { + val pageTitle = "Interim rent set by the court" + val view: InterimRentSetByTheCourtView = inject[InterimRentSetByTheCourtView] + val controller: InterimRentSetByTheCourtController = new InterimRentSetByTheCourtController( + interimRentSetByTheCourtView = view, + authenticate = mockAuthJourney, + hasLinkedProperties = mockPropertyLinkingAction, + inputText = mockInputText, + raldRepo = mockRaldRepo, + mcc = mcc)(mockConfig) + + "InterimRentSetByTheCourtController" must { + "method show" must { + "Return OK and the correct view" in { + val result = controller.show()(authenticatedFakeRequest()) + status(result) mustBe OK + val content = contentAsString(result) + content must include(pageTitle) + } + "Return NotFoundException when property is not found in the mongo" in { + mockRequestWithoutProperty() + val exception = intercept[NotFoundException] { + await(controller.show(authenticatedFakeRequest())) + } + exception.getMessage contains "Couldn't find property in mongo" mustBe true + } + } + + "method submit" must { + "Return OK and the correct view" in { + val fakePostRequest = FakeRequest(routes.InterimRentSetByTheCourtController.submit) + .withFormUrlEncodedBody( + "interimAmount" -> "10000", + "date.month" -> "1", + "date.year" -> "1990" + ) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controller.submit()(authenticatedFakeRequest(fakePostRequest)) + status(result) mustBe SEE_OTHER + redirectLocation(result) mustBe Some(routes.CheckRentFreePeriodController.show.url) + } + "Return BAD_REQUEST for missing how much input and the correct view" in { + mockRequest() + val fakePostRequest = FakeRequest(routes.InterimRentSetByTheCourtController.submit) + .withFormUrlEncodedBody( + "interimAmount" -> "", + "date.month" -> "1", + "date.year" -> "1990" + ) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controller.submit()(authenticatedFakeRequest(fakePostRequest)) + status(result) mustBe BAD_REQUEST + val content = contentAsString(result) + content must include("Enter how much the interim rent was, in pounds") + } + "Return BAD_REQUEST for missing month input and the correct view" in { + mockRequest() + val fakePostRequest = FakeRequest(routes.InterimRentSetByTheCourtController.submit) + .withFormUrlEncodedBody( + "interimAmount" -> "1000", + "date.month" -> "", + "date.year" -> "1990" + ) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controller.submit()(authenticatedFakeRequest(fakePostRequest)) + status(result) mustBe BAD_REQUEST + val content = contentAsString(result) + content must include("Date you started paying the interim rent must include a month") + } + "Return BAD_REQUEST for missing year input and the correct view" in { + mockRequest() + val fakePostRequest = FakeRequest(routes.InterimRentSetByTheCourtController.submit) + .withFormUrlEncodedBody( + "interimAmount" -> "1000", + "date.month" -> "1", + "date.year" -> "" + ) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controller.submit()(authenticatedFakeRequest(fakePostRequest)) + status(result) mustBe BAD_REQUEST + val content = contentAsString(result) + content must include("Date you started paying the interim rent must include a year") + } + "Return Exception if no address is in the mongo" in { + mockRequestWithoutProperty() + val fakePostRequest = FakeRequest(routes.InterimRentSetByTheCourtController.submit) + .withFormUrlEncodedBody(("how–much–is–total–annual–rent-value", "")) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + val exception = intercept[NotFoundException] { + await(controller.submit()(authenticatedFakeRequest(fakePostRequest))) + } + exception.getMessage contains "Couldn't find property in mongo" mustBe true + } + } + } +} diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/RentInterimControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/RentInterimControllerSpec.scala index 23d682e8..50d05066 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/controllers/RentInterimControllerSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/RentInterimControllerSpec.scala @@ -53,7 +53,7 @@ class RentInterimControllerSpec extends ControllerSpecSupport { val result = controller.submit()(authenticatedFakeRequest(fakePostRequest)) status(result) mustBe SEE_OTHER - redirectLocation(result) mustBe Some(routes.ProvideDetailsOfFirstSecondRentPeriodController.show.url) + redirectLocation(result) mustBe Some(routes.InterimRentSetByTheCourtController.show.url) } "Return OK and the correct view when no is selected" in { val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit) diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/InterimRentSetByTheCourtSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/InterimRentSetByTheCourtSpec.scala new file mode 100644 index 00000000..b9e92844 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/InterimRentSetByTheCourtSpec.scala @@ -0,0 +1,44 @@ +/* + * 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 play.api.libs.json.{JsValue, Json} +import uk.gov.hmrc.ngrraldfrontend.helpers.TestSupport + +class InterimRentSetByTheCourtSpec extends TestSupport { + + val interimRentSetByTheCourt: InterimRentSetByTheCourt = InterimRentSetByTheCourt(amount = "1000.00", date = "2020-1") + + val interimRentSetByTheCourtJson: JsValue = Json.parse( + """ + |{ + |"amount": "1000.00", + |"date":"2020-1" + |} + |""".stripMargin + ) + + "RentBasedOn" should { + "deserialize to json" in { + Json.toJson(interimRentSetByTheCourt) mustBe interimRentSetByTheCourtJson + } + "serialize to json" in { + interimRentSetByTheCourtJson.as[InterimRentSetByTheCourt] mustBe interimRentSetByTheCourt + } + } +} + diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchIsTotalAnnualRentFormSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchIsTotalAnnualRentFormSpec.scala index 77e5b970..bfd96dda 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchIsTotalAnnualRentFormSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchIsTotalAnnualRentFormSpec.scala @@ -37,6 +37,16 @@ class HowMuchIsTotalAnnualRentFormSpec extends AnyWordSpec with Matchers { boundForm.value shouldBe Some(HowMuchIsTotalAnnualRentForm(BigDecimal("123456.78"))) } + "bind amount with commas" in { + val data = Map( + "how–much–is–total–annual–rent-value" -> "9,999,999.99", + ) + val boundForm = HowMuchIsTotalAnnualRentForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(HowMuchIsTotalAnnualRentForm(BigDecimal("9999999.99"))) + } + "fail to bind empty input" in { val data = Map("how–much–is–total–annual–rent-value" -> "") val boundForm = HowMuchIsTotalAnnualRentForm.form.bind(data) diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/forms/InterimRentSetByTheCourtFormSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/InterimRentSetByTheCourtFormSpec.scala new file mode 100644 index 00000000..b499801b --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/InterimRentSetByTheCourtFormSpec.scala @@ -0,0 +1,258 @@ +/* + * 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.matchers.should.Matchers.{should, shouldBe} +import org.scalatest.wordspec.AnyWordSpec +import play.api.data.FormError +import play.api.libs.json.Json +import uk.gov.hmrc.ngrraldfrontend.models.NGRMonthYear + +import scala.collection.immutable.ArraySeq + +class InterimRentSetByTheCourtFormSpec extends AnyWordSpec with Matchers { + + "InterimRentSetByTheCourtForm" should { + + "bind valid input" in { + val data = Map( + "interimAmount" -> "123456.78", + "date.month" -> "1", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(InterimRentSetByTheCourtForm( + amount = BigDecimal(123456.78), + date = NGRMonthYear(month = "1", year = "1990") + )) + } + + "fail to bind empty input" in { + val data = Map.empty[String, String] + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.hasErrors shouldBe true + boundForm.errors should contain(FormError("interimAmount", "error.required")) + } + + "fail to bind non-numeric input for how much input field" in { + val data = Map( + "interimAmount" -> "hello", + "date.month" -> "1", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + boundForm.hasErrors shouldBe true + boundForm.errors shouldBe List(FormError("interimAmount", List("interimRentSetByTheCourt.interimAmount.format.error"), ArraySeq("""([0-9]+\.[0-9]+|[0-9]+)"""))) + } + + "fail to bind input greater than 9999999.99 for how much input field" in { + val data = Map( + "interimAmount" -> "123456789.78", + "date.month" -> "1", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.errors shouldBe List(FormError("interimAmount", List("interimRentSetByTheCourt.interimAmount.tooLarge.error"), ArraySeq(9999999.99))) + } + + "bind edge case of exactly 9999999.99" in { + val data = Map( + "interimAmount" -> "9999999.99", + "date.month" -> "1", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(InterimRentSetByTheCourtForm( + amount = BigDecimal("9999999.99"), + date = NGRMonthYear(month = "1", year = "1990") + )) + } + + "bind and drop decimal places to make 2" in { + val data = Map( + "interimAmount" -> "11.123", + "date.month" -> "1", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(InterimRentSetByTheCourtForm( + amount = BigDecimal("11.12"), + date = NGRMonthYear(month = "1", year = "1990") + )) + } + + "bind and round the decimal places up" in { + val data = Map( + "interimAmount" -> "11.125", + "date.month" -> "1", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(InterimRentSetByTheCourtForm( + amount = BigDecimal("11.13"), + date = NGRMonthYear(month = "1", year = "1990") + )) + } + + "bind decimal with two .00" in { + val data = Map( + "interimAmount" -> "11.00", + "date.month" -> "1", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(InterimRentSetByTheCourtForm( + amount = BigDecimal("11.00"), + date = NGRMonthYear(month = "1", year = "1990") + )) + } + + "bind amount with commas" in { + val data = Map( + "interimAmount" -> "9,999,999.99", + "date.month" -> "1", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(InterimRentSetByTheCourtForm( + amount = BigDecimal("9999999.99"), + date = NGRMonthYear(month = "1", year = "1990") + )) + } + + "fail to bind incorrect month for month input field" in { + val data = Map( + "interimAmount" -> "123456.78", + "date.month" -> "13", + "date.year" -> "1990" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.errors should contain(FormError("date", "interimRentSetByTheCourt.monthYear.format.error")) + } + "fail to bind year before 1900 for year input field" in { + val data = Map( + "interimAmount" -> "123456.78", + "date.month" -> "10", + "date.year" -> "1899" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.errors should contain(FormError("date", "interimRentSetByTheCourt.startDate.before.1900.error")) + } + + "fail to bind missing month input" in { + val data = Map( + "interimAmount" -> "123456.78", + "date.month" -> "", + "date.year" -> "1899" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.errors should contain(FormError("date", "interimRentSetByTheCourt.month.required.error")) + } + + "fail to bind missing year input" in { + val data = Map( + "interimAmount" -> "123456.78", + "date.month" -> "1", + "date.year" -> "" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.errors should contain(FormError("date", "interimRentSetByTheCourt.year.required.error")) + } + + "fail to bind non numeric format for month input" in { + val data = Map( + "interimAmount" -> "123456.78", + "date.month" -> "hello", + "date.year" -> "1999" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.errors should contain(FormError("date", "interimRentSetByTheCourt.monthYear.format.error")) + } + + "fail to bind non numeric format for year input" in { + val data = Map( + "interimAmount" -> "123456.78", + "date.month" -> "1", + "date.year" -> "hello" + ) + val boundForm = InterimRentSetByTheCourtForm.form.bind(data) + + boundForm.errors should contain(FormError("date", "interimRentSetByTheCourt.monthYear.format.error")) + } + + } + + "serialize to JSON correctly" in { + val form = InterimRentSetByTheCourtForm( + amount = BigDecimal("9999999.99"), + date = NGRMonthYear(month = "1", year = "1990") + ) + val json = Json.toJson(form) + + json shouldBe Json.obj( + "amount" -> 9999999.99, + "date" -> Json.obj( + "month" -> "1", + "year" -> "1990" + ) + ) + } + + "deserialize from JSON correctly" in { + val json = Json.obj( + "amount" -> 9999999.99, + "date" -> Json.obj( + "month" -> "1", + "year" -> "1990" + ) + ) + val result = json.validate[InterimRentSetByTheCourtForm] + + result.isSuccess shouldBe true + result.get shouldBe InterimRentSetByTheCourtForm( + amount = BigDecimal(9999999.99), + date = NGRMonthYear(month = "1", year = "1990") + ) + } + + "fail deserialization if value is missing" in { + val json = Json.obj() + val result = json.validate[InterimRentSetByTheCourtForm] + + result.isError shouldBe true + } +} \ No newline at end of file diff --git a/test/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepoSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepoSpec.scala index df05ff85..cf2974a4 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepoSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/repo/RaldRepoSpec.scala @@ -505,6 +505,18 @@ class RaldRepoSpec extends TestSupport with TestData actual shouldBe Some(RaldUserAnswers(credId, NewAgreement, property, doesYourRentIncludeParking = Some(true))) } + "insert interim rent amount and start date" in { + await(repository.insertInterimRentSetByTheCourt(credId, 10000.12, "2020-1")) + val actual = await(repository.findByCredId(credId)) + actual shouldBe Some(RaldUserAnswers(credId, NewAgreement, property, interimRentSetByTheCourt = Some(InterimRentSetByTheCourt(amount = "10000.12", date = "2020-1")))) + } + + "insert interim rent amount With decimal of .00 and start date" in { + await(repository.insertInterimRentSetByTheCourt(credId, 10000.00, "2020-1")) + val actual = await(repository.findByCredId(credId)) + actual shouldBe Some(RaldUserAnswers(credId, NewAgreement, property, interimRentSetByTheCourt = Some(InterimRentSetByTheCourt(amount = "10000.0", date = "2020-1")))) + } + "insert has parking included in rent successfully with no" in { await(repository.insertDoesYourRentIncludeParking(credId, "No")) val actual = await(repository.findByCredId(credId)) diff --git a/test/uk/gov/hmrc/ngrraldfrontend/views/InterimRentSetByTheCourtViewSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/views/InterimRentSetByTheCourtViewSpec.scala new file mode 100644 index 00000000..a29c106d --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/views/InterimRentSetByTheCourtViewSpec.scala @@ -0,0 +1,110 @@ +/* + * 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.data.Form +import play.api.i18n.Messages +import play.twirl.api.HtmlFormat +import uk.gov.hmrc.govukfrontend.views.Aliases.{PrefixOrSuffix, Text} +import uk.gov.hmrc.ngrraldfrontend.helpers.ViewBaseSpec +import uk.gov.hmrc.ngrraldfrontend.models.NGRMonthYear +import uk.gov.hmrc.ngrraldfrontend.models.forms.InterimRentSetByTheCourtForm +import uk.gov.hmrc.ngrraldfrontend.views.html.InterimRentSetByTheCourtView +import uk.gov.hmrc.ngrraldfrontend.views.html.components.InputText + +class InterimRentSetByTheCourtViewSpec extends ViewBaseSpec { + lazy val view: InterimRentSetByTheCourtView = inject[InterimRentSetByTheCourtView] + + object Strings { + val heading = "Interim rent set by the court" + val label1 = "How much was the interim rent?" + val label2 = "When did you start paying the interim rent?" + val hint2 = "For example, 6 2026" + val saveAndContinue = "Continue" + } + + object Selectors { + val heading = "#main-content > div > div.govuk-grid-column-two-thirds > form > h1" + val label1 = "#main-content > div > div.govuk-grid-column-two-thirds > form > div:nth-child(3) > h1" + val label2 = "#main-content > div > div.govuk-grid-column-two-thirds > form > div:nth-child(4) > fieldset > legend" + val hint2 = "#date-hint" + val saveAndContinue = "#continue" + } + + val address = "5 Brixham Marina, Berry Head Road, Brixham, Devon, TQ5 9BW" + + val form = InterimRentSetByTheCourtForm.form.fillAndValidate(InterimRentSetByTheCourtForm(amount = BigDecimal(1000), date = NGRMonthYear(month = "1", year = "2020"))) + val mockInputText: InputText = inject[InputText] + def generateInputText(form: Form[InterimRentSetByTheCourtForm], inputFieldName: String)(implicit messages: Messages): HtmlFormat.Appendable = { + mockInputText( + form = form, + id = inputFieldName, + name = inputFieldName, + label = messages(s"interimRentSetByTheCourt.label.1"), + headingMessageArgs = Seq("govuk-fieldset__legend govuk-fieldset__legend--s"), + isPageHeading = true, + isVisible = true, + classes = Some("govuk-input govuk-input--width-5"), + prefix = Some(PrefixOrSuffix(content = Text("£"))) + ) + } + + val howMuch: HtmlFormat.Appendable = generateInputText(form, "howMuch") + + "InterimRentSetByTheCourtView" must { + val interimRentSetByTheCourtView = view(form, address, howMuch) + lazy implicit val document: Document = Jsoup.parse(interimRentSetByTheCourtView.body) + val htmlApply = view.apply(form, address, howMuch).body + val htmlRender = view.render(form, address, howMuch, request, messages, mockConfig).body + lazy val htmlF = view.f(form, address, howMuch) + + "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 label for how much field" in { + elementText(Selectors.label1) mustBe Strings.label1 + } + + "show correct label for month and year fields" in { + elementText(Selectors.label2) mustBe Strings.label2 + } + + "show correct hint for the month and year fields" in { + elementText(Selectors.hint2) mustBe Strings.hint2 + } + + "show correct continue button" in { + elementText(Selectors.saveAndContinue) mustBe Strings.saveAndContinue + } + } +} +