diff --git a/app/controllers/annualallowance/taxyear/CheckYourAAPeriodAnswersController.scala b/app/controllers/annualallowance/taxyear/CheckYourAAPeriodAnswersController.scala index b90979f02..d96ff0e20 100644 --- a/app/controllers/annualallowance/taxyear/CheckYourAAPeriodAnswersController.scala +++ b/app/controllers/annualallowance/taxyear/CheckYourAAPeriodAnswersController.scala @@ -23,7 +23,7 @@ import play.api.i18n.{I18nSupport, MessagesApi} import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController -import viewmodels.checkAnswers.annualallowance.taxyear.{PayAChargeSummary, PensionSchemeDetailsSummary, PensionSchemeInputAmountsSummary} +import viewmodels.checkAnswers.annualallowance.taxyear._ import viewmodels.govuk.summarylist._ import views.html.CheckYourAnswersView @@ -45,7 +45,10 @@ class CheckYourAAPeriodAnswersController @Inject() ( Seq( PensionSchemeDetailsSummary.row(request.userAnswers, period, index), PensionSchemeInputAmountsSummary.row(request.userAnswers, period, index), - PayAChargeSummary.row(request.userAnswers, period, index) + PayAChargeSummary.row(request.userAnswers, period, index), + WhoPaidAAChargeSummary.row(request.userAnswers, period, index), + HowMuchAAChargeYouPaidSummary.row(request.userAnswers, period, index), + HowMuchAAChargeSchemePaidSummary.row(request.userAnswers, period, index) ) ) diff --git a/app/controllers/annualallowance/taxyear/HowMuchAAChargeSchemePaidController.scala b/app/controllers/annualallowance/taxyear/HowMuchAAChargeSchemePaidController.scala new file mode 100644 index 000000000..b8ed372af --- /dev/null +++ b/app/controllers/annualallowance/taxyear/HowMuchAAChargeSchemePaidController.scala @@ -0,0 +1,71 @@ +/* + * Copyright 2023 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 controllers.annualallowance.taxyear + +import controllers.actions._ +import forms.annualallowance.taxyear.HowMuchAAChargeSchemePaidFormProvider +import models.{Mode, Period, SchemeIndex} +import pages.annualallowance.taxyear.HowMuchAAChargeSchemePaidPage +import play.api.i18n.{I18nSupport, MessagesApi} +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import repositories.SessionRepository +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController +import views.html.annualallowance.taxyear.HowMuchAAChargeSchemePaidView + +import javax.inject.Inject +import scala.concurrent.{ExecutionContext, Future} + +class HowMuchAAChargeSchemePaidController @Inject() ( + override val messagesApi: MessagesApi, + sessionRepository: SessionRepository, + identify: IdentifierAction, + getData: DataRetrievalAction, + requireData: DataRequiredAction, + formProvider: HowMuchAAChargeSchemePaidFormProvider, + val controllerComponents: MessagesControllerComponents, + view: HowMuchAAChargeSchemePaidView +)(implicit ec: ExecutionContext) + extends FrontendBaseController + with I18nSupport { + + val form = formProvider() + + def onPageLoad(mode: Mode, period: Period, schemeIndex: SchemeIndex): Action[AnyContent] = + (identify andThen getData andThen requireData) { implicit request => + val preparedForm = request.userAnswers.get(HowMuchAAChargeSchemePaidPage(period, schemeIndex)) match { + case None => form + case Some(value) => form.fill(value) + } + + Ok(view(preparedForm, mode, period, schemeIndex)) + } + + def onSubmit(mode: Mode, period: Period, schemeIndex: SchemeIndex): Action[AnyContent] = + (identify andThen getData andThen requireData).async { implicit request => + form + .bindFromRequest() + .fold( + formWithErrors => Future.successful(BadRequest(view(formWithErrors, mode, period, schemeIndex))), + value => + for { + updatedAnswers <- + Future.fromTry(request.userAnswers.set(HowMuchAAChargeSchemePaidPage(period, schemeIndex), value)) + _ <- sessionRepository.set(updatedAnswers) + } yield Redirect(HowMuchAAChargeSchemePaidPage(period, schemeIndex).navigate(mode, updatedAnswers)) + ) + } +} diff --git a/app/controllers/annualallowance/taxyear/HowMuchAAChargeYouPaidController.scala b/app/controllers/annualallowance/taxyear/HowMuchAAChargeYouPaidController.scala new file mode 100644 index 000000000..5b5d85f09 --- /dev/null +++ b/app/controllers/annualallowance/taxyear/HowMuchAAChargeYouPaidController.scala @@ -0,0 +1,71 @@ +/* + * Copyright 2023 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 controllers.annualallowance.taxyear + +import controllers.actions._ +import forms.annualallowance.taxyear.HowMuchAAChargeYouPaidFormProvider +import models.{Mode, Period, SchemeIndex} +import pages.annualallowance.taxyear.HowMuchAAChargeYouPaidPage +import play.api.i18n.{I18nSupport, MessagesApi} +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import repositories.SessionRepository +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController +import views.html.annualallowance.taxyear.HowMuchAAChargeYouPaidView + +import javax.inject.Inject +import scala.concurrent.{ExecutionContext, Future} + +class HowMuchAAChargeYouPaidController @Inject() ( + override val messagesApi: MessagesApi, + sessionRepository: SessionRepository, + identify: IdentifierAction, + getData: DataRetrievalAction, + requireData: DataRequiredAction, + formProvider: HowMuchAAChargeYouPaidFormProvider, + val controllerComponents: MessagesControllerComponents, + view: HowMuchAAChargeYouPaidView +)(implicit ec: ExecutionContext) + extends FrontendBaseController + with I18nSupport { + + val form = formProvider() + + def onPageLoad(mode: Mode, period: Period, schemeIndex: SchemeIndex): Action[AnyContent] = + (identify andThen getData andThen requireData) { implicit request => + val preparedForm = request.userAnswers.get(HowMuchAAChargeYouPaidPage(period, schemeIndex)) match { + case None => form + case Some(value) => form.fill(value) + } + + Ok(view(preparedForm, mode, period, schemeIndex)) + } + + def onSubmit(mode: Mode, period: Period, schemeIndex: SchemeIndex): Action[AnyContent] = + (identify andThen getData andThen requireData).async { implicit request => + form + .bindFromRequest() + .fold( + formWithErrors => Future.successful(BadRequest(view(formWithErrors, mode, period, schemeIndex))), + value => + for { + updatedAnswers <- + Future.fromTry(request.userAnswers.set(HowMuchAAChargeYouPaidPage(period, schemeIndex), value)) + _ <- sessionRepository.set(updatedAnswers) + } yield Redirect(HowMuchAAChargeYouPaidPage(period, schemeIndex).navigate(mode, updatedAnswers)) + ) + } +} diff --git a/app/controllers/annualallowance/taxyear/WhoPaidAAChargeController.scala b/app/controllers/annualallowance/taxyear/WhoPaidAAChargeController.scala new file mode 100644 index 000000000..c423987cc --- /dev/null +++ b/app/controllers/annualallowance/taxyear/WhoPaidAAChargeController.scala @@ -0,0 +1,70 @@ +/* + * Copyright 2023 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 controllers.annualallowance.taxyear + +import controllers.actions._ +import forms.annualallowance.taxyear.WhoPaidAAChargeFormProvider +import models.{Mode, Period, SchemeIndex} +import pages.annualallowance.taxyear.WhoPaidAAChargePage +import play.api.i18n.{I18nSupport, MessagesApi} +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import repositories.SessionRepository +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController +import views.html.annualallowance.taxyear.WhoPaidAAChargeView + +import javax.inject.Inject +import scala.concurrent.{ExecutionContext, Future} + +class WhoPaidAAChargeController @Inject() ( + override val messagesApi: MessagesApi, + sessionRepository: SessionRepository, + identify: IdentifierAction, + getData: DataRetrievalAction, + requireData: DataRequiredAction, + formProvider: WhoPaidAAChargeFormProvider, + val controllerComponents: MessagesControllerComponents, + view: WhoPaidAAChargeView +)(implicit ec: ExecutionContext) + extends FrontendBaseController + with I18nSupport { + + val form = formProvider() + + def onPageLoad(mode: Mode, period: Period, schemeIndex: SchemeIndex): Action[AnyContent] = + (identify andThen getData andThen requireData) { implicit request => + val preparedForm = request.userAnswers.get(WhoPaidAAChargePage(period, schemeIndex)) match { + case None => form + case Some(value) => form.fill(value) + } + + Ok(view(preparedForm, mode, period, schemeIndex)) + } + + def onSubmit(mode: Mode, period: Period, schemeIndex: SchemeIndex): Action[AnyContent] = + (identify andThen getData andThen requireData).async { implicit request => + form + .bindFromRequest() + .fold( + formWithErrors => Future.successful(BadRequest(view(formWithErrors, mode, period, schemeIndex))), + value => + for { + updatedAnswers <- Future.fromTry(request.userAnswers.set(WhoPaidAAChargePage(period, schemeIndex), value)) + _ <- sessionRepository.set(updatedAnswers) + } yield Redirect(WhoPaidAAChargePage(period, schemeIndex).navigate(mode, updatedAnswers)) + ) + } +} diff --git a/app/forms/annualallowance/taxyear/HowMuchAAChargeSchemePaidFormProvider.scala b/app/forms/annualallowance/taxyear/HowMuchAAChargeSchemePaidFormProvider.scala new file mode 100644 index 000000000..cccfecf2c --- /dev/null +++ b/app/forms/annualallowance/taxyear/HowMuchAAChargeSchemePaidFormProvider.scala @@ -0,0 +1,35 @@ +/* + * Copyright 2023 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 forms.annualallowance.taxyear + +import forms.mappings.Mappings +import play.api.data.Form + +import javax.inject.Inject + +class HowMuchAAChargeSchemePaidFormProvider @Inject() extends Mappings { + + def apply(): Form[Int] = + Form( + "value" -> int( + "howMuchAAChargeSchemePaid.error.required", + "howMuchAAChargeSchemePaid.error.wholeNumber", + "howMuchAAChargeSchemePaid.error.nonNumeric" + ) + .verifying(inRange(0, Int.MaxValue, "howMuchAAChargeSchemePaid.error.outOfRange")) + ) +} diff --git a/app/forms/annualallowance/taxyear/HowMuchAAChargeYouPaidFormProvider.scala b/app/forms/annualallowance/taxyear/HowMuchAAChargeYouPaidFormProvider.scala new file mode 100644 index 000000000..dd55725b0 --- /dev/null +++ b/app/forms/annualallowance/taxyear/HowMuchAAChargeYouPaidFormProvider.scala @@ -0,0 +1,35 @@ +/* + * Copyright 2023 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 forms.annualallowance.taxyear + +import forms.mappings.Mappings +import play.api.data.Form + +import javax.inject.Inject + +class HowMuchAAChargeYouPaidFormProvider @Inject() extends Mappings { + + def apply(): Form[Int] = + Form( + "value" -> int( + "howMuchAAChargeYouPaid.error.required", + "howMuchAAChargeYouPaid.error.wholeNumber", + "howMuchAAChargeYouPaid.error.nonNumeric" + ) + .verifying(inRange(0, Int.MaxValue, "howMuchAAChargeYouPaid.error.outOfRange")) + ) +} diff --git a/app/forms/annualallowance/taxyear/WhoPaidAAChargeFormProvider.scala b/app/forms/annualallowance/taxyear/WhoPaidAAChargeFormProvider.scala new file mode 100644 index 000000000..ede7f0bc7 --- /dev/null +++ b/app/forms/annualallowance/taxyear/WhoPaidAAChargeFormProvider.scala @@ -0,0 +1,31 @@ +/* + * Copyright 2023 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 forms.annualallowance.taxyear + +import forms.mappings.Mappings +import models.WhoPaidAACharge +import play.api.data.Form + +import javax.inject.Inject + +class WhoPaidAAChargeFormProvider @Inject() extends Mappings { + + def apply(): Form[WhoPaidAACharge] = + Form( + "value" -> enumerable[WhoPaidAACharge]("whoPaidAACharge.error.required") + ) +} diff --git a/app/models/WhoPaidAACharge.scala b/app/models/WhoPaidAACharge.scala new file mode 100644 index 000000000..6ce84f031 --- /dev/null +++ b/app/models/WhoPaidAACharge.scala @@ -0,0 +1,47 @@ +/* + * Copyright 2023 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 models + +import play.api.i18n.Messages +import uk.gov.hmrc.govukfrontend.views.Aliases.Text +import uk.gov.hmrc.govukfrontend.views.viewmodels.radios.RadioItem + +sealed trait WhoPaidAACharge + +object WhoPaidAACharge extends Enumerable.Implicits { + + case object You extends WithName("you") with WhoPaidAACharge + case object Scheme extends WithName("scheme") with WhoPaidAACharge + case object Both extends WithName("both") with WhoPaidAACharge + + val values: Seq[WhoPaidAACharge] = Seq( + You, + Scheme, + Both + ) + + def options(implicit messages: Messages): Seq[RadioItem] = values.zipWithIndex.map { case (value, index) => + RadioItem( + content = Text(messages(s"whoPaidAACharge.${value.toString}")), + value = Some(value.toString), + id = Some(s"value_$index") + ) + } + + implicit val enumerable: Enumerable[WhoPaidAACharge] = + Enumerable(values.map(v => v.toString -> v): _*) +} diff --git a/app/pages/annualallowance/taxyear/HowMuchAAChargeSchemePaidPage.scala b/app/pages/annualallowance/taxyear/HowMuchAAChargeSchemePaidPage.scala new file mode 100644 index 000000000..7948e1ac7 --- /dev/null +++ b/app/pages/annualallowance/taxyear/HowMuchAAChargeSchemePaidPage.scala @@ -0,0 +1,44 @@ +/* + * Copyright 2023 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.annualallowance.taxyear + +import controllers.routes +import models.{Period, SchemeIndex, UserAnswers} +import pages.QuestionPage +import play.api.libs.json.JsPath +import play.api.mvc.Call + +case class HowMuchAAChargeSchemePaidPage(period: Period, schemeIndex: SchemeIndex) extends QuestionPage[Int] { + + override def path: JsPath = JsPath \ "aa" \ "years" \ period.toString \ "schemes" \ schemeIndex.toString \ toString + + override def toString: String = "howMuchAAChargeSchemePaid" + + override protected def navigateInNormalMode(answers: UserAnswers): Call = addAnotherMaybe(answers) + + def addAnotherMaybe(answers: UserAnswers): Call = answers.get(MemberMoreThanOnePensionPage(period)) match { + case Some(true) => + controllers.annualallowance.taxyear.routes.AddAnotherSchemeController.onPageLoad(period, schemeIndex) + case Some(false) => + controllers.annualallowance.taxyear.routes.CheckYourAAPeriodAnswersController + .onPageLoad(period) // TODO until onward pages are added + case None => routes.JourneyRecoveryController.onPageLoad(None) + } + + override protected def navigateInCheckMode(answers: UserAnswers): Call = + controllers.annualallowance.taxyear.routes.CheckYourAAPeriodAnswersController.onPageLoad(period) +} diff --git a/app/pages/annualallowance/taxyear/HowMuchAAChargeYouPaidPage.scala b/app/pages/annualallowance/taxyear/HowMuchAAChargeYouPaidPage.scala new file mode 100644 index 000000000..2204cc52b --- /dev/null +++ b/app/pages/annualallowance/taxyear/HowMuchAAChargeYouPaidPage.scala @@ -0,0 +1,62 @@ +/* + * Copyright 2023 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.annualallowance.taxyear + +import controllers.routes +import models.WhoPaidAACharge.{Both, You} +import models.{CheckMode, NormalMode, Period, SchemeIndex, UserAnswers} +import pages.QuestionPage +import play.api.libs.json.JsPath +import play.api.mvc.Call + +case class HowMuchAAChargeYouPaidPage(period: Period, schemeIndex: SchemeIndex) extends QuestionPage[Int] { + + override def path: JsPath = JsPath \ "aa" \ "years" \ period.toString \ "schemes" \ schemeIndex.toString \ toString + + override def toString: String = "howMuchAAChargeYouPaid" + + override protected def navigateInNormalMode(answers: UserAnswers): Call = + answers.get(WhoPaidAAChargePage(period, schemeIndex)) match { + case Some(You) => addAnotherMaybe(answers) + case Some(Both) => + controllers.annualallowance.taxyear.routes.HowMuchAAChargeSchemePaidController + .onPageLoad(NormalMode, period, schemeIndex) + + case _ => routes.JourneyRecoveryController.onPageLoad(None) + } + + def addAnotherMaybe(answers: UserAnswers): Call = answers.get(MemberMoreThanOnePensionPage(period)) match { + case Some(true) => + controllers.annualallowance.taxyear.routes.AddAnotherSchemeController.onPageLoad(period, schemeIndex) + case Some(false) => + controllers.annualallowance.taxyear.routes.CheckYourAAPeriodAnswersController + .onPageLoad(period) // TODO until onward pages are added + case None => routes.JourneyRecoveryController.onPageLoad(None) + } + + override protected def navigateInCheckMode(answers: UserAnswers): Call = + answers.get(WhoPaidAAChargePage(period, schemeIndex)) match { + case Some(You) => + controllers.annualallowance.taxyear.routes.CheckYourAAPeriodAnswersController + .onPageLoad(period) + case Some(Both) => + controllers.annualallowance.taxyear.routes.HowMuchAAChargeSchemePaidController + .onPageLoad(CheckMode, period, schemeIndex) + + case _ => routes.JourneyRecoveryController.onPageLoad(None) + } +} diff --git a/app/pages/annualallowance/taxyear/PayAChargePage.scala b/app/pages/annualallowance/taxyear/PayAChargePage.scala index 625936398..d55808396 100644 --- a/app/pages/annualallowance/taxyear/PayAChargePage.scala +++ b/app/pages/annualallowance/taxyear/PayAChargePage.scala @@ -17,11 +17,13 @@ package pages.annualallowance.taxyear import controllers.routes -import models.{Period, SchemeIndex, UserAnswers} +import models.{CheckMode, Mode, NormalMode, Period, SchemeIndex, UserAnswers} import pages.QuestionPage import play.api.libs.json.JsPath import play.api.mvc.Call +import scala.util.Try + case class PayAChargePage(period: Period, schemeIndex: SchemeIndex) extends QuestionPage[Boolean] { override def path: JsPath = JsPath \ "aa" \ "years" \ period.toString \ "schemes" \ schemeIndex.toString \ toString @@ -30,11 +32,42 @@ case class PayAChargePage(period: Period, schemeIndex: SchemeIndex) extends Ques override protected def navigateInNormalMode(answers: UserAnswers): Call = answers.get(PayAChargePage(period, schemeIndex)) match { - case Some(true) => addAnotherMaybe(answers) // TODO until who paid pages are added + case Some(true) => navigateToWhoPaidOrHowMuchSchemePaid(NormalMode) case Some(false) => addAnotherMaybe(answers) case _ => routes.JourneyRecoveryController.onPageLoad(None) } + override protected def navigateInCheckMode(answers: UserAnswers): Call = + answers.get(PayAChargePage(period, schemeIndex)) match { + case Some(true) => navigateToWhoPaidOrHowMuchSchemePaid(CheckMode) + case Some(false) => + controllers.annualallowance.taxyear.routes.CheckYourAAPeriodAnswersController.onPageLoad(period) + case _ => routes.JourneyRecoveryController.onPageLoad(None) + } + + override def cleanup(value: Option[Boolean], userAnswers: UserAnswers): Try[UserAnswers] = + value + .map { + case false => + for { + updated1 <- userAnswers.remove(WhoPaidAAChargePage(period, schemeIndex)) + updated2 <- updated1.remove(HowMuchAAChargeYouPaidPage(period, schemeIndex)) + updated3 <- updated2.remove(HowMuchAAChargeSchemePaidPage(period, schemeIndex)) + } yield updated3 + case true => super.cleanup(value, userAnswers) + } + .getOrElse(super.cleanup(value, userAnswers)) + + private def navigateToWhoPaidOrHowMuchSchemePaid(mode: Mode) = + if (isFirstSchemeInPeriod) + controllers.annualallowance.taxyear.routes.WhoPaidAAChargeController.onPageLoad(mode, period, schemeIndex) + else + controllers.annualallowance.taxyear.routes.HowMuchAAChargeSchemePaidController + .onPageLoad(mode, period, schemeIndex) + + private def isFirstSchemeInPeriod = + schemeIndex.value == 0 + def addAnotherMaybe(answers: UserAnswers): Call = answers.get(MemberMoreThanOnePensionPage(period)) match { case Some(true) => controllers.annualallowance.taxyear.routes.AddAnotherSchemeController.onPageLoad(period, schemeIndex) @@ -44,6 +77,4 @@ case class PayAChargePage(period: Period, schemeIndex: SchemeIndex) extends Ques case None => routes.JourneyRecoveryController.onPageLoad(None) } - override protected def navigateInCheckMode(answers: UserAnswers): Call = - controllers.annualallowance.taxyear.routes.CheckYourAAPeriodAnswersController.onPageLoad(period) } diff --git a/app/pages/annualallowance/taxyear/WhoPaidAAChargePage.scala b/app/pages/annualallowance/taxyear/WhoPaidAAChargePage.scala new file mode 100644 index 000000000..625dac5dd --- /dev/null +++ b/app/pages/annualallowance/taxyear/WhoPaidAAChargePage.scala @@ -0,0 +1,62 @@ +/* + * Copyright 2023 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.annualallowance.taxyear + +import controllers.routes +import models.WhoPaidAACharge.{Both, Scheme, You} +import models.{CheckMode, Mode, NormalMode, Period, SchemeIndex, UserAnswers, WhoPaidAACharge} +import pages.QuestionPage +import play.api.libs.json.JsPath +import play.api.mvc.Call + +import scala.util.Try + +case class WhoPaidAAChargePage(period: Period, schemeIndex: SchemeIndex) extends QuestionPage[WhoPaidAACharge] { + + override def path: JsPath = JsPath \ "aa" \ "years" \ period.toString \ "schemes" \ schemeIndex.toString \ toString + + override def toString: String = "whoPaidAACharge" + + override protected def navigateInNormalMode(answers: UserAnswers): Call = navigateInEitherMode(answers, NormalMode) + + override protected def navigateInCheckMode(answers: UserAnswers): Call = navigateInEitherMode(answers, CheckMode) + + private def navigateInEitherMode(answers: UserAnswers, mode: Mode): Call = + answers.get(WhoPaidAAChargePage(period, schemeIndex)) match { + case Some(You) => + controllers.annualallowance.taxyear.routes.HowMuchAAChargeYouPaidController + .onPageLoad(mode, period, schemeIndex) + case Some(Both) => + controllers.annualallowance.taxyear.routes.HowMuchAAChargeYouPaidController + .onPageLoad(mode, period, schemeIndex) + case Some(Scheme) => + controllers.annualallowance.taxyear.routes.HowMuchAAChargeSchemePaidController + .onPageLoad(mode, period, schemeIndex) + + case _ => routes.JourneyRecoveryController.onPageLoad(None) + } + + override def cleanup(value: Option[WhoPaidAACharge], userAnswers: UserAnswers): Try[UserAnswers] = + value + .map { case _ => + for { + updated1 <- userAnswers.remove(HowMuchAAChargeYouPaidPage(period, schemeIndex)) + updated2 <- updated1.remove(HowMuchAAChargeSchemePaidPage(period, schemeIndex)) + } yield updated2 + } + .getOrElse(super.cleanup(value, userAnswers)) +} diff --git a/app/viewmodels/checkAnswers/annualallowance/taxyear/HowMuchAAChargeSchemePaidSummary.scala b/app/viewmodels/checkAnswers/annualallowance/taxyear/HowMuchAAChargeSchemePaidSummary.scala new file mode 100644 index 000000000..4bf1b33fd --- /dev/null +++ b/app/viewmodels/checkAnswers/annualallowance/taxyear/HowMuchAAChargeSchemePaidSummary.scala @@ -0,0 +1,46 @@ +/* + * Copyright 2023 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 viewmodels.checkAnswers.annualallowance.taxyear + +import models.{CheckMode, Period, SchemeIndex, UserAnswers} +import pages.annualallowance.taxyear.HowMuchAAChargeSchemePaidPage +import play.api.i18n.Messages +import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow +import viewmodels.govuk.summarylist._ +import viewmodels.implicits._ + +object HowMuchAAChargeSchemePaidSummary { + + def row(answers: UserAnswers, period: Period, schemeIndex: SchemeIndex)(implicit + messages: Messages + ): Option[SummaryListRow] = + answers.get(HowMuchAAChargeSchemePaidPage(period, schemeIndex)).map { answer => + SummaryListRowViewModel( + key = "howMuchAAChargeSchemePaid.checkYourAnswersLabel", + value = ValueViewModel(answer.toString), + actions = Seq( + ActionItemViewModel( + "site.change", + controllers.annualallowance.taxyear.routes.HowMuchAAChargeSchemePaidController + .onPageLoad(CheckMode, period, schemeIndex) + .url + ) + .withVisuallyHiddenText(messages("howMuchAAChargeSchemePaid.change.hidden")) + ) + ) + } +} diff --git a/app/viewmodels/checkAnswers/annualallowance/taxyear/HowMuchAAChargeYouPaidSummary.scala b/app/viewmodels/checkAnswers/annualallowance/taxyear/HowMuchAAChargeYouPaidSummary.scala new file mode 100644 index 000000000..42dd84a4c --- /dev/null +++ b/app/viewmodels/checkAnswers/annualallowance/taxyear/HowMuchAAChargeYouPaidSummary.scala @@ -0,0 +1,46 @@ +/* + * Copyright 2023 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 viewmodels.checkAnswers.annualallowance.taxyear + +import models.{CheckMode, Period, SchemeIndex, UserAnswers} +import pages.annualallowance.taxyear.HowMuchAAChargeYouPaidPage +import play.api.i18n.Messages +import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow +import viewmodels.govuk.summarylist._ +import viewmodels.implicits._ + +object HowMuchAAChargeYouPaidSummary { + + def row(answers: UserAnswers, period: Period, schemeIndex: SchemeIndex)(implicit + messages: Messages + ): Option[SummaryListRow] = + answers.get(HowMuchAAChargeYouPaidPage(period, schemeIndex)).map { answer => + SummaryListRowViewModel( + key = "howMuchAAChargeYouPaid.checkYourAnswersLabel", + value = ValueViewModel(answer.toString), + actions = Seq( + ActionItemViewModel( + "site.change", + controllers.annualallowance.taxyear.routes.HowMuchAAChargeYouPaidController + .onPageLoad(CheckMode, period, schemeIndex) + .url + ) + .withVisuallyHiddenText(messages("howMuchAAChargeYouPaid.change.hidden")) + ) + ) + } +} diff --git a/app/viewmodels/checkAnswers/annualallowance/taxyear/WhoPaidAAChargeSummary.scala b/app/viewmodels/checkAnswers/annualallowance/taxyear/WhoPaidAAChargeSummary.scala new file mode 100644 index 000000000..5ee3cd33e --- /dev/null +++ b/app/viewmodels/checkAnswers/annualallowance/taxyear/WhoPaidAAChargeSummary.scala @@ -0,0 +1,54 @@ +/* + * Copyright 2023 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 viewmodels.checkAnswers.annualallowance.taxyear + +import models.{CheckMode, Period, SchemeIndex, UserAnswers} +import pages.annualallowance.taxyear.WhoPaidAAChargePage +import play.api.i18n.Messages +import play.twirl.api.HtmlFormat +import uk.gov.hmrc.govukfrontend.views.viewmodels.content.HtmlContent +import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow +import viewmodels.govuk.summarylist._ +import viewmodels.implicits._ + +object WhoPaidAAChargeSummary { + + def row(answers: UserAnswers, period: Period, schemeIndex: SchemeIndex)(implicit + messages: Messages + ): Option[SummaryListRow] = + answers.get(WhoPaidAAChargePage(period, schemeIndex)).map { answer => + val value = ValueViewModel( + HtmlContent( + HtmlFormat.escape(messages(s"whoPaidAACharge.$answer")) + ) + ) + + SummaryListRowViewModel( + key = "whoPaidAACharge.checkYourAnswersLabel", + value = value, + actions = Seq( + ActionItemViewModel( + "site.change", + controllers.annualallowance.taxyear.routes.WhoPaidAAChargeController + .onPageLoad(CheckMode, period, schemeIndex) + .url + ) + .withVisuallyHiddenText(messages("whoPaidAACharge.change.hidden")) + ) + ) + } +} diff --git a/app/views/annualallowance/taxyear/HowMuchAAChargeSchemePaidView.scala.html b/app/views/annualallowance/taxyear/HowMuchAAChargeSchemePaidView.scala.html new file mode 100644 index 000000000..c0d68b041 --- /dev/null +++ b/app/views/annualallowance/taxyear/HowMuchAAChargeSchemePaidView.scala.html @@ -0,0 +1,50 @@ +@* + * Copyright 2023 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 viewmodels.InputWidth._ + +@this( + layout: templates.Layout, + formHelper: FormWithCSRF, + govukErrorSummary: GovukErrorSummary, + govukInput: GovukInput, + govukButton: GovukButton +) + +@(form: Form[_], mode: Mode, period: Period, schemeIndex: SchemeIndex)(implicit request: Request[_], messages: Messages) + +@layout(pageTitle = title(form, messages("howMuchAAChargeSchemePaid.title"))) { + + @formHelper(action = controllers.annualallowance.taxyear.routes.HowMuchAAChargeSchemePaidController.onSubmit(mode,period, schemeIndex), 'autoComplete -> "off") { + + @if(form.errors.nonEmpty) { + @govukErrorSummary(ErrorSummaryViewModel(form)) + } + + @govukInput( + InputViewModel( + field = form("value"), + label = LabelViewModel(messages("howMuchAAChargeSchemePaid.heading", period, schemeIndex)).asPageHeading() + ) + .asNumeric() + .withWidth(Fixed10) + ) + + @govukButton( + ButtonViewModel(messages("site.continue")) + ) + } +} diff --git a/app/views/annualallowance/taxyear/HowMuchAAChargeYouPaidView.scala.html b/app/views/annualallowance/taxyear/HowMuchAAChargeYouPaidView.scala.html new file mode 100644 index 000000000..9badd6313 --- /dev/null +++ b/app/views/annualallowance/taxyear/HowMuchAAChargeYouPaidView.scala.html @@ -0,0 +1,50 @@ +@* + * Copyright 2023 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 viewmodels.InputWidth._ + +@this( + layout: templates.Layout, + formHelper: FormWithCSRF, + govukErrorSummary: GovukErrorSummary, + govukInput: GovukInput, + govukButton: GovukButton +) + +@(form: Form[_], mode: Mode, period: Period, schemeIndex: SchemeIndex)(implicit request: Request[_], messages: Messages) + +@layout(pageTitle = title(form, messages("howMuchAAChargeYouPaid.title"))) { + + @formHelper(action = controllers.annualallowance.taxyear.routes.HowMuchAAChargeYouPaidController.onSubmit(mode,period, schemeIndex), 'autoComplete -> "off") { + + @if(form.errors.nonEmpty) { + @govukErrorSummary(ErrorSummaryViewModel(form)) + } + + @govukInput( + InputViewModel( + field = form("value"), + label = LabelViewModel(messages("howMuchAAChargeYouPaid.heading", period, schemeIndex)).asPageHeading() + ) + .asNumeric() + .withWidth(Fixed10) + ) + + @govukButton( + ButtonViewModel(messages("site.continue")) + ) + } +} diff --git a/app/views/annualallowance/taxyear/WhoPaidAAChargeView.scala.html b/app/views/annualallowance/taxyear/WhoPaidAAChargeView.scala.html new file mode 100644 index 000000000..ae8de6550 --- /dev/null +++ b/app/views/annualallowance/taxyear/WhoPaidAAChargeView.scala.html @@ -0,0 +1,47 @@ +@* + * Copyright 2023 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. + *@ + +@this( + layout: templates.Layout, + formHelper: FormWithCSRF, + govukErrorSummary: GovukErrorSummary, + govukRadios: GovukRadios, + govukButton: GovukButton +) + +@(form: Form[_], mode: Mode, period: Period, schemeIndex: SchemeIndex)(implicit request: Request[_], messages: Messages) + +@layout(pageTitle = title(form, messages("whoPaidAACharge.title"))) { + + @formHelper(action = controllers.annualallowance.taxyear.routes.WhoPaidAAChargeController.onSubmit(mode,period, schemeIndex), 'autoComplete -> "off") { + + @if(form.errors.nonEmpty) { + @govukErrorSummary(ErrorSummaryViewModel(form, errorLinkOverrides = Map("value" -> "value_0"))) + } + + @govukRadios( + RadiosViewModel( + field = form("value"), + legend = LegendViewModel(messages("whoPaidAACharge.heading", period, schemeIndex)).asPageHeading(), + items = WhoPaidAACharge.options + ) + ) + + @govukButton( + ButtonViewModel(messages("site.continue")) + ) + } +} diff --git a/conf/app.routes b/conf/app.routes index 7503f1249..944efff5e 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -144,20 +144,20 @@ POST /protection-changed-new-type con GET /change-protection-changed-new-type controllers.lifetimeallowance.WhatNewProtectionTypeEnhancementController.onPageLoad(mode: Mode = CheckMode) POST /change-protection-changed-new-type controllers.lifetimeallowance.WhatNewProtectionTypeEnhancementController.onSubmit(mode: Mode = CheckMode) -GET /lta-charge-2015-2023 controllers.lifetimeallowance.LifetimeAllowanceChargeController.onPageLoad(mode: Mode = NormalMode) -POST /lta-charge-2015-2023 controllers.lifetimeallowance.LifetimeAllowanceChargeController.onSubmit(mode: Mode = NormalMode) -GET /change-lta-charge-2015-2023 controllers.lifetimeallowance.LifetimeAllowanceChargeController.onPageLoad(mode: Mode = CheckMode) -POST /change-lta-charge-2015-2023 controllers.lifetimeallowance.LifetimeAllowanceChargeController.onSubmit(mode: Mode = CheckMode) +GET /lta-charge-2015-2023 controllers.lifetimeallowance.LifetimeAllowanceChargeController.onPageLoad(mode: Mode = NormalMode) +POST /lta-charge-2015-2023 controllers.lifetimeallowance.LifetimeAllowanceChargeController.onSubmit(mode: Mode = NormalMode) +GET /change-lta-charge-2015-2023 controllers.lifetimeallowance.LifetimeAllowanceChargeController.onPageLoad(mode: Mode = CheckMode) +POST /change-lta-charge-2015-2023 controllers.lifetimeallowance.LifetimeAllowanceChargeController.onSubmit(mode: Mode = CheckMode) -GET /how-excess-was-paid controllers.lifetimeallowance.ExcessLifetimeAllowancePaidController.onPageLoad(mode: Mode = NormalMode) -POST /how-excess-was-paid controllers.lifetimeallowance.ExcessLifetimeAllowancePaidController.onSubmit(mode: Mode = NormalMode) -GET /change-how-excess-was-paid controllers.lifetimeallowance.ExcessLifetimeAllowancePaidController.onPageLoad(mode: Mode = CheckMode) -POST /change-how-excess-was-paid controllers.lifetimeallowance.ExcessLifetimeAllowancePaidController.onSubmit(mode: Mode = CheckMode) +GET /how-excess-was-paid controllers.lifetimeallowance.ExcessLifetimeAllowancePaidController.onPageLoad(mode: Mode = NormalMode) +POST /how-excess-was-paid controllers.lifetimeallowance.ExcessLifetimeAllowancePaidController.onSubmit(mode: Mode = NormalMode) +GET /change-how-excess-was-paid controllers.lifetimeallowance.ExcessLifetimeAllowancePaidController.onPageLoad(mode: Mode = CheckMode) +POST /change-how-excess-was-paid controllers.lifetimeallowance.ExcessLifetimeAllowancePaidController.onSubmit(mode: Mode = CheckMode) -GET /how-much-lta-charge controllers.lifetimeallowance.LifetimeAllowanceChargeAmountController.onPageLoad(mode: Mode = NormalMode) -POST /how-much-lta-charge controllers.lifetimeallowance.LifetimeAllowanceChargeAmountController.onSubmit(mode: Mode = NormalMode) -GET /change-how-much-lta-charge controllers.lifetimeallowance.LifetimeAllowanceChargeAmountController.onPageLoad(mode: Mode = CheckMode) -POST /change-how-much-lta-charge controllers.lifetimeallowance.LifetimeAllowanceChargeAmountController.onSubmit(mode: Mode = CheckMode) +GET /how-much-lta-charge controllers.lifetimeallowance.LifetimeAllowanceChargeAmountController.onPageLoad(mode: Mode = NormalMode) +POST /how-much-lta-charge controllers.lifetimeallowance.LifetimeAllowanceChargeAmountController.onSubmit(mode: Mode = NormalMode) +GET /change-how-much-lta-charge controllers.lifetimeallowance.LifetimeAllowanceChargeAmountController.onPageLoad(mode: Mode = CheckMode) +POST /change-how-much-lta-charge controllers.lifetimeallowance.LifetimeAllowanceChargeAmountController.onSubmit(mode: Mode = CheckMode) GET /task-list controllers.TaskListController.onPageLoad POST /task-list controllers.TaskListController.onSubmit @@ -190,3 +190,19 @@ GET /which-scheme/:period/:schemeIndex con POST /which-scheme/:period/:schemeIndex controllers.annualallowance.taxyear.WhichSchemeController.onSubmit(mode: Mode = NormalMode, period:Period, schemeIndex:SchemeIndex) GET /change-which-scheme/:period/:schemeIndex controllers.annualallowance.taxyear.WhichSchemeController.onPageLoad(mode: Mode = CheckMode, period:Period, schemeIndex:SchemeIndex) POST /change-which-scheme/:period/:schemeIndex controllers.annualallowance.taxyear.WhichSchemeController.onSubmit(mode: Mode = CheckMode, period:Period, schemeIndex:SchemeIndex) + +GET /who-paid-annual-allowance-charge/:period/:schemeIndex controllers.annualallowance.taxyear.WhoPaidAAChargeController.onPageLoad(mode: Mode = NormalMode, period:Period, schemeIndex:SchemeIndex) +POST /who-paid-annual-allowance-charge/:period/:schemeIndex controllers.annualallowance.taxyear.WhoPaidAAChargeController.onSubmit(mode: Mode = NormalMode, period:Period, schemeIndex:SchemeIndex) +GET /change-who-paid-annual-allowance-charge/:period/:schemeIndex controllers.annualallowance.taxyear.WhoPaidAAChargeController.onPageLoad(mode: Mode = CheckMode, period:Period, schemeIndex:SchemeIndex) +POST /change-who-paid-annual-allowance-charge/:period/:schemeIndex controllers.annualallowance.taxyear.WhoPaidAAChargeController.onSubmit(mode: Mode = CheckMode, period:Period, schemeIndex:SchemeIndex) + +GET /how-much-pension-pay-charge/:period/:schemeIndex controllers.annualallowance.taxyear.HowMuchAAChargeSchemePaidController.onPageLoad(mode: Mode = NormalMode, period:Period, schemeIndex:SchemeIndex) +POST /how-much-pension-pay-charge/:period/:schemeIndex controllers.annualallowance.taxyear.HowMuchAAChargeSchemePaidController.onSubmit(mode: Mode = NormalMode, period:Period, schemeIndex:SchemeIndex) +GET /change-how-much-pension-pay-charge/:period/:schemeIndex controllers.annualallowance.taxyear.HowMuchAAChargeSchemePaidController.onPageLoad(mode: Mode = CheckMode, period:Period, schemeIndex:SchemeIndex) +POST /change-how-much-pension-pay-charge/:period/:schemeIndex controllers.annualallowance.taxyear.HowMuchAAChargeSchemePaidController.onSubmit(mode: Mode = CheckMode, period:Period, schemeIndex:SchemeIndex) + +GET /how-much-you-pay-charge/:period/:schemeIndex controllers.annualallowance.taxyear.HowMuchAAChargeYouPaidController.onPageLoad(mode: Mode = NormalMode, period:Period, schemeIndex:SchemeIndex) +POST /how-much-you-pay-charge/:period/:schemeIndex controllers.annualallowance.taxyear.HowMuchAAChargeYouPaidController.onSubmit(mode: Mode = NormalMode, period:Period, schemeIndex:SchemeIndex) +GET /change-how-much-you-pay-charge/:period/:schemeIndex controllers.annualallowance.taxyear.HowMuchAAChargeYouPaidController.onPageLoad(mode: Mode = CheckMode, period:Period, schemeIndex:SchemeIndex) +POST /change-how-much-you-pay-charge/:period/:schemeIndex controllers.annualallowance.taxyear.HowMuchAAChargeYouPaidController.onSubmit(mode: Mode = CheckMode, period:Period, schemeIndex:SchemeIndex) + diff --git a/conf/messages.cy b/conf/messages.cy index a4eadcd25..48f6b925f 100644 --- a/conf/messages.cy +++ b/conf/messages.cy @@ -434,7 +434,7 @@ pensionSchemeInputAmounts.title = cy: Pension scheme that has paid pensionSchemeInputAmounts.heading = cy: Pension scheme [Name for SchemeIndex {1}] that has paid {0} pensionSchemeInputAmounts.originalPIA = cy: What is your pension input amount? pensionSchemeInputAmounts.revisedPIA = cy: What is your revised pension input amount? -pensionSchemeInputAmounts.checkYourAnswersLabel = cy: Pensions scheme [Name for SchemeIndex {1}] that has paid {0} +pensionSchemeInputAmounts.checkYourAnswersLabel = cy: Pension scheme that has paid pensionSchemeInputAmounts.error.originalPIA.required = cy: Enter originalPIA pensionSchemeInputAmounts.error.revisedPIA.required = cy: Enter revisedPIA pensionSchemeInputAmounts.error.originalPIA.length = cy: originalPIA must be 100 characters or less @@ -447,4 +447,31 @@ whichScheme.heading = cy: Select which scheme paid the charge or add new. {0} {1 whichScheme.New = cy: New whichScheme.checkYourAnswersLabel = cy: Select which scheme paid the charge or add new. whichScheme.error.required = cy: Select which scheme paid the charge or add new. -whichScheme.change.hidden = cy: Select which scheme paid the charge or add new. \ No newline at end of file +whichScheme.change.hidden = cy: Select which scheme paid the charge or add new. + +whoPaidAACharge.title = cy: Who paid the annual allowance charge? +whoPaidAACharge.heading = cy: Who paid the annual allowance charge for? {0} {1} +whoPaidAACharge.you = cy: You +whoPaidAACharge.scheme = cy: Pension Scheme +whoPaidAACharge.both = cy: Both +whoPaidAACharge.checkYourAnswersLabel = cy: Who paid the annual allowance charge? +whoPaidAACharge.error.required = cy: Enter who paid the annual allowance charge +whoPaidAACharge.change.hidden = cy: Who paid the annual allowance charge? + +howMuchAAChargeSchemePaid.title = cy: How much of the charge did your scheme pay? +howMuchAAChargeSchemePaid.heading = cy: How much of the charge did your scheme pay? {0} {1} +howMuchAAChargeSchemePaid.checkYourAnswersLabel = cy: How much of the charge did your scheme pay? +howMuchAAChargeSchemePaid.error.nonNumeric = cy: Enter your how much your scheme paid using numbers +howMuchAAChargeSchemePaid.error.required = cy: Enter your how much your scheme paid +howMuchAAChargeSchemePaid.error.wholeNumber = cy: Enter your how much your scheme paid using whole numbers +howMuchAAChargeSchemePaid.error.outOfRange = cy: How much your scheme paid must be between {0} and {1} +howMuchAAChargeSchemePaid.change.hidden = cy: How much of the charge did your scheme pay? + +howMuchAAChargeYouPaid.title = cy: How much of the charge did you pay? +howMuchAAChargeYouPaid.heading = cy: How much of the charge did you pay? {0} {1} +howMuchAAChargeYouPaid.checkYourAnswersLabel = cy: How much of the charge did you pay? +howMuchAAChargeYouPaid.error.nonNumeric = cy: Enter your how much you paid using numbers +howMuchAAChargeYouPaid.error.required = cy: Enter your how much you paid +howMuchAAChargeYouPaid.error.wholeNumber = cy: Enter your how much you paid using whole numbers +howMuchAAChargeYouPaid.error.outOfRange = cy: How much you paid must be between {0} and {1} +howMuchAAChargeYouPaid.change.hidden = cy: How much of the charge did you pay? \ No newline at end of file diff --git a/conf/messages.en b/conf/messages.en index a5af7b9d7..e7b6911eb 100644 --- a/conf/messages.en +++ b/conf/messages.en @@ -434,7 +434,7 @@ pensionSchemeInputAmounts.title = Pension scheme that has paid pensionSchemeInputAmounts.heading = Pension scheme [Name for SchemeIndex {1}] that has paid {0} pensionSchemeInputAmounts.originalPIA = What is your pension input amount? pensionSchemeInputAmounts.revisedPIA = What is your revised pension input amount? -pensionSchemeInputAmounts.checkYourAnswersLabel = Pensions scheme that has paid +pensionSchemeInputAmounts.checkYourAnswersLabel = Pension scheme that has paid pensionSchemeInputAmounts.error.originalPIA.required = Enter originalPIA pensionSchemeInputAmounts.error.revisedPIA.required = Enter revisedPIA pensionSchemeInputAmounts.error.originalPIA.length = originalPIA must be 100 characters or less @@ -447,4 +447,31 @@ whichScheme.heading = Select which scheme paid the charge or add new. {0} {1} whichScheme.New = New whichScheme.checkYourAnswersLabel = Select which scheme paid the charge or add new. whichScheme.error.required = Select which scheme paid the charge or add new. -whichScheme.change.hidden = Select which scheme paid the charge or add new. \ No newline at end of file +whichScheme.change.hidden = Select which scheme paid the charge or add new. + +whoPaidAACharge.title = Who paid the annual allowance charge? +whoPaidAACharge.heading = Who paid the annual allowance charge for? {0} {1} +whoPaidAACharge.you = You +whoPaidAACharge.scheme = Pension Scheme +whoPaidAACharge.both = Both +whoPaidAACharge.checkYourAnswersLabel = Who paid the annual allowance charge? +whoPaidAACharge.error.required = Enter who paid the annual allowance charge +whoPaidAACharge.change.hidden = Who paid the annual allowance charge? + +howMuchAAChargeSchemePaid.title = How much of the charge did your scheme pay? +howMuchAAChargeSchemePaid.heading = How much of the charge did your scheme pay? {0} {1} +howMuchAAChargeSchemePaid.checkYourAnswersLabel = How much of the charge did your scheme pay? +howMuchAAChargeSchemePaid.error.nonNumeric = Enter your how much your scheme paid using numbers +howMuchAAChargeSchemePaid.error.required = Enter your how much your scheme paid +howMuchAAChargeSchemePaid.error.wholeNumber = Enter your how much your scheme paid using whole numbers +howMuchAAChargeSchemePaid.error.outOfRange = How much your scheme paid must be between {0} and {1} +howMuchAAChargeSchemePaid.change.hidden = How much of the charge did your scheme pay? + +howMuchAAChargeYouPaid.title = How much of the charge did you pay? +howMuchAAChargeYouPaid.heading = How much of the charge did you pay? {0} {1} +howMuchAAChargeYouPaid.checkYourAnswersLabel = How much of the charge did you pay? +howMuchAAChargeYouPaid.error.nonNumeric = Enter your how much you paid using numbers +howMuchAAChargeYouPaid.error.required = Enter your how much you paid +howMuchAAChargeYouPaid.error.wholeNumber = Enter your how much you paid using whole numbers +howMuchAAChargeYouPaid.error.outOfRange = How much you paid must be between {0} and {1} +howMuchAAChargeYouPaid.change.hidden = How much of the charge did you pay? \ No newline at end of file diff --git a/test-utils/generators/ModelGenerators.scala b/test-utils/generators/ModelGenerators.scala index 125dc2daf..ca45ddb42 100644 --- a/test-utils/generators/ModelGenerators.scala +++ b/test-utils/generators/ModelGenerators.scala @@ -21,6 +21,11 @@ import org.scalacheck.{Arbitrary, Gen} trait ModelGenerators { + implicit lazy val arbitraryWhoPaidAACharge: Arbitrary[WhoPaidAACharge] = + Arbitrary { + Gen.oneOf(WhoPaidAACharge.values.toSeq) + } + implicit lazy val arbitraryProtectionType: Arbitrary[ProtectionType] = Arbitrary { Gen.oneOf(ProtectionType.values.toSeq) diff --git a/test-utils/generators/PageGenerators.scala b/test-utils/generators/PageGenerators.scala index e5cdc32c2..60a23dfa5 100644 --- a/test-utils/generators/PageGenerators.scala +++ b/test-utils/generators/PageGenerators.scala @@ -18,11 +18,21 @@ package generators import org.scalacheck.Arbitrary import pages.annualallowance.preaaquestions.{ScottishTaxpayerFrom2016Page, WhichYearsScottishTaxpayerPage} +import pages.annualallowance.taxyear.{HowMuchAAChargeSchemePaidPage, HowMuchAAChargeYouPaidPage, WhoPaidAAChargePage} import pages.lifetimeallowance._ import pages.setupquestions.ReportingChangePage trait PageGenerators { + implicit lazy val arbitraryWhoPaidAAChargePage: Arbitrary[WhoPaidAAChargePage.type] = + Arbitrary(WhoPaidAAChargePage) + + implicit lazy val arbitraryHowMuchAAChargeYouPaidPage: Arbitrary[HowMuchAAChargeYouPaidPage.type] = + Arbitrary(HowMuchAAChargeYouPaidPage) + + implicit lazy val arbitraryHowMuchAAChargeSchemePaidPage: Arbitrary[HowMuchAAChargeSchemePaidPage.type] = + Arbitrary(HowMuchAAChargeSchemePaidPage) + implicit lazy val arbitraryProtectionReferencePage: Arbitrary[ProtectionReferencePage.type] = Arbitrary(ProtectionReferencePage) diff --git a/test-utils/generators/UserAnswersEntryGenerators.scala b/test-utils/generators/UserAnswersEntryGenerators.scala index 2c817f454..87c1abdda 100644 --- a/test-utils/generators/UserAnswersEntryGenerators.scala +++ b/test-utils/generators/UserAnswersEntryGenerators.scala @@ -20,12 +20,39 @@ import models._ import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary.arbitrary import pages.annualallowance.preaaquestions.{ScottishTaxpayerFrom2016Page, WhichYearsScottishTaxpayerPage} +import pages.annualallowance.taxyear.{HowMuchAAChargeSchemePaidPage, HowMuchAAChargeYouPaidPage, WhoPaidAAChargePage} import pages.lifetimeallowance._ import pages.setupquestions.ReportingChangePage import play.api.libs.json.{JsValue, Json} trait UserAnswersEntryGenerators extends PageGenerators with ModelGenerators { + implicit lazy val arbitraryWhoPaidAAChargeUserAnswersEntry: Arbitrary[(WhoPaidAAChargePage.type, JsValue)] = + Arbitrary { + for { + page <- arbitrary[WhoPaidAAChargePage.type] + value <- arbitrary[WhoPaidAACharge].map(Json.toJson(_)) + } yield (page, value) + } + + implicit lazy val arbitraryHowMuchAAChargeYouPaidUserAnswersEntry + : Arbitrary[(HowMuchAAChargeYouPaidPage.type, JsValue)] = + Arbitrary { + for { + page <- arbitrary[HowMuchAAChargeYouPaidPage.type] + value <- arbitrary[Int].map(Json.toJson(_)) + } yield (page, value) + } + + implicit lazy val arbitraryHowMuchAAChargeSchemePaidUserAnswersEntry + : Arbitrary[(HowMuchAAChargeSchemePaidPage.type, JsValue)] = + Arbitrary { + for { + page <- arbitrary[HowMuchAAChargeSchemePaidPage.type] + value <- arbitrary[Int].map(Json.toJson(_)) + } yield (page, value) + } + implicit lazy val arbitraryProtectionReferenceUserAnswersEntry: Arbitrary[(ProtectionReferencePage.type, JsValue)] = Arbitrary { for { diff --git a/test/controllers/annualallowance/taxyear/HowMuchAAChargeSchemePaidControllerSpec.scala b/test/controllers/annualallowance/taxyear/HowMuchAAChargeSchemePaidControllerSpec.scala new file mode 100644 index 000000000..056c8d6a4 --- /dev/null +++ b/test/controllers/annualallowance/taxyear/HowMuchAAChargeSchemePaidControllerSpec.scala @@ -0,0 +1,178 @@ +/* + * Copyright 2023 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 controllers.annualallowance.taxyear + +import base.SpecBase +import controllers.routes +import forms.annualallowance.taxyear.HowMuchAAChargeSchemePaidFormProvider +import models.{NormalMode, Period, SchemeIndex, UserAnswers} +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.when +import org.scalatestplus.mockito.MockitoSugar +import pages.annualallowance.taxyear.HowMuchAAChargeSchemePaidPage +import play.api.inject.bind +import play.api.mvc.Call +import play.api.test.FakeRequest +import play.api.test.Helpers._ +import repositories.SessionRepository +import views.html.annualallowance.taxyear.HowMuchAAChargeSchemePaidView + +import scala.concurrent.Future + +class HowMuchAAChargeSchemePaidControllerSpec extends SpecBase with MockitoSugar { + + val formProvider = new HowMuchAAChargeSchemePaidFormProvider() + val form = formProvider() + + def onwardRoute = Call("GET", "/foo") + + val validAnswer = 0 + + lazy val howMuchAAChargeSchemePaidRoute = + controllers.annualallowance.taxyear.routes.HowMuchAAChargeSchemePaidController + .onPageLoad(NormalMode, Period._2018, SchemeIndex(0)) + .url + + "HowMuchAAChargeSchemePaid Controller" - { + + "must return OK and the correct view for a GET" in { + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = FakeRequest(GET, howMuchAAChargeSchemePaidRoute) + + val result = route(application, request).value + + val view = application.injector.instanceOf[HowMuchAAChargeSchemePaidView] + + status(result) mustEqual OK + contentAsString(result) mustEqual view(form, NormalMode, Period._2018, SchemeIndex(0))( + request, + messages(application) + ).toString + } + } + + "must populate the view correctly on a GET when the question has previously been answered" in { + + val userAnswers = UserAnswers(userAnswersId) + .set(HowMuchAAChargeSchemePaidPage(Period._2018, SchemeIndex(0)), validAnswer) + .success + .value + + val application = applicationBuilder(userAnswers = Some(userAnswers)).build() + + running(application) { + val request = FakeRequest(GET, howMuchAAChargeSchemePaidRoute) + + val view = application.injector.instanceOf[HowMuchAAChargeSchemePaidView] + + val result = route(application, request).value + + status(result) mustEqual OK + contentAsString(result) mustEqual view(form.fill(validAnswer), NormalMode, Period._2018, SchemeIndex(0))( + request, + messages(application) + ).toString + } + } + + "must redirect to the next page when valid data is submitted" in { + + val mockSessionRepository = mock[SessionRepository] + + when(mockSessionRepository.set(any())) thenReturn Future.successful(true) + + val application = + applicationBuilder(userAnswers = Some(emptyUserAnswers)) + .overrides( + bind[SessionRepository].toInstance(mockSessionRepository) + ) + .build() + + running(application) { + val request = + FakeRequest(POST, howMuchAAChargeSchemePaidRoute) + .withFormUrlEncodedBody(("value", validAnswer.toString)) + + val userAnswers = emptyUserAnswers.set(HowMuchAAChargeSchemePaidPage(Period._2018, SchemeIndex(0)), 1000) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual HowMuchAAChargeSchemePaidPage(Period._2018, SchemeIndex(0)) + .navigate(NormalMode, userAnswers.get) + .url + } + } + + "must return a Bad Request and errors when invalid data is submitted" in { + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = + FakeRequest(POST, howMuchAAChargeSchemePaidRoute) + .withFormUrlEncodedBody(("value", "invalid value")) + + val boundForm = form.bind(Map("value" -> "invalid value")) + + val view = application.injector.instanceOf[HowMuchAAChargeSchemePaidView] + + val result = route(application, request).value + + status(result) mustEqual BAD_REQUEST + contentAsString(result) mustEqual view(boundForm, NormalMode, Period._2018, SchemeIndex(0))( + request, + messages(application) + ).toString + } + } + + "must redirect to Journey Recovery for a GET if no existing data is found" in { + + val application = applicationBuilder(userAnswers = None).build() + + running(application) { + val request = FakeRequest(GET, howMuchAAChargeSchemePaidRoute) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual routes.JourneyRecoveryController.onPageLoad().url + } + } + + "must redirect to Journey Recovery for a POST if no existing data is found" in { + + val application = applicationBuilder(userAnswers = None).build() + + running(application) { + val request = + FakeRequest(POST, howMuchAAChargeSchemePaidRoute) + .withFormUrlEncodedBody(("value", validAnswer.toString)) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + + redirectLocation(result).value mustEqual routes.JourneyRecoveryController.onPageLoad().url + } + } + } +} diff --git a/test/controllers/annualallowance/taxyear/HowMuchAAChargeYouPaidControllerSpec.scala b/test/controllers/annualallowance/taxyear/HowMuchAAChargeYouPaidControllerSpec.scala new file mode 100644 index 000000000..d9909540f --- /dev/null +++ b/test/controllers/annualallowance/taxyear/HowMuchAAChargeYouPaidControllerSpec.scala @@ -0,0 +1,178 @@ +/* + * Copyright 2023 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 controllers.annualallowance.taxyear + +import base.SpecBase +import controllers.routes +import forms.annualallowance.taxyear.HowMuchAAChargeYouPaidFormProvider +import models.{NormalMode, Period, SchemeIndex, UserAnswers} +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.when +import org.scalatestplus.mockito.MockitoSugar +import pages.annualallowance.taxyear.HowMuchAAChargeYouPaidPage +import play.api.inject.bind +import play.api.mvc.Call +import play.api.test.FakeRequest +import play.api.test.Helpers._ +import repositories.SessionRepository +import views.html.annualallowance.taxyear.HowMuchAAChargeYouPaidView + +import scala.concurrent.Future + +class HowMuchAAChargeYouPaidControllerSpec extends SpecBase with MockitoSugar { + + val formProvider = new HowMuchAAChargeYouPaidFormProvider() + val form = formProvider() + + def onwardRoute = Call("GET", "/foo") + + val validAnswer = 0 + + lazy val howMuchAAChargeYouPaidRoute = + controllers.annualallowance.taxyear.routes.HowMuchAAChargeYouPaidController + .onPageLoad(NormalMode, Period._2018, SchemeIndex(0)) + .url + + "HowMuchAAChargeYouPaid Controller" - { + + "must return OK and the correct view for a GET" in { + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = FakeRequest(GET, howMuchAAChargeYouPaidRoute) + + val result = route(application, request).value + + val view = application.injector.instanceOf[HowMuchAAChargeYouPaidView] + + status(result) mustEqual OK + contentAsString(result) mustEqual view(form, NormalMode, Period._2018, SchemeIndex(0))( + request, + messages(application) + ).toString + } + } + + "must populate the view correctly on a GET when the question has previously been answered" in { + + val userAnswers = UserAnswers(userAnswersId) + .set(HowMuchAAChargeYouPaidPage(Period._2018, SchemeIndex(0)), validAnswer) + .success + .value + + val application = applicationBuilder(userAnswers = Some(userAnswers)).build() + + running(application) { + val request = FakeRequest(GET, howMuchAAChargeYouPaidRoute) + + val view = application.injector.instanceOf[HowMuchAAChargeYouPaidView] + + val result = route(application, request).value + + status(result) mustEqual OK + contentAsString(result) mustEqual view(form.fill(validAnswer), NormalMode, Period._2018, SchemeIndex(0))( + request, + messages(application) + ).toString + } + } + + "must redirect to the next page when valid data is submitted" in { + + val mockSessionRepository = mock[SessionRepository] + + when(mockSessionRepository.set(any())) thenReturn Future.successful(true) + + val application = + applicationBuilder(userAnswers = Some(emptyUserAnswers)) + .overrides( + bind[SessionRepository].toInstance(mockSessionRepository) + ) + .build() + + running(application) { + val request = + FakeRequest(POST, howMuchAAChargeYouPaidRoute) + .withFormUrlEncodedBody(("value", validAnswer.toString)) + + val userAnswers = emptyUserAnswers.set(HowMuchAAChargeYouPaidPage(Period._2018, SchemeIndex(0)), 1000) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual HowMuchAAChargeYouPaidPage(Period._2018, SchemeIndex(0)) + .navigate(NormalMode, userAnswers.get) + .url + } + } + + "must return a Bad Request and errors when invalid data is submitted" in { + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = + FakeRequest(POST, howMuchAAChargeYouPaidRoute) + .withFormUrlEncodedBody(("value", "invalid value")) + + val boundForm = form.bind(Map("value" -> "invalid value")) + + val view = application.injector.instanceOf[HowMuchAAChargeYouPaidView] + + val result = route(application, request).value + + status(result) mustEqual BAD_REQUEST + contentAsString(result) mustEqual view(boundForm, NormalMode, Period._2018, SchemeIndex(0))( + request, + messages(application) + ).toString + } + } + + "must redirect to Journey Recovery for a GET if no existing data is found" in { + + val application = applicationBuilder(userAnswers = None).build() + + running(application) { + val request = FakeRequest(GET, howMuchAAChargeYouPaidRoute) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual routes.JourneyRecoveryController.onPageLoad().url + } + } + + "must redirect to Journey Recovery for a POST if no existing data is found" in { + + val application = applicationBuilder(userAnswers = None).build() + + running(application) { + val request = + FakeRequest(POST, howMuchAAChargeYouPaidRoute) + .withFormUrlEncodedBody(("value", validAnswer.toString)) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + + redirectLocation(result).value mustEqual routes.JourneyRecoveryController.onPageLoad().url + } + } + } +} diff --git a/test/controllers/annualallowance/taxyear/PayAChargeControllerSpec.scala b/test/controllers/annualallowance/taxyear/PayAChargeControllerSpec.scala index 83cae4a64..be58feea2 100644 --- a/test/controllers/annualallowance/taxyear/PayAChargeControllerSpec.scala +++ b/test/controllers/annualallowance/taxyear/PayAChargeControllerSpec.scala @@ -23,7 +23,7 @@ import models.{NormalMode, Period, SchemeIndex, UserAnswers} import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.when import org.scalatestplus.mockito.MockitoSugar -import pages.annualallowance.taxyear.PayAChargePage +import pages.annualallowance.taxyear.{MemberMoreThanOnePensionPage, PayAChargePage} import play.api.inject.bind import play.api.mvc.Call import play.api.test.FakeRequest @@ -105,7 +105,10 @@ class PayAChargeControllerSpec extends SpecBase with MockitoSugar { FakeRequest(POST, payAChargeRoute) .withFormUrlEncodedBody(("value", "true")) - val userAnswers = emptyUserAnswers.set(PayAChargePage(Period._2018, SchemeIndex(0)), false) + val userAnswers = emptyUserAnswers + .set(PayAChargePage(Period._2018, SchemeIndex(0)), true) + .get + .set(MemberMoreThanOnePensionPage(Period._2018), false) val result = route(application, request).value diff --git a/test/controllers/annualallowance/taxyear/WhoPaidAAChargeControllerSpec.scala b/test/controllers/annualallowance/taxyear/WhoPaidAAChargeControllerSpec.scala new file mode 100644 index 000000000..011f3545c --- /dev/null +++ b/test/controllers/annualallowance/taxyear/WhoPaidAAChargeControllerSpec.scala @@ -0,0 +1,181 @@ +/* + * Copyright 2023 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 controllers.annualallowance.taxyear + +import base.SpecBase +import controllers.routes +import forms.annualallowance.taxyear.WhoPaidAAChargeFormProvider +import models.{NormalMode, Period, SchemeIndex, UserAnswers, WhoPaidAACharge} +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.when +import org.scalatestplus.mockito.MockitoSugar +import pages.annualallowance.taxyear.WhoPaidAAChargePage +import play.api.inject.bind +import play.api.mvc.Call +import play.api.test.FakeRequest +import play.api.test.Helpers._ +import repositories.SessionRepository +import views.html.annualallowance.taxyear.WhoPaidAAChargeView + +import scala.concurrent.Future + +class WhoPaidAAChargeControllerSpec extends SpecBase with MockitoSugar { + + def onwardRoute = Call("GET", "/foo") + + lazy val whoPaidAAChargeRoute = + controllers.annualallowance.taxyear.routes.WhoPaidAAChargeController + .onPageLoad(NormalMode, Period._2018, SchemeIndex(0)) + .url + + val formProvider = new WhoPaidAAChargeFormProvider() + val form = formProvider() + + "WhoPaidAACharge Controller" - { + + "must return OK and the correct view for a GET" in { + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = FakeRequest(GET, whoPaidAAChargeRoute) + + val result = route(application, request).value + + val view = application.injector.instanceOf[WhoPaidAAChargeView] + + status(result) mustEqual OK + contentAsString(result) mustEqual view(form, NormalMode, Period._2018, SchemeIndex(0))( + request, + messages(application) + ).toString + } + } + + "must populate the view correctly on a GET when the question has previously been answered" in { + + val userAnswers = UserAnswers(userAnswersId) + .set(WhoPaidAAChargePage(Period._2018, SchemeIndex(0)), WhoPaidAACharge.values.head) + .success + .value + + val application = applicationBuilder(userAnswers = Some(userAnswers)).build() + + running(application) { + val request = FakeRequest(GET, whoPaidAAChargeRoute) + + val view = application.injector.instanceOf[WhoPaidAAChargeView] + + val result = route(application, request).value + + status(result) mustEqual OK + contentAsString(result) mustEqual view( + form.fill(WhoPaidAACharge.values.head), + NormalMode, + Period._2018, + SchemeIndex(0) + )( + request, + messages(application) + ).toString + } + } + + "must redirect to the next page when valid data is submitted" in { + + val mockSessionRepository = mock[SessionRepository] + + when(mockSessionRepository.set(any())) thenReturn Future.successful(true) + + val application = + applicationBuilder(userAnswers = Some(emptyUserAnswers)) + .overrides( + bind[SessionRepository].toInstance(mockSessionRepository) + ) + .build() + + running(application) { + val request = + FakeRequest(POST, whoPaidAAChargeRoute) + .withFormUrlEncodedBody(("value", WhoPaidAACharge.values.head.toString)) + + val userAnswers = emptyUserAnswers.set(WhoPaidAAChargePage(Period._2018, SchemeIndex(0)), WhoPaidAACharge.You) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual WhoPaidAAChargePage(Period._2018, SchemeIndex(0)) + .navigate(NormalMode, userAnswers.get) + .url + } + } + + "must return a Bad Request and errors when invalid data is submitted" in { + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = + FakeRequest(POST, whoPaidAAChargeRoute) + .withFormUrlEncodedBody(("value", "invalid value")) + + val boundForm = form.bind(Map("value" -> "invalid value")) + + val view = application.injector.instanceOf[WhoPaidAAChargeView] + + val result = route(application, request).value + + status(result) mustEqual BAD_REQUEST + contentAsString(result) mustEqual view(boundForm, NormalMode, Period._2018, SchemeIndex(0))( + request, + messages(application) + ).toString + } + } + + "must redirect to Journey Recovery for a GET if no existing data is found" in { + + val application = applicationBuilder(userAnswers = None).build() + + running(application) { + val request = FakeRequest(GET, whoPaidAAChargeRoute) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual routes.JourneyRecoveryController.onPageLoad().url + } + } + + "redirect to Journey Recovery for a POST if no existing data is found" in { + + val application = applicationBuilder(userAnswers = None).build() + + running(application) { + val request = + FakeRequest(POST, whoPaidAAChargeRoute) + .withFormUrlEncodedBody(("value", WhoPaidAACharge.values.head.toString)) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + + redirectLocation(result).value mustEqual routes.JourneyRecoveryController.onPageLoad().url + } + } + } +} diff --git a/test/forms/annualallowance/taxyear/HowMuchAAChargeSchemePaidFormProviderSpec.scala b/test/forms/annualallowance/taxyear/HowMuchAAChargeSchemePaidFormProviderSpec.scala new file mode 100644 index 000000000..03ae5d853 --- /dev/null +++ b/test/forms/annualallowance/taxyear/HowMuchAAChargeSchemePaidFormProviderSpec.scala @@ -0,0 +1,62 @@ +/* + * Copyright 2023 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 forms.annualallowance.taxyear + +import forms.behaviours.IntFieldBehaviours +import play.api.data.FormError + +class HowMuchAAChargeSchemePaidFormProviderSpec extends IntFieldBehaviours { + + val form = new HowMuchAAChargeSchemePaidFormProvider()() + + ".value" - { + + val fieldName = "value" + + val minimum = 0 + val maximum = Int.MaxValue + + val validDataGenerator = intsInRangeWithCommas(minimum, maximum) + + behave like fieldThatBindsValidData( + form, + fieldName, + validDataGenerator + ) + + behave like intField( + form, + fieldName, + nonNumericError = FormError(fieldName, "howMuchAAChargeSchemePaid.error.nonNumeric"), + wholeNumberError = FormError(fieldName, "howMuchAAChargeSchemePaid.error.wholeNumber") + ) + + behave like intFieldWithRange( + form, + fieldName, + minimum = minimum, + maximum = maximum, + expectedError = FormError(fieldName, "howMuchAAChargeSchemePaid.error.outOfRange", Seq(minimum, maximum)) + ) + + behave like mandatoryField( + form, + fieldName, + requiredError = FormError(fieldName, "howMuchAAChargeSchemePaid.error.required") + ) + } +} diff --git a/test/forms/annualallowance/taxyear/HowMuchAAChargeYouPaidFormProviderSpec.scala b/test/forms/annualallowance/taxyear/HowMuchAAChargeYouPaidFormProviderSpec.scala new file mode 100644 index 000000000..b6b29823b --- /dev/null +++ b/test/forms/annualallowance/taxyear/HowMuchAAChargeYouPaidFormProviderSpec.scala @@ -0,0 +1,62 @@ +/* + * Copyright 2023 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 forms.annualallowance.taxyear + +import forms.behaviours.IntFieldBehaviours +import play.api.data.FormError + +class HowMuchAAChargeYouPaidFormProviderSpec extends IntFieldBehaviours { + + val form = new HowMuchAAChargeYouPaidFormProvider()() + + ".value" - { + + val fieldName = "value" + + val minimum = 0 + val maximum = Int.MaxValue + + val validDataGenerator = intsInRangeWithCommas(minimum, maximum) + + behave like fieldThatBindsValidData( + form, + fieldName, + validDataGenerator + ) + + behave like intField( + form, + fieldName, + nonNumericError = FormError(fieldName, "howMuchAAChargeYouPaid.error.nonNumeric"), + wholeNumberError = FormError(fieldName, "howMuchAAChargeYouPaid.error.wholeNumber") + ) + + behave like intFieldWithRange( + form, + fieldName, + minimum = minimum, + maximum = maximum, + expectedError = FormError(fieldName, "howMuchAAChargeYouPaid.error.outOfRange", Seq(minimum, maximum)) + ) + + behave like mandatoryField( + form, + fieldName, + requiredError = FormError(fieldName, "howMuchAAChargeYouPaid.error.required") + ) + } +} diff --git a/test/forms/annualallowance/taxyear/WhoPaidAAChargeFormProviderSpec.scala b/test/forms/annualallowance/taxyear/WhoPaidAAChargeFormProviderSpec.scala new file mode 100644 index 000000000..5ec9c4297 --- /dev/null +++ b/test/forms/annualallowance/taxyear/WhoPaidAAChargeFormProviderSpec.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2023 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 forms.annualallowance.taxyear + +import forms.behaviours.OptionFieldBehaviours +import models.WhoPaidAACharge +import play.api.data.FormError + +class WhoPaidAAChargeFormProviderSpec extends OptionFieldBehaviours { + + val form = new WhoPaidAAChargeFormProvider()() + + ".value" - { + + val fieldName = "value" + val requiredKey = "whoPaidAACharge.error.required" + + behave like optionsField[WhoPaidAACharge]( + form, + fieldName, + validValues = WhoPaidAACharge.values, + invalidError = FormError(fieldName, "error.invalid") + ) + + behave like mandatoryField( + form, + fieldName, + requiredError = FormError(fieldName, requiredKey) + ) + } +} diff --git a/test/forms/behaviours/IntFieldBehaviours.scala b/test/forms/behaviours/IntFieldBehaviours.scala index 3c013f2d4..00eba64ff 100644 --- a/test/forms/behaviours/IntFieldBehaviours.scala +++ b/test/forms/behaviours/IntFieldBehaviours.scala @@ -73,12 +73,12 @@ trait IntFieldBehaviours extends FieldBehaviours { } } -// def intFieldWithRange(form: Form[_], fieldName: String, minimum: Int, maximum: Int, expectedError: FormError): Unit = -// s"not bind integers outside the range $minimum to $maximum" in { -// -// forAll(intsOutsideRange(minimum, maximum) -> "intOutsideRange") { number => -// val result = form.bind(Map(fieldName -> number.toString)).apply(fieldName) -// result.errors must contain only expectedError -// } -// } + def intFieldWithRange(form: Form[_], fieldName: String, minimum: Int, maximum: Int, expectedError: FormError): Unit = + s"not bind integers outside the range $minimum to $maximum" in { + + forAll(intsOutsideRange(minimum, maximum) -> "intOutsideRange") { number => + val result = form.bind(Map(fieldName -> number.toString)).apply(fieldName) + result.errors must contain only expectedError + } + } } diff --git a/test/models/WhoPaidAAChargeSpec.scala b/test/models/WhoPaidAAChargeSpec.scala new file mode 100644 index 000000000..4db293351 --- /dev/null +++ b/test/models/WhoPaidAAChargeSpec.scala @@ -0,0 +1,58 @@ +/* + * Copyright 2023 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 models + +import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Gen +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.must.Matchers +import org.scalatest.OptionValues +import play.api.libs.json.{JsError, JsString, Json} + +class WhoPaidAAChargeSpec extends AnyFreeSpec with Matchers with ScalaCheckPropertyChecks with OptionValues { + + "WhoPaidAACharge" - { + + "must deserialise valid values" in { + + val gen = Gen.oneOf(WhoPaidAACharge.values.toSeq) + + forAll(gen) { whoPaidAACharge => + JsString(whoPaidAACharge.toString).validate[WhoPaidAACharge].asOpt.value mustEqual whoPaidAACharge + } + } + + "must fail to deserialise invalid values" in { + + val gen = arbitrary[String] suchThat (!WhoPaidAACharge.values.map(_.toString).contains(_)) + + forAll(gen) { invalidValue => + JsString(invalidValue).validate[WhoPaidAACharge] mustEqual JsError("error.invalid") + } + } + + "must serialise" in { + + val gen = Gen.oneOf(WhoPaidAACharge.values.toSeq) + + forAll(gen) { whoPaidAACharge => + Json.toJson(whoPaidAACharge) mustEqual JsString(whoPaidAACharge.toString) + } + } + } +} diff --git a/test/pages/annualallowance/taxyear/HowMuchAAChargeSchemePaidPageSpec.scala b/test/pages/annualallowance/taxyear/HowMuchAAChargeSchemePaidPageSpec.scala new file mode 100644 index 000000000..b8e9291cc --- /dev/null +++ b/test/pages/annualallowance/taxyear/HowMuchAAChargeSchemePaidPageSpec.scala @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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.annualallowance.taxyear + +import models.{Period, SchemeIndex} +import pages.behaviours.PageBehaviours + +class HowMuchAAChargeSchemePaidPageSpec extends PageBehaviours { + + "HowMuchAAChargeSchemePaidPage" - { + + beRetrievable[Int](HowMuchAAChargeSchemePaidPage(Period._2018, SchemeIndex(0))) + + beSettable[Int](HowMuchAAChargeSchemePaidPage(Period._2018, SchemeIndex(0))) + + beRemovable[Int](HowMuchAAChargeSchemePaidPage(Period._2018, SchemeIndex(0))) + } +} diff --git a/test/pages/annualallowance/taxyear/HowMuchAAChargeYouPaidPageSpec.scala b/test/pages/annualallowance/taxyear/HowMuchAAChargeYouPaidPageSpec.scala new file mode 100644 index 000000000..b6ae89058 --- /dev/null +++ b/test/pages/annualallowance/taxyear/HowMuchAAChargeYouPaidPageSpec.scala @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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.annualallowance.taxyear + +import models.{Period, SchemeIndex} +import pages.behaviours.PageBehaviours + +class HowMuchAAChargeYouPaidPageSpec extends PageBehaviours { + + "HowMuchAAChargeYouPaidPage" - { + + beRetrievable[Int](HowMuchAAChargeYouPaidPage(Period._2018, SchemeIndex(0))) + + beSettable[Int](HowMuchAAChargeYouPaidPage(Period._2018, SchemeIndex(0))) + + beRemovable[Int](HowMuchAAChargeYouPaidPage(Period._2018, SchemeIndex(0))) + } +} diff --git a/test/pages/annualallowance/taxyear/PayAChargePageSpec.scala b/test/pages/annualallowance/taxyear/PayAChargePageSpec.scala index f0577bea4..ff9f442fc 100644 --- a/test/pages/annualallowance/taxyear/PayAChargePageSpec.scala +++ b/test/pages/annualallowance/taxyear/PayAChargePageSpec.scala @@ -52,14 +52,14 @@ class PayAChargePageSpec extends PageBehaviours { checkNavigation(nextPageUrl, "/check-your-answers-period/2018") // TODO until onward pages are added } - "when did pay charge then then check onward navigation" in { + "when did pay charge then check onward navigation" in { val page = PayAChargePage(Period._2018, SchemeIndex(0)) val userAnswers = emptyUserAnswers.set(page, true).get.set(MemberMoreThanOnePensionPage(Period._2018), true).get val nextPageUrl: String = page.navigate(NormalMode, userAnswers).url - checkNavigation(nextPageUrl, "/add-another-scheme/2018/0") // TODO until who paid pages are added + checkNavigation(nextPageUrl, "/who-paid-annual-allowance-charge/2018/0") } } } diff --git a/test/pages/annualallowance/taxyear/WhoPaidAAChargePageSpec.scala b/test/pages/annualallowance/taxyear/WhoPaidAAChargePageSpec.scala new file mode 100644 index 000000000..52ada5df2 --- /dev/null +++ b/test/pages/annualallowance/taxyear/WhoPaidAAChargePageSpec.scala @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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.annualallowance.taxyear + +import models.{Period, SchemeIndex, WhoPaidAACharge} +import pages.behaviours.PageBehaviours + +class WhoPaidAAChargeSpec extends PageBehaviours { + + "WhoPaidAAChargePage" - { + + beRetrievable[WhoPaidAACharge](WhoPaidAAChargePage(Period._2018, SchemeIndex(0))) + + beSettable[WhoPaidAACharge](WhoPaidAAChargePage(Period._2018, SchemeIndex(0))) + + beRemovable[WhoPaidAACharge](WhoPaidAAChargePage(Period._2018, SchemeIndex(0))) + } +}