Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions app/controllers/IncomeSourceSummaryController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ package controllers
import cats.implicits.*
import controllers.auth.AuthJourney
import pages.benefits.EndCompanyBenefitsUpdateIncomePage
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents, Result}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.http.UpstreamErrorResponse
import uk.gov.hmrc.play.audit.http.connector.AuditConnector
import uk.gov.hmrc.tai.model.TaxYear
import uk.gov.hmrc.tai.model.domain.{Available, Employment, IabdDetails, TemporarilyUnavailable}
import uk.gov.hmrc.tai.service.{EmploymentService, IabdService, RtiService, TaxAccountService}
import uk.gov.hmrc.tai.util.{EmpIdCheck, TaxAccountHelper}
import uk.gov.hmrc.tai.viewModels.IncomeSourceSummaryViewModel
import views.html.IncomeSourceSummaryView
import uk.gov.hmrc.tai.model.domain.{Available, Employment, IabdDetails}

import javax.inject.Inject
import scala.concurrent.{ExecutionContext, Future}
Expand Down Expand Up @@ -62,34 +62,38 @@ class IncomeSourceSummaryController @Inject() (
taxAccountService.taxCodeIncomes(nino, TaxYear()),
taxAccountService.taxAccountSummary(nino, TaxYear()).value,
rtiService.getPaymentsForEmploymentAndYear(nino, TaxYear(), empId).value,
rtiService.getAllPaymentsForYear(nino, TaxYear()).value,
cacheUpdatedIncomeAmountFuture,
iabdService.getIabds(nino, TaxYear()).value
).mapN {
case (
Some(employment),
taxCodeIncomes,
taxAccountSummary,
payments,
paymentsForEmp,
paymentsForYear,
cacheUpdatedIncomeAmount,
Right(iabds)
) =>
val TaxAccountSummaryDate = taxAccountSummary.fold(_ => None, _.date)
val estimatedPayOverrides =
TaxAccountHelper.getIabdLatestEstimatedIncome(iabds, TaxAccountSummaryDate, Some(empId))

val rtiUnavailableMarkerPresent: Boolean = paymentsForYear.toOption
.exists(_.exists(a => a.sequenceNumber == 0 && a.realTimeStatus == TemporarilyUnavailable))

val rtiAvailableCalculated: Boolean =
paymentsForEmp.fold(_ => false, _.fold(false)(_.realTimeStatus == Available))

val vm = IncomeSourceSummaryViewModel.apply(
empId = empId,
displayName = request.fullName,
optTaxCodeIncome =
taxCodeIncomes.fold(_ => None, _.find(_.employmentId.contains(employment.sequenceNumber))),
employment = employment,
payments = payments.toOption.flatten,
// TODO: handle a failure vs no payment present
// The way the rti availability is implemented using a stub Annual account is not compatible with None type
// So when no annual account found for an employment, assuming rti is down.
// The service also does not handle the case when there is no payments but assume the rti api not been available.
rtiAvailable = payments.fold(_ => false, _.fold(false)(_.realTimeStatus == Available)),
cacheUpdatedIncomeAmount = cacheUpdatedIncomeAmount,
payments = paymentsForEmp.toOption.flatten,
rtiAvailable = if (rtiUnavailableMarkerPresent) false else rtiAvailableCalculated,
cacheUpdatedIncomeAmount = cacheUpdatedIncomeAmountFuture.value.get.getOrElse(None),
estimatedPayOverrides = estimatedPayOverrides
)

Expand Down
74 changes: 55 additions & 19 deletions app/controllers/IncomeTaxComparisonController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ import cats.data.{EitherT, NonEmptyList}
import cats.implicits._
import controllers.auth.{AuthJourney, DataRequest}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.http.UpstreamErrorResponse
import uk.gov.hmrc.mongoFeatureToggles.services.FeatureFlagService
import uk.gov.hmrc.play.audit.http.connector.AuditConnector
import uk.gov.hmrc.tai.config.ApplicationConfig
import uk.gov.hmrc.tai.model.TaxYear
import uk.gov.hmrc.tai.model.admin.CyPlusOneToggle
import uk.gov.hmrc.tai.model.domain.calculation.CodingComponent
import uk.gov.hmrc.tai.model.domain.income.TaxCodeIncome
import uk.gov.hmrc.tai.model.domain.{Employment, TaxAccountSummary}
Expand All @@ -33,7 +36,7 @@ import uk.gov.hmrc.tai.viewModels.incomeTaxComparison.{EstimatedIncomeTaxCompari
import views.html.incomeTaxComparison.MainView

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

class IncomeTaxComparisonController @Inject() (
val auditConnector: AuditConnector,
Expand All @@ -43,6 +46,7 @@ class IncomeTaxComparisonController @Inject() (
updateNextYearsIncomeService: UpdateNextYearsIncomeService,
authenticate: AuthJourney,
applicationConfig: ApplicationConfig,
featureFlagService: FeatureFlagService,
mcc: MessagesControllerComponents,
mainView: MainView,
errorPagesHandler: ErrorPagesHandler
Expand All @@ -54,23 +58,55 @@ class IncomeTaxComparisonController @Inject() (
val currentTaxYear = TaxYear()
val nextTaxYear = currentTaxYear.next

(
taxAccountService.taxAccountSummary(nino, currentTaxYear).leftMap(msg => NonEmptyList.one(new Throwable(msg))),
taxAccountService.taxAccountSummary(nino, nextTaxYear).leftMap(msg => NonEmptyList.one(new Throwable(msg))),
EitherT(taxAccountService.taxCodeIncomes(nino, currentTaxYear)).leftMap(msg =>
NonEmptyList.one(new Throwable(msg))
),
EitherT(taxAccountService.taxCodeIncomes(nino, nextTaxYear)).leftMap(msg => NonEmptyList.one(new Throwable(msg))),
codingComponentService.taxFreeAmountComponents(nino, currentTaxYear).attemptTNel,
codingComponentService.taxFreeAmountComponents(nino, nextTaxYear).attemptTNel,
employmentService.employments(nino, currentTaxYear).leftMap(msg => NonEmptyList.one(new Throwable(msg))),
updateNextYearsIncomeService.isEstimatedPayJourneyComplete(request.userAnswers).attemptTNel
).parMapN(computeModel(currentTaxYear, nextTaxYear))
.fold(
errorPagesHandler
.internalServerError("Not able to fetch income tax comparison details in IncomeTaxComparisonController", _),
model => Ok(mainView(model, applicationConfig))
)
featureFlagService.getAsEitherT[UpstreamErrorResponse](CyPlusOneToggle).value.flatMap {
case Right(toggle) if !toggle.isEnabled =>
Future.successful(Redirect(routes.WhatDoYouWantToDoController.whatDoYouWantToDoPage()))

case Left(error) =>
Future.successful(
errorPagesHandler.internalServerError(
"Not able to fetch income tax comparison details in IncomeTaxComparisonController",
NonEmptyList.one(new Throwable(error.message))
)
)

case Right(_) =>
taxAccountService.taxAccountSummary(nino, nextTaxYear).value.flatMap {
case Left(error) if error.statusCode == NOT_FOUND =>
Future.successful(Redirect(routes.WhatDoYouWantToDoController.whatDoYouWantToDoPage()))

case Left(error) =>
Future.successful(
errorPagesHandler.internalServerError(
"Not able to fetch income tax comparison details in IncomeTaxComparisonController",
NonEmptyList.one(new Throwable(error.message))
)
)

case Right(cyPlusOneTaxAccountSummary) =>
(
taxAccountService
.taxAccountSummary(nino, currentTaxYear)
.leftMap(msg => NonEmptyList.one(new Throwable(msg))),
EitherT.rightT[Future, NonEmptyList[Throwable]](cyPlusOneTaxAccountSummary),
EitherT(taxAccountService.taxCodeIncomes(nino, currentTaxYear))
.leftMap(msg => NonEmptyList.one(new Throwable(msg))),
EitherT(taxAccountService.taxCodeIncomes(nino, nextTaxYear))
.leftMap(msg => NonEmptyList.one(new Throwable(msg))),
codingComponentService.taxFreeAmountComponents(nino, currentTaxYear).attemptTNel,
codingComponentService.taxFreeAmountComponents(nino, nextTaxYear).attemptTNel,
employmentService.employments(nino, currentTaxYear).leftMap(msg => NonEmptyList.one(new Throwable(msg))),
updateNextYearsIncomeService.isEstimatedPayJourneyComplete(request.userAnswers).attemptTNel
).parMapN(computeModel(currentTaxYear, nextTaxYear))
.fold(
errorPagesHandler.internalServerError(
"Not able to fetch income tax comparison details in IncomeTaxComparisonController",
_
),
model => Ok(mainView(model, applicationConfig))
)
}
}
}

private def computeModel(currentTaxYear: TaxYear, nextTaxYear: TaxYear)(
Expand Down Expand Up @@ -100,7 +136,7 @@ class IncomeTaxComparisonController @Inject() (
val cyCodingComponents = CodingComponentForYear(currentTaxYear, codingComponentsCY)
val cyPlusOneTaxComponents = CodingComponentForYear(nextTaxYear, codingComponentsCYPlusOne)
val cyTaxSummary = TaxAccountSummaryForYear(currentTaxYear, taxAccountSummaryCY)
val cyPlusOneTaxSummary = TaxAccountSummaryForYear(currentTaxYear, taxAccountSummaryCYPlusOne)
val cyPlusOneTaxSummary = TaxAccountSummaryForYear(nextTaxYear, taxAccountSummaryCYPlusOne)

TaxFreeAmountComparisonViewModel(
Seq(cyCodingComponents, cyPlusOneTaxComponents),
Expand Down
34 changes: 22 additions & 12 deletions app/controllers/IncomeTaxHistoryController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ package controllers
import cats.data.EitherT
import cats.implicits.*
import controllers.auth.{AuthJourney, AuthenticatedRequest}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import play.api.Logging
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.domain.Nino
import uk.gov.hmrc.http.{HeaderCarrier, UpstreamErrorResponse}
import uk.gov.hmrc.tai.config.ApplicationConfig
Expand All @@ -43,7 +43,8 @@ class IncomeTaxHistoryController @Inject() (
mcc: MessagesControllerComponents,
taxAccountService: TaxAccountService,
employmentService: EmploymentService,
rtiService: RtiService
rtiService: RtiService,
implicit val errorPagesHandler: ErrorPagesHandler
)(implicit ec: ExecutionContext)
extends TaiBaseController(mcc)
with Logging {
Expand Down Expand Up @@ -78,8 +79,6 @@ class IncomeTaxHistoryController @Inject() (
taxCode <- incomes.headOption
} yield taxCode

// TODO: handle a failure vs no payment
// a failure is treated as no payment instead of marking the value as temporary not available
val maybeLastPayment: Option[Payment] = lastPaymentForEmployment(accounts, employment.sequenceNumber)

val isPension = maybeTaxCode.exists(_.componentType == PensionIncome)
Expand All @@ -106,22 +105,33 @@ class IncomeTaxHistoryController @Inject() (

def onPageLoad(): Action[AnyContent] = authenticate.authWithValidatePerson.async { implicit request =>
val nino = request.taiUser.nino
val messages = request2Messages
val taxYears = (TaxYear().year to (TaxYear().year - config.numberOfPreviousYearsToShowIncomeTaxHistory) by -1)
.map(TaxYear(_))
.toList

taxYears
.traverse(taxYear =>
.traverse { taxYear =>
getIncomeTaxYear(nino, taxYear).value.map {
case Right(taxCodeIncome) =>
taxCodeIncome
case Left(e) =>
case Right(incomeTaxYear) =>
Right(incomeTaxYear)

case Left(e) if e.statusCode == 502 || e.statusCode == 503 || e.statusCode == 504 =>
Left(e)

case Left(e) =>
logger.error(e.getMessage, e)
IncomeTaxYear(taxYear, Nil)
Right(IncomeTaxYear(taxYear, Nil))
}
}
.map { results =>
results.collectFirst { case Left(e) => e } match {
case Some(e) =>
InternalServerError(errorPagesHandler.error5xx(messages("tai.technical.error.message")))
case None =>
val years = results.collect { case Right(y) => y }
Ok(incomeTaxHistoryView(request.person, years))
}
)
.map { taxCodeIncome =>
Ok(incomeTaxHistoryView(request.person, taxCodeIncome))
}
}
}
30 changes: 13 additions & 17 deletions app/controllers/WhatDoYouWantToDoController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
package controllers

import cats.data.EitherT
import cats.implicits._
import cats.implicits.*
import controllers.auth.{AuthJourney, AuthedUser}
import play.api.Logging
import play.api.mvc._
import play.api.mvc.*
import uk.gov.hmrc.domain.Nino
import uk.gov.hmrc.http.{HeaderCarrier, HttpException, UpstreamErrorResponse}
import uk.gov.hmrc.mongoFeatureToggles.services.FeatureFlagService
Expand All @@ -31,7 +31,7 @@ import uk.gov.hmrc.tai.forms.WhatDoYouWantToDoForm
import uk.gov.hmrc.tai.model.TaxYear
import uk.gov.hmrc.tai.model.admin.{CyPlusOneToggle, IncomeTaxHistoryToggle}
import uk.gov.hmrc.tai.model.domain.{JobSeekerAllowanceIncome, TaxAccountSummary, TaxCodeChange}
import uk.gov.hmrc.tai.service._
import uk.gov.hmrc.tai.service.*
import uk.gov.hmrc.tai.viewModels.WhatDoYouWantToDoViewModel
import views.html.WhatDoYouWantToDoTileView

Expand Down Expand Up @@ -59,25 +59,28 @@ class WhatDoYouWantToDoController @Inject() (
nino: Nino
)(implicit hc: HeaderCarrier): EitherT[Future, UpstreamErrorResponse, Boolean] = {
val taxYears =
// start from the current year which is the most probable year to be present
(TaxYear().year to (TaxYear().year - applicationConfig.numberOfPreviousYearsToShowIncomeTaxHistory) by -1)
.map(TaxYear(_))
.toList

taxYears.foldLeft(EitherT.rightT[Future, UpstreamErrorResponse](false)) { (acc, year) =>
EitherT(acc.value.flatMap {
case Right(true) =>
// Short-circuit if we've already found an employment
Future.successful(Right[UpstreamErrorResponse, Boolean](true))
case _ =>

case _ =>
employmentService
.employments(nino, year)
.transform {
case Right(employments) if employments.forall(_.employmentType == JobSeekerAllowanceIncome) =>
Right(false)
case Right(_) => Right(true)
case Left(error) if error.statusCode == NOT_FOUND => Right(false)
case Left(error) => Left(error)
case Left(error) =>
logger.warn(
s"Employments lookup failed for year=${year.year} status=${error.statusCode}; allowing entry"
)
Right(true)
}
.value
})
Expand All @@ -90,14 +93,11 @@ class WhatDoYouWantToDoController @Inject() (
for {
hasTaxCodeChanged <- taxCodeChangeService.hasTaxCodeChanged(nino).transform {
case Right(taxCodeChange) => Right(taxCodeChange)
case Left(_) =>
// don't fail the page when the tax code change banner is failing
Right(false)
case Left(_) => Right(false)
}
taxCodeChange <- if (hasTaxCodeChanged) {
taxCodeChangeService.taxCodeChange(nino).transform {
case Right(taxCodeChange) => Right(Some(taxCodeChange))
// don't fail the page when the tax code change banner is failing
case _ => Right(None)
}: EitherT[Future, UpstreamErrorResponse, Option[TaxCodeChange]]
} else {
Expand All @@ -116,13 +116,10 @@ class WhatDoYouWantToDoController @Inject() (
case Right(taxAccount) =>
Right[UpstreamErrorResponse, Option[TaxAccountSummary]](Some(taxAccount))
case Left(error) if error.statusCode == NOT_FOUND =>
logger.error(
"No CY+1 tax account summary found, consider disabling the CY+1 toggles"
)
logger.error("No CY+1 tax account summary found, consider disabling the CY+1 toggles")
Right(None)
case Left(_) =>
// don't fail the page when we get an error for CY+1
Right(none)
Right(None)
}
} else {
EitherT.rightT[Future, UpstreamErrorResponse](None: Option[TaxAccountSummary])
Expand Down Expand Up @@ -193,5 +190,4 @@ class WhatDoYouWantToDoController @Inject() (
case Left(error) => Future.successful(Failure(error.message))
})
}

}
2 changes: 1 addition & 1 deletion app/controllers/auth/AuthedUser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package controllers.auth

import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
import uk.gov.hmrc.domain.Nino
import uk.gov.hmrc.sca.models.TrustedHelper

final case class AuthedUser(
nino: Nino,
Expand Down
6 changes: 3 additions & 3 deletions project/AppDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import sbt.*

private object AppDependencies {
private val playVersion = "play-30"
private val scaWrapperVersion = "4.8.0"
private val mongoFeatureToggleVersion = "2.4.0"
private val scaWrapperVersion = "4.13.0"
private val mongoFeatureToggleVersion = "2.5.0"

val compile: Seq[ModuleID] = Seq(
filters,
"org.typelevel" %% "cats-core" % "2.13.0",
"uk.gov.hmrc" %% s"play-conditional-form-mapping-$playVersion" % "3.4.0",
"uk.gov.hmrc" %% s"play-conditional-form-mapping-$playVersion" % "3.5.0",
"uk.gov.hmrc" %% s"mongo-feature-toggles-client-$playVersion" % mongoFeatureToggleVersion,
"uk.gov.hmrc" %% s"sca-wrapper-$playVersion" % scaWrapperVersion
)
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolvers += Resolver.url("HMRC-open-artefacts-ivy2", url("https://open.artefact
Resolver.ivyStylePatterns
)

addSbtPlugin("org.playframework" % "sbt-plugin" % "3.0.9")
addSbtPlugin("org.playframework" % "sbt-plugin" % "3.0.10")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.1")
addSbtPlugin("uk.gov.hmrc" % "sbt-distributables" % "2.6.0")
addSbtPlugin("uk.gov.hmrc" % "sbt-auto-build" % "3.24.0")
Expand Down
2 changes: 1 addition & 1 deletion test/builders/UserBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
package builders

import controllers.auth.AuthedUser
import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
import uk.gov.hmrc.domain.{Generator, Nino}
import uk.gov.hmrc.sca.models.TrustedHelper

object UserBuilder {
val nino: Nino = new Generator().nextNino
Expand Down
Loading