diff --git a/app/config/FrontendAppConfig.scala b/app/config/FrontendAppConfig.scala index f7628bf75..632ba43ea 100644 --- a/app/config/FrontendAppConfig.scala +++ b/app/config/FrontendAppConfig.scala @@ -41,12 +41,14 @@ class FrontendAppConfig @Inject() (configuration: Configuration) { s"$contactHost/contact/beta-feedback?service=$contactFormServiceIdentifier&backUrl=$safeBackUrl" } - val baseUrl: String = configuration.get[String]("urls.base") - val loginUrl: String = configuration.get[String]("urls.login") - val loginContinueUrl: String = configuration.get[String]("urls.loginContinue") - val signOutUrl: String = configuration.get[String]("urls.signOut") - val submitFrontend: String = configuration.get[String]("urls.submitFrontend") - val redirectToStartPage: String = configuration.get[String]("urls.redirectToStartPage") + val baseUrl: String = configuration.get[String]("urls.base") + val loginUrl: String = configuration.get[String]("urls.login") + val loginContinueUrl: String = configuration.get[String]("urls.loginContinue") + val signOutUrl: String = configuration.get[String]("urls.signOut") + val submitFrontend: String = configuration.get[String]("urls.submitFrontend") + val redirectToStartPage: String = configuration.get[String]("urls.redirectToStartPage") + val redirectToOutstandingTasksPage: String = configuration.get[String]("urls.redirectToOutstandingTasksPage") + val redirectToTaskListPage: String = configuration.get[String]("urls.redirectToTaskListPage") val confidenceUpliftUrl: String = configuration.get[String]("urls.confidenceUplift") val upliftCompletionUrl = configuration.get[String]("urls.upliftCompletion") diff --git a/app/config/Module.scala b/app/config/Module.scala index cdbbbb5e5..dff684792 100644 --- a/app/config/Module.scala +++ b/app/config/Module.scala @@ -29,6 +29,7 @@ class Module extends AbstractModule { bind(classOf[DataRetrievalAction]).to(classOf[DataRetrievalActionImpl]).asEagerSingleton() bind(classOf[DataRequiredAction]).to(classOf[DataRequiredActionImpl]).asEagerSingleton() bind(classOf[IdentifierAction]).to(classOf[OptionalAuthIdentifierAction]).asEagerSingleton() + bind(classOf[RequireTasksCompletedAction]).to(classOf[RequireTasksCompletedActionImpl]).asEagerSingleton() bind(classOf[Clock]).toInstance(Clock.systemDefaultZone.withZone(ZoneOffset.UTC)) bind(classOf[Encrypter]).toProvider(classOf[CryptoProvider]) bind(classOf[Decrypter]).toProvider(classOf[CryptoProvider]) diff --git a/app/controllers/CalculationReviewController.scala b/app/controllers/CalculationReviewController.scala index 78dfc9f4e..c74a3cae6 100644 --- a/app/controllers/CalculationReviewController.scala +++ b/app/controllers/CalculationReviewController.scala @@ -39,6 +39,7 @@ class CalculationReviewController @Inject() ( identify: IdentifierAction, getData: DataRetrievalAction, requireData: DataRequiredAction, + requireTasksCompleted: RequireTasksCompletedAction, val controllerComponents: MessagesControllerComponents, view: CalculationReviewView, calculationResultService: CalculationResultService, @@ -49,39 +50,42 @@ class CalculationReviewController @Inject() ( val form = Form("_" -> ignored(())) - def onPageLoad: Action[AnyContent] = (identify andThen getData andThen requireData).async { implicit request => - calculationResultService.sendRequest(request.userAnswers).map { calculationResponse => - val includeCompensation2015: Boolean = calculationResponse.totalAmounts.outDatesCompensation > 0 - val isInCredit: Boolean = calculationResponse.totalAmounts.inDatesCredit > 0 - val isInDebit: Boolean = calculationResponse.totalAmounts.inDatesDebit > 0 - val isUserAuthenticated: Boolean = request.userAnswers.authenticated - val isLTACompleteWithoutKickout = LTASection.status(request.userAnswers) == SectionStatus.Completed && !LTASection - .kickoutHasBeenReached(request.userAnswers) - val hasInDates: Boolean = calculationResponse.inDates.isDefinedAt(0) - Ok( - view( - form, - calculationResultService.calculationReviewViewModel(calculationResponse), - includeCompensation2015, - isInCredit, - isInDebit, - isUserAuthenticated, - isLTACompleteWithoutKickout, - hasInDates + def onPageLoad: Action[AnyContent] = + (identify andThen getData andThen requireData andThen requireTasksCompleted).async { implicit request => + calculationResultService.sendRequest(request.userAnswers).map { calculationResponse => + val includeCompensation2015: Boolean = calculationResponse.totalAmounts.outDatesCompensation > 0 + val isInCredit: Boolean = calculationResponse.totalAmounts.inDatesCredit > 0 + val isInDebit: Boolean = calculationResponse.totalAmounts.inDatesDebit > 0 + val isUserAuthenticated: Boolean = request.userAnswers.authenticated + val isLTACompleteWithoutKickout = + LTASection.status(request.userAnswers) == SectionStatus.Completed && !LTASection + .kickoutHasBeenReached(request.userAnswers) + val hasInDates: Boolean = calculationResponse.inDates.isDefinedAt(0) + Ok( + view( + form, + calculationResultService.calculationReviewViewModel(calculationResponse), + includeCompensation2015, + isInCredit, + isInDebit, + isUserAuthenticated, + isLTACompleteWithoutKickout, + hasInDates + ) ) - ) + } } - } - def onSubmit(): Action[AnyContent] = (identify andThen getData andThen requireData).async { implicit request => - calculationResultService.submitUserAnswersAndCalculation(request.userAnswers, request.userId).map { - submissionResponse: SubmissionResponse => - submissionResponse match { - case submission.Success(uniqueId) => Redirect(submitFrontendLandingPageUrl(uniqueId)) - case submission.Failure(_) => Redirect(routes.JourneyRecoveryController.onPageLoad()) - } + def onSubmit(): Action[AnyContent] = + (identify andThen getData andThen requireData andThen requireTasksCompleted).async { implicit request => + calculationResultService.submitUserAnswersAndCalculation(request.userAnswers, request.userId).map { + submissionResponse: SubmissionResponse => + submissionResponse match { + case submission.Success(uniqueId) => Redirect(submitFrontendLandingPageUrl(uniqueId)) + case submission.Failure(_) => Redirect(routes.JourneyRecoveryController.onPageLoad()) + } + } } - } private def submitFrontendLandingPageUrl(uniqueId: String) = s"${config.submitFrontend}/landing-page?submissionUniqueId=$uniqueId" diff --git a/app/controllers/CalculationReviewIndividualAAController.scala b/app/controllers/CalculationReviewIndividualAAController.scala index 696caa942..7e7c2575a 100644 --- a/app/controllers/CalculationReviewIndividualAAController.scala +++ b/app/controllers/CalculationReviewIndividualAAController.scala @@ -17,19 +17,22 @@ package controllers import config.FrontendAppConfig -import controllers.actions._ +import controllers.actions.{IsRevelantPeriodAction, _} import models.CalculationResults.IndividualAASummaryModel -import models.Period +import models.{Period, UserAnswers} +import models.tasklist.sections.AASection +import pages.annualallowance.preaaquestions.StopPayingPublicPensionPage import play.api.data.Form import play.api.data.Forms.ignored import play.api.i18n.{I18nSupport, MessagesApi} import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} -import services.CalculationResultService +import services.{CalculationResultService, PeriodService, TaskListService} import uk.gov.hmrc.http.HeaderCarrier import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController import uk.gov.hmrc.play.http.HeaderCarrierConverter import views.html.CalculationReviewIndividualAAView +import java.time.LocalDate import javax.inject.Inject import scala.concurrent.{ExecutionContext, Future} @@ -41,6 +44,8 @@ class CalculationReviewIndividualAAController @Inject() ( val controllerComponents: MessagesControllerComponents, view: CalculationReviewIndividualAAView, calculationResultService: CalculationResultService, + requireTasksCompleted: RequireTasksCompletedAction, + isRelevantPeriod: IsRevelantPeriodAction, config: FrontendAppConfig )(implicit ec: ExecutionContext) extends FrontendBaseController @@ -48,38 +53,41 @@ class CalculationReviewIndividualAAController @Inject() ( val form = Form("_" -> ignored(())) - def onPageLoad(period: Period): Action[AnyContent] = (identify andThen getData andThen requireData).async { - implicit request => - implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequestAndSession(request, request.session) + def onPageLoad(period: Period): Action[AnyContent] = + (identify andThen getData andThen requireData andThen requireTasksCompleted andThen isRelevantPeriod(period)) + .async { implicit request => + implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequestAndSession(request, request.session) - calculationResultService.sendRequest(request.userAnswers).flatMap { calculationResponse => - val allYears: Seq[IndividualAASummaryModel] = - calculationResultService.individualAASummaryModel(calculationResponse) - val individualYear: IndividualAASummaryModel = allYears.find(year => year.period == period).get - calculationResultService - .calculationReviewIndividualAAViewModel(calculationResponse, Some(period.toString()), request.userAnswers) - .map { calculationReviewIndividualAAViewModel => - val isInCredit: Boolean = calculationResponse.totalAmounts.inDatesCredit > 0 - val isInDebit: Boolean = calculationResponse.totalAmounts.inDatesDebit > 0 - val outDates: List[Period] = List(Period._2016, Period._2017, Period._2018, Period._2019) - val isOutDate: Boolean = outDates.contains(individualYear.period) - Ok( - view( - form, - period.toString(), - calculationReviewIndividualAAViewModel, - isInCredit, - isInDebit, - individualYear, - isOutDate, - routes.CalculationReviewController.onPageLoad() + calculationResultService.sendRequest(request.userAnswers).flatMap { calculationResponse => + val allYears: Seq[IndividualAASummaryModel] = + calculationResultService.individualAASummaryModel(calculationResponse) + val individualYear: IndividualAASummaryModel = allYears.find(year => year.period == period).get + calculationResultService + .calculationReviewIndividualAAViewModel(calculationResponse, Some(period.toString()), request.userAnswers) + .map { calculationReviewIndividualAAViewModel => + val isInCredit: Boolean = calculationResponse.totalAmounts.inDatesCredit > 0 + val isInDebit: Boolean = calculationResponse.totalAmounts.inDatesDebit > 0 + val outDates: List[Period] = List(Period._2016, Period._2017, Period._2018, Period._2019) + val isOutDate: Boolean = outDates.contains(individualYear.period) + Ok( + view( + form, + period.toString(), + calculationReviewIndividualAAViewModel, + isInCredit, + isInDebit, + individualYear, + isOutDate, + routes.CalculationReviewController.onPageLoad() + ) ) - ) - } + } + } } - } - def onSubmit(): Action[AnyContent] = (identify andThen getData andThen requireData).async { implicit request => - Future.successful(Redirect(routes.CalculationReviewController.onPageLoad())) - } + def onSubmit(period: Period): Action[AnyContent] = + (identify andThen getData andThen requireData andThen requireTasksCompleted andThen isRelevantPeriod(period)) + .async { implicit request => + Future.successful(Redirect(routes.CalculationReviewController.onPageLoad())) + } } diff --git a/app/controllers/OutstandingTasksController.scala b/app/controllers/OutstandingTasksController.scala new file mode 100644 index 000000000..45a66e1cb --- /dev/null +++ b/app/controllers/OutstandingTasksController.scala @@ -0,0 +1,39 @@ +/* + * Copyright 2024 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 + +import controllers.actions._ +import javax.inject.Inject +import play.api.i18n.{I18nSupport, MessagesApi} +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController +import views.html.OutstandingTasksView + +class OutstandingTasksController @Inject() ( + override val messagesApi: MessagesApi, + identify: IdentifierAction, + getData: DataRetrievalAction, + requireData: DataRequiredAction, + val controllerComponents: MessagesControllerComponents, + view: OutstandingTasksView +) extends FrontendBaseController + with I18nSupport { + + def onPageLoad: Action[AnyContent] = (identify andThen getData andThen requireData) { implicit request => + Ok(view()) + } +} diff --git a/app/controllers/PrintReviewController.scala b/app/controllers/PrintReviewController.scala index 090a920e5..6cec336e9 100644 --- a/app/controllers/PrintReviewController.scala +++ b/app/controllers/PrintReviewController.scala @@ -43,6 +43,7 @@ class PrintReviewController @Inject() ( getData: DataRetrievalAction, requireData: DataRequiredAction, val controllerComponents: MessagesControllerComponents, + requireTasksCompleted: RequireTasksCompletedAction, view: PrintReviewView, calculationResultService: CalculationResultService )(implicit ec: ExecutionContext) @@ -51,71 +52,72 @@ class PrintReviewController @Inject() ( val form = Form("_" -> ignored(())) - def onPageLoad: Action[AnyContent] = (identify andThen getData andThen requireData).async { implicit request => - implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequestAndSession(request, request.session) + def onPageLoad: Action[AnyContent] = + (identify andThen getData andThen requireData andThen requireTasksCompleted).async { implicit request => + implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequestAndSession(request, request.session) - val rows: Seq[Option[SummaryListRow]] = Seq( - DateOfBenefitCrystallisationEventSummary.row(request.userAnswers, changeAllowed = false), - LtaProtectionOrEnhancementsSummary.row(request.userAnswers, changeAllowed = false), - ProtectionTypeSummary.row(request.userAnswers, changeAllowed = false), - ProtectionReferenceSummary.row(request.userAnswers, changeAllowed = false), - EnhancementTypeSummary.row(request.userAnswers, changeAllowed = false), - InternationalEnhancementReferenceSummary.row(request.userAnswers, changeAllowed = false), - PensionCreditReferenceSummary.row(request.userAnswers, changeAllowed = false), - ProtectionEnhancedChangedSummary.row(request.userAnswers, changeAllowed = false), - WhatNewProtectionTypeEnhancementSummary.row(request.userAnswers, changeAllowed = false), - ReferenceNewProtectionTypeEnhancementSummary.row(request.userAnswers, changeAllowed = false), - NewEnhancementTypeSummary.row(request.userAnswers, changeAllowed = false), - NewInternationalEnhancementReferenceSummary.row(request.userAnswers, changeAllowed = false), - NewPensionCreditReferenceSummary.row(request.userAnswers, changeAllowed = false), - LifetimeAllowanceChargeSummary.row(request.userAnswers, changeAllowed = false), - ExcessLifetimeAllowancePaidSummary.row(request.userAnswers, changeAllowed = false), - LumpSumValueSummary.row(request.userAnswers, changeAllowed = false), - AnnualPaymentValueSummary.row(request.userAnswers, changeAllowed = false), - WhoPaidLTAChargeSummary.row(request.userAnswers, changeAllowed = false), - SchemeNameAndTaxRefSummary.row(request.userAnswers, changeAllowed = false), - UserSchemeDetailsSummary.row(request.userAnswers, changeAllowed = false), - QuarterChargePaidSummary.row(request.userAnswers, changeAllowed = false), - YearChargePaidSummary.row(request.userAnswers, changeAllowed = false), - NewExcessLifetimeAllowancePaidSummary.row(request.userAnswers, false), - NewLumpSumValueSummary.row(request.userAnswers, changeAllowed = false), - NewAnnualPaymentValueSummary.row(request.userAnswers, changeAllowed = false), - WhoPayingExtraLtaChargeSummary.row(request.userAnswers, changeAllowed = false), - LtaPensionSchemeDetailsSummary.row(request.userAnswers, changeAllowed = false) - ) + val rows: Seq[Option[SummaryListRow]] = Seq( + DateOfBenefitCrystallisationEventSummary.row(request.userAnswers, changeAllowed = false), + LtaProtectionOrEnhancementsSummary.row(request.userAnswers, changeAllowed = false), + ProtectionTypeSummary.row(request.userAnswers, changeAllowed = false), + ProtectionReferenceSummary.row(request.userAnswers, changeAllowed = false), + EnhancementTypeSummary.row(request.userAnswers, changeAllowed = false), + InternationalEnhancementReferenceSummary.row(request.userAnswers, changeAllowed = false), + PensionCreditReferenceSummary.row(request.userAnswers, changeAllowed = false), + ProtectionEnhancedChangedSummary.row(request.userAnswers, changeAllowed = false), + WhatNewProtectionTypeEnhancementSummary.row(request.userAnswers, changeAllowed = false), + ReferenceNewProtectionTypeEnhancementSummary.row(request.userAnswers, changeAllowed = false), + NewEnhancementTypeSummary.row(request.userAnswers, changeAllowed = false), + NewInternationalEnhancementReferenceSummary.row(request.userAnswers, changeAllowed = false), + NewPensionCreditReferenceSummary.row(request.userAnswers, changeAllowed = false), + LifetimeAllowanceChargeSummary.row(request.userAnswers, changeAllowed = false), + ExcessLifetimeAllowancePaidSummary.row(request.userAnswers, changeAllowed = false), + LumpSumValueSummary.row(request.userAnswers, changeAllowed = false), + AnnualPaymentValueSummary.row(request.userAnswers, changeAllowed = false), + WhoPaidLTAChargeSummary.row(request.userAnswers, changeAllowed = false), + SchemeNameAndTaxRefSummary.row(request.userAnswers, changeAllowed = false), + UserSchemeDetailsSummary.row(request.userAnswers, changeAllowed = false), + QuarterChargePaidSummary.row(request.userAnswers, changeAllowed = false), + YearChargePaidSummary.row(request.userAnswers, changeAllowed = false), + NewExcessLifetimeAllowancePaidSummary.row(request.userAnswers, false), + NewLumpSumValueSummary.row(request.userAnswers, changeAllowed = false), + NewAnnualPaymentValueSummary.row(request.userAnswers, changeAllowed = false), + WhoPayingExtraLtaChargeSummary.row(request.userAnswers, changeAllowed = false), + LtaPensionSchemeDetailsSummary.row(request.userAnswers, changeAllowed = false) + ) - val isLTACompleteWithoutKickout = LTASection.status(request.userAnswers) == SectionStatus.Completed && !LTASection - .kickoutHasBeenReached(request.userAnswers) + val isLTACompleteWithoutKickout = LTASection.status(request.userAnswers) == SectionStatus.Completed && !LTASection + .kickoutHasBeenReached(request.userAnswers) - calculationResultService.sendRequest(request.userAnswers).flatMap { calculationResponse => - val outDatesStringValues = calculationResultService.outDatesSummary(calculationResponse) - val inDatesStringValues = calculationResultService.inDatesSummary(calculationResponse) - val hasinDates: Boolean = calculationResponse.inDates.isDefinedAt(0) + calculationResultService.sendRequest(request.userAnswers).flatMap { calculationResponse => + val outDatesStringValues = calculationResultService.outDatesSummary(calculationResponse) + val inDatesStringValues = calculationResultService.inDatesSummary(calculationResponse) + val hasinDates: Boolean = calculationResponse.inDates.isDefinedAt(0) - calculationResultService - .calculationReviewIndividualAAViewModel(calculationResponse, None, request.userAnswers) - .map { calculationReviewIndividualAAViewModel => - val isInCredit: Boolean = calculationResponse.totalAmounts.inDatesCredit > 0 - val isInDebit: Boolean = calculationResponse.totalAmounts.inDatesDebit > 0 - val includeCompensation2015: Boolean = calculationResponse.totalAmounts.outDatesCompensation > 0 - Ok( - view( - form, - calculationReviewIndividualAAViewModel, - isInCredit, - isInDebit, - outDatesStringValues, - inDatesStringValues, - calculationResultService.calculationReviewViewModel(calculationResponse), - SummaryListViewModel(rows.flatten), - isLTACompleteWithoutKickout, - includeCompensation2015, - controllers.routes.CalculationReviewController.onPageLoad(), - hasinDates + calculationResultService + .calculationReviewIndividualAAViewModel(calculationResponse, None, request.userAnswers) + .map { calculationReviewIndividualAAViewModel => + val isInCredit: Boolean = calculationResponse.totalAmounts.inDatesCredit > 0 + val isInDebit: Boolean = calculationResponse.totalAmounts.inDatesDebit > 0 + val includeCompensation2015: Boolean = calculationResponse.totalAmounts.outDatesCompensation > 0 + Ok( + view( + form, + calculationReviewIndividualAAViewModel, + isInCredit, + isInDebit, + outDatesStringValues, + inDatesStringValues, + calculationResultService.calculationReviewViewModel(calculationResponse), + SummaryListViewModel(rows.flatten), + isLTACompleteWithoutKickout, + includeCompensation2015, + controllers.routes.CalculationReviewController.onPageLoad(), + hasinDates + ) ) - ) - } + } + } } - } } diff --git a/app/controllers/actions/IsRelevantPeriodAction.scala b/app/controllers/actions/IsRelevantPeriodAction.scala new file mode 100644 index 000000000..070831072 --- /dev/null +++ b/app/controllers/actions/IsRelevantPeriodAction.scala @@ -0,0 +1,46 @@ +/* + * Copyright 2024 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.actions + +import com.google.inject.Inject +import config.FrontendAppConfig +import models.Period +import models.requests.DataRequest +import play.api.mvc.{ActionRefiner, Result} +import play.api.mvc.Results.Redirect +import services.PeriodService + +import scala.concurrent.{ExecutionContext, Future} + +class IsRelevantPeriod( + config: FrontendAppConfig, + period: Period +)(implicit val executionContext: ExecutionContext) + extends ActionRefiner[DataRequest, DataRequest] { + + override protected def refine[A](request: DataRequest[A]): Future[Either[Result, DataRequest[A]]] = { + val maybeRelevantPeriod = PeriodService.relevantPeriods(request.userAnswers).contains(period) + maybeRelevantPeriod match { + case true => Future.successful(Right(DataRequest(request.request, request.userId, request.userAnswers))) + case _ => Future.successful(Left(Redirect(config.redirectToTaskListPage))) + } + } +} + +class IsRevelantPeriodAction @Inject() (config: FrontendAppConfig)(implicit val executionContext: ExecutionContext) { + def apply(period: Period) = new IsRelevantPeriod(config, period) +} diff --git a/app/controllers/actions/RequireTasksCompletedAction.scala b/app/controllers/actions/RequireTasksCompletedAction.scala new file mode 100644 index 000000000..5465eb1f1 --- /dev/null +++ b/app/controllers/actions/RequireTasksCompletedAction.scala @@ -0,0 +1,50 @@ +/* + * Copyright 2024 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.actions + +import com.google.inject.Inject +import config.FrontendAppConfig +import models.SectionStatus +import models.requests.DataRequest +import models.tasklist.SectionStatus.{CannotStartYet, Completed, InProgress, NotStarted} +import play.api.mvc.{ActionRefiner, Result} +import play.api.mvc.Results.Redirect +import services.TaskListService + +import scala.concurrent.{ExecutionContext, Future} + +class RequireTasksCompletedActionImpl @Inject() ( + config: FrontendAppConfig, + taskListService: TaskListService +)(implicit val executionContext: ExecutionContext) + extends RequireTasksCompletedAction { + + override protected def refine[A](request: DataRequest[A]): Future[Either[Result, DataRequest[A]]] = { + val maybeReadyToCalculate = taskListService + .taskListViewModel(request.userAnswers) + .nextStepsGroup + .sections + .map(section => section.status == NotStarted) + .head + maybeReadyToCalculate match { + case true => Future.successful(Right(DataRequest(request.request, request.userId, request.userAnswers))) + case _ => Future.successful(Left(Redirect(config.redirectToOutstandingTasksPage))) + } + } +} + +trait RequireTasksCompletedAction extends ActionRefiner[DataRequest, DataRequest] diff --git a/app/views/OutstandingTasksView.scala.html b/app/views/OutstandingTasksView.scala.html new file mode 100644 index 000000000..38aa9f8d3 --- /dev/null +++ b/app/views/OutstandingTasksView.scala.html @@ -0,0 +1,30 @@ +@* + * Copyright 2024 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, + govukButton: GovukButton) + +@()(implicit request: Request[_], messages: Messages) + +@layout(pageTitle = titleNoForm(messages("outstandingTasks.title")), showBackLink = false) { + +
@messages("outstandingTasks.message1") + @messages("outstandingTasks.link") + @messages("outstandingTasks.message2")
+} diff --git a/conf/app.routes b/conf/app.routes index d1bfb9b42..89c844648 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -611,6 +611,8 @@ GET /calculation-results controllers.CalculationRev POST /calculation-results controllers.CalculationReviewController.onSubmit() GET /calculation-results/:period/detailed-breakdown controllers.CalculationReviewIndividualAAController.onPageLoad(period:Period) -POST /calculation-results/detailed-breakdown controllers.CalculationReviewIndividualAAController.onSubmit() +POST /calculation-results/:period/detailed-breakdown controllers.CalculationReviewIndividualAAController.onSubmit(period:Period) GET /print-calculation-results controllers.PrintReviewController.onPageLoad() + +GET /complete-tasks-before-calculation controllers.OutstandingTasksController.onPageLoad() diff --git a/conf/application.conf b/conf/application.conf index d0ae8bae4..6bcf1eaa9 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -90,6 +90,8 @@ urls { submitFrontend = "http://localhost:12805/submit-public-pension-adjustment" signOut = "http://localhost:9553/bas-gateway/sign-out-without-state" redirectToStartPage = "http://localhost:12804/public-pension-adjustment" + redirectToOutstandingTasksPage = "http://localhost:12804/public-pension-adjustment/complete-tasks-before-calculation" + redirectToTaskListPage = "http://localhost:12804/public-pension-adjustment/task-list" allowedRedirects = ["localhost"] confidenceUplift = "http://localhost:9948/iv-stub/uplift" upliftCompletion = "http://localhost:12804/public-pension-adjustment/maybe-previous-claim-continue" diff --git a/conf/messages.cy b/conf/messages.cy index cf0b7899b..535a323f3 100644 --- a/conf/messages.cy +++ b/conf/messages.cy @@ -1786,3 +1786,9 @@ printReview.printLink = cy: print this page printReview.printYourResults.hint = cy: To keep a record of this, you should printReview.bullet1 = cy: any new or additional tax charges you may owe printReview.bullet2 = cy: any compensation or refunds that may be owed to you + +outstandingTasks.title = cy: Sorry, you cannot view this page +outstandingTasks.heading = cy: Sorry, you cannot view this page +outstandingTasks.message1 = cy: You must +outstandingTasks.message2 = cy: for each tax year before calculating your public service pension adjustment. +outstandingTasks.link = cy: complete all required tasks diff --git a/conf/messages.en b/conf/messages.en index 11585b9b2..22b064c87 100644 --- a/conf/messages.en +++ b/conf/messages.en @@ -1786,3 +1786,9 @@ printReview.printLink = print this page printReview.printYourResults.hint = To keep a record of this, you should printReview.bullet1 = any new or additional tax charges you may owe printReview.bullet2 = any compensation or refunds that may be owed to you + +outstandingTasks.title = Sorry, you cannot view this page +outstandingTasks.heading = Sorry, you cannot view this page +outstandingTasks.message1 = You must +outstandingTasks.message2 = for each tax year before calculating your public service pension adjustment. +outstandingTasks.link = complete all required tasks diff --git a/test/controllers/CalculationReviewControllerSpec.scala b/test/controllers/CalculationReviewControllerSpec.scala index 330c2ad88..53dc66f54 100644 --- a/test/controllers/CalculationReviewControllerSpec.scala +++ b/test/controllers/CalculationReviewControllerSpec.scala @@ -17,8 +17,11 @@ package controllers import base.SpecBase +import config.FrontendAppConfig import models.CalculationResults.CalculationResponse import models.submission.{Failure, Success} +import models.tasklist.{SectionGroupViewModel, SectionViewModel, TaskListViewModel} +import models.tasklist.SectionStatus.{Completed, NotStarted} import models.tasklist.sections.LTASection import org.jsoup.Jsoup import org.mockito.ArgumentMatchers.any @@ -30,7 +33,7 @@ import play.api.libs.json.{JsValue, Json} import play.api.mvc.Call import play.api.test.FakeRequest import play.api.test.Helpers._ -import services.CalculationResultService +import services.{CalculationResultService, TaskListService} import uk.gov.hmrc.http.HeaderCarrier import views.html.CalculationReviewView @@ -59,13 +62,24 @@ class CalculationReviewControllerSpec extends SpecBase with MockitoSugar { readCalculationResult("test/resources/CalculationResultsTestData.json") val mockCalculationResultService = mock[CalculationResultService] + val mockTaskListService = mock[TaskListService] + + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) when(mockCalculationResultService.sendRequest(any)(any)).thenReturn(Future.successful(calculationResult)) when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -79,20 +93,59 @@ class CalculationReviewControllerSpec extends SpecBase with MockitoSugar { } } + "must redirect to outstanding tasks page on a GET if next steps section is not ready to start" in { + + val application = applicationBuilder(Some(emptyUserAnswers)).build() + + running(application) { + val appConfig = application.injector.instanceOf[FrontendAppConfig] + val request = FakeRequest(GET, normalRoute) + + val result = route(application, request).value + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual appConfig.redirectToOutstandingTasksPage + } + } + + "must redirect to outstanding tasks page on a POST if next steps section is not ready to start" in { + + val application = applicationBuilder(Some(emptyUserAnswers)).build() + + running(application) { + val appConfig = application.injector.instanceOf[FrontendAppConfig] + val request = FakeRequest(POST, normalRoute) + + val result = route(application, request).value + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual appConfig.redirectToOutstandingTasksPage + + } + } + "when only out of dates AA, only show content relevant for out dates" in { val calculationResult: CalculationResponse = readCalculationResult("test/resources/CalculationResultsOutDatesTestData.json") val mockCalculationResultService = mock[CalculationResultService] - + val mockTaskListService = mock[TaskListService] + + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) when(mockCalculationResultService.sendRequest(any)(any)).thenReturn(Future.successful(calculationResult)) when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -121,13 +174,24 @@ class CalculationReviewControllerSpec extends SpecBase with MockitoSugar { val userAnswers = LTASection.saveNavigation(emptyUserAnswers, LTASection.checkYourLTAAnswersPage.url) + val mockTaskListService = mock[TaskListService] + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + when(mockCalculationResultService.sendRequest(any)(any)).thenReturn(Future.successful(calculationResult)) when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() val application = applicationBuilder(userAnswers = Some(userAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -148,14 +212,24 @@ class CalculationReviewControllerSpec extends SpecBase with MockitoSugar { readCalculationResult("test/resources/CalculationResultsTestData.json") val mockCalculationResultService = mock[CalculationResultService] - + val mockTaskListService = mock[TaskListService] + + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) when(mockCalculationResultService.sendRequest(any)(any)).thenReturn(Future.successful(calculationResult)) when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -177,14 +251,24 @@ class CalculationReviewControllerSpec extends SpecBase with MockitoSugar { readCalculationResult("test/resources/CalculationResultsTestData.json") val mockCalculationResultService = mock[CalculationResultService] - + val mockTaskListService = mock[TaskListService] + + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) when(mockCalculationResultService.sendRequest(any)(any)).thenReturn(Future.successful(calculationResult)) when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -207,13 +291,24 @@ class CalculationReviewControllerSpec extends SpecBase with MockitoSugar { "must redirect to submit landing page on a POST when answers / calculation are submitted to backend successfully" in { val mockCalculationResultService = mock[CalculationResultService] + val mockTaskListService = mock[TaskListService] + + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) when(mockCalculationResultService.submitUserAnswersAndCalculation(any, any)(any)) .thenReturn(Future.successful(Success("123"))) val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -232,13 +327,24 @@ class CalculationReviewControllerSpec extends SpecBase with MockitoSugar { "must redirect to journey recovery on a POST when answers / calculation submission fails" in { val mockCalculationResultService = mock[CalculationResultService] + val mockTaskListService = mock[TaskListService] + + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) when(mockCalculationResultService.submitUserAnswersAndCalculation(any, any)(any)) .thenReturn(Future.successful(Failure(Seq("someError")))) val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() diff --git a/test/controllers/CalculationReviewIndividualAAControllerSpec.scala b/test/controllers/CalculationReviewIndividualAAControllerSpec.scala index 40ab443e6..59d32e543 100644 --- a/test/controllers/CalculationReviewIndividualAAControllerSpec.scala +++ b/test/controllers/CalculationReviewIndividualAAControllerSpec.scala @@ -17,22 +17,27 @@ package controllers import base.SpecBase +import config.FrontendAppConfig import models.CalculationResults.{CalculationResponse, CalculationReviewIndividualAAViewModel, IndividualAASummaryModel, RowViewModel} import models.Period import models.submission.{Failure, Success} -import models.tasklist.sections.LTASection +import models.tasklist.SectionStatus.{Completed, InProgress, NotStarted} +import models.tasklist.{SectionGroupViewModel, SectionStatus, SectionViewModel, TaskListViewModel} +import models.tasklist.sections.{LTASection, NextStepsSection} import models.tasklist.sections.LTASection.cannotUseLtaServiceNoChargePage import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.when import org.scalatestplus.mockito.MockitoSugar +import pages.annualallowance.preaaquestions.StopPayingPublicPensionPage import play.api.inject.bind import play.api.libs.json.{JsValue, Json} import play.api.mvc.{Call, Result} import play.api.test.FakeRequest import play.api.test.Helpers.{route, status, _} -import services.CalculationResultService +import services.{CalculationResultService, TaskListService} import uk.gov.hmrc.http.HeaderCarrier +import java.time.LocalDate import scala.concurrent.Future import scala.io.Source @@ -44,10 +49,102 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS lazy val normalRouteOutDate = routes.CalculationReviewIndividualAAController.onPageLoad(Period._2016).url lazy val normalRouteInDate = routes.CalculationReviewIndividualAAController.onPageLoad(Period._2022).url - lazy val submitRoute = routes.CalculationReviewIndividualAAController.onSubmit().url + lazy val submitRoute = routes.CalculationReviewIndividualAAController.onSubmit(Period._2020).url "CalculationReviewIndividualAA Controller" - { + "must redirect to outstanding tasks page on a GET if next steps section is not ready to start" in { + + val application = applicationBuilder(Some(emptyUserAnswers)).build() + + running(application) { + val appConfig = application.injector.instanceOf[FrontendAppConfig] + val request = FakeRequest(GET, normalRouteOutDate) + + val result = route(application, request).value + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual appConfig.redirectToOutstandingTasksPage + } + } + + "must redirect to outstanding tasks page on a POST if next steps section is not ready to start" in { + + val application = applicationBuilder(Some(emptyUserAnswers)).build() + + running(application) { + val appConfig = application.injector.instanceOf[FrontendAppConfig] + val request = FakeRequest(GET, normalRouteOutDate) + + val result = route(application, request).value + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual appConfig.redirectToOutstandingTasksPage + } + } + + "must redirect to task list when user accessing a period which they have not indicated on a GET" in { + + val mockTaskListService = mock[TaskListService] + + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + + val userAnswers = emptyUserAnswers.set(StopPayingPublicPensionPage, LocalDate.of(2015, 7, 1)).success.value + + val application = + applicationBuilder(userAnswers = Some(userAnswers)) + .overrides( + bind[TaskListService].toInstance(mockTaskListService) + ) + .build() + + running(application) { + val appConfig = application.injector.instanceOf[FrontendAppConfig] + val request = FakeRequest(GET, normalRouteInDate) + + val result = route(application, request).value + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual appConfig.redirectToTaskListPage + } + } + + "must redirect to task list when user accessing a period which they have not indicated on a POST" in { + + val mockTaskListService = mock[TaskListService] + + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + + val userAnswers = emptyUserAnswers.set(StopPayingPublicPensionPage, LocalDate.of(2015, 7, 1)).success.value + + val application = + applicationBuilder(userAnswers = Some(userAnswers)) + .overrides( + bind[TaskListService].toInstance(mockTaskListService) + ) + .build() + + running(application) { + val appConfig = application.injector.instanceOf[FrontendAppConfig] + val request = FakeRequest(POST, normalRouteInDate) + + val result = route(application, request).value + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual appConfig.redirectToTaskListPage + } + } + "must show the calculation review individual AA view on a GET for out date" - { "when decrease in tax charge" in { @@ -56,6 +153,7 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS readCalculationResult("test/resources/CalculationResultsTestData.json") val mockRowViewModel = RowViewModel("test", "test") + val mockTaskListService = mock[TaskListService] val mockCalculationResultService = mock[CalculationResultService] val mockCalculationReviewIndividualAAViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) @@ -78,13 +176,23 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS when(mockCalculationResultService.calculationReviewIndividualAAViewModel(any, any, any)(any, any)) .thenReturn(Future.successful(mockCalculationReviewIndividualAAViewModel)) + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + when(mockCalculationResultService.individualAASummaryModel(calculationResult)) .thenReturn(Seq(mockIndividualAASummaryModel)) val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -105,6 +213,7 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS readCalculationResult("test/resources/CalculationResultsTestData.json") val mockRowViewModel = RowViewModel("test", "test") + val mockTaskListService = mock[TaskListService] val mockCalculationResultService = mock[CalculationResultService] val mockCalculationReviewIndividualAAViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) @@ -127,13 +236,23 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS when(mockCalculationResultService.calculationReviewIndividualAAViewModel(any, any, any)(any, any)) .thenReturn(Future.successful(mockCalculationReviewIndividualAAViewModel)) + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + when(mockCalculationResultService.individualAASummaryModel(calculationResult)) .thenReturn(Seq(mockIndividualAASummaryModel)) val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -160,6 +279,7 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS readCalculationResult("test/resources/CalculationResultsTestData.json") val mockRowViewModel = RowViewModel("test", "test") + val mockTaskListService = mock[TaskListService] val mockCalculationResultService = mock[CalculationResultService] val mockCalculationReviewIndividualAAViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) @@ -182,13 +302,23 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS when(mockCalculationResultService.calculationReviewIndividualAAViewModel(any, any, any)(any, any)) .thenReturn(Future.successful(mockCalculationReviewIndividualAAViewModel)) + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + when(mockCalculationResultService.individualAASummaryModel(calculationResult)) .thenReturn(Seq(mockIndividualAASummaryModel)) val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -209,6 +339,7 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS readCalculationResult("test/resources/CalculationResultsTestData.json") val mockRowViewModel = RowViewModel("test", "test") + val mockTaskListService = mock[TaskListService] val mockCalculationResultService = mock[CalculationResultService] val mockCalculationReviewIndividualAAViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) @@ -231,13 +362,23 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS when(mockCalculationResultService.calculationReviewIndividualAAViewModel(any, any, any)(any, any)) .thenReturn(Future.successful(mockCalculationReviewIndividualAAViewModel)) + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + when(mockCalculationResultService.individualAASummaryModel(calculationResult)) .thenReturn(Seq(mockIndividualAASummaryModel)) val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -258,6 +399,7 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS readCalculationResult("test/resources/CalculationResultsTestData.json") val mockRowViewModel = RowViewModel("test", "test") + val mockTaskListService = mock[TaskListService] val mockCalculationResultService = mock[CalculationResultService] val mockCalculationReviewIndividualAAViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) @@ -280,13 +422,23 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS when(mockCalculationResultService.calculationReviewIndividualAAViewModel(any, any, any)(any, any)) .thenReturn(Future.successful(mockCalculationReviewIndividualAAViewModel)) + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + when(mockCalculationResultService.individualAASummaryModel(calculationResult)) .thenReturn(Seq(mockIndividualAASummaryModel)) val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) .overrides( - bind[CalculationResultService].toInstance(mockCalculationResultService) + bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -304,8 +456,22 @@ class CalculationReviewIndividualAAControllerSpec extends SpecBase with MockitoS "must redirect to the CalculationReviewController on a POST" in { + val mockTaskListService = mock[TaskListService] + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + val application = - applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + applicationBuilder(userAnswers = Some(emptyUserAnswers)) + .overrides( + bind[TaskListService].toInstance(mockTaskListService) + ) + .build() running(application) { val request = diff --git a/test/controllers/OutstandingTasksControllerSpec.scala b/test/controllers/OutstandingTasksControllerSpec.scala new file mode 100644 index 000000000..61c35ff5b --- /dev/null +++ b/test/controllers/OutstandingTasksControllerSpec.scala @@ -0,0 +1,44 @@ +/* + * Copyright 2024 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 + +import base.SpecBase +import play.api.test.FakeRequest +import play.api.test.Helpers._ +import views.html.OutstandingTasksView + +class OutstandingTasksControllerSpec extends SpecBase { + + "OutstandingTasks 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, routes.OutstandingTasksController.onPageLoad().url) + + val result = route(application, request).value + + val view = application.injector.instanceOf[OutstandingTasksView] + + status(result) mustEqual OK + contentAsString(result) mustEqual view()(request, messages(application)).toString + } + } + } +} diff --git a/test/controllers/PrintReviewControllerSpec.scala b/test/controllers/PrintReviewControllerSpec.scala index 50f2bd3b4..4e6910fdf 100644 --- a/test/controllers/PrintReviewControllerSpec.scala +++ b/test/controllers/PrintReviewControllerSpec.scala @@ -19,16 +19,19 @@ package controllers import base.SpecBase import models.CalculationResults.{CalculationResponse, CalculationReviewIndividualAAViewModel, IndividualAASummaryModel, RowViewModel} import models.Period +import models.tasklist.{SectionGroupViewModel, SectionViewModel, TaskListViewModel} +import models.tasklist.SectionStatus.{Completed, NotStarted} import models.tasklist.sections.LTASection import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.when import org.scalatestplus.mockito.MockitoSugar import play.api.inject +import play.api.inject.bind import play.api.libs.json.{JsValue, Json} import play.api.mvc.Call import play.api.test.FakeRequest import play.api.test.Helpers._ -import services.CalculationResultService +import services.{CalculationResultService, TaskListService} import uk.gov.hmrc.http.HeaderCarrier import viewmodels.govuk.all.SummaryListViewModel @@ -51,6 +54,7 @@ class PrintReviewControllerSpec extends SpecBase with MockitoSugar { readCalculationResult("test/resources/CalculationResultsTestData.json") val mockRowViewModel = RowViewModel("test", "test") + val mockTaskListService = mock[TaskListService] val mockCalculationResultService = mock[CalculationResultService] val mockPrintReviewViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) @@ -97,10 +101,20 @@ class PrintReviewControllerSpec extends SpecBase with MockitoSugar { when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + val application = applicationBuilder(userAnswers = Some(userAnswers)) .overrides( - inject.bind[CalculationResultService].toInstance(mockCalculationResultService) + inject.bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -126,6 +140,7 @@ class PrintReviewControllerSpec extends SpecBase with MockitoSugar { readCalculationResult("test/resources/CalculationResultsOutDatesTestData.json") val mockRowViewModel = RowViewModel("test", "test") + val mockTaskListService = mock[TaskListService] val mockCalculationResultService = mock[CalculationResultService] val mockPrintReviewViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) @@ -159,10 +174,20 @@ class PrintReviewControllerSpec extends SpecBase with MockitoSugar { when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + val application = applicationBuilder(userAnswers = Some(userAnswers)) .overrides( - inject.bind[CalculationResultService].toInstance(mockCalculationResultService) + inject.bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -188,6 +213,7 @@ class PrintReviewControllerSpec extends SpecBase with MockitoSugar { readCalculationResult("test/resources/CalculationResultsTestData.json") val mockRowViewModel = RowViewModel("test", "test") + val mockTaskListService = mock[TaskListService] val mockCalculationResultService = mock[CalculationResultService] val mockPrintReviewViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) @@ -234,10 +260,20 @@ class PrintReviewControllerSpec extends SpecBase with MockitoSugar { when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + val application = applicationBuilder(userAnswers = Some(userAnswers)) .overrides( - inject.bind[CalculationResultService].toInstance(mockCalculationResultService) + inject.bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build() @@ -265,6 +301,7 @@ class PrintReviewControllerSpec extends SpecBase with MockitoSugar { val mockRowViewModel = RowViewModel("test", "test") val mockCalculationResultService = mock[CalculationResultService] + val mockTaskListService = mock[TaskListService] val mockPrintReviewViewModel = CalculationReviewIndividualAAViewModel(Seq(Seq(mockRowViewModel)), Seq(Seq(mockRowViewModel))) val list = SummaryListViewModel(Seq.empty) @@ -310,10 +347,20 @@ class PrintReviewControllerSpec extends SpecBase with MockitoSugar { when(mockCalculationResultService.calculationReviewViewModel(any)).thenCallRealMethod() + when(mockTaskListService.taskListViewModel(any())).thenReturn( + TaskListViewModel( + SectionGroupViewModel("", Seq(SectionViewModel("", "", Completed, "", None))), + None, + None, + SectionGroupViewModel("", Seq(SectionViewModel("", "", NotStarted, "", None))) + ) + ) + val application = applicationBuilder(userAnswers = Some(userAnswers)) .overrides( - inject.bind[CalculationResultService].toInstance(mockCalculationResultService) + inject.bind[CalculationResultService].toInstance(mockCalculationResultService), + bind[TaskListService].toInstance(mockTaskListService) ) .build()