diff --git a/app/controllers/taxCalculation/TaxCalculationBeforeYouStartController.scala b/app/controllers/taxCalculation/TaxCalculationBeforeYouStartController.scala
index fe0d4859c..01e65aa3b 100644
--- a/app/controllers/taxCalculation/TaxCalculationBeforeYouStartController.scala
+++ b/app/controllers/taxCalculation/TaxCalculationBeforeYouStartController.scala
@@ -17,24 +17,63 @@
package controllers.taxCalculation
import controllers.actions.*
+import controllers.routes.*
+import models.NormalMode
+import models.prelimQuestions.TransactionType
+import models.prelimQuestions.TransactionType.GrantOfLease
+import models.taxCalculation.{MissingFullReturnError, TaxCalculationResult}
+import navigation.Navigator
+import pages.preliminary.TransactionTypePage
+import pages.taxCalculation.{IsLeaseholdAndSelfAssessedPage, TaxCalculationBeforeYouStartPage}
import play.api.i18n.{I18nSupport, MessagesApi}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
+import repositories.SessionRepository
+import services.taxCalculation.SdltCalculationService
+import uk.gov.hmrc.http.HeaderCarrier
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController
+import uk.gov.hmrc.play.http.HeaderCarrierConverter
+import utils.TaxCalculationHelper.*
import views.html.taxCalculation.TaxCalculationBeforeYouStartView
import javax.inject.Inject
+import scala.concurrent.{ExecutionContext, Future}
class TaxCalculationBeforeYouStartController @Inject()(
- override val messagesApi: MessagesApi,
- identify: IdentifierAction,
- getData: DataRetrievalAction,
- requireData: DataRequiredAction,
- val controllerComponents: MessagesControllerComponents,
- view: TaxCalculationBeforeYouStartView
- ) extends FrontendBaseController with I18nSupport {
-
- def onPageLoad: Action[AnyContent] = (identify andThen getData andThen requireData) {
+ override val messagesApi: MessagesApi,
+ identify: IdentifierAction,
+ getData: DataRetrievalAction,
+ requireData: DataRequiredAction,
+ sdltCalculationService: SdltCalculationService,
+ sessionRepository: SessionRepository,
+ navigator: Navigator,
+ val controllerComponents: MessagesControllerComponents,
+ view: TaxCalculationBeforeYouStartView
+ )(implicit ec: ExecutionContext) extends FrontendBaseController with I18nSupport {
+
+ def onPageLoad: Action[AnyContent] = (identify andThen getData andThen requireData).async {
+ implicit request =>
+
+ implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequestAndSession(request, request.session)
+
+ sdltCalculationService.calculateStampDutyLandTax(request.userAnswers).flatMap {
+ case Right(result) =>
+
+ val isLeasehold: Boolean = request.userAnswers.get(TransactionTypePage).contains(GrantOfLease)
+
+ val leaseholdSelfAssessedFlag = isLeasehold && isSelfAssessedResponse(result)
+
+ for {
+ updatedAnswers <- Future.fromTry(request.userAnswers.set(IsLeaseholdAndSelfAssessedPage, leaseholdSelfAssessedFlag))
+ _ <- sessionRepository.set(updatedAnswers)
+ } yield Ok(view(leaseholdSelfAssessedFlag))
+
+ case Left(MissingFullReturnError) => Future.successful(Redirect(NoReturnReferenceController.onPageLoad()))
+ case Left(_) => Future.successful(Redirect(ReturnTaskListController.onPageLoad()))
+ }
+ }
+
+ def onSubmit: Action[AnyContent] = (identify andThen getData andThen requireData) {
implicit request =>
- Ok(view())
+ Redirect(navigator.nextPage(TaxCalculationBeforeYouStartPage, NormalMode, request.userAnswers))
}
}
diff --git a/app/navigation/Navigator.scala b/app/navigation/Navigator.scala
index 456622f72..cf212fabd 100644
--- a/app/navigation/Navigator.scala
+++ b/app/navigation/Navigator.scala
@@ -26,6 +26,7 @@ import pages.vendor.*
import pages.vendorAgent.*
import pages.land.*
import pages.transaction.*
+import pages.taxCalculation.*
import pages.ukResidency.*
import play.api.mvc.Call
@@ -65,6 +66,7 @@ class Navigator @Inject()() {
case landPage if isLandSection(landPage) => landRoutes(landPage)
case residencyPage if isResidencySection(residencyPage) => residencyRoutes(residencyPage)
case transactionPage if isTransactionSection(transactionPage) => transactionRoutes(transactionPage)
+ case taxCalcPage if isTaxCalculationSection(taxCalcPage) => taxCalculationRoutes(taxCalcPage)
case _ => _ => routes.IndexController.onPageLoad()
}
@@ -254,6 +256,30 @@ class Navigator @Inject()() {
case _ => _ => routes.IndexController.onPageLoad()
}
+ private def isTaxCalculationSection(page: Page): Boolean = page match {
+ case TaxCalculationBeforeYouStartPage | CalculatedSdltPage | CalculatedSdltBreakdownPage
+ | SelfAssessmentAmountPage | PremiumPayableTaxPage | NpvTaxPage
+ | TotalAmountDuePage | PenaltiesAndInterestPage | TaxCalculationCheckYourAnswersPage => true
+ case _ => false
+ }
+
+ private def taxCalculationRoutes(page: Page): UserAnswers => Call = page match {
+
+ case TaxCalculationBeforeYouStartPage => answers =>
+ if (answers.get(IsLeaseholdAndSelfAssessedPage).contains(true)) routes.IndexController.onPageLoad() // TODO: replace -> PremiumPayableTaxController
+ else routes.IndexController.onPageLoad() // TODO: replace -> CalculatedSdltController
+ case CalculatedSdltPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> SelfAssessmentAmountController
+ case CalculatedSdltBreakdownPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> CalculatedSdltController
+ case SelfAssessmentAmountPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> TotalAmountDueController
+ case PremiumPayableTaxPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> NpvTaxController
+ case NpvTaxPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> TotalAmountDueController
+ case TotalAmountDuePage => _ => routes.IndexController.onPageLoad() // TODO: replace -> PenaltiesAndInterestController
+ case PenaltiesAndInterestPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> TaxCalculationCheckYourAnswersController
+ case TaxCalculationCheckYourAnswersPage => _ => routes.ReturnTaskListController.onPageLoad()
+
+ case _ => _ => routes.IndexController.onPageLoad()
+ }
+
private val checkRouteMap: Page => UserAnswers => Call = {
case WhoIsMakingThePurchasePage => _ => controllers.purchaser.routes.PurchaserCheckYourAnswersController.onPageLoad()
case NameOfPurchaserPage => _ => controllers.purchaser.routes.PurchaserCheckYourAnswersController.onPageLoad()
@@ -315,6 +341,13 @@ class Navigator @Inject()() {
case LandMineralsOrMineralRightsPage => _ => controllers.land.routes.LandCheckYourAnswersController.onPageLoad()
case LandSelectMeasurementUnitPage => _ => controllers.land.routes.AreaOfLandController.onPageLoad(CheckMode)
+ // TODO: replace -> TaxCalculationCheckYourAnswersController
+ case SelfAssessmentAmountPage => _ => routes.IndexController.onPageLoad()
+ case PremiumPayableTaxPage => _ => routes.IndexController.onPageLoad()
+ case NpvTaxPage => _ => routes.IndexController.onPageLoad()
+ case TotalAmountDuePage => _ => routes.IndexController.onPageLoad()
+ case PenaltiesAndInterestPage => _ => routes.IndexController.onPageLoad()
+
case _ => _ => controllers.routes.ReturnTaskListController.onPageLoad()
}
diff --git a/app/pages/taxCalculation/CalculatedSdltBreakdownPage.scala b/app/pages/taxCalculation/CalculatedSdltBreakdownPage.scala
new file mode 100644
index 000000000..35b36e15a
--- /dev/null
+++ b/app/pages/taxCalculation/CalculatedSdltBreakdownPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object CalculatedSdltBreakdownPage extends QuestionPage[Boolean] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "calculatedSdltBreakdown"
+}
diff --git a/app/pages/taxCalculation/CalculatedSdltPage.scala b/app/pages/taxCalculation/CalculatedSdltPage.scala
new file mode 100644
index 000000000..5787c274e
--- /dev/null
+++ b/app/pages/taxCalculation/CalculatedSdltPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object CalculatedSdltPage extends QuestionPage[Boolean] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "calculatedSdlt"
+}
diff --git a/app/pages/taxCalculation/IsLeaseholdAndSelfAssessedPage.scala b/app/pages/taxCalculation/IsLeaseholdAndSelfAssessedPage.scala
new file mode 100644
index 000000000..18da49269
--- /dev/null
+++ b/app/pages/taxCalculation/IsLeaseholdAndSelfAssessedPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object IsLeaseholdAndSelfAssessedPage extends QuestionPage[Boolean] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "isLeaseholdAndSelfAssessed"
+}
diff --git a/app/pages/taxCalculation/NpvTaxPage.scala b/app/pages/taxCalculation/NpvTaxPage.scala
new file mode 100644
index 000000000..e1c56e9e7
--- /dev/null
+++ b/app/pages/taxCalculation/NpvTaxPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object NpvTaxPage extends QuestionPage[BigDecimal] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "npvTax"
+}
diff --git a/app/pages/taxCalculation/PenaltiesAndInterestPage.scala b/app/pages/taxCalculation/PenaltiesAndInterestPage.scala
new file mode 100644
index 000000000..555664447
--- /dev/null
+++ b/app/pages/taxCalculation/PenaltiesAndInterestPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object PenaltiesAndInterestPage extends QuestionPage[Boolean] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "penaltiesAndInterest"
+}
diff --git a/app/pages/taxCalculation/PremiumPayableTaxPage.scala b/app/pages/taxCalculation/PremiumPayableTaxPage.scala
new file mode 100644
index 000000000..a6da5b028
--- /dev/null
+++ b/app/pages/taxCalculation/PremiumPayableTaxPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object PremiumPayableTaxPage extends QuestionPage[BigDecimal] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "premiumPayableTax"
+}
diff --git a/app/pages/taxCalculation/SelfAssessmentAmountPage.scala b/app/pages/taxCalculation/SelfAssessmentAmountPage.scala
new file mode 100644
index 000000000..367c03249
--- /dev/null
+++ b/app/pages/taxCalculation/SelfAssessmentAmountPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object SelfAssessmentAmountPage extends QuestionPage[BigDecimal] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "selfAssessmentAmount"
+}
diff --git a/app/pages/taxCalculation/TaxCalculationBeforeYouStartPage.scala b/app/pages/taxCalculation/TaxCalculationBeforeYouStartPage.scala
new file mode 100644
index 000000000..f800e54e3
--- /dev/null
+++ b/app/pages/taxCalculation/TaxCalculationBeforeYouStartPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object TaxCalculationBeforeYouStartPage extends QuestionPage[Boolean] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "taxCalculationBeforeYouStart"
+}
diff --git a/app/pages/taxCalculation/TaxCalculationCheckYourAnswersPage.scala b/app/pages/taxCalculation/TaxCalculationCheckYourAnswersPage.scala
new file mode 100644
index 000000000..40ba2d71d
--- /dev/null
+++ b/app/pages/taxCalculation/TaxCalculationCheckYourAnswersPage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object TaxCalculationCheckYourAnswersPage extends QuestionPage[Boolean] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "taxCalculationCheckYourAnswers"
+}
diff --git a/app/pages/taxCalculation/TotalAmountDuePage.scala b/app/pages/taxCalculation/TotalAmountDuePage.scala
new file mode 100644
index 000000000..7e6a8107d
--- /dev/null
+++ b/app/pages/taxCalculation/TotalAmountDuePage.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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 pages.taxCalculation
+
+import pages.QuestionPage
+import play.api.libs.json.JsPath
+
+case object TotalAmountDuePage extends QuestionPage[BigDecimal] {
+
+ override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString
+
+ override def toString: String = "totalAmountDue"
+}
diff --git a/app/services/taxCalculation/SdltCalculationService.scala b/app/services/taxCalculation/SdltCalculationService.scala
index aab0b93b4..c5b470e4d 100644
--- a/app/services/taxCalculation/SdltCalculationService.scala
+++ b/app/services/taxCalculation/SdltCalculationService.scala
@@ -31,7 +31,8 @@ class SdltCalculationService @Inject()(
// TODO: DTR-2815: Must Implement Self-Assessed response for Residential before 2012-03-22
- def calculateStampDutyLandTax(userAnswers: UserAnswers)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[Either[MissingDataError, TaxCalculationResult]] =
+ def calculateStampDutyLandTax(userAnswers: UserAnswers)
+ (implicit hc: HeaderCarrier, ec: ExecutionContext): Future[Either[MissingDataError, TaxCalculationResult]] =
TaxCalcRequestValidator.buildRequest(userAnswers) match {
case Right(request) =>
logger.info(s"[SdltCalculationService][calculateStampDutyLandTax] sending calculation request")
diff --git a/app/services/taxCalculation/TaxCalcRequestValidator.scala b/app/services/taxCalculation/TaxCalcRequestValidator.scala
index 287837c9f..db45bd621 100644
--- a/app/services/taxCalculation/TaxCalcRequestValidator.scala
+++ b/app/services/taxCalculation/TaxCalcRequestValidator.scala
@@ -55,7 +55,7 @@ object TaxCalcRequestValidator {
effectiveDateYear = parsedDate.getYear,
nonUKResident = handleNonUkResident(fullReturn.residency, parsedDate, propertyType),
premium = premium,
- highestRent = BigDecimal(0),
+ highestRent = fullReturn.lease.flatMap(_.startingRent).flatMap(v => Try(BigDecimal(v)).toOption).getOrElse(BigDecimal(0)),
propertyDetails = buildPropertyDetails(propertyCode),
leaseDetails = leaseDetails,
relevantRentDetails = fullReturn.lease.map(buildRelevantRentDetails),
@@ -82,7 +82,7 @@ object TaxCalcRequestValidator {
private def handleNonUkResident(residency: Option[Residency], effectiveDate: LocalDate, propertyType: PropertyTypes.Value): Option[String] =
if (effectiveDate.isBefore(APR2021_RESIDENTIAL_DATE) && propertyType == PropertyTypes.residential) None
- else residency.flatMap(_.isNonUkResidents).map(_.capitalize)
+ else residency.flatMap(_.isNonUkResidents).map(_.toLowerCase.capitalize)
private def buildPropertyDetails(propertyCode: String): Option[PropertyDetails] =
propertyCode match {
diff --git a/app/utils/TaxCalculationHelper.scala b/app/utils/TaxCalculationHelper.scala
new file mode 100644
index 000000000..e20f1b94c
--- /dev/null
+++ b/app/utils/TaxCalculationHelper.scala
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2026 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 utils
+
+import models.UserAnswers
+import models.prelimQuestions.TransactionType
+import models.taxCalculation.TaxCalculationResult
+import pages.preliminary.TransactionTypePage
+
+object TaxCalculationHelper {
+
+ private val SELF_ASSESSED_HINT_TEXT: String = "self-assessed"
+
+ def isSelfAssessedResponse(taxCalculationResult: TaxCalculationResult): Boolean =
+ taxCalculationResult
+ .resultHeading
+ .contains(SELF_ASSESSED_HINT_TEXT)
+
+ def isLeasehold(answers: UserAnswers): Boolean =
+ answers.get(TransactionTypePage).contains(TransactionType.GrantOfLease)
+}
diff --git a/app/views/taxCalculation/TaxCalculationBeforeYouStartView.scala.html b/app/views/taxCalculation/TaxCalculationBeforeYouStartView.scala.html
index 88e0abb01..44ff9627a 100644
--- a/app/views/taxCalculation/TaxCalculationBeforeYouStartView.scala.html
+++ b/app/views/taxCalculation/TaxCalculationBeforeYouStartView.scala.html
@@ -14,37 +14,43 @@
* limitations under the License.
*@
-@import views.html.components.*
-
+@import views.html.components._
@this(
- layout: templates.Layout,
- govukButton: GovukButton,
- h1: h1,
- p: Paragraph,
- caption: Caption
+ layout: templates.Layout,
+ formHelper: FormWithCSRF,
+ govukButton: GovukButton,
+ h1: h1,
+ p: Paragraph,
+ caption: Caption
)
-@()(implicit request: Request[_], messages: Messages)
+@(isLeaseholdAndSelfAssessed: Boolean)(implicit request: Request[_], messages: Messages)
@layout(pageTitle = titleNoForm(messages("taxCalculation.beforeStart.title"))) {
- @caption(messages("site.taxCalculation.caption"))
- @h1(messages("site.beforeYouStart.heading"))
-
- @p(messages("taxCalculation.beforeStart.p1"))
- @p(messages("taxCalculation.beforeStart.p2"))
-
-
- - @messages("taxCalculation.beforeStart.bullet1")
- - @messages("taxCalculation.beforeStart.bullet2")
- - @messages("taxCalculation.beforeStart.bullet3")
- - @messages("taxCalculation.beforeStart.bullet4")
- - @messages("taxCalculation.beforeStart.bullet5")
-
-
- @govukButton(
- ButtonViewModel(messages("site.continue"))
- .asLink("#")
- .withCssClass("govuk-!-margin-top-2")
- )
+ @formHelper(action = controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onSubmit(), Symbol("autoComplete") -> "off") {
+
+ @caption(messages("site.taxCalculation.caption"))
+
+ @h1(messages("site.beforeYouStart.heading"))
+
+ @p(messages("taxCalculation.beforeStart.p1"))
+ @p(messages("taxCalculation.beforeStart.p2"))
+
+
+ - @messages("taxCalculation.beforeStart.bullet1")
+ @if(isLeaseholdAndSelfAssessed) {
+ - @messages("taxCalculation.beforeStart.leaseholdAndSelfAssessed.bullet")
+ } else {
+ - @messages("taxCalculation.beforeStart.bullet2")
+ - @messages("taxCalculation.beforeStart.bullet3")
+ }
+ - @messages("taxCalculation.beforeStart.bullet4")
+ - @messages("taxCalculation.beforeStart.bullet5")
+
+
+ @govukButton(
+ ButtonViewModel(messages("site.continue"))
+ )
+ }
}
diff --git a/conf/app.routes b/conf/app.routes
index 46677e213..b3e2b5e88 100644
--- a/conf/app.routes
+++ b/conf/app.routes
@@ -502,9 +502,11 @@ POST /about-the-transaction/claiming-partial-relief
GET /about-the-transaction/claiming-partial-relief/change controllers.transaction.ClaimingPartialReliefAmountController.onPageLoad(mode: Mode = CheckMode)
POST /about-the-transaction/claiming-partial-relief/change controllers.transaction.ClaimingPartialReliefAmountController.onSubmit(mode: Mode = CheckMode)
-GET /tax-calculation/before-you-start controllers.taxCalculation.TaxCalculationBeforeYouStartController.onPageLoad()
-
GET /about-the-transaction/enter-registered-charity-number controllers.transaction.CharityRegisteredNumberController.onPageLoad(mode: Mode = NormalMode)
POST /about-the-transaction/enter-registered-charity-number controllers.transaction.CharityRegisteredNumberController.onSubmit(mode: Mode = NormalMode)
GET /about-the-transaction/enter-registered-charity-number/change controllers.transaction.CharityRegisteredNumberController.onPageLoad(mode: Mode = CheckMode)
POST /about-the-transaction/enter-registered-charity-number/change controllers.transaction.CharityRegisteredNumberController.onSubmit(mode: Mode = CheckMode)
+
+
+GET /tax-calculation/before-you-start controllers.taxCalculation.TaxCalculationBeforeYouStartController.onPageLoad()
+POST /tax-calculation/before-you-start controllers.taxCalculation.TaxCalculationBeforeYouStartController.onSubmit()
diff --git a/conf/messages.en b/conf/messages.en
index 8cebee408..0130693fa 100644
--- a/conf/messages.en
+++ b/conf/messages.en
@@ -1390,6 +1390,7 @@ transaction.claimingPartialReliefAmount.missing
taxCalculation.beforeStart.title =
taxCalculation.beforeStart.p1 = In this section, you need to provide information about your tax calculation.
taxCalculation.beforeStart.p2 = You will need to know:
+taxCalculation.beforeStart.leaseholdAndSelfAssessed.bullet = your self-assessed SDLT calculation
taxCalculation.beforeStart.bullet1 = the effective date of the transaction
taxCalculation.beforeStart.bullet2 = the tax due on the total premium payable
taxCalculation.beforeStart.bullet3 = the tax due on the NPV
diff --git a/test/controllers/taxCalculation/TaxCalculationBeforeYouStartControllerSpec.scala b/test/controllers/taxCalculation/TaxCalculationBeforeYouStartControllerSpec.scala
index 582378dab..bcdb04906 100644
--- a/test/controllers/taxCalculation/TaxCalculationBeforeYouStartControllerSpec.scala
+++ b/test/controllers/taxCalculation/TaxCalculationBeforeYouStartControllerSpec.scala
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 HM Revenue & Customs
+ * Copyright 2026 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.
@@ -17,27 +17,164 @@
package controllers.taxCalculation
import base.SpecBase
+import models.prelimQuestions.TransactionType
+import models.taxCalculation.{MissingAboutTheTransactionError, MissingFullReturnError, TaxCalculationResult}
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.when
+import org.scalatestplus.mockito.MockitoSugar
+import pages.preliminary.TransactionTypePage
+import play.api.inject.bind
+import play.api.test.CSRFTokenHelper.*
import play.api.test.FakeRequest
import play.api.test.Helpers.*
+import services.taxCalculation.SdltCalculationService
import views.html.taxCalculation.TaxCalculationBeforeYouStartView
-class TaxCalculationBeforeYouStartControllerSpec extends SpecBase {
+import scala.concurrent.Future
+
+class TaxCalculationBeforeYouStartControllerSpec extends SpecBase with MockitoSugar {
+
+ private val calculatedResult = TaxCalculationResult(totalTax = 5000, resultHeading = None, resultHint = None, npv = None, taxCalcs = Seq.empty)
+ private val selfAssessedResult = TaxCalculationResult(totalTax = 0, resultHeading = Some("self-assessed"), resultHint = None, npv = None, taxCalcs = Seq.empty)
+
+ private lazy val onPageLoadUrl = controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onPageLoad().url
"TaxCalculationBeforeYouStart Controller" - {
- "must return OK and the correct view for a GET" in {
+ "must render the view with false when the calculation succeeds and the transaction is not leasehold" in {
- val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build()
+ val mockService = mock[SdltCalculationService]
+ when(mockService.calculateStampDutyLandTax(any())(any(), any()))
+ .thenReturn(Future.successful(Right(calculatedResult)))
+
+ val answers = emptyUserAnswers.set(TransactionTypePage, TransactionType.ConveyanceTransfer).success.value
+ val application = applicationBuilder(userAnswers = Some(answers))
+ .overrides(bind[SdltCalculationService].toInstance(mockService))
+ .build()
running(application) {
- val request = FakeRequest(GET, controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onPageLoad().url)
+ val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken
+ val result = route(application, request).value
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ status(result) mustEqual OK
+ contentAsString(result) mustEqual view(isLeaseholdAndSelfAssessed = false)(request, messages(application)).toString
+ }
+ }
- val result = route(application, request).value
+ "must render the view with true when the calculation is self-assessed and the transaction is leasehold" in {
- val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+ val mockService = mock[SdltCalculationService]
+ when(mockService.calculateStampDutyLandTax(any())(any(), any()))
+ .thenReturn(Future.successful(Right(selfAssessedResult)))
+
+ val answers = emptyUserAnswers.set(TransactionTypePage, TransactionType.GrantOfLease).success.value
+ val application = applicationBuilder(userAnswers = Some(answers))
+ .overrides(bind[SdltCalculationService].toInstance(mockService))
+ .build()
+
+ running(application) {
+ val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken
+ val result = route(application, request).value
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
status(result) mustEqual OK
- contentAsString(result) mustEqual view()(request, messages(application)).toString
+ contentAsString(result) mustEqual view(isLeaseholdAndSelfAssessed = true)(request, messages(application)).toString
+ }
+ }
+
+ "must render the view with false when the calculation is self-assessed but the transaction is not leasehold" in {
+
+ val mockService = mock[SdltCalculationService]
+ when(mockService.calculateStampDutyLandTax(any())(any(), any()))
+ .thenReturn(Future.successful(Right(selfAssessedResult)))
+
+ val answers = emptyUserAnswers.set(TransactionTypePage, TransactionType.ConveyanceTransfer).success.value
+ val application = applicationBuilder(userAnswers = Some(answers))
+ .overrides(bind[SdltCalculationService].toInstance(mockService))
+ .build()
+
+ running(application) {
+ val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken
+ val result = route(application, request).value
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ status(result) mustEqual OK
+ contentAsString(result) mustEqual view(isLeaseholdAndSelfAssessed = false)(request, messages(application)).toString
+ }
+ }
+
+ "must render the view with false when the transaction is leasehold but the calculation is not self-assessed" in {
+
+ val mockService = mock[SdltCalculationService]
+ when(mockService.calculateStampDutyLandTax(any())(any(), any()))
+ .thenReturn(Future.successful(Right(calculatedResult)))
+
+ val answers = emptyUserAnswers.set(TransactionTypePage, TransactionType.GrantOfLease).success.value
+ val application = applicationBuilder(userAnswers = Some(answers))
+ .overrides(bind[SdltCalculationService].toInstance(mockService))
+ .build()
+
+ running(application) {
+ val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken
+ val result = route(application, request).value
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ status(result) mustEqual OK
+ contentAsString(result) mustEqual view(isLeaseholdAndSelfAssessed = false)(request, messages(application)).toString
+ }
+ }
+
+ "must redirect to NoReturnReferenceController when the service reports no full return" in {
+
+ val mockService = mock[SdltCalculationService]
+ when(mockService.calculateStampDutyLandTax(any())(any(), any()))
+ .thenReturn(Future.successful(Left(MissingFullReturnError)))
+
+ val application = applicationBuilder(userAnswers = Some(emptyUserAnswers))
+ .overrides(bind[SdltCalculationService].toInstance(mockService))
+ .build()
+
+ running(application) {
+ val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken
+ val result = route(application, request).value
+
+ status(result) mustEqual SEE_OTHER
+ redirectLocation(result).value mustEqual controllers.routes.NoReturnReferenceController.onPageLoad().url
+ }
+ }
+
+ "must redirect to ReturnTaskListController when the service reports any other missing data" in {
+
+ val mockService = mock[SdltCalculationService]
+ when(mockService.calculateStampDutyLandTax(any())(any(), any()))
+ .thenReturn(Future.successful(Left(MissingAboutTheTransactionError)))
+
+ val application = applicationBuilder(userAnswers = Some(emptyUserAnswers))
+ .overrides(bind[SdltCalculationService].toInstance(mockService))
+ .build()
+
+ running(application) {
+ val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken
+ val result = route(application, request).value
+
+ status(result) mustEqual SEE_OTHER
+ redirectLocation(result).value mustEqual controllers.routes.ReturnTaskListController.onPageLoad().url
+ }
+ }
+
+ "onSubmit must redirect to the next page determined by the navigator" in {
+ val onSubmitUrl = controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onSubmit().url
+ val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build()
+
+ running(application) {
+ val request = FakeRequest(POST, onSubmitUrl).withCSRFToken
+ val result = route(application, request).value
+
+ status(result) mustEqual SEE_OTHER
+ // Navigator currently routes both branches to IndexController (placeholders until
+ // downstream tax-calc controllers exist) — assert we redirect somewhere, not nowhere.
+ redirectLocation(result) mustBe defined
}
}
}
diff --git a/test/navigation/NavigatorSpec.scala b/test/navigation/NavigatorSpec.scala
index 09742f072..94a647c3d 100644
--- a/test/navigation/NavigatorSpec.scala
+++ b/test/navigation/NavigatorSpec.scala
@@ -28,7 +28,7 @@ import pages.transaction.*
import pages.ukResidency.{CloseCompanyPage, CrownEmploymentReliefPage, NonUkResidentPurchaserPage}
import pages.vendor.*
import pages.vendorAgent.*
-
+import pages.taxCalculation.*
class NavigatorSpec extends SpecBase {
val navigator = new Navigator
@@ -466,6 +466,70 @@ class NavigatorSpec extends SpecBase {
navigator.nextPage(AgentNamePage, NormalMode, userAnswers) mustBe
controllers.vendorAgent.routes.VendorAgentAddressController.redirectToAddressLookupVendorAgent()
}
+
+ "tax calculation routes" - {
+
+ // Placeholders: downstream controllers haven't been built yet, so each page currently redirects to IndexController
+
+ "go from TaxCalculationBeforeYouStartPage to PremiumPayableTax page when leasehold and self-assessed" in {
+ val answers = UserAnswers("id", storn = "TESTSTORN")
+ .set(IsLeaseholdAndSelfAssessedPage, true).success.value
+ navigator.nextPage(TaxCalculationBeforeYouStartPage, NormalMode, answers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.PremiumPayableTaxController.onPageLoad()
+ }
+
+ "go from TaxCalculationBeforeYouStartPage to CalculatedSdlt page when flag is false" in {
+ val answers = UserAnswers("id", storn = "TESTSTORN")
+ .set(IsLeaseholdAndSelfAssessedPage, false).success.value
+ navigator.nextPage(TaxCalculationBeforeYouStartPage, NormalMode, answers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.CalculatedSdltController.onPageLoad()
+ }
+
+ "go from TaxCalculationBeforeYouStartPage to CalculatedSdlt page when flag is unset" in {
+ navigator.nextPage(TaxCalculationBeforeYouStartPage, NormalMode, userAnswers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.CalculatedSdltController.onPageLoad()
+ }
+
+ "go from CalculatedSdltPage to SelfAssessmentAmount page" in {
+ navigator.nextPage(CalculatedSdltPage, NormalMode, userAnswers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.SelfAssessmentAmountController.onPageLoad()
+ }
+
+ "go from CalculatedSdltBreakdownPage to CalculatedSdlt page" in {
+ navigator.nextPage(CalculatedSdltBreakdownPage, NormalMode, userAnswers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.CalculatedSdltController.onPageLoad()
+ }
+
+ "go from SelfAssessmentAmountPage to TotalAmountDue page" in {
+ navigator.nextPage(SelfAssessmentAmountPage, NormalMode, userAnswers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.TotalAmountDueController.onPageLoad()
+ }
+
+ "go from PremiumPayableTaxPage to NpvTax page" in {
+ navigator.nextPage(PremiumPayableTaxPage, NormalMode, userAnswers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.NpvTaxController.onPageLoad()
+ }
+
+ "go from NpvTaxPage to TotalAmountDue page" in {
+ navigator.nextPage(NpvTaxPage, NormalMode, userAnswers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.TotalAmountDueController.onPageLoad()
+ }
+
+ "go from TotalAmountDuePage to PenaltiesAndInterest page" in {
+ navigator.nextPage(TotalAmountDuePage, NormalMode, userAnswers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.PenaltiesAndInterestController.onPageLoad()
+ }
+
+ "go from PenaltiesAndInterestPage to TaxCalculationCheckYourAnswers page" in {
+ navigator.nextPage(PenaltiesAndInterestPage, NormalMode, userAnswers) mustBe
+ routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.TaxCalculationCheckYourAnswersController.onPageLoad()
+ }
+
+ "go from TaxCalculationCheckYourAnswersPage to return task list" in {
+ navigator.nextPage(TaxCalculationCheckYourAnswersPage, NormalMode, userAnswers) mustBe
+ routes.ReturnTaskListController.onPageLoad()
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/test/services/taxCalculation/TaxCalcRequestValidatorSpec.scala b/test/services/taxCalculation/TaxCalcRequestValidatorSpec.scala
index 91e304130..f7c4eff3a 100644
--- a/test/services/taxCalculation/TaxCalcRequestValidatorSpec.scala
+++ b/test/services/taxCalculation/TaxCalcRequestValidatorSpec.scala
@@ -51,7 +51,8 @@ class TaxCalcRequestValidatorSpec extends SpecBase {
effectiveDate: String = "2025-06-15",
consideration: BigDecimal = 250000,
npv: String = "100000",
- annualRentOver1000: Option[String] = Some("yes")
+ annualRentOver1000: Option[String] = Some("yes"),
+ startingRent: Option[String] = None
): FullReturn = FullReturn(
stornId = "STORN", returnResourceRef = "REF",
land = Some(Seq(Land(propertyType = Some("01"), interestCreatedTransferred = Some("LG")))),
@@ -62,7 +63,8 @@ class TaxCalcRequestValidatorSpec extends SpecBase {
residency = Some(Residency(isNonUkResidents = Some("no"))),
lease = Some(Lease(
contractStartDate = Some(startDate), contractEndDate = Some(endDate),
- isAnnualRentOver1000 = annualRentOver1000, netPresentValue = Some(npv)
+ isAnnualRentOver1000 = annualRentOver1000, netPresentValue = Some(npv),
+ startingRent = startingRent
))
)
@@ -294,6 +296,13 @@ class TaxCalcRequestValidatorSpec extends SpecBase {
}
}
+ "highest rent" - {
+
+ "must be parsed from startingRent for leasehold" in {
+ TaxCalcRequestValidator.buildRequest(userAnswersWith(leaseholdReturn(startingRent = Some("1500")))).toOption.get.highestRent mustBe BigDecimal(1500)
+ }
+ }
+
"relevant rent details" - {
"must set relevantRent to 1000 when annual rent is over 1000" in {
diff --git a/test/utils/TaxCalculationHelperSpec.scala b/test/utils/TaxCalculationHelperSpec.scala
new file mode 100644
index 000000000..bb7de7db8
--- /dev/null
+++ b/test/utils/TaxCalculationHelperSpec.scala
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2026 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 utils
+
+import models.UserAnswers
+import models.prelimQuestions.TransactionType
+import models.taxCalculation.TaxCalculationResult
+import org.scalatest.TryValues.convertTryToSuccessOrFailure
+import org.scalatest.freespec.AnyFreeSpec
+import org.scalatest.matchers.must.Matchers
+import pages.preliminary.TransactionTypePage
+
+class TaxCalculationHelperSpec extends AnyFreeSpec with Matchers {
+
+ private def result(heading: Option[String]): TaxCalculationResult =
+ TaxCalculationResult(totalTax = 0, resultHeading = heading, resultHint = None, npv = None, taxCalcs = Seq.empty)
+
+ "isSelfAssessedResponse" - {
+
+ "must return true when resultHeading equals 'self-assessed'" in {
+ TaxCalculationHelper.isSelfAssessedResponse(result(Some("self-assessed"))) mustBe true
+ }
+
+ "must return false when resultHeading is a different string" in {
+ TaxCalculationHelper.isSelfAssessedResponse(result(Some("calculated"))) mustBe false
+ }
+
+ "must return false when resultHeading is None" in {
+ TaxCalculationHelper.isSelfAssessedResponse(result(None)) mustBe false
+ }
+
+ "must return false when resultHeading merely contains 'self-assessed' as a substring" in {
+ TaxCalculationHelper.isSelfAssessedResponse(result(Some("self-assessed result"))) mustBe false
+ }
+
+ "must return false when the case does not match (different capitalisation)" in {
+ TaxCalculationHelper.isSelfAssessedResponse(result(Some("Self-Assessed"))) mustBe false
+ }
+ }
+}
diff --git a/test/views/taxCalculation/TaxCalculationBeforeYouStartViewSpec.scala b/test/views/taxCalculation/TaxCalculationBeforeYouStartViewSpec.scala
new file mode 100644
index 000000000..379583881
--- /dev/null
+++ b/test/views/taxCalculation/TaxCalculationBeforeYouStartViewSpec.scala
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2026 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 views.taxCalculation
+
+import base.SpecBase
+import org.jsoup.Jsoup
+import play.api.i18n.Messages
+import play.api.mvc.AnyContentAsEmpty
+import play.api.test.CSRFTokenHelper.*
+import play.api.test.FakeRequest
+import play.api.test.Helpers.*
+import views.html.taxCalculation.TaxCalculationBeforeYouStartView
+
+class TaxCalculationBeforeYouStartViewSpec extends SpecBase {
+
+ "TaxCalculationBeforeYouStartView" - {
+
+ "must render the page title" in {
+ val application = applicationBuilder().build()
+
+ running(application) {
+ implicit val messagesInstance: Messages = messages(application)
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]]
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString())
+
+ doc.select("title").first().text() must include(messagesInstance("taxCalculation.beforeStart.title"))
+ }
+ }
+
+ "must render the heading and caption" in {
+ val application = applicationBuilder().build()
+
+ running(application) {
+ implicit val messagesInstance: Messages = messages(application)
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]]
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString())
+
+ doc.select("h1").first().text() mustBe messagesInstance("site.beforeYouStart.heading")
+ doc.text() must include(messagesInstance("site.taxCalculation.caption"))
+ }
+ }
+
+ "must render the two introductory paragraphs" in {
+ val application = applicationBuilder().build()
+
+ running(application) {
+ implicit val messagesInstance: Messages = messages(application)
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]]
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString())
+
+ doc.text() must include(messagesInstance("taxCalculation.beforeStart.p1"))
+ doc.text() must include(messagesInstance("taxCalculation.beforeStart.p2"))
+ }
+ }
+
+ "when isLeaseholdAndSelfAssessed is false" - {
+
+ "must render the standard bullet list (bullet1, bullet2, bullet3, bullet4, bullet5) and omit the leaseholdAndSelfAssessed bullets" in {
+ val application = applicationBuilder().build()
+
+ running(application) {
+ implicit val messagesInstance: Messages = messages(application)
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]]
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString())
+ val bullets = doc.select("ul.govuk-list--bullet li").eachText()
+
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet1"))
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet2"))
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet3"))
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet4"))
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet5"))
+
+ bullets must not contain messagesInstance("taxCalculation.beforeStart.leaseholdAndSelfAssessed.bullet")
+ }
+ }
+ }
+
+ "when isLeaseholdAndSelfAssessed is true" - {
+
+ "must swap bullet2 and bullet3 for the leaseholdAndSelfAssessed bullet" in {
+ val application = applicationBuilder().build()
+
+ running(application) {
+ implicit val messagesInstance: Messages = messages(application)
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]]
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = true).toString())
+ val bullets = doc.select("ul.govuk-list--bullet li").eachText()
+
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet1"))
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.leaseholdAndSelfAssessed.bullet"))
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet4"))
+ bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet5"))
+
+ bullets must not contain messagesInstance("taxCalculation.beforeStart.bullet2")
+ }
+ }
+ }
+
+ "must render the continue button inside a form posting to onSubmit" in {
+ val application = applicationBuilder().build()
+
+ running(application) {
+ implicit val messagesInstance: Messages = messages(application)
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]]
+ val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView]
+
+ val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString())
+
+ val form = doc.select("form").first()
+ form.attr("method").toLowerCase mustBe "post"
+ form.attr("action") mustBe controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onSubmit().url
+
+ val button = doc.select("button[type=submit]").first()
+ button.text() mustBe messagesInstance("site.continue")
+ }
+ }
+ }
+}