From 0c4038d16c9de44a08a2cba7b54983bb359047b9 Mon Sep 17 00:00:00 2001 From: Jacob Raffe Date: Thu, 20 Jun 2024 17:04:01 +0100 Subject: [PATCH 1/3] [SASS-8612] Create common task list endpoint --- app/controllers/JourneyStatusController.scala | 12 +- app/models/common/JourneyName.scala | 175 +++++++++++++++--- app/models/domain/JourneyStatusAndLink.scala | 38 ++++ .../journeyAnswers/JourneyStatusService.scala | 9 +- conf/app.routes | 3 + .../JourneyStatusControllerSpec.scala | 25 ++- 6 files changed, 221 insertions(+), 41 deletions(-) create mode 100644 app/models/domain/JourneyStatusAndLink.scala diff --git a/app/controllers/JourneyStatusController.scala b/app/controllers/JourneyStatusController.scala index 3c4cc41a..42bb7bd4 100644 --- a/app/controllers/JourneyStatusController.scala +++ b/app/controllers/JourneyStatusController.scala @@ -43,14 +43,18 @@ class JourneyStatusController @Inject() (journeyStatusService: JourneyStatusServ handleApiResultT(result) } + def setStatus(businessId: BusinessId, journey: JourneyName, taxYear: TaxYear): Action[AnyContent] = auth.async { implicit request => + getBody[JourneyStatusData](request) { statusData => + journeyStatusService.set(JourneyContext(taxYear, businessId, request.getMtditid, journey), statusData.status).map(_ => NoContent) + } + } + def getTaskList(taxYear: TaxYear, nino: Nino): Action[AnyContent] = auth.async { implicit request => val result = journeyStatusService.getTaskList(taxYear, request.getMtditid, nino) handleApiResultT(result) } - def setStatus(businessId: BusinessId, journey: JourneyName, taxYear: TaxYear): Action[AnyContent] = auth.async { implicit request => - getBody[JourneyStatusData](request) { statusData => - journeyStatusService.set(JourneyContext(taxYear, businessId, request.getMtditid, journey), statusData.status).map(_ => NoContent) - } + def getCommonTaskList(taxYear: TaxYear, nino: Nino): Action[AnyContent] = auth.async { implicit user => + handleApiResultT(journeyStatusService.getCommonTaskList(taxYear, user.getMtditid, nino)) } } diff --git a/app/models/common/JourneyName.scala b/app/models/common/JourneyName.scala index 03da2436..55d47c28 100644 --- a/app/models/common/JourneyName.scala +++ b/app/models/common/JourneyName.scala @@ -21,41 +21,156 @@ import play.api.mvc.PathBindable sealed abstract class JourneyName(override val entryName: String) extends EnumEntry { override def toString: String = entryName + + def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean = false): String } object JourneyName extends Enum[JourneyName] with utils.PlayJsonEnum[JourneyName] { val values: IndexedSeq[JourneyName] = findValues - case object TradeDetails extends JourneyName("trade-details") - case object SelfEmploymentAbroad extends JourneyName("self-employment-abroad") - case object Income extends JourneyName("income") - case object IncomePrepop extends JourneyName("income-prepop") - case object AdjustmentsPrepop extends JourneyName("adjustments-prepop") - case object BusinessDetailsPrepop extends JourneyName("business-details-prepop") - case object ExpensesTailoring extends JourneyName("expenses-categories") - case object GoodsToSellOrUse extends JourneyName("expenses-goods-to-sell-or-use") - case object WorkplaceRunningCosts extends JourneyName("expenses-workplace-running-costs") - case object RepairsAndMaintenanceCosts extends JourneyName("expenses-repairs-and-maintenance") - case object AdvertisingOrMarketing extends JourneyName("expenses-advertising-marketing") - case object OfficeSupplies extends JourneyName("expenses-office-supplies") - case object Entertainment extends JourneyName("expenses-entertainment") - case object StaffCosts extends JourneyName("expenses-staff-costs") - case object Construction extends JourneyName("expenses-construction") - case object ProfessionalFees extends JourneyName("expenses-professional-fees") - case object Interest extends JourneyName("expenses-interest") - case object OtherExpenses extends JourneyName("expenses-other-expenses") - case object FinancialCharges extends JourneyName("expenses-financial-charges") - case object IrrecoverableDebts extends JourneyName("expenses-irrecoverable-debts") - case object Depreciation extends JourneyName("expenses-depreciation") - case object CapitalAllowancesTailoring extends JourneyName("capital-allowances-tailoring") - case object ZeroEmissionCars extends JourneyName("capital-allowances-zero-emission-cars") - case object ZeroEmissionGoodsVehicle extends JourneyName("capital-allowances-zero-emission-goods-vehicle") - case object ElectricVehicleChargePoints extends JourneyName("capital-allowances-electric-vehicle-charge-points") - case object BalancingAllowance extends JourneyName("capital-allowances-balancing-allowance") - case object WritingDownAllowance extends JourneyName("capital-allowances-writing-down-allowance") - case object AnnualInvestmentAllowance extends JourneyName("capital-allowances-annual-investment-allowance") - case object SpecialTaxSites extends JourneyName("capital-allowances-special-tax-sites") - case object StructuresBuildings extends JourneyName("capital-allowances-structures-buildings") + case object TradeDetails extends JourneyName("trade-details") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = "" // TODO do I need one here? + } + case object SelfEmploymentAbroad extends JourneyName("self-employment-abroad") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/details/self-employment-abroad/check" + else s"$taxYear/$businessId/details/self-employment-abroad" + } + case object Income extends JourneyName("income") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/income/check-your-income" + else s"$taxYear/$businessId/income/not-counted-turnover" + } + case object IncomePrepop extends JourneyName("income-prepop") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = "" // TODO assuming we don't need these prepop ones + } + case object AdjustmentsPrepop extends JourneyName("adjustments-prepop") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = "" + } + case object BusinessDetailsPrepop extends JourneyName("business-details-prepop") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = "" // TODO do I need one here? + } + case object ExpensesTailoring extends JourneyName("expenses-categories") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/check" + else s"$taxYear/$businessId/expenses" + } + case object GoodsToSellOrUse extends JourneyName("expenses-goods-to-sell-or-use") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/goods-sell-use/check" + else s"$taxYear/$businessId/expenses/goods-sell-use/taxi-minicab-road-haulage-industry-driver" + } + case object WorkplaceRunningCosts extends JourneyName("expenses-workplace-running-costs") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/workplace-running-costs/workplace-running-costs/check" + else + s"$taxYear/$businessId/expenses/workplace-running-costs/working-from-home/more-than-25-hours" // TODO add logic to determine whether this should be WFH or WFBP + } + case object RepairsAndMaintenanceCosts extends JourneyName("expenses-repairs-and-maintenance") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/repairs-maintenance/check" + else s"$taxYear/$businessId/expenses/repairs-maintenance/amount" + } + case object AdvertisingOrMarketing extends JourneyName("expenses-advertising-marketing") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/advertising-marketing/check" + else s"$taxYear/$businessId/expenses/advertising-marketing/amount" + } + case object OfficeSupplies extends JourneyName("expenses-office-supplies") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/office-supplies/check" + else s"$taxYear/$businessId/expenses/office-supplies/amount" + } + case object Entertainment extends JourneyName("expenses-entertainment") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/entertainment/check" + else s"$taxYear/$businessId/expenses/entertainment/disallowable-amount" + } + case object StaffCosts extends JourneyName("expenses-staff-costs") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/staff/check" + else s"$taxYear/$businessId/expenses/staff/amount" + } + case object Construction extends JourneyName("expenses-construction") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/construction-industry/check" + else s"$taxYear/$businessId/expenses/construction-industry/amount" + } + case object ProfessionalFees extends JourneyName("expenses-professional-fees") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/professional-fees/check" + else s"$taxYear/$businessId/expenses/professional-fees/amount" + } + case object Interest extends JourneyName("expenses-interest") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/interest-bank-business-loans/check" + else s"$taxYear/$businessId/expenses/interest-bank-business-loans/amount" + } + case object OtherExpenses extends JourneyName("expenses-other-expenses") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/other-expenses/check " + else s"$taxYear/$businessId/expenses/other-expenses/amount" + } + case object FinancialCharges extends JourneyName("expenses-financial-charges") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/bank-credit-card-financial-charges/check" + else s"$taxYear/$businessId/expenses/bank-credit-card-financial-charges/amount" + } + case object IrrecoverableDebts extends JourneyName("expenses-irrecoverable-debts") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/irrecoverable-debts/check" + else s"$taxYear/$businessId/expenses/irrecoverable-debts/amount" + } + case object Depreciation extends JourneyName("expenses-depreciation") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/depreciation/check" + else s"$taxYear/$businessId/expenses/depreciation/disallowable-amount" + } + case object CapitalAllowancesTailoring extends JourneyName("capital-allowances-tailoring") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/check" + else s"$taxYear/$businessId/capital-allowances" + } + case object ZeroEmissionCars extends JourneyName("capital-allowances-zero-emission-cars") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/cars/check" + else s"$taxYear/$businessId/capital-allowances/cars" + } + case object ZeroEmissionGoodsVehicle extends JourneyName("capital-allowances-zero-emission-goods-vehicle") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/goods-vehicles/check" + else s"$taxYear/$businessId/capital-allowances/goods-vehicles" + } + case object ElectricVehicleChargePoints extends JourneyName("capital-allowances-electric-vehicle-charge-points") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/check" + else s"$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/buy" + } + case object BalancingAllowance extends JourneyName("capital-allowances-balancing-allowance") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/balancing-allowance/check" + else s"$taxYear/$businessId/capital-allowances/balancing-allowance" + } + case object WritingDownAllowance extends JourneyName("capital-allowances-writing-down-allowance") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/writing-down-allowance/check" + else s"$taxYear/$businessId/capital-allowances/writing-down-allowance" + } + case object AnnualInvestmentAllowance extends JourneyName("capital-allowances-annual-investment-allowance") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/check" + else s"$taxYear/$businessId/capital-allowances/details" + } + case object SpecialTaxSites extends JourneyName("capital-allowances-special-tax-sites") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/special-tax-sites/check" + else s"$taxYear/$businessId/capital-allowances/special-tax-sites" + } + case object StructuresBuildings extends JourneyName("capital-allowances-structures-buildings") { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/structures-buildings/check" + else s"$taxYear/$businessId/capital-allowances/structures-buildings" + } // Are we using this code? Should we be if not? implicit def pathBindable(implicit strBinder: PathBindable[String]): PathBindable[JourneyName] = new PathBindable[JourneyName] { diff --git a/app/models/domain/JourneyStatusAndLink.scala b/app/models/domain/JourneyStatusAndLink.scala new file mode 100644 index 00000000..51158901 --- /dev/null +++ b/app/models/domain/JourneyStatusAndLink.scala @@ -0,0 +1,38 @@ +/* + * 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.domain + +import models.common.JourneyStatus.NotStarted +import models.common.{BusinessId, JourneyName, JourneyStatus, TaxYear} +import play.api.libs.json.{Json, OFormat} + +final case class JourneyStatusAndLink(name: JourneyName, status: JourneyStatus, href: String) + +object JourneyStatusAndLink { + implicit val format: OFormat[JourneyStatusAndLink] = Json.format[JourneyStatusAndLink] + + def getStatusAndLinksFromJourneys(taxYear: TaxYear, businessId: BusinessId, savedJourneys: List[JourneyNameAndStatus]): List[JourneyStatusAndLink] = + JourneyName.values.map { journey => // TODO should it be getting all journeys, or just the ones that are selected in tailoring? + val maybeJourney: Option[JourneyNameAndStatus] = savedJourneys.find(_.name == journey) + maybeJourney match { + case Some(nameAndStatus) => // TODO do we need logic to determine 'InProgress' like we do in Pensions? + JourneyStatusAndLink(nameAndStatus.name, nameAndStatus.journeyStatus, nameAndStatus.name.getHref(taxYear, businessId, toCYA = true)) + case None => + JourneyStatusAndLink(journey, NotStarted, journey.getHref(taxYear, businessId)) // TODO what if status should be cannot start yet? + } + }.toList +} diff --git a/app/services/journeyAnswers/JourneyStatusService.scala b/app/services/journeyAnswers/JourneyStatusService.scala index 23cfa5e9..a43aba65 100644 --- a/app/services/journeyAnswers/JourneyStatusService.scala +++ b/app/services/journeyAnswers/JourneyStatusService.scala @@ -21,7 +21,7 @@ import cats.implicits._ import connectors.SelfEmploymentConnector import models.common._ import models.connector.api_1171 -import models.domain.{ApiResultT, Business} +import models.domain.{ApiResultT, Business, JourneyStatusAndLink} import models.error.ServiceError import models.frontend.TaskList import repositories.JourneyAnswersRepository @@ -34,6 +34,7 @@ trait JourneyStatusService { def set(ctx: JourneyContext, status: JourneyStatus): ApiResultT[Unit] def get(ctx: JourneyContext): ApiResultT[JourneyStatus] def getTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[TaskList] + def getCommonTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[Seq[List[JourneyStatusAndLink]]] } @Singleton @@ -62,4 +63,10 @@ class JourneyStatusServiceImpl @Inject() (businessConnector: SelfEmploymentConne taskList <- repository.getAll(taxYear, mtditid, businesses) } yield taskList } + + // TODO do I need to package the response into different businesses (Seq[List[_]] is a placeholder)? + def getCommonTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[Seq[List[JourneyStatusAndLink]]] = + getTaskList(taxYear, mtditid, nino).map(_.businesses.map(journeyList => + JourneyStatusAndLink.getStatusAndLinksFromJourneys(taxYear, journeyList.businessId, journeyList.journeyStatuses))) + } diff --git a/conf/app.routes b/conf/app.routes index c0d05f69..0e0fbccc 100755 --- a/conf/app.routes +++ b/conf/app.routes @@ -1,5 +1,8 @@ # microservice specific routes +# Common Task List +GET /:taxYear/common-task-list/:nino controllers.JourneyStatusController.getCommonTaskList(taxYear: TaxYear, nino: Nino) + # Business GET /individuals/business/details/:nino/list controllers.BusinessDetailsController.getBusinesses(nino: String) GET /individuals/business/details/:nino/:businessId controllers.BusinessDetailsController.getBusiness(nino: String, businessId: String) diff --git a/test/controllers/JourneyStatusControllerSpec.scala b/test/controllers/JourneyStatusControllerSpec.scala index 4e276255..f4918ec8 100644 --- a/test/controllers/JourneyStatusControllerSpec.scala +++ b/test/controllers/JourneyStatusControllerSpec.scala @@ -47,6 +47,17 @@ class JourneyStatusControllerSpec extends ControllerBehaviours { } } + "setStatus" should { + "set a new status" in { + behave like testRoute( + request = buildRequest(JourneyStatusData(JourneyStatus.Completed)), + expectedStatus = NO_CONTENT, + expectedBody = "", + methodBlock = () => underTest.setStatus(businessId, JourneyName.Income, currTaxYear) + ) + } + } + "getTaskList" should { "return task list" in { behave like testRoute( @@ -60,13 +71,15 @@ class JourneyStatusControllerSpec extends ControllerBehaviours { } } - "setStatus" should { - "set a new status" in { + "getCommonTaskList" should { + "return a list of all " in { behave like testRoute( - request = buildRequest(JourneyStatusData(JourneyStatus.Completed)), - expectedStatus = NO_CONTENT, - expectedBody = "", - methodBlock = () => underTest.setStatus(businessId, JourneyName.Income, currTaxYear) + request = buildRequestNoContent, + expectedStatus = OK, + expectedBody = Json + .toJson(TaskList.empty) + .toString(), + methodBlock = () => underTest.getTaskList(currTaxYear, nino) ) } } From 049130f94f64b90328c7e10e8212bbccf6bd5b08 Mon Sep 17 00:00:00 2001 From: Jacob Raffe Date: Mon, 24 Jun 2024 13:30:46 +0100 Subject: [PATCH 2/3] [SASS-8612] Create common task list endpoint --- app/models/common/JourneyName.scala | 173 ++-------- app/models/common/JourneyStatus.scala | 19 +- app/models/commonTaskList/SectionTitle.scala | 37 ++ .../commonTaskList/SelfEmploymentTitles.scala | 319 ++++++++++++++++++ app/models/commonTaskList/TaskListData.scala | 53 +++ app/models/commonTaskList/TaskListModel.scala | 25 ++ .../commonTaskList/TaskListSection.scala | 25 ++ .../commonTaskList/TaskListSectionItem.scala | 40 +++ app/models/commonTaskList/TaskStatus.scala | 67 ++++ app/models/commonTaskList/TaskTitle.scala | 37 ++ app/models/domain/JourneyStatusAndLink.scala | 38 --- .../journeyAnswers/JourneyStatusService.scala | 18 +- .../bulders/JourneyNameAndStatusBuilder.scala | 37 ++ .../JourneyStatusControllerSpec.scala | 7 +- 14 files changed, 699 insertions(+), 196 deletions(-) create mode 100644 app/models/commonTaskList/SectionTitle.scala create mode 100644 app/models/commonTaskList/SelfEmploymentTitles.scala create mode 100644 app/models/commonTaskList/TaskListData.scala create mode 100644 app/models/commonTaskList/TaskListModel.scala create mode 100644 app/models/commonTaskList/TaskListSection.scala create mode 100644 app/models/commonTaskList/TaskListSectionItem.scala create mode 100644 app/models/commonTaskList/TaskStatus.scala create mode 100644 app/models/commonTaskList/TaskTitle.scala delete mode 100644 app/models/domain/JourneyStatusAndLink.scala create mode 100644 test/bulders/JourneyNameAndStatusBuilder.scala diff --git a/app/models/common/JourneyName.scala b/app/models/common/JourneyName.scala index 55d47c28..10d2a51d 100644 --- a/app/models/common/JourneyName.scala +++ b/app/models/common/JourneyName.scala @@ -21,156 +21,39 @@ import play.api.mvc.PathBindable sealed abstract class JourneyName(override val entryName: String) extends EnumEntry { override def toString: String = entryName - - def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean = false): String } object JourneyName extends Enum[JourneyName] with utils.PlayJsonEnum[JourneyName] { + val values: IndexedSeq[JourneyName] = findValues - case object TradeDetails extends JourneyName("trade-details") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = "" // TODO do I need one here? - } - case object SelfEmploymentAbroad extends JourneyName("self-employment-abroad") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/details/self-employment-abroad/check" - else s"$taxYear/$businessId/details/self-employment-abroad" - } - case object Income extends JourneyName("income") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/income/check-your-income" - else s"$taxYear/$businessId/income/not-counted-turnover" - } - case object IncomePrepop extends JourneyName("income-prepop") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = "" // TODO assuming we don't need these prepop ones - } - case object AdjustmentsPrepop extends JourneyName("adjustments-prepop") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = "" - } - case object BusinessDetailsPrepop extends JourneyName("business-details-prepop") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = "" // TODO do I need one here? - } - case object ExpensesTailoring extends JourneyName("expenses-categories") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/check" - else s"$taxYear/$businessId/expenses" - } - case object GoodsToSellOrUse extends JourneyName("expenses-goods-to-sell-or-use") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/goods-sell-use/check" - else s"$taxYear/$businessId/expenses/goods-sell-use/taxi-minicab-road-haulage-industry-driver" - } - case object WorkplaceRunningCosts extends JourneyName("expenses-workplace-running-costs") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/workplace-running-costs/workplace-running-costs/check" - else - s"$taxYear/$businessId/expenses/workplace-running-costs/working-from-home/more-than-25-hours" // TODO add logic to determine whether this should be WFH or WFBP - } - case object RepairsAndMaintenanceCosts extends JourneyName("expenses-repairs-and-maintenance") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/repairs-maintenance/check" - else s"$taxYear/$businessId/expenses/repairs-maintenance/amount" - } - case object AdvertisingOrMarketing extends JourneyName("expenses-advertising-marketing") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/advertising-marketing/check" - else s"$taxYear/$businessId/expenses/advertising-marketing/amount" - } - case object OfficeSupplies extends JourneyName("expenses-office-supplies") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/office-supplies/check" - else s"$taxYear/$businessId/expenses/office-supplies/amount" - } - case object Entertainment extends JourneyName("expenses-entertainment") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/entertainment/check" - else s"$taxYear/$businessId/expenses/entertainment/disallowable-amount" - } - case object StaffCosts extends JourneyName("expenses-staff-costs") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/staff/check" - else s"$taxYear/$businessId/expenses/staff/amount" - } - case object Construction extends JourneyName("expenses-construction") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/construction-industry/check" - else s"$taxYear/$businessId/expenses/construction-industry/amount" - } - case object ProfessionalFees extends JourneyName("expenses-professional-fees") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/professional-fees/check" - else s"$taxYear/$businessId/expenses/professional-fees/amount" - } - case object Interest extends JourneyName("expenses-interest") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/interest-bank-business-loans/check" - else s"$taxYear/$businessId/expenses/interest-bank-business-loans/amount" - } - case object OtherExpenses extends JourneyName("expenses-other-expenses") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/other-expenses/check " - else s"$taxYear/$businessId/expenses/other-expenses/amount" - } - case object FinancialCharges extends JourneyName("expenses-financial-charges") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/bank-credit-card-financial-charges/check" - else s"$taxYear/$businessId/expenses/bank-credit-card-financial-charges/amount" - } - case object IrrecoverableDebts extends JourneyName("expenses-irrecoverable-debts") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/irrecoverable-debts/check" - else s"$taxYear/$businessId/expenses/irrecoverable-debts/amount" - } - case object Depreciation extends JourneyName("expenses-depreciation") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/expenses/depreciation/check" - else s"$taxYear/$businessId/expenses/depreciation/disallowable-amount" - } - case object CapitalAllowancesTailoring extends JourneyName("capital-allowances-tailoring") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/check" - else s"$taxYear/$businessId/capital-allowances" - } - case object ZeroEmissionCars extends JourneyName("capital-allowances-zero-emission-cars") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/cars/check" - else s"$taxYear/$businessId/capital-allowances/cars" - } - case object ZeroEmissionGoodsVehicle extends JourneyName("capital-allowances-zero-emission-goods-vehicle") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/goods-vehicles/check" - else s"$taxYear/$businessId/capital-allowances/goods-vehicles" - } - case object ElectricVehicleChargePoints extends JourneyName("capital-allowances-electric-vehicle-charge-points") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/check" - else s"$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/buy" - } - case object BalancingAllowance extends JourneyName("capital-allowances-balancing-allowance") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/balancing-allowance/check" - else s"$taxYear/$businessId/capital-allowances/balancing-allowance" - } - case object WritingDownAllowance extends JourneyName("capital-allowances-writing-down-allowance") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/writing-down-allowance/check" - else s"$taxYear/$businessId/capital-allowances/writing-down-allowance" - } - case object AnnualInvestmentAllowance extends JourneyName("capital-allowances-annual-investment-allowance") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/check" - else s"$taxYear/$businessId/capital-allowances/details" - } - case object SpecialTaxSites extends JourneyName("capital-allowances-special-tax-sites") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/special-tax-sites/check" - else s"$taxYear/$businessId/capital-allowances/special-tax-sites" - } - case object StructuresBuildings extends JourneyName("capital-allowances-structures-buildings") { - override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = - if (toCYA) s"/$taxYear/$businessId/capital-allowances/structures-buildings/check" - else s"$taxYear/$businessId/capital-allowances/structures-buildings" - } + case object TradeDetails extends JourneyName("trade-details") + case object SelfEmploymentAbroad extends JourneyName("self-employment-abroad") + case object Income extends JourneyName("income") + case object ExpensesTailoring extends JourneyName("expenses-categories") + case object GoodsToSellOrUse extends JourneyName("expenses-goods-to-sell-or-use") + case object WorkplaceRunningCosts extends JourneyName("expenses-workplace-running-costs") + case object RepairsAndMaintenanceCosts extends JourneyName("expenses-repairs-and-maintenance") + case object AdvertisingOrMarketing extends JourneyName("expenses-advertising-marketing") + case object OfficeSupplies extends JourneyName("expenses-office-supplies") + case object Entertainment extends JourneyName("expenses-entertainment") + case object StaffCosts extends JourneyName("expenses-staff-costs") + case object Construction extends JourneyName("expenses-construction") + case object ProfessionalFees extends JourneyName("expenses-professional-fees") + case object Interest extends JourneyName("expenses-interest") + case object OtherExpenses extends JourneyName("expenses-other-expenses") + case object FinancialCharges extends JourneyName("expenses-financial-charges") + case object IrrecoverableDebts extends JourneyName("expenses-irrecoverable-debts") + case object Depreciation extends JourneyName("expenses-depreciation") + case object CapitalAllowancesTailoring extends JourneyName("capital-allowances-tailoring") + case object ZeroEmissionCars extends JourneyName("capital-allowances-zero-emission-cars") + case object ZeroEmissionGoodsVehicle extends JourneyName("capital-allowances-zero-emission-goods-vehicle") + case object ElectricVehicleChargePoints extends JourneyName("capital-allowances-electric-vehicle-charge-points") + case object BalancingAllowance extends JourneyName("capital-allowances-balancing-allowance") + case object WritingDownAllowance extends JourneyName("capital-allowances-writing-down-allowance") + case object AnnualInvestmentAllowance extends JourneyName("capital-allowances-annual-investment-allowance") + case object SpecialTaxSites extends JourneyName("capital-allowances-special-tax-sites") + case object StructuresBuildings extends JourneyName("capital-allowances-structures-buildings") // Are we using this code? Should we be if not? implicit def pathBindable(implicit strBinder: PathBindable[String]): PathBindable[JourneyName] = new PathBindable[JourneyName] { diff --git a/app/models/common/JourneyStatus.scala b/app/models/common/JourneyStatus.scala index 7e036d3c..f0d09103 100644 --- a/app/models/common/JourneyStatus.scala +++ b/app/models/common/JourneyStatus.scala @@ -17,9 +17,12 @@ package models.common import enumeratum._ +import models.commonTaskList.TaskStatus sealed abstract class JourneyStatus(override val entryName: String) extends EnumEntry { override def toString: String = entryName + + def toCommonTaskListStatus: TaskStatus } object JourneyStatus extends Enum[JourneyStatus] with utils.PlayJsonEnum[JourneyStatus] { @@ -27,14 +30,22 @@ object JourneyStatus extends Enum[JourneyStatus] with utils.PlayJsonEnum[Journey val values: IndexedSeq[JourneyStatus] = findValues /** This status is used if there are no answers persisted */ - case object CheckOurRecords extends JourneyStatus("checkOurRecords") + case object CheckOurRecords extends JourneyStatus("checkOurRecords") { + override def toCommonTaskListStatus: TaskStatus = TaskStatus.CheckNow() + } /** It is used to indicate the answers were submitted, but the 'Have you completed' question has not been answered */ - case object NotStarted extends JourneyStatus("notStarted") + case object NotStarted extends JourneyStatus("notStarted") { + override def toCommonTaskListStatus: TaskStatus = TaskStatus.NotStarted() + } /** The completion page has been passed with answer No */ - case object InProgress extends JourneyStatus("inProgress") + case object InProgress extends JourneyStatus("inProgress") { + override def toCommonTaskListStatus: TaskStatus = TaskStatus.InProgress() + } /** The completion page has been passed with answer Yes */ - case object Completed extends JourneyStatus("completed") + case object Completed extends JourneyStatus("completed") { + override def toCommonTaskListStatus: TaskStatus = TaskStatus.Completed() + } } diff --git a/app/models/commonTaskList/SectionTitle.scala b/app/models/commonTaskList/SectionTitle.scala new file mode 100644 index 00000000..41ad3214 --- /dev/null +++ b/app/models/commonTaskList/SectionTitle.scala @@ -0,0 +1,37 @@ +/* + * 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 models.commonTaskList + +import models.common.{Enumerable, WithName} +import play.api.libs.json.{Json, OWrites, Reads} + +trait SectionTitle extends Enumerable.Implicits + +object SectionTitle extends SectionTitle { + + case class SelfEmploymentTitle() extends WithName("SelfEmployment") with SectionTitle + object SelfEmploymentTitle { + implicit val nonStrictReads: Reads[SelfEmploymentTitle] = Reads.pure(SelfEmploymentTitle()) + implicit val writes: OWrites[SelfEmploymentTitle] = OWrites[SelfEmploymentTitle](_ => Json.obj()) + } + + val values: Seq[SectionTitle] = Seq(SelfEmploymentTitle()) + + implicit val enumerable: Enumerable[SectionTitle] = + Enumerable(values.map(v => v.toString -> v): _*) + +} diff --git a/app/models/commonTaskList/SelfEmploymentTitles.scala b/app/models/commonTaskList/SelfEmploymentTitles.scala new file mode 100644 index 00000000..903ff734 --- /dev/null +++ b/app/models/commonTaskList/SelfEmploymentTitles.scala @@ -0,0 +1,319 @@ +/* + * 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 models.commonTaskList + +import models.common.{BusinessId, TaxYear, WithName} +import play.api.libs.json.{Json, OWrites, Reads} + +object SelfEmploymentTitles { + + val values: List[TaskTitle] = List( + TradeDetails(), // TODO 8771 should trade details be excluded? + SelfEmploymentAbroad(), + Income(), + ExpensesTailoring(), + WorkplaceRunningCosts(), + RepairsAndMaintenanceCosts(), + OfficeSupplies(), + Entertainment(), + StaffCosts(), + Construction(), + ProfessionalFees(), + Interest(), + OtherExpenses(), + FinancialCharges(), + IrrecoverableDebts(), + Depreciation(), + CapitalAllowancesTailoring(), + ZeroEmissionCars(), + ZeroEmissionGoodsVehicle(), + ElectricVehicleChargePoints(), + BalancingAllowance(), + WritingDownAllowance(), + AnnualInvestmentAllowance(), + SpecialTaxSites(), + StructuresBuildings() + ) + + case class TradeDetails() extends WithName("TradeDetailsTitle") with TaskTitle + object TradeDetails { + implicit val nonStrictReads: Reads[TradeDetails] = Reads.pure(TradeDetails()) + implicit val writes: OWrites[TradeDetails] = OWrites[TradeDetails](_ => Json.obj()) + } + + case class SelfEmploymentAbroad() extends WithName("SelfEmploymentAbroadTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/details/self-employment-abroad/check" + else s"$taxYear/$businessId/details/self-employment-abroad" + } + object SelfEmploymentAbroad { + implicit val nonStrictReads: Reads[SelfEmploymentAbroad] = Reads.pure(SelfEmploymentAbroad()) + implicit val writes: OWrites[SelfEmploymentAbroad] = OWrites[SelfEmploymentAbroad](_ => Json.obj()) + } + + case class Income() extends WithName("IncomeTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/income/check-your-income" + else s"$taxYear/$businessId/income/not-counted-turnover" + } + object Income { + implicit val nonStrictReads: Reads[Income] = Reads.pure(Income()) + implicit val writes: OWrites[Income] = OWrites[Income](_ => Json.obj()) + } + + case class ExpensesTailoring() extends WithName("ExpensesTailoringTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/check" + else s"$taxYear/$businessId/expenses" + } + object ExpensesTailoring { + implicit val nonStrictReads: Reads[ExpensesTailoring] = Reads.pure(ExpensesTailoring()) + implicit val writes: OWrites[ExpensesTailoring] = OWrites[ExpensesTailoring](_ => Json.obj()) + } + + case class GoodsToSellOrUse() extends WithName("GoodsToSellOrUseTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/goods-sell-use/check" + else s"$taxYear/$businessId/expenses/goods-sell-use/taxi-minicab-road-haulage-industry-driver" + } + object GoodsToSellOrUse { + implicit val nonStrictReads: Reads[GoodsToSellOrUse] = Reads.pure(GoodsToSellOrUse()) + implicit val writes: OWrites[GoodsToSellOrUse] = OWrites[GoodsToSellOrUse](_ => Json.obj()) + } + + case class WorkplaceRunningCosts() extends WithName("WorkplaceRunningCostsTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/workplace-running-costs/workplace-running-costs/check" + else + s"$taxYear/$businessId/expenses/workplace-running-costs/working-from-home/more-than-25-hours" // TODO 8773 add logic to determine whether this should be WFH or WFBP + } + object WorkplaceRunningCosts { + implicit val nonStrictReads: Reads[WorkplaceRunningCosts] = Reads.pure(WorkplaceRunningCosts()) + implicit val writes: OWrites[WorkplaceRunningCosts] = OWrites[WorkplaceRunningCosts](_ => Json.obj()) + } + + case class RepairsAndMaintenanceCosts() extends WithName("RepairsAndMaintenanceCostsTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/repairs-maintenance/check" + else s"$taxYear/$businessId/expenses/repairs-maintenance/amount" + } + object RepairsAndMaintenanceCosts { + implicit val nonStrictReads: Reads[RepairsAndMaintenanceCosts] = Reads.pure(RepairsAndMaintenanceCosts()) + implicit val writes: OWrites[RepairsAndMaintenanceCosts] = OWrites[RepairsAndMaintenanceCosts](_ => Json.obj()) + } + + case class AdvertisingOrMarketing() extends WithName("AdvertisingOrMarketingTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/advertising-marketing/check" + else s"$taxYear/$businessId/expenses/advertising-marketing/amount" + } + object AdvertisingOrMarketing { + implicit val nonStrictReads: Reads[AdvertisingOrMarketing] = Reads.pure(AdvertisingOrMarketing()) + implicit val writes: OWrites[AdvertisingOrMarketing] = OWrites[AdvertisingOrMarketing](_ => Json.obj()) + } + + case class OfficeSupplies() extends WithName("OfficeSuppliesTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/office-supplies/check" + else s"$taxYear/$businessId/expenses/office-supplies/amount" + } + object OfficeSupplies { + implicit val nonStrictReads: Reads[OfficeSupplies] = Reads.pure(OfficeSupplies()) + implicit val writes: OWrites[OfficeSupplies] = OWrites[OfficeSupplies](_ => Json.obj()) + } + + case class Entertainment() extends WithName("EntertainmentTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/entertainment/check" + else s"$taxYear/$businessId/expenses/entertainment/disallowable-amount" + } + object Entertainment { + implicit val nonStrictReads: Reads[Entertainment] = Reads.pure(Entertainment()) + implicit val writes: OWrites[Entertainment] = OWrites[Entertainment](_ => Json.obj()) + } + + case class StaffCosts() extends WithName("StaffCostsTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/staff/check" + else s"$taxYear/$businessId/expenses/staff/amount" + } + object StaffCosts { + implicit val nonStrictReads: Reads[StaffCosts] = Reads.pure(StaffCosts()) + implicit val writes: OWrites[StaffCosts] = OWrites[StaffCosts](_ => Json.obj()) + } + + case class Construction() extends WithName("ConstructionTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/construction-industry/check" + else s"$taxYear/$businessId/expenses/construction-industry/amount" + } + object Construction { + implicit val nonStrictReads: Reads[Construction] = Reads.pure(Construction()) + implicit val writes: OWrites[Construction] = OWrites[Construction](_ => Json.obj()) + } + + case class ProfessionalFees() extends WithName("ProfessionalFeesTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/professional-fees/check" + else s"$taxYear/$businessId/expenses/professional-fees/amount" + } + object ProfessionalFees { + implicit val nonStrictReads: Reads[ProfessionalFees] = Reads.pure(ProfessionalFees()) + implicit val writes: OWrites[ProfessionalFees] = OWrites[ProfessionalFees](_ => Json.obj()) + } + + case class Interest() extends WithName("InterestTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/interest-bank-business-loans/check" + else s"$taxYear/$businessId/expenses/interest-bank-business-loans/amount" + } + object Interest { + implicit val nonStrictReads: Reads[Interest] = Reads.pure(Interest()) + implicit val writes: OWrites[Interest] = OWrites[Interest](_ => Json.obj()) + } + + case class OtherExpenses() extends WithName("OtherExpensesTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/other-expenses/check " + else s"$taxYear/$businessId/expenses/other-expenses/amount" + } + object OtherExpenses { + implicit val nonStrictReads: Reads[OtherExpenses] = Reads.pure(OtherExpenses()) + implicit val writes: OWrites[OtherExpenses] = OWrites[OtherExpenses](_ => Json.obj()) + } + + case class FinancialCharges() extends WithName("FinancialChargesTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/bank-credit-card-financial-charges/check" + else s"$taxYear/$businessId/expenses/bank-credit-card-financial-charges/amount" + } + object FinancialCharges { + implicit val nonStrictReads: Reads[FinancialCharges] = Reads.pure(FinancialCharges()) + implicit val writes: OWrites[FinancialCharges] = OWrites[FinancialCharges](_ => Json.obj()) + } + + case class IrrecoverableDebts() extends WithName("IrrecoverableDebtsTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/irrecoverable-debts/check" + else s"$taxYear/$businessId/expenses/irrecoverable-debts/amount" + } + object IrrecoverableDebts { + implicit val nonStrictReads: Reads[IrrecoverableDebts] = Reads.pure(IrrecoverableDebts()) + implicit val writes: OWrites[IrrecoverableDebts] = OWrites[IrrecoverableDebts](_ => Json.obj()) + } + + case class Depreciation() extends WithName("DepreciationTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/expenses/depreciation/check" + else s"$taxYear/$businessId/expenses/depreciation/disallowable-amount" + } + object Depreciation { + implicit val nonStrictReads: Reads[Depreciation] = Reads.pure(Depreciation()) + implicit val writes: OWrites[Depreciation] = OWrites[Depreciation](_ => Json.obj()) + } + + case class CapitalAllowancesTailoring() extends WithName("CapitalAllowancesTailoringTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/check" + else s"$taxYear/$businessId/capital-allowances" + } + object CapitalAllowancesTailoring { + implicit val nonStrictReads: Reads[CapitalAllowancesTailoring] = Reads.pure(CapitalAllowancesTailoring()) + implicit val writes: OWrites[CapitalAllowancesTailoring] = OWrites[CapitalAllowancesTailoring](_ => Json.obj()) + } + + case class ZeroEmissionCars() extends WithName("ZeroEmissionCarsTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/cars/check" + else s"$taxYear/$businessId/capital-allowances/cars" + } + object ZeroEmissionCars { + implicit val nonStrictReads: Reads[ZeroEmissionCars] = Reads.pure(ZeroEmissionCars()) + implicit val writes: OWrites[ZeroEmissionCars] = OWrites[ZeroEmissionCars](_ => Json.obj()) + } + + case class ZeroEmissionGoodsVehicle() extends WithName("ZeroEmissionGoodsVehicleTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/goods-vehicles/check" + else s"$taxYear/$businessId/capital-allowances/goods-vehicles" + } + object ZeroEmissionGoodsVehicle { + implicit val nonStrictReads: Reads[ZeroEmissionGoodsVehicle] = Reads.pure(ZeroEmissionGoodsVehicle()) + implicit val writes: OWrites[ZeroEmissionGoodsVehicle] = OWrites[ZeroEmissionGoodsVehicle](_ => Json.obj()) + } + + case class ElectricVehicleChargePoints() extends WithName("ElectricVehicleChargePointsTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/check" + else s"$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/buy" + } + object ElectricVehicleChargePoints { + implicit val nonStrictReads: Reads[ElectricVehicleChargePoints] = Reads.pure(ElectricVehicleChargePoints()) + implicit val writes: OWrites[ElectricVehicleChargePoints] = OWrites[ElectricVehicleChargePoints](_ => Json.obj()) + } + + case class BalancingAllowance() extends WithName("BalancingAllowanceTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/balancing-allowance/check" + else s"$taxYear/$businessId/capital-allowances/balancing-allowance" + } + object BalancingAllowance { + implicit val nonStrictReads: Reads[BalancingAllowance] = Reads.pure(BalancingAllowance()) + implicit val writes: OWrites[BalancingAllowance] = OWrites[BalancingAllowance](_ => Json.obj()) + } + + case class WritingDownAllowance() extends WithName("WritingDownAllowanceTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/writing-down-allowance/check" + else s"$taxYear/$businessId/capital-allowances/writing-down-allowance" + } + object WritingDownAllowance { + implicit val nonStrictReads: Reads[WritingDownAllowance] = Reads.pure(WritingDownAllowance()) + implicit val writes: OWrites[WritingDownAllowance] = OWrites[WritingDownAllowance](_ => Json.obj()) + } + + case class AnnualInvestmentAllowance() extends WithName("AnnualInvestmentAllowanceTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/check" + else s"$taxYear/$businessId/capital-allowances/details" + } + object AnnualInvestmentAllowance { + implicit val nonStrictReads: Reads[AnnualInvestmentAllowance] = Reads.pure(AnnualInvestmentAllowance()) + implicit val writes: OWrites[AnnualInvestmentAllowance] = OWrites[AnnualInvestmentAllowance](_ => Json.obj()) + } + + case class SpecialTaxSites() extends WithName("SpecialTaxSitesTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/special-tax-sites/check" + else s"$taxYear/$businessId/capital-allowances/special-tax-sites" + } + object SpecialTaxSites { + implicit val nonStrictReads: Reads[SpecialTaxSites] = Reads.pure(SpecialTaxSites()) + implicit val writes: OWrites[SpecialTaxSites] = OWrites[SpecialTaxSites](_ => Json.obj()) + } + + case class StructuresBuildings() extends WithName("StructuresBuildingsTitle") with TaskTitle { + override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = + if (toCYA) s"/$taxYear/$businessId/capital-allowances/structures-buildings/check" + else s"$taxYear/$businessId/capital-allowances/structures-buildings" + } + object StructuresBuildings { + implicit val nonStrictReads: Reads[StructuresBuildings] = Reads.pure(StructuresBuildings()) + implicit val writes: OWrites[StructuresBuildings] = OWrites[StructuresBuildings](_ => Json.obj()) + } + +} diff --git a/app/models/commonTaskList/TaskListData.scala b/app/models/commonTaskList/TaskListData.scala new file mode 100644 index 00000000..e3db11c5 --- /dev/null +++ b/app/models/commonTaskList/TaskListData.scala @@ -0,0 +1,53 @@ +/* + * 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.commonTaskList + +import play.api.libs.json._ +import uk.gov.hmrc.mongo.play.json.formats.MongoJavatimeFormats + +import java.time.Instant + +final case class TaskListData(mtdItId: String, taxYear: Int, data: JsObject = Json.obj(), lastUpdated: Instant = Instant.now) + +object TaskListData { + + val reads: Reads[TaskListData] = { + + import play.api.libs.functional.syntax._ + + ( + (__ \ "mtdItId").read[String] and + (__ \ "taxYear").read[Int].filter(_.toString.matches("^20\\d{2}$")) and + (__ \ "data").read[JsObject] and + (__ \ "lastUpdated").read(MongoJavatimeFormats.instantFormat) + )(TaskListData.apply _) + } + + val writes: OWrites[TaskListData] = { + + import play.api.libs.functional.syntax._ + + ( + (__ \ "mtdItId").write[String] and + (__ \ "taxYear").write[Int] and + (__ \ "data").write[JsObject] and + (__ \ "lastUpdated").write(MongoJavatimeFormats.instantFormat) + )(unlift(TaskListData.unapply)) + } + + implicit val format: OFormat[TaskListData] = OFormat(reads, writes) +} diff --git a/app/models/commonTaskList/TaskListModel.scala b/app/models/commonTaskList/TaskListModel.scala new file mode 100644 index 00000000..c94c00fc --- /dev/null +++ b/app/models/commonTaskList/TaskListModel.scala @@ -0,0 +1,25 @@ +/* + * 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 models.commonTaskList + +import play.api.libs.json.{Json, OFormat} + +case class TaskListModel(taskList: Seq[TaskListSection]) + +object TaskListModel { + implicit val format: OFormat[TaskListModel] = Json.format[TaskListModel] +} diff --git a/app/models/commonTaskList/TaskListSection.scala b/app/models/commonTaskList/TaskListSection.scala new file mode 100644 index 00000000..12d0d75c --- /dev/null +++ b/app/models/commonTaskList/TaskListSection.scala @@ -0,0 +1,25 @@ +/* + * 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 models.commonTaskList + +import play.api.libs.json.{Json, OFormat} + +case class TaskListSection(sectionTitle: SectionTitle, taskItems: Option[Seq[TaskListSectionItem]]) + +object TaskListSection { + implicit val format: OFormat[TaskListSection] = Json.format[TaskListSection] +} diff --git a/app/models/commonTaskList/TaskListSectionItem.scala b/app/models/commonTaskList/TaskListSectionItem.scala new file mode 100644 index 00000000..f71b29b6 --- /dev/null +++ b/app/models/commonTaskList/TaskListSectionItem.scala @@ -0,0 +1,40 @@ +/* + * 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 models.commonTaskList + +import cats.implicits.catsSyntaxOptionId +import models.common.{BusinessId, TaxYear} +import models.commonTaskList.TaskStatus.CheckNow +import models.domain.JourneyNameAndStatus +import play.api.libs.json.{Json, OFormat} + +case class TaskListSectionItem(title: TaskTitle, status: TaskStatus, href: Option[String]) + +object TaskListSectionItem { + implicit val format: OFormat[TaskListSectionItem] = Json.format[TaskListSectionItem] + + def fromJourneys(taxYear: TaxYear, businessId: BusinessId, savedJourneys: Seq[JourneyNameAndStatus]): Seq[TaskListSectionItem] = + SelfEmploymentTitles.values.map { journey => // TODO 8771 only get tailored and default journeys + val maybeJourneyStatus: Option[JourneyNameAndStatus] = savedJourneys.find(_.name.entryName == journey.journeyName) + maybeJourneyStatus match { + case Some(nameAndStatus) => // Status means submission exists -> NotStarted, InProgress or Completed + TaskListSectionItem(journey, nameAndStatus.journeyStatus.toCommonTaskListStatus, journey.getHref(taxYear, businessId, toCYA = true).some) + case None => // No status -> CheckOurRecords or CannotStartYet + TaskListSectionItem(journey, CheckNow(), journey.getHref(taxYear, businessId).some) // TODO 8772 add logic for CannotStartYet status + } + } +} diff --git a/app/models/commonTaskList/TaskStatus.scala b/app/models/commonTaskList/TaskStatus.scala new file mode 100644 index 00000000..1fbe207e --- /dev/null +++ b/app/models/commonTaskList/TaskStatus.scala @@ -0,0 +1,67 @@ +/* + * 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 models.commonTaskList + +import models.common.{Enumerable, WithName} +import play.api.libs.json.{Json, OWrites, Reads} + +trait TaskStatus extends Enumerable.Implicits + +object TaskStatus extends TaskStatus { + + case class Completed() extends WithName("completed") with TaskStatus + object Completed { + implicit val nonStrictReads: Reads[Completed] = Reads.pure(Completed()) + implicit val writes: OWrites[Completed] = OWrites[Completed](_ => Json.obj()) + } + + case class InProgress() extends WithName("inProgress") with TaskStatus + object InProgress { + implicit val nonStrictReads: Reads[InProgress] = Reads.pure(InProgress()) + implicit val writes: OWrites[InProgress] = OWrites[InProgress](_ => Json.obj()) + } + + case class NotStarted() extends WithName("notStarted") with TaskStatus + object NotStarted { + implicit val nonStrictReads: Reads[NotStarted] = Reads.pure(NotStarted()) + implicit val writes: OWrites[NotStarted] = OWrites[NotStarted](_ => Json.obj()) + } + + case class CannotStartYet() extends WithName("cannotStartYet") with TaskStatus + object CannotStartYet { // TODO 8772 this isn't in tailor-returns-FE. Need to let them know + implicit val nonStrictReads: Reads[CannotStartYet] = Reads.pure(CannotStartYet()) + implicit val writes: OWrites[CannotStartYet] = OWrites[CannotStartYet](_ => Json.obj()) + } + + case class CheckNow() extends WithName("checkNow") with TaskStatus + object CheckNow { + implicit val nonStrictReads: Reads[CheckNow] = Reads.pure(CheckNow()) + implicit val writes: OWrites[CheckNow] = OWrites[CheckNow](_ => Json.obj()) + } + + val values: Seq[TaskStatus] = Seq( + Completed(), + InProgress(), + NotStarted(), + CannotStartYet(), + CheckNow() + ) + + implicit val enumerable: Enumerable[TaskStatus] = + Enumerable(values.map(v => v.toString -> v): _*) + +} diff --git a/app/models/commonTaskList/TaskTitle.scala b/app/models/commonTaskList/TaskTitle.scala new file mode 100644 index 00000000..0afb5d14 --- /dev/null +++ b/app/models/commonTaskList/TaskTitle.scala @@ -0,0 +1,37 @@ +/* + * 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 models.commonTaskList + +import models.common.{BusinessId, Enumerable, TaxYear} + +trait TaskTitle extends Enumerable.Implicits { + + val journeyName: String = this.toString + + def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean = false): String = "" +} + +object TaskTitle extends TaskTitle { + + val selfEmploymentTitles: SelfEmploymentTitles.type = SelfEmploymentTitles + + val values: Seq[TaskTitle] = selfEmploymentTitles.values + + implicit val enumerable: Enumerable[TaskTitle] = + Enumerable(values.map(v => v.toString -> v): _*) + +} diff --git a/app/models/domain/JourneyStatusAndLink.scala b/app/models/domain/JourneyStatusAndLink.scala deleted file mode 100644 index 51158901..00000000 --- a/app/models/domain/JourneyStatusAndLink.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.domain - -import models.common.JourneyStatus.NotStarted -import models.common.{BusinessId, JourneyName, JourneyStatus, TaxYear} -import play.api.libs.json.{Json, OFormat} - -final case class JourneyStatusAndLink(name: JourneyName, status: JourneyStatus, href: String) - -object JourneyStatusAndLink { - implicit val format: OFormat[JourneyStatusAndLink] = Json.format[JourneyStatusAndLink] - - def getStatusAndLinksFromJourneys(taxYear: TaxYear, businessId: BusinessId, savedJourneys: List[JourneyNameAndStatus]): List[JourneyStatusAndLink] = - JourneyName.values.map { journey => // TODO should it be getting all journeys, or just the ones that are selected in tailoring? - val maybeJourney: Option[JourneyNameAndStatus] = savedJourneys.find(_.name == journey) - maybeJourney match { - case Some(nameAndStatus) => // TODO do we need logic to determine 'InProgress' like we do in Pensions? - JourneyStatusAndLink(nameAndStatus.name, nameAndStatus.journeyStatus, nameAndStatus.name.getHref(taxYear, businessId, toCYA = true)) - case None => - JourneyStatusAndLink(journey, NotStarted, journey.getHref(taxYear, businessId)) // TODO what if status should be cannot start yet? - } - }.toList -} diff --git a/app/services/journeyAnswers/JourneyStatusService.scala b/app/services/journeyAnswers/JourneyStatusService.scala index a43aba65..35b9e86a 100644 --- a/app/services/journeyAnswers/JourneyStatusService.scala +++ b/app/services/journeyAnswers/JourneyStatusService.scala @@ -20,8 +20,9 @@ import cats.data.EitherT import cats.implicits._ import connectors.SelfEmploymentConnector import models.common._ +import models.commonTaskList.{SectionTitle, TaskListModel, TaskListSection, TaskListSectionItem} import models.connector.api_1171 -import models.domain.{ApiResultT, Business, JourneyStatusAndLink} +import models.domain.{ApiResultT, Business} import models.error.ServiceError import models.frontend.TaskList import repositories.JourneyAnswersRepository @@ -34,7 +35,7 @@ trait JourneyStatusService { def set(ctx: JourneyContext, status: JourneyStatus): ApiResultT[Unit] def get(ctx: JourneyContext): ApiResultT[JourneyStatus] def getTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[TaskList] - def getCommonTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[Seq[List[JourneyStatusAndLink]]] + def getCommonTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[TaskListModel] } @Singleton @@ -64,9 +65,14 @@ class JourneyStatusServiceImpl @Inject() (businessConnector: SelfEmploymentConne } yield taskList } - // TODO do I need to package the response into different businesses (Seq[List[_]] is a placeholder)? - def getCommonTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[Seq[List[JourneyStatusAndLink]]] = - getTaskList(taxYear, mtditid, nino).map(_.businesses.map(journeyList => - JourneyStatusAndLink.getStatusAndLinksFromJourneys(taxYear, journeyList.businessId, journeyList.journeyStatuses))) + def getCommonTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[TaskListModel] = + getTaskList(taxYear, mtditid, nino).map { data => + val selfEmploymentJourneyItems: Seq[TaskListSectionItem] = // This combines lists of journeys for all business IDs + data.businesses.flatMap(journeyList => TaskListSectionItem.fromJourneys(taxYear, journeyList.businessId, journeyList.journeyStatuses)) + val selfEmploymentSections + : Seq[TaskListSection] = // Until business finishes mapping self employment to common TL, all are returned in one section + Seq(TaskListSection(SectionTitle.SelfEmploymentTitle(), selfEmploymentJourneyItems.some)) + TaskListModel(selfEmploymentSections) + } } diff --git a/test/bulders/JourneyNameAndStatusBuilder.scala b/test/bulders/JourneyNameAndStatusBuilder.scala new file mode 100644 index 00000000..878f39d7 --- /dev/null +++ b/test/bulders/JourneyNameAndStatusBuilder.scala @@ -0,0 +1,37 @@ +package bulders + +import models.common.JourneyName._ +import models.common.JourneyStatus.Completed +import models.domain.JourneyStatusAndLink +import utils.BaseSpec.{businessId, taxYear} + +object JourneyNameAndStatusBuilder { + + val expensesJourneysListCompleted = List( + JourneyStatusAndLink(ExpensesTailoring, Completed, ExpensesTailoring.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(GoodsToSellOrUse, Completed, GoodsToSellOrUse.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(WorkplaceRunningCosts, Completed, WorkplaceRunningCosts.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(RepairsAndMaintenanceCosts, Completed, RepairsAndMaintenanceCosts.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(AdvertisingOrMarketing, Completed, AdvertisingOrMarketing.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(OfficeSupplies, Completed, OfficeSupplies.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(Entertainment, Completed, Entertainment.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(StaffCosts, Completed, StaffCosts.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(Construction, Completed, Construction.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(ProfessionalFees, Completed, ProfessionalFees.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(Interest, Completed, Interest.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(OtherExpenses, Completed, OtherExpenses.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(FinancialCharges, Completed, FinancialCharges.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(IrrecoverableDebts, Completed, IrrecoverableDebts.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(Depreciation, Completed, Depreciation.getHref(taxYear, businessId, true)) + ) + + val commonTaskListData = Seq( + List( + JourneyStatusAndLink(TradeDetails, Completed, TradeDetails.getHref(taxYear, businessId)), + JourneyStatusAndLink(SelfEmploymentAbroad, Completed, SelfEmploymentAbroad.getHref(taxYear, businessId, true)), + JourneyStatusAndLink(Income, Completed, Income.getHref(taxYear, businessId, true)) + ) ++ expensesJourneysListCompleted, + List() + ) + +} diff --git a/test/controllers/JourneyStatusControllerSpec.scala b/test/controllers/JourneyStatusControllerSpec.scala index f4918ec8..372e5cde 100644 --- a/test/controllers/JourneyStatusControllerSpec.scala +++ b/test/controllers/JourneyStatusControllerSpec.scala @@ -18,7 +18,7 @@ package controllers import controllers.ControllerBehaviours.{buildRequest, buildRequestNoContent} import models.common.{JourneyName, JourneyStatus} -import models.domain.JourneyNameAndStatus +import models.domain.{JourneyNameAndStatus, JourneyStatusAndLink} import models.frontend.{JourneyStatusData, TaskList} import play.api.http.Status.{NO_CONTENT, OK} import play.api.libs.json.Json @@ -73,13 +73,14 @@ class JourneyStatusControllerSpec extends ControllerBehaviours { "getCommonTaskList" should { "return a list of all " in { + val expectedResult = Seq(List(JourneyStatusAndLink())) behave like testRoute( request = buildRequestNoContent, expectedStatus = OK, expectedBody = Json - .toJson(TaskList.empty) + .toJson(expectedResult) .toString(), - methodBlock = () => underTest.getTaskList(currTaxYear, nino) + methodBlock = () => underTest.getCommonTaskList(currTaxYear, nino) ) } } From 17176a1b9adabae403db3f29caf6a268b85c216f Mon Sep 17 00:00:00 2001 From: Jacob Raffe Date: Tue, 25 Jun 2024 16:59:17 +0100 Subject: [PATCH 3/3] [SASS-8612] Create common task list endpoint --- app/models/common/JourneyStatus.scala | 5 + .../commonTaskList/SelfEmploymentTitles.scala | 110 ++++---- app/models/commonTaskList/TaskListModel.scala | 2 + .../commonTaskList/TaskListSectionItem.scala | 10 +- app/models/commonTaskList/TaskStatus.scala | 2 +- .../bulders/JourneyNameAndStatusBuilder.scala | 235 ++++++++++++++++-- .../JourneyStatusControllerSpec.scala | 16 +- test/models/common/JourneyStatusSpec.scala | 43 ++++ .../TaskListSectionItemSpec.scala | 45 ++++ .../JourneyStatusServiceImplSpec.scala | 49 +++- .../services/StubJourneyStatusService.scala | 13 +- test/utils/BaseSpec.scala | 5 +- 12 files changed, 423 insertions(+), 112 deletions(-) create mode 100644 test/models/common/JourneyStatusSpec.scala create mode 100644 test/models/commonTaskList/TaskListSectionItemSpec.scala diff --git a/app/models/common/JourneyStatus.scala b/app/models/common/JourneyStatus.scala index f0d09103..dce35e75 100644 --- a/app/models/common/JourneyStatus.scala +++ b/app/models/common/JourneyStatus.scala @@ -48,4 +48,9 @@ object JourneyStatus extends Enum[JourneyStatus] with utils.PlayJsonEnum[Journey case object Completed extends JourneyStatus("completed") { override def toCommonTaskListStatus: TaskStatus = TaskStatus.Completed() } + + /** The completion page has been passed with answer Yes */ + case object CannotStartYet extends JourneyStatus("cannotStartYet") { + override def toCommonTaskListStatus: TaskStatus = TaskStatus.CannotStartYet() + } } diff --git a/app/models/commonTaskList/SelfEmploymentTitles.scala b/app/models/commonTaskList/SelfEmploymentTitles.scala index 903ff734..0d06d988 100644 --- a/app/models/commonTaskList/SelfEmploymentTitles.scala +++ b/app/models/commonTaskList/SelfEmploymentTitles.scala @@ -22,12 +22,14 @@ import play.api.libs.json.{Json, OWrites, Reads} object SelfEmploymentTitles { val values: List[TaskTitle] = List( - TradeDetails(), // TODO 8771 should trade details be excluded? + TradeDetails(), // TODO should trade details be excluded, or maybe in it's own section? SelfEmploymentAbroad(), Income(), ExpensesTailoring(), + GoodsToSellOrUse(), WorkplaceRunningCosts(), RepairsAndMaintenanceCosts(), + AdvertisingOrMarketing(), OfficeSupplies(), Entertainment(), StaffCosts(), @@ -49,267 +51,267 @@ object SelfEmploymentTitles { StructuresBuildings() ) - case class TradeDetails() extends WithName("TradeDetailsTitle") with TaskTitle + case class TradeDetails() extends WithName("trade-details") with TaskTitle object TradeDetails { implicit val nonStrictReads: Reads[TradeDetails] = Reads.pure(TradeDetails()) implicit val writes: OWrites[TradeDetails] = OWrites[TradeDetails](_ => Json.obj()) } - case class SelfEmploymentAbroad() extends WithName("SelfEmploymentAbroadTitle") with TaskTitle { + case class SelfEmploymentAbroad() extends WithName("self-employment-abroad") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/details/self-employment-abroad/check" - else s"$taxYear/$businessId/details/self-employment-abroad" + else s"/$taxYear/$businessId/details/self-employment-abroad" } object SelfEmploymentAbroad { implicit val nonStrictReads: Reads[SelfEmploymentAbroad] = Reads.pure(SelfEmploymentAbroad()) implicit val writes: OWrites[SelfEmploymentAbroad] = OWrites[SelfEmploymentAbroad](_ => Json.obj()) } - case class Income() extends WithName("IncomeTitle") with TaskTitle { + case class Income() extends WithName("income") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/income/check-your-income" - else s"$taxYear/$businessId/income/not-counted-turnover" + else s"/$taxYear/$businessId/income/not-counted-turnover" } object Income { implicit val nonStrictReads: Reads[Income] = Reads.pure(Income()) implicit val writes: OWrites[Income] = OWrites[Income](_ => Json.obj()) } - case class ExpensesTailoring() extends WithName("ExpensesTailoringTitle") with TaskTitle { + case class ExpensesTailoring() extends WithName("expenses-categories") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/check" - else s"$taxYear/$businessId/expenses" + else s"/$taxYear/$businessId/expenses" } object ExpensesTailoring { implicit val nonStrictReads: Reads[ExpensesTailoring] = Reads.pure(ExpensesTailoring()) implicit val writes: OWrites[ExpensesTailoring] = OWrites[ExpensesTailoring](_ => Json.obj()) } - case class GoodsToSellOrUse() extends WithName("GoodsToSellOrUseTitle") with TaskTitle { + case class GoodsToSellOrUse() extends WithName("expenses-goods-to-sell-or-use") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/goods-sell-use/check" - else s"$taxYear/$businessId/expenses/goods-sell-use/taxi-minicab-road-haulage-industry-driver" + else s"/$taxYear/$businessId/expenses/goods-sell-use/taxi-minicab-road-haulage-industry-driver" } object GoodsToSellOrUse { implicit val nonStrictReads: Reads[GoodsToSellOrUse] = Reads.pure(GoodsToSellOrUse()) implicit val writes: OWrites[GoodsToSellOrUse] = OWrites[GoodsToSellOrUse](_ => Json.obj()) } - case class WorkplaceRunningCosts() extends WithName("WorkplaceRunningCostsTitle") with TaskTitle { + case class WorkplaceRunningCosts() extends WithName("expenses-workplace-running-costs") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/workplace-running-costs/workplace-running-costs/check" else - s"$taxYear/$businessId/expenses/workplace-running-costs/working-from-home/more-than-25-hours" // TODO 8773 add logic to determine whether this should be WFH or WFBP + s"/$taxYear/$businessId/expenses/workplace-running-costs/working-from-home/more-than-25-hours" // TODO add logic to determine whether this should be WFH or WFBP } object WorkplaceRunningCosts { implicit val nonStrictReads: Reads[WorkplaceRunningCosts] = Reads.pure(WorkplaceRunningCosts()) implicit val writes: OWrites[WorkplaceRunningCosts] = OWrites[WorkplaceRunningCosts](_ => Json.obj()) } - case class RepairsAndMaintenanceCosts() extends WithName("RepairsAndMaintenanceCostsTitle") with TaskTitle { + case class RepairsAndMaintenanceCosts() extends WithName("expenses-repairs-and-maintenance") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/repairs-maintenance/check" - else s"$taxYear/$businessId/expenses/repairs-maintenance/amount" + else s"/$taxYear/$businessId/expenses/repairs-maintenance/amount" } object RepairsAndMaintenanceCosts { implicit val nonStrictReads: Reads[RepairsAndMaintenanceCosts] = Reads.pure(RepairsAndMaintenanceCosts()) implicit val writes: OWrites[RepairsAndMaintenanceCosts] = OWrites[RepairsAndMaintenanceCosts](_ => Json.obj()) } - case class AdvertisingOrMarketing() extends WithName("AdvertisingOrMarketingTitle") with TaskTitle { + case class AdvertisingOrMarketing() extends WithName("expenses-advertising-marketing") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/advertising-marketing/check" - else s"$taxYear/$businessId/expenses/advertising-marketing/amount" + else s"/$taxYear/$businessId/expenses/advertising-marketing/amount" } object AdvertisingOrMarketing { implicit val nonStrictReads: Reads[AdvertisingOrMarketing] = Reads.pure(AdvertisingOrMarketing()) implicit val writes: OWrites[AdvertisingOrMarketing] = OWrites[AdvertisingOrMarketing](_ => Json.obj()) } - case class OfficeSupplies() extends WithName("OfficeSuppliesTitle") with TaskTitle { + case class OfficeSupplies() extends WithName("expenses-office-supplies") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/office-supplies/check" - else s"$taxYear/$businessId/expenses/office-supplies/amount" + else s"/$taxYear/$businessId/expenses/office-supplies/amount" } object OfficeSupplies { implicit val nonStrictReads: Reads[OfficeSupplies] = Reads.pure(OfficeSupplies()) implicit val writes: OWrites[OfficeSupplies] = OWrites[OfficeSupplies](_ => Json.obj()) } - case class Entertainment() extends WithName("EntertainmentTitle") with TaskTitle { + case class Entertainment() extends WithName("expenses-entertainment") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/entertainment/check" - else s"$taxYear/$businessId/expenses/entertainment/disallowable-amount" + else s"/$taxYear/$businessId/expenses/entertainment/disallowable-amount" } object Entertainment { implicit val nonStrictReads: Reads[Entertainment] = Reads.pure(Entertainment()) implicit val writes: OWrites[Entertainment] = OWrites[Entertainment](_ => Json.obj()) } - case class StaffCosts() extends WithName("StaffCostsTitle") with TaskTitle { + case class StaffCosts() extends WithName("expenses-staff-costs") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/staff/check" - else s"$taxYear/$businessId/expenses/staff/amount" + else s"/$taxYear/$businessId/expenses/staff/amount" } object StaffCosts { implicit val nonStrictReads: Reads[StaffCosts] = Reads.pure(StaffCosts()) implicit val writes: OWrites[StaffCosts] = OWrites[StaffCosts](_ => Json.obj()) } - case class Construction() extends WithName("ConstructionTitle") with TaskTitle { + case class Construction() extends WithName("expenses-construction") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/construction-industry/check" - else s"$taxYear/$businessId/expenses/construction-industry/amount" + else s"/$taxYear/$businessId/expenses/construction-industry/amount" } object Construction { implicit val nonStrictReads: Reads[Construction] = Reads.pure(Construction()) implicit val writes: OWrites[Construction] = OWrites[Construction](_ => Json.obj()) } - case class ProfessionalFees() extends WithName("ProfessionalFeesTitle") with TaskTitle { + case class ProfessionalFees() extends WithName("expenses-professional-fees") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/professional-fees/check" - else s"$taxYear/$businessId/expenses/professional-fees/amount" + else s"/$taxYear/$businessId/expenses/professional-fees/amount" } object ProfessionalFees { implicit val nonStrictReads: Reads[ProfessionalFees] = Reads.pure(ProfessionalFees()) implicit val writes: OWrites[ProfessionalFees] = OWrites[ProfessionalFees](_ => Json.obj()) } - case class Interest() extends WithName("InterestTitle") with TaskTitle { + case class Interest() extends WithName("expenses-interest") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/interest-bank-business-loans/check" - else s"$taxYear/$businessId/expenses/interest-bank-business-loans/amount" + else s"/$taxYear/$businessId/expenses/interest-bank-business-loans/amount" } object Interest { implicit val nonStrictReads: Reads[Interest] = Reads.pure(Interest()) implicit val writes: OWrites[Interest] = OWrites[Interest](_ => Json.obj()) } - case class OtherExpenses() extends WithName("OtherExpensesTitle") with TaskTitle { + case class OtherExpenses() extends WithName("expenses-other-expenses") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/other-expenses/check " - else s"$taxYear/$businessId/expenses/other-expenses/amount" + else s"/$taxYear/$businessId/expenses/other-expenses/amount" } object OtherExpenses { implicit val nonStrictReads: Reads[OtherExpenses] = Reads.pure(OtherExpenses()) implicit val writes: OWrites[OtherExpenses] = OWrites[OtherExpenses](_ => Json.obj()) } - case class FinancialCharges() extends WithName("FinancialChargesTitle") with TaskTitle { + case class FinancialCharges() extends WithName("expenses-financial-charges") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/bank-credit-card-financial-charges/check" - else s"$taxYear/$businessId/expenses/bank-credit-card-financial-charges/amount" + else s"/$taxYear/$businessId/expenses/bank-credit-card-financial-charges/amount" } object FinancialCharges { implicit val nonStrictReads: Reads[FinancialCharges] = Reads.pure(FinancialCharges()) implicit val writes: OWrites[FinancialCharges] = OWrites[FinancialCharges](_ => Json.obj()) } - case class IrrecoverableDebts() extends WithName("IrrecoverableDebtsTitle") with TaskTitle { + case class IrrecoverableDebts() extends WithName("expenses-irrecoverable-debts") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/irrecoverable-debts/check" - else s"$taxYear/$businessId/expenses/irrecoverable-debts/amount" + else s"/$taxYear/$businessId/expenses/irrecoverable-debts/amount" } object IrrecoverableDebts { implicit val nonStrictReads: Reads[IrrecoverableDebts] = Reads.pure(IrrecoverableDebts()) implicit val writes: OWrites[IrrecoverableDebts] = OWrites[IrrecoverableDebts](_ => Json.obj()) } - case class Depreciation() extends WithName("DepreciationTitle") with TaskTitle { + case class Depreciation() extends WithName("expenses-depreciation") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/expenses/depreciation/check" - else s"$taxYear/$businessId/expenses/depreciation/disallowable-amount" + else s"/$taxYear/$businessId/expenses/depreciation/disallowable-amount" } object Depreciation { implicit val nonStrictReads: Reads[Depreciation] = Reads.pure(Depreciation()) implicit val writes: OWrites[Depreciation] = OWrites[Depreciation](_ => Json.obj()) } - case class CapitalAllowancesTailoring() extends WithName("CapitalAllowancesTailoringTitle") with TaskTitle { + case class CapitalAllowancesTailoring() extends WithName("capital-allowances-tailoring") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/check" - else s"$taxYear/$businessId/capital-allowances" + else s"/$taxYear/$businessId/capital-allowances" } object CapitalAllowancesTailoring { implicit val nonStrictReads: Reads[CapitalAllowancesTailoring] = Reads.pure(CapitalAllowancesTailoring()) implicit val writes: OWrites[CapitalAllowancesTailoring] = OWrites[CapitalAllowancesTailoring](_ => Json.obj()) } - case class ZeroEmissionCars() extends WithName("ZeroEmissionCarsTitle") with TaskTitle { + case class ZeroEmissionCars() extends WithName("capital-allowances-zero-emission-cars") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/cars/check" - else s"$taxYear/$businessId/capital-allowances/cars" + else s"/$taxYear/$businessId/capital-allowances/cars" } object ZeroEmissionCars { implicit val nonStrictReads: Reads[ZeroEmissionCars] = Reads.pure(ZeroEmissionCars()) implicit val writes: OWrites[ZeroEmissionCars] = OWrites[ZeroEmissionCars](_ => Json.obj()) } - case class ZeroEmissionGoodsVehicle() extends WithName("ZeroEmissionGoodsVehicleTitle") with TaskTitle { + case class ZeroEmissionGoodsVehicle() extends WithName("capital-allowances-zero-emission-goods-vehicle") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/goods-vehicles/check" - else s"$taxYear/$businessId/capital-allowances/goods-vehicles" + else s"/$taxYear/$businessId/capital-allowances/goods-vehicles" } object ZeroEmissionGoodsVehicle { implicit val nonStrictReads: Reads[ZeroEmissionGoodsVehicle] = Reads.pure(ZeroEmissionGoodsVehicle()) implicit val writes: OWrites[ZeroEmissionGoodsVehicle] = OWrites[ZeroEmissionGoodsVehicle](_ => Json.obj()) } - case class ElectricVehicleChargePoints() extends WithName("ElectricVehicleChargePointsTitle") with TaskTitle { + case class ElectricVehicleChargePoints() extends WithName("capital-allowances-electric-vehicle-charge-points") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/check" - else s"$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/buy" + else s"/$taxYear/$businessId/capital-allowances/electric-vehicle-charge-points/buy" } object ElectricVehicleChargePoints { implicit val nonStrictReads: Reads[ElectricVehicleChargePoints] = Reads.pure(ElectricVehicleChargePoints()) implicit val writes: OWrites[ElectricVehicleChargePoints] = OWrites[ElectricVehicleChargePoints](_ => Json.obj()) } - case class BalancingAllowance() extends WithName("BalancingAllowanceTitle") with TaskTitle { + case class BalancingAllowance() extends WithName("capital-allowances-balancing-allowance") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/balancing-allowance/check" - else s"$taxYear/$businessId/capital-allowances/balancing-allowance" + else s"/$taxYear/$businessId/capital-allowances/balancing-allowance" } object BalancingAllowance { implicit val nonStrictReads: Reads[BalancingAllowance] = Reads.pure(BalancingAllowance()) implicit val writes: OWrites[BalancingAllowance] = OWrites[BalancingAllowance](_ => Json.obj()) } - case class WritingDownAllowance() extends WithName("WritingDownAllowanceTitle") with TaskTitle { + case class WritingDownAllowance() extends WithName("capital-allowances-writing-down-allowance") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/writing-down-allowance/check" - else s"$taxYear/$businessId/capital-allowances/writing-down-allowance" + else s"/$taxYear/$businessId/capital-allowances/writing-down-allowance" } object WritingDownAllowance { implicit val nonStrictReads: Reads[WritingDownAllowance] = Reads.pure(WritingDownAllowance()) implicit val writes: OWrites[WritingDownAllowance] = OWrites[WritingDownAllowance](_ => Json.obj()) } - case class AnnualInvestmentAllowance() extends WithName("AnnualInvestmentAllowanceTitle") with TaskTitle { + case class AnnualInvestmentAllowance() extends WithName("capital-allowances-annual-investment-allowance") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/check" - else s"$taxYear/$businessId/capital-allowances/details" + else s"/$taxYear/$businessId/capital-allowances/details" } object AnnualInvestmentAllowance { implicit val nonStrictReads: Reads[AnnualInvestmentAllowance] = Reads.pure(AnnualInvestmentAllowance()) implicit val writes: OWrites[AnnualInvestmentAllowance] = OWrites[AnnualInvestmentAllowance](_ => Json.obj()) } - case class SpecialTaxSites() extends WithName("SpecialTaxSitesTitle") with TaskTitle { + case class SpecialTaxSites() extends WithName("capital-allowances-special-tax-sites") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/special-tax-sites/check" - else s"$taxYear/$businessId/capital-allowances/special-tax-sites" + else s"/$taxYear/$businessId/capital-allowances/special-tax-sites" } object SpecialTaxSites { implicit val nonStrictReads: Reads[SpecialTaxSites] = Reads.pure(SpecialTaxSites()) implicit val writes: OWrites[SpecialTaxSites] = OWrites[SpecialTaxSites](_ => Json.obj()) } - case class StructuresBuildings() extends WithName("StructuresBuildingsTitle") with TaskTitle { + case class StructuresBuildings() extends WithName("capital-allowances-structures-buildings") with TaskTitle { override def getHref(taxYear: TaxYear, businessId: BusinessId, toCYA: Boolean): String = if (toCYA) s"/$taxYear/$businessId/capital-allowances/structures-buildings/check" - else s"$taxYear/$businessId/capital-allowances/structures-buildings" + else s"/$taxYear/$businessId/capital-allowances/structures-buildings" } object StructuresBuildings { implicit val nonStrictReads: Reads[StructuresBuildings] = Reads.pure(StructuresBuildings()) diff --git a/app/models/commonTaskList/TaskListModel.scala b/app/models/commonTaskList/TaskListModel.scala index c94c00fc..3a6bb11d 100644 --- a/app/models/commonTaskList/TaskListModel.scala +++ b/app/models/commonTaskList/TaskListModel.scala @@ -22,4 +22,6 @@ case class TaskListModel(taskList: Seq[TaskListSection]) object TaskListModel { implicit val format: OFormat[TaskListModel] = Json.format[TaskListModel] + + val empty = TaskListModel(Nil) } diff --git a/app/models/commonTaskList/TaskListSectionItem.scala b/app/models/commonTaskList/TaskListSectionItem.scala index f71b29b6..f2f3e230 100644 --- a/app/models/commonTaskList/TaskListSectionItem.scala +++ b/app/models/commonTaskList/TaskListSectionItem.scala @@ -18,7 +18,7 @@ package models.commonTaskList import cats.implicits.catsSyntaxOptionId import models.common.{BusinessId, TaxYear} -import models.commonTaskList.TaskStatus.CheckNow +import models.commonTaskList.TaskStatus._ import models.domain.JourneyNameAndStatus import play.api.libs.json.{Json, OFormat} @@ -28,13 +28,17 @@ object TaskListSectionItem { implicit val format: OFormat[TaskListSectionItem] = Json.format[TaskListSectionItem] def fromJourneys(taxYear: TaxYear, businessId: BusinessId, savedJourneys: Seq[JourneyNameAndStatus]): Seq[TaskListSectionItem] = - SelfEmploymentTitles.values.map { journey => // TODO 8771 only get tailored and default journeys + SelfEmploymentTitles.values.map { journey => // TODO only get default and tailored journeys, not all val maybeJourneyStatus: Option[JourneyNameAndStatus] = savedJourneys.find(_.name.entryName == journey.journeyName) maybeJourneyStatus match { case Some(nameAndStatus) => // Status means submission exists -> NotStarted, InProgress or Completed TaskListSectionItem(journey, nameAndStatus.journeyStatus.toCommonTaskListStatus, journey.getHref(taxYear, businessId, toCYA = true).some) case None => // No status -> CheckOurRecords or CannotStartYet - TaskListSectionItem(journey, CheckNow(), journey.getHref(taxYear, businessId).some) // TODO 8772 add logic for CannotStartYet status + TaskListSectionItem( + journey, + CheckNow(), + journey.getHref(taxYear, businessId).some + ) // TODO move logic for CannotStartYet status from FE to BE } } } diff --git a/app/models/commonTaskList/TaskStatus.scala b/app/models/commonTaskList/TaskStatus.scala index 1fbe207e..db51ed58 100644 --- a/app/models/commonTaskList/TaskStatus.scala +++ b/app/models/commonTaskList/TaskStatus.scala @@ -42,7 +42,7 @@ object TaskStatus extends TaskStatus { } case class CannotStartYet() extends WithName("cannotStartYet") with TaskStatus - object CannotStartYet { // TODO 8772 this isn't in tailor-returns-FE. Need to let them know + object CannotStartYet { implicit val nonStrictReads: Reads[CannotStartYet] = Reads.pure(CannotStartYet()) implicit val writes: OWrites[CannotStartYet] = OWrites[CannotStartYet](_ => Json.obj()) } diff --git a/test/bulders/JourneyNameAndStatusBuilder.scala b/test/bulders/JourneyNameAndStatusBuilder.scala index 878f39d7..1afde7c0 100644 --- a/test/bulders/JourneyNameAndStatusBuilder.scala +++ b/test/bulders/JourneyNameAndStatusBuilder.scala @@ -1,37 +1,222 @@ +/* + * 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 bulders +import cats.implicits.catsSyntaxOptionId import models.common.JourneyName._ -import models.common.JourneyStatus.Completed -import models.domain.JourneyStatusAndLink +import models.common.JourneyStatus._ +import models.commonTaskList.{SelfEmploymentTitles, TaskListSectionItem, TaskStatus} +import models.domain.JourneyNameAndStatus import utils.BaseSpec.{businessId, taxYear} object JourneyNameAndStatusBuilder { - val expensesJourneysListCompleted = List( - JourneyStatusAndLink(ExpensesTailoring, Completed, ExpensesTailoring.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(GoodsToSellOrUse, Completed, GoodsToSellOrUse.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(WorkplaceRunningCosts, Completed, WorkplaceRunningCosts.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(RepairsAndMaintenanceCosts, Completed, RepairsAndMaintenanceCosts.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(AdvertisingOrMarketing, Completed, AdvertisingOrMarketing.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(OfficeSupplies, Completed, OfficeSupplies.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(Entertainment, Completed, Entertainment.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(StaffCosts, Completed, StaffCosts.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(Construction, Completed, Construction.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(ProfessionalFees, Completed, ProfessionalFees.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(Interest, Completed, Interest.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(OtherExpenses, Completed, OtherExpenses.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(FinancialCharges, Completed, FinancialCharges.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(IrrecoverableDebts, Completed, IrrecoverableDebts.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(Depreciation, Completed, Depreciation.getHref(taxYear, businessId, true)) + val completedExpensesJourneys: Seq[JourneyNameAndStatus] = List( + JourneyNameAndStatus(ExpensesTailoring, Completed), + JourneyNameAndStatus(GoodsToSellOrUse, Completed), + JourneyNameAndStatus(WorkplaceRunningCosts, Completed), + JourneyNameAndStatus(RepairsAndMaintenanceCosts, Completed), + JourneyNameAndStatus(AdvertisingOrMarketing, Completed), + JourneyNameAndStatus(OfficeSupplies, Completed), + JourneyNameAndStatus(Entertainment, Completed), + JourneyNameAndStatus(StaffCosts, Completed), + JourneyNameAndStatus(Construction, Completed), + JourneyNameAndStatus(ProfessionalFees, Completed), + JourneyNameAndStatus(Interest, Completed), + JourneyNameAndStatus(OtherExpenses, Completed), + JourneyNameAndStatus(FinancialCharges, Completed), + JourneyNameAndStatus(IrrecoverableDebts, Completed), + JourneyNameAndStatus(Depreciation, Completed) + ) + + val completedCapitalAllowancesJourneys: Seq[JourneyNameAndStatus] = List( + JourneyNameAndStatus(CapitalAllowancesTailoring, Completed), + JourneyNameAndStatus(ZeroEmissionCars, Completed), + JourneyNameAndStatus(ZeroEmissionGoodsVehicle, Completed), + JourneyNameAndStatus(ElectricVehicleChargePoints, Completed), + JourneyNameAndStatus(BalancingAllowance, Completed), + JourneyNameAndStatus(WritingDownAllowance, Completed), + JourneyNameAndStatus(AnnualInvestmentAllowance, Completed), + JourneyNameAndStatus(SpecialTaxSites, Completed), + JourneyNameAndStatus(StructuresBuildings, Completed) + ) + + val allCompetedJourneyStatuses: Seq[JourneyNameAndStatus] = Seq( + JourneyNameAndStatus(TradeDetails, Completed), + JourneyNameAndStatus(SelfEmploymentAbroad, Completed), + JourneyNameAndStatus(Income, Completed) + ) ++ completedExpensesJourneys ++ completedCapitalAllowancesJourneys + + val mixedPartialJourneyStatuses: Seq[JourneyNameAndStatus] = Seq( + JourneyNameAndStatus(TradeDetails, Completed), + JourneyNameAndStatus(SelfEmploymentAbroad, InProgress), + JourneyNameAndStatus(Income, InProgress) + ) ++ completedExpensesJourneys.map(_.copy(journeyStatus = NotStarted)) + + val completedExpensesTaskListSectionItems: Seq[TaskListSectionItem] = List( + TaskListSectionItem( + SelfEmploymentTitles.ExpensesTailoring(), + TaskStatus.Completed(), + SelfEmploymentTitles.ExpensesTailoring().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.GoodsToSellOrUse(), + TaskStatus.Completed(), + SelfEmploymentTitles.GoodsToSellOrUse().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.WorkplaceRunningCosts(), + TaskStatus.Completed(), + SelfEmploymentTitles.WorkplaceRunningCosts().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.RepairsAndMaintenanceCosts(), + TaskStatus.Completed(), + SelfEmploymentTitles.RepairsAndMaintenanceCosts().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.AdvertisingOrMarketing(), + TaskStatus.Completed(), + SelfEmploymentTitles.AdvertisingOrMarketing().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.OfficeSupplies(), + TaskStatus.Completed(), + SelfEmploymentTitles.OfficeSupplies().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.Entertainment(), + TaskStatus.Completed(), + SelfEmploymentTitles.Entertainment().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.StaffCosts(), + TaskStatus.Completed(), + SelfEmploymentTitles.StaffCosts().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.Construction(), + TaskStatus.Completed(), + SelfEmploymentTitles.Construction().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.ProfessionalFees(), + TaskStatus.Completed(), + SelfEmploymentTitles.ProfessionalFees().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.Interest(), + TaskStatus.Completed(), + SelfEmploymentTitles.Interest().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.OtherExpenses(), + TaskStatus.Completed(), + SelfEmploymentTitles.OtherExpenses().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.FinancialCharges(), + TaskStatus.Completed(), + SelfEmploymentTitles.FinancialCharges().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.IrrecoverableDebts(), + TaskStatus.Completed(), + SelfEmploymentTitles.IrrecoverableDebts().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.Depreciation(), + TaskStatus.Completed(), + SelfEmploymentTitles.Depreciation().getHref(taxYear, businessId, toCYA = true).some) ) - val commonTaskListData = Seq( - List( - JourneyStatusAndLink(TradeDetails, Completed, TradeDetails.getHref(taxYear, businessId)), - JourneyStatusAndLink(SelfEmploymentAbroad, Completed, SelfEmploymentAbroad.getHref(taxYear, businessId, true)), - JourneyStatusAndLink(Income, Completed, Income.getHref(taxYear, businessId, true)) - ) ++ expensesJourneysListCompleted, - List() + val completedCapitalAllowancesTaskListSectionItems: Seq[TaskListSectionItem] = List( + TaskListSectionItem( + SelfEmploymentTitles.CapitalAllowancesTailoring(), + TaskStatus.Completed(), + SelfEmploymentTitles.CapitalAllowancesTailoring().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.ZeroEmissionCars(), + TaskStatus.Completed(), + SelfEmploymentTitles.ZeroEmissionCars().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.ZeroEmissionGoodsVehicle(), + TaskStatus.Completed(), + SelfEmploymentTitles.ZeroEmissionGoodsVehicle().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.ElectricVehicleChargePoints(), + TaskStatus.Completed(), + SelfEmploymentTitles.ElectricVehicleChargePoints().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.BalancingAllowance(), + TaskStatus.Completed(), + SelfEmploymentTitles.BalancingAllowance().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.WritingDownAllowance(), + TaskStatus.Completed(), + SelfEmploymentTitles.WritingDownAllowance().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.AnnualInvestmentAllowance(), + TaskStatus.Completed(), + SelfEmploymentTitles.AnnualInvestmentAllowance().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.SpecialTaxSites(), + TaskStatus.Completed(), + SelfEmploymentTitles.SpecialTaxSites().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.StructuresBuildings(), + TaskStatus.Completed(), + SelfEmploymentTitles.StructuresBuildings().getHref(taxYear, businessId, toCYA = true).some + ) ) + val allCompletedTaskListSectionItems: Seq[TaskListSectionItem] = Seq( + TaskListSectionItem( + SelfEmploymentTitles.TradeDetails(), + TaskStatus.Completed(), + SelfEmploymentTitles.TradeDetails().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.SelfEmploymentAbroad(), + TaskStatus.Completed(), + SelfEmploymentTitles.SelfEmploymentAbroad().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.Income(), + TaskStatus.Completed(), + SelfEmploymentTitles.Income().getHref(taxYear, businessId, toCYA = true).some) + ) ++ completedExpensesTaskListSectionItems ++ completedCapitalAllowancesTaskListSectionItems + + val allCheckNowTaskListSectionItems: Seq[TaskListSectionItem] = + allCompletedTaskListSectionItems.map(s => s.copy(status = TaskStatus.CheckNow(), href = s.title.getHref(taxYear, businessId).some)) + + val notStartedExpensesTaskListSectionItems: Seq[TaskListSectionItem] = completedExpensesTaskListSectionItems.map(s => + s.copy(status = TaskStatus.NotStarted(), href = s.title.getHref(taxYear, businessId, toCYA = true).some)) + val checkNowCapitalAllowancesTaskListSectionItems: Seq[TaskListSectionItem] = + completedCapitalAllowancesTaskListSectionItems.map(s => s.copy(status = TaskStatus.CheckNow(), href = s.title.getHref(taxYear, businessId).some)) + + val mixedTaskListSectionItems: Seq[TaskListSectionItem] = Seq( + TaskListSectionItem( + SelfEmploymentTitles.TradeDetails(), + TaskStatus.Completed(), + SelfEmploymentTitles.TradeDetails().getHref(taxYear, businessId, toCYA = true).some), + TaskListSectionItem( + SelfEmploymentTitles.SelfEmploymentAbroad(), + TaskStatus.InProgress(), + SelfEmploymentTitles.SelfEmploymentAbroad().getHref(taxYear, businessId, toCYA = true).some + ), + TaskListSectionItem( + SelfEmploymentTitles.Income(), + TaskStatus.InProgress(), + SelfEmploymentTitles.Income().getHref(taxYear, businessId, toCYA = true).some) + ) ++ notStartedExpensesTaskListSectionItems ++ checkNowCapitalAllowancesTaskListSectionItems } diff --git a/test/controllers/JourneyStatusControllerSpec.scala b/test/controllers/JourneyStatusControllerSpec.scala index 372e5cde..1353f222 100644 --- a/test/controllers/JourneyStatusControllerSpec.scala +++ b/test/controllers/JourneyStatusControllerSpec.scala @@ -18,7 +18,7 @@ package controllers import controllers.ControllerBehaviours.{buildRequest, buildRequestNoContent} import models.common.{JourneyName, JourneyStatus} -import models.domain.{JourneyNameAndStatus, JourneyStatusAndLink} +import models.domain.JourneyNameAndStatus import models.frontend.{JourneyStatusData, TaskList} import play.api.http.Status.{NO_CONTENT, OK} import play.api.libs.json.Json @@ -71,18 +71,4 @@ class JourneyStatusControllerSpec extends ControllerBehaviours { } } - "getCommonTaskList" should { - "return a list of all " in { - val expectedResult = Seq(List(JourneyStatusAndLink())) - behave like testRoute( - request = buildRequestNoContent, - expectedStatus = OK, - expectedBody = Json - .toJson(expectedResult) - .toString(), - methodBlock = () => underTest.getCommonTaskList(currTaxYear, nino) - ) - } - } - } diff --git a/test/models/common/JourneyStatusSpec.scala b/test/models/common/JourneyStatusSpec.scala new file mode 100644 index 00000000..ce855f54 --- /dev/null +++ b/test/models/common/JourneyStatusSpec.scala @@ -0,0 +1,43 @@ +/* + * 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.common + +import models.common.JourneyStatus._ +import models.commonTaskList.TaskStatus +import org.scalatest.prop.TableDrivenPropertyChecks +import utils.BaseSpec + +class JourneyStatusSpec extends BaseSpec with TableDrivenPropertyChecks { + + private val cases = Table( + ("journeyStatus", "taskStatus"), + (CheckOurRecords, TaskStatus.CheckNow()), + (NotStarted, TaskStatus.NotStarted()), + (InProgress, TaskStatus.InProgress()), + (Completed, TaskStatus.Completed()), + (CannotStartYet, TaskStatus.CannotStartYet()) + ) + + "toCommonTaskListStatus" should { + "convert JourneyStatus to equivalent TaskStatus" in { + forAll(cases) { (journeyStatus, taskStatus) => + assert(journeyStatus.toCommonTaskListStatus == taskStatus) + } + } + } + +} diff --git a/test/models/commonTaskList/TaskListSectionItemSpec.scala b/test/models/commonTaskList/TaskListSectionItemSpec.scala new file mode 100644 index 00000000..eeb71cb3 --- /dev/null +++ b/test/models/commonTaskList/TaskListSectionItemSpec.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 models.commonTaskList + +import bulders.JourneyNameAndStatusBuilder._ +import org.scalatest.prop.TableDrivenPropertyChecks +import utils.BaseSpec +import utils.BaseSpec.{businessId, taxYear} + +class TaskListSectionItemSpec extends BaseSpec with TableDrivenPropertyChecks { + + private val cases = Table( + ("caseDescription", "savedData", "expectedResult"), + ("all journeys have a saved completed status", allCompetedJourneyStatuses, allCompletedTaskListSectionItems), + ("there are no saved journey statuses", Seq.empty, allCheckNowTaskListSectionItems), + ("there are a mix of InProgress, Completed, NotStarted and no status", mixedPartialJourneyStatuses, mixedTaskListSectionItems) + ) + + "fromJourneys" should { + "return TaskListSectionItems for all SelfEmploymentTitles.values with correct statuses and hrefs" when { + forAll(cases) { (caseDescription, savedData, expectedResult) => + s"$caseDescription" in { + val result = TaskListSectionItem.fromJourneys(taxYear, businessId, savedData) + + assert(result == expectedResult) + } + } + } + } + +} diff --git a/test/services/journeyAnswers/JourneyStatusServiceImplSpec.scala b/test/services/journeyAnswers/JourneyStatusServiceImplSpec.scala index a11453d7..365dfc3b 100644 --- a/test/services/journeyAnswers/JourneyStatusServiceImplSpec.scala +++ b/test/services/journeyAnswers/JourneyStatusServiceImplSpec.scala @@ -16,19 +16,26 @@ package services.journeyAnswers -import models.common.JourneyStatus._ -import org.scalatest.concurrent.ScalaFutures.convertScalaFuture -import org.scalatest.wordspec.AnyWordSpecLike -import stubs.connectors.StubSelfEmploymentConnector -import stubs.repositories.StubJourneyAnswersRepository -import utils.BaseSpec._ +import bulders.BusinessDataBuilder.aTradesJourneyStatusesSeq +import bulders.JourneyNameAndStatusBuilder.{allCompetedJourneyStatuses, allCompletedTaskListSectionItems} import cats.implicits._ +import models.common.JourneyStatus._ import models.common.{JourneyName, JourneyStatus} +import models.commonTaskList.SectionTitle.SelfEmploymentTitle +import models.commonTaskList.{TaskListModel, TaskListSection} import models.database.JourneyAnswers import models.domain.JourneyNameAndStatus +import models.error.DownstreamError.SingleDownstreamError +import models.error.DownstreamErrorBody.SingleDownstreamErrorBody import models.frontend.TaskList +import org.scalatest.concurrent.ScalaFutures.convertScalaFuture import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike +import play.api.http.Status.INTERNAL_SERVER_ERROR import play.api.libs.json.JsObject +import stubs.connectors.StubSelfEmploymentConnector +import stubs.repositories.StubJourneyAnswersRepository +import utils.BaseSpec._ import java.time.Instant @@ -85,4 +92,34 @@ class JourneyStatusServiceImplSpec extends AnyWordSpecLike with Matchers { result.value.futureValue shouldBe taskList.asRight } } + + "getCommonTaskList" should { + "create TaskListModel from saved journey statuses" in { + val taskList = TaskList( + Some(JourneyNameAndStatus(JourneyName.TradeDetails, JourneyStatus.Completed)), + aTradesJourneyStatusesSeq.map(_.copy(journeyStatuses = allCompetedJourneyStatuses.toList)) + ) + val underTest = new JourneyStatusServiceImpl( + businessConnector, + repository.copy( + getAllResult = Right(taskList) + ) + ) + val result = underTest.getCommonTaskList(taxYear, mtditid, nino) + result.value.futureValue shouldBe TaskListModel(List(TaskListSection(SelfEmploymentTitle(), Some(allCompletedTaskListSectionItems)))).asRight + } + + "return an error from downstream" in { + val downstreamError = SingleDownstreamError(INTERNAL_SERVER_ERROR, SingleDownstreamErrorBody.parsingError) + val underTest = new JourneyStatusServiceImpl( + businessConnector, + repository.copy( + getAllResult = downstreamError.asLeft + ) + ) + + val result = underTest.getTaskList(taxYear, mtditid, nino) + result.value.futureValue shouldBe downstreamError.asLeft + } + } } diff --git a/test/stubs/services/StubJourneyStatusService.scala b/test/stubs/services/StubJourneyStatusService.scala index cd59a427..7790cef5 100644 --- a/test/stubs/services/StubJourneyStatusService.scala +++ b/test/stubs/services/StubJourneyStatusService.scala @@ -17,20 +17,22 @@ package stubs.services import cats.data.EitherT -import models.common.{JourneyContext, JourneyStatus, Mtditid, Nino, TaxYear} +import cats.implicits._ +import models.common._ +import models.commonTaskList.TaskListModel import models.domain.ApiResultT +import models.error.ServiceError import models.frontend.TaskList import services.journeyAnswers.JourneyStatusService import uk.gov.hmrc.http.HeaderCarrier -import cats.implicits._ -import models.error.ServiceError import scala.concurrent.{ExecutionContext, Future} case class StubJourneyStatusService( setRes: Either[ServiceError, Unit] = ().asRight[ServiceError], getRes: Either[ServiceError, JourneyStatus] = JourneyStatus.CheckOurRecords.asRight[ServiceError], - getTaskListRes: Either[ServiceError, TaskList] = TaskList.empty.asRight[ServiceError] + getTaskListRes: Either[ServiceError, TaskList] = TaskList.empty.asRight[ServiceError], + getCommonTaskListRes: Either[ServiceError, TaskListModel] = TaskListModel.empty.asRight[ServiceError] ) extends JourneyStatusService { implicit val ec: ExecutionContext = ExecutionContext.global @@ -40,4 +42,7 @@ case class StubJourneyStatusService( def getTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[TaskList] = EitherT.fromEither[Future](getTaskListRes) + + def getCommonTaskList(taxYear: TaxYear, mtditid: Mtditid, nino: Nino)(implicit hc: HeaderCarrier): ApiResultT[TaskListModel] = + EitherT.fromEither[Future](getCommonTaskListRes) } diff --git a/test/utils/BaseSpec.scala b/test/utils/BaseSpec.scala index d5d6b172..8c9849bc 100644 --- a/test/utils/BaseSpec.scala +++ b/test/utils/BaseSpec.scala @@ -37,9 +37,6 @@ trait BaseSpec extends AnyWordSpec with MockitoSugar with ArgumentMatchersSugar protected implicit val ec: ExecutionContext = ExecutionContext.Implicits.global protected implicit val hc: HeaderCarrier = HeaderCarrier() - protected val businessId: BusinessId = BusinessId("someBusinessId") - protected val nino: Nino = Nino("someNino") - protected val stubControllerComponents: ControllerComponents = Helpers.stubControllerComponents() protected val defaultActionBuilder: DefaultActionBuilder = DefaultActionBuilder(stubControllerComponents.parsers.default) @@ -54,7 +51,7 @@ object BaseSpec { val taxYear: TaxYear = TaxYear(2024) val taxYearStart: String = TaxYear.startDate(taxYear) val taxYearEnd: String = TaxYear.endDate(taxYear) - val businessId: BusinessId = BusinessId("someBusinessId") + val businessId: BusinessId = BusinessId("SJPR05893938418") val nino: Nino = Nino("nino") val mtditid: Mtditid = Mtditid("1234567890")