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
@@ -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.controllers

import play.api.i18n.I18nSupport
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.ngrraldfrontend.actions.{AuthRetrievals, DataRetrievalAction}
import uk.gov.hmrc.ngrraldfrontend.config.AppConfig
import uk.gov.hmrc.ngrraldfrontend.models.forms.HowMuchWasTheLumpSumForm
import uk.gov.hmrc.ngrraldfrontend.models.forms.HowMuchWasTheLumpSumForm.form
import uk.gov.hmrc.ngrraldfrontend.models.{Mode, UserAnswers}
import uk.gov.hmrc.ngrraldfrontend.navigation.Navigator
import uk.gov.hmrc.ngrraldfrontend.pages.HowMuchWasTheLumpSumPage
import uk.gov.hmrc.ngrraldfrontend.repo.SessionRepository
import uk.gov.hmrc.ngrraldfrontend.views.html.HowMuchWasTheLumpSumView
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController

import javax.inject.{Inject, Singleton}
import scala.concurrent.{ExecutionContext, Future}

@Singleton
class HowMuchWasTheLumpSumController @Inject()(howMuchWasTheLumpSumView: HowMuchWasTheLumpSumView,
authenticate: AuthRetrievals,
getData: DataRetrievalAction,
sessionRepository: SessionRepository,
navigator: Navigator,
mcc: MessagesControllerComponents)(implicit appConfig: AppConfig, ec: ExecutionContext)
extends FrontendController(mcc) with I18nSupport {


def show(mode: Mode): Action[AnyContent] = {
(authenticate andThen getData).async { implicit request =>
val preparedForm = request.userAnswers.getOrElse(UserAnswers(request.credId)).get(HowMuchWasTheLumpSumPage) match {
case None => form
case Some(value) => form.fill(HowMuchWasTheLumpSumForm(value))
}
Future.successful(Ok(howMuchWasTheLumpSumView(
form = preparedForm,
propertyAddress = request.property.addressFull,
mode = mode
)))
}
}

def submit(mode: Mode): Action[AnyContent] =
(authenticate andThen getData).async { implicit request =>
form.bindFromRequest().fold(
formWithErrors => {
Future.successful(BadRequest(howMuchWasTheLumpSumView(
form = formWithErrors,
propertyAddress = request.property.addressFull,
mode = mode
)))
},
lumpSumAmount =>
for {
updatedAnswers <- Future.fromTry(request.userAnswers.getOrElse(UserAnswers(request.credId)).set(HowMuchWasTheLumpSumPage, lumpSumAmount.lumpSum))
_ <- sessionRepository.set(updatedAnswers)
} yield Redirect(navigator.nextPage(HowMuchWasTheLumpSumPage, mode, updatedAnswers))
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 scala.math.BigDecimal.RoundingMode
import scala.util.Try


final case class HowMuchWasTheLumpSumForm(lumpSum: BigDecimal)

object HowMuchWasTheLumpSumForm extends CommonFormValidators {
implicit val format: OFormat[HowMuchWasTheLumpSumForm] = Json.format[HowMuchWasTheLumpSumForm]

private lazy val lumpSum = "how–much–was–the–lump–sum-value"
private lazy val lumpSumEmptyError = "howMuchWasTheLumpSum.empty.error"
private lazy val lumpSumMaxError = "howMuchWasTheLumpSum.tooLarge.error"
private lazy val lumpSumFormatError = "howMuchWasTheLumpSum.format.error"


def unapply(howMuchWasTheLumpSumForm: HowMuchWasTheLumpSumForm): Option[BigDecimal] = Some(howMuchWasTheLumpSumForm.lumpSum)

val form: Form[HowMuchWasTheLumpSumForm] = Form(
mapping(
lumpSum -> text()
.transform[String](_.strip().replaceAll("[£|,|\\s]", ""), identity)
.verifying(
firstError(
isNotEmpty(lumpSum, lumpSumEmptyError),
regexp(amountRegex.pattern(),lumpSumFormatError)
)
)
.transform[BigDecimal](BigDecimal(_).setScale(2, RoundingMode.HALF_UP), _.toString)
.verifying(
maximumValue[BigDecimal](BigDecimal("9999999.99"), lumpSumMaxError)
)
)(HowMuchWasTheLumpSumForm.apply)(HowMuchWasTheLumpSumForm.unapply)
)

}

1 change: 1 addition & 0 deletions app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class Navigator @Inject()() {
case ConfirmBreakClausePage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.LandlordController.show(NormalMode) //TODO This needs to be amended when the journey is completed
//TODO Next page not made yet
case RentReviewPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode)
case HowMuchWasTheLumpSumPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) //TODO This needs to be amended when the journey is completed
case ParkingSpacesOrGaragesNotIncludedInYourRentPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.RepairsAndInsuranceController.show(NormalMode)
case RepairsAndInsurancePage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.ConfirmBreakClauseController.show(NormalMode) //TODO Needs journey mapping
}
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.pages

import play.api.libs.json.JsPath

case object HowMuchWasTheLumpSumPage extends QuestionPage[BigDecimal]{

override def toString: String = "howMuchWasTheLumpSumPage"

override def path: JsPath = JsPath \ toString

}
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.
*@

@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.HowMuchWasTheLumpSumForm

@this(
layout: Layout,
formHelper: FormWithCSRF,
govukErrorSummary: GovukErrorSummary,
inputText: components.InputText,
saveAndContinueButton: saveAndContinueButton
)

@(form:Form[HowMuchWasTheLumpSumForm], propertyAddress: String, mode: Mode)(implicit request: RequestHeader, messages: Messages, appConfig: AppConfig)

@layout(pageTitle = Some(messages("howMuchWasTheLumpSum.title")), showBackLink = true, fullWidth = false) {
@formHelper(action = uk.gov.hmrc.ngrraldfrontend.controllers.routes.HowMuchWasTheLumpSumController.submit(mode), Symbol("autoComplete") -> "off") {
@if(form.errors.nonEmpty) {
@govukErrorSummary(ErrorSummaryViewModel(form))
}
<span class="govuk-caption-m">@propertyAddress</span>
<h1 class="govuk-heading-l">@messages("howMuchWasTheLumpSum.title")</h1>

@inputText(
form = form,
id = "how–much–was–the–lump–sum-value",
name = "how–much–was–the–lump–sum-value",
label = messages("howMuchWasTheLumpSum.title"),
isPageHeading = false,
hint = None,
isVisible = false,
classes = Some("govuk-!-width-two-thirds"),
prefix = Some(PrefixOrSuffix(content = Text("£")))
)
@saveAndContinueButton(msg = messages("service.continue"), isStartButton = false)
}
}
8 changes: 7 additions & 1 deletion conf/app.routes
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,10 @@ POST /repairs-and-insurance/change uk.gov.hmrc.n
GET /rent-review uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.show(mode: Mode = NormalMode)
POST /rent-review uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.submit(mode: Mode = NormalMode)
GET /rent-review/change uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.show(mode: Mode = CheckMode)
POST /rent-review/change uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.submit(mode: Mode = CheckMode)
POST /rent-review/change uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.submit(mode: Mode = CheckMode)

#Rent review
GET /how-much-was-the-lump-sum uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.show(mode: Mode = NormalMode)
POST /how-much-was-the-lump-sum uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.submit(mode: Mode = NormalMode)
GET /how-much-was-the-lump-sum/change uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.show(mode: Mode = CheckMode)
POST /how-much-was-the-lump-sum/change uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.submit(mode: Mode = CheckMode)
9 changes: 8 additions & 1 deletion conf/messages
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,12 @@ rentReview.rentReviewMonthsYears.months.maximum.11.error = The number of months
rentReview.rentReviewMonthsYears.years.invalid.error = The number of years for how often your rent is reviewed must be a number, like 2
rentReview.rentReviewMonthsYears.years.maximum.1000.error = How often your rent is reviewed must be 1,000 years or less

#How much was the lump sum
howMuchWasTheLumpSum.title = How much was the lump sum?
howMuchWasTheLumpSum.empty.error = Enter the lump sum, in pounds
howMuchWasTheLumpSum.tooLarge.error = Lump sum must be £9,999,999.99 or less
howMuchWasTheLumpSum.format.error = Lump sum must be a number, like 50,000

#ParkingSpacesOrGaragesNotIncludedInYourRent
parkingSpacesOrGaragesNotIncludedInYourRent.title = Parking spaces or garages not included in your rent
parkingSpacesOrGaragesNotIncludedInYourRent.subheading = How many parking spaces or garages do you pay extra for?
Expand Down Expand Up @@ -442,4 +448,5 @@ parkingSpacesOrGaragesNotIncludedInYourRent.agreementDate.month.required.error =
parkingSpacesOrGaragesNotIncludedInYourRent.agreementDate.monthAndYear.required.error = Date this payment was agreed for parking and garages must include a month and year
parkingSpacesOrGaragesNotIncludedInYourRent.agreementDate.year.required.error = Date this payment was agreed for parking and garages must include a year
parkingSpacesOrGaragesNotIncludedInYourRent.agreementDate.invalid.error = Date this payment was agreed for parking and garages must be a real date
parkingSpacesOrGaragesNotIncludedInYourRent.agreementDate.before.1900.error = Year payment was agreed for parking and garages must be 1900 or after
parkingSpacesOrGaragesNotIncludedInYourRent.agreementDate.before.1900.error = Year payment was agreed for parking and garages must be 1900 or after

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2025 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.ngrraldfrontend.controllers

import org.jsoup.Jsoup
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.when
import play.api.http.Status.{BAD_REQUEST, OK, SEE_OTHER}
import play.api.test.FakeRequest
import play.api.test.Helpers.{await, contentAsString, defaultAwaitTimeout, redirectLocation, status}
import uk.gov.hmrc.http.{HeaderNames, NotFoundException}
import uk.gov.hmrc.ngrraldfrontend.helpers.ControllerSpecSupport
import uk.gov.hmrc.ngrraldfrontend.models.AgreementType.{NewAgreement, RenewedAgreement}
import uk.gov.hmrc.ngrraldfrontend.models.registration.CredId
import uk.gov.hmrc.ngrraldfrontend.models.{NormalMode, UserAnswers}
import uk.gov.hmrc.ngrraldfrontend.pages.{HowMuchWasTheLumpSumPage, TellUsAboutYourNewAgreementPage, TellUsAboutYourRenewedAgreementPage}
import uk.gov.hmrc.ngrraldfrontend.views.html.HowMuchWasTheLumpSumView

import scala.concurrent.Future


class HowMuchWasTheLumpSumControllerSpec extends ControllerSpecSupport {
val pageTitle = "How much was the lump sum?"
val view: HowMuchWasTheLumpSumView = inject[HowMuchWasTheLumpSumView]
val controllerNoProperty: HowMuchWasTheLumpSumController = new HowMuchWasTheLumpSumController(view, fakeAuth, fakeData(None), mockSessionRepository, mockNavigator, mcc)(mockConfig)
val controllerProperty: HowMuchWasTheLumpSumController = new HowMuchWasTheLumpSumController(view, fakeAuth, fakeDataProperty(Some(property),None), mockSessionRepository, mockNavigator, mcc)(mockConfig)
lazy val howMuchIsTotalAnnualRentAnswers: Option[UserAnswers] = UserAnswers("id").set(HowMuchWasTheLumpSumPage, BigDecimal(1234.67)).toOption
lazy val filledController: Option[UserAnswers] => HowMuchWasTheLumpSumController = answers => HowMuchWasTheLumpSumController(
view, fakeAuth, fakeDataProperty(Some(property), answers), mockSessionRepository, mockNavigator, mcc
)

"HowMuchWasTheLumpSumControllerSpec" must {
"method show" must {
"Return OK and the correct view" in {
val result = controllerProperty.show(NormalMode)(authenticatedFakeRequest)
status(result) mustBe OK
val content = contentAsString(result)
content must include(pageTitle)
}
"Return OK and the correct with prepopulated answers" in {
val result = filledController(howMuchIsTotalAnnualRentAnswers).show(NormalMode)(authenticatedFakeRequest)
status(result) mustBe OK
val content = contentAsString(result)
val document = Jsoup.parse(content)
document.select("input[name=how–much–was–the–lump–sum-value]").attr("value") mustBe "1234.67"
}
"Return NotFoundException when property is not found in the mongo" in {
when(mockNGRConnector.getLinkedProperty(any[CredId])(any())).thenReturn(Future.successful(None))
val exception = intercept[NotFoundException] {
await(controllerNoProperty.show(NormalMode)(authenticatedFakeRequest))
}
exception.getMessage contains "Could not find answers in backend mongo" mustBe true
}
}

"method submit" must {
"Return BAD_REQUEST for missing input and the correct view" in {
val fakePostRequest = FakeRequest(routes.HowMuchWasTheLumpSumController.submit(NormalMode))
.withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", ""))
.withHeaders(HeaderNames.authorisation -> "Bearer 1")

val result = controllerProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest))
status(result) mustBe BAD_REQUEST
val content = contentAsString(result)
content must include(pageTitle)
content must include("Enter the lump sum, in pounds")
}
"Return BAD_REQUEST for incorrect input and the correct view" in {
val fakePostRequest = FakeRequest(routes.HowMuchWasTheLumpSumController.submit(NormalMode))
.withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "xyz"))
.withHeaders(HeaderNames.authorisation -> "Bearer 1")

val result = controllerProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest))
status(result) mustBe BAD_REQUEST
val content = contentAsString(result)
content must include(pageTitle)
content must include("Lump sum must be a number, like 50,000")
}
"Return Exception if no address is in the mongo" in {
val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit(NormalMode))
.withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", ""))
.withHeaders(HeaderNames.authorisation -> "Bearer 1")
val exception = intercept[NotFoundException] {
await(controllerNoProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)))
}
exception.getMessage contains "Could not find answers in backend mongo" mustBe true
}
}
}
}
Loading