Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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))
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add spec for this?

date: String
)

object InterimRentSetByTheCourt {
implicit val format: OFormat[InterimRentSetByTheCourt] = Json.format[InterimRentSetByTheCourt]
}
55 changes: 55 additions & 0 deletions app/uk/gov/hmrc/ngrraldfrontend/models/NGRMonthYear.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
3 changes: 2 additions & 1 deletion app/uk/gov/hmrc/ngrraldfrontend/models/RaldUserAnswers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
)

}


Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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),
Expand Down
Loading