Skip to content

Commit

Permalink
ADR-1368 show spirits on view your return (#210)
Browse files Browse the repository at this point in the history
* ADR-1227 Fix test data

* ADR-1368 show spirits on view your return

* ADR-1368 Add caption to table view model
  • Loading branch information
davidfes authored Nov 22, 2024
1 parent e086b98 commit ceed575
Show file tree
Hide file tree
Showing 11 changed files with 483 additions and 110 deletions.
96 changes: 57 additions & 39 deletions app/controllers/returns/ViewReturnController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import viewmodels.returns.ViewReturnViewModel
import views.html.returns.ViewReturnView

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

class ViewReturnController @Inject() (
override val messagesApi: MessagesApi,
Expand All @@ -47,50 +47,68 @@ class ViewReturnController @Inject() (
def onPageLoad(periodKey: String): Action[AnyContent] = identify.async { implicit request =>
val appaId = request.appaId

(for {
returnDetails <- alcoholDutyReturnsConnector.getReturn(appaId, periodKey)
periodKey = returnDetails.identification.periodKey
returnPeriodsAndTaxCodes =
returnDetails.alcoholDeclared.taxCodes.map((periodKey, _)) ++ returnDetails.adjustments.returnPeriodsAndTaxCodes
ratePeriodsAndTaxCodes = returnPeriodsAndTaxCodes.flatMap { case (periodKey, taxCode) =>
ReturnPeriod
.fromPeriodKey(periodKey)
.map(returnPeriod => (returnPeriod.period, taxCode))
}
ratePeriodsAndTaxCodesToRateBands <- calculatorConnector.rateBands(ratePeriodsAndTaxCodes)
} yield ReturnPeriod
ReturnPeriod
.fromPeriodKey(periodKey)
.fold {
logger.warn(s"Cannot parse period key $periodKey for $appaId on return")
Redirect(controllers.routes.JourneyRecoveryController.onPageLoad())
Future.successful(Redirect(controllers.routes.JourneyRecoveryController.onPageLoad()))
} { returnPeriod =>
val dutyToDeclareViewModel =
viewModel.createAlcoholDeclaredViewModel(returnDetails, ratePeriodsAndTaxCodesToRateBands)
val adjustmentsViewModel =
viewModel.createAdjustmentsViewModel(returnDetails, ratePeriodsAndTaxCodesToRateBands)
val totalDueViewModel = viewModel.createTotalDueViewModel(returnDetails)
val netDutySuspension = viewModel.createNetDutySuspensionViewModel(returnDetails)
val returnPeriodStr = dateTimeHelper.formatMonthYear(returnPeriod.period)
val submittedDate = dateTimeHelper.instantToLocalDate(returnDetails.identification.submittedTime)
val submittedDateStr = dateTimeHelper.formatDateMonthYear(submittedDate)
val submittedTime = dateTimeHelper.instantToLocalTime(returnDetails.identification.submittedTime)
val submittedTimeStr = dateTimeHelper.formatHourMinuteMeridiem(submittedTime)
(for {
returnDetails <- alcoholDutyReturnsConnector.getReturn(appaId, periodKey)
_ <- if (returnDetails.identification.periodKey != periodKey) {
val error =
s"Period key on the return ${returnDetails.identification.periodKey} does not match the return $periodKey requested for $appaId"
logger.warn(error)
Future.failed(new RuntimeException(error))
} else {
Future.unit
}
periodKey = returnDetails.identification.periodKey
returnPeriodsAndTaxCodes =
returnDetails.alcoholDeclared.taxCodes.map(
(periodKey, _)
) ++ returnDetails.adjustments.returnPeriodsAndTaxCodes
ratePeriodsAndTaxCodes = returnPeriodsAndTaxCodes.flatMap { case (periodKey, taxCode) =>
ReturnPeriod
.fromPeriodKey(periodKey)
.map(returnPeriod => (returnPeriod.period, taxCode))
}
ratePeriodsAndTaxCodesToRateBands <- calculatorConnector.rateBands(ratePeriodsAndTaxCodes)
} yield {
val dutyToDeclareViewModel =
viewModel.createAlcoholDeclaredViewModel(returnDetails, ratePeriodsAndTaxCodesToRateBands)
val adjustmentsViewModel =
viewModel.createAdjustmentsViewModel(returnDetails, ratePeriodsAndTaxCodesToRateBands)
val totalDueViewModel = viewModel.createTotalDueViewModel(returnDetails)
val netDutySuspension = viewModel.createNetDutySuspensionViewModel(returnDetails)
val spirits = if (returnPeriod.hasQuarterlySpirits) {
viewModel.createSpiritsViewModels(returnDetails)
} else {
Seq.empty
}
val returnPeriodStr = dateTimeHelper.formatMonthYear(returnPeriod.period)
val submittedDate = dateTimeHelper.instantToLocalDate(returnDetails.identification.submittedTime)
val submittedDateStr = dateTimeHelper.formatDateMonthYear(submittedDate)
val submittedTime = dateTimeHelper.instantToLocalTime(returnDetails.identification.submittedTime)
val submittedTimeStr = dateTimeHelper.formatHourMinuteMeridiem(submittedTime)

Ok(
view(
returnPeriodStr,
submittedDateStr,
submittedTimeStr,
dutyToDeclareViewModel,
adjustmentsViewModel,
totalDueViewModel,
netDutySuspension
Ok(
view(
returnPeriodStr,
submittedDateStr,
submittedTimeStr,
dutyToDeclareViewModel,
adjustmentsViewModel,
totalDueViewModel,
netDutySuspension,
spirits
)
)
)
})
.recover { case _ =>
logger.warn(s"Unable to fetch return $appaId $periodKey")
Redirect(controllers.routes.JourneyRecoveryController.onPageLoad())
})
.recover { case e =>
logger.warn(s"Unable to fetch return $appaId $periodKey: ${e.getMessage}")
Redirect(controllers.routes.JourneyRecoveryController.onPageLoad())
}
}
}
}
4 changes: 2 additions & 2 deletions app/models/checkAndSubmit/AdrReturnSubmission.scala
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ object AdrDutySuspended {

case class AdrSpiritsVolumes(
totalSpirits: BigDecimal,
scotchWhiskey: BigDecimal,
irishWhisky: BigDecimal
scotchWhisky: BigDecimal,
irishWhiskey: BigDecimal
)

object AdrSpiritsVolumes {
Expand Down
24 changes: 23 additions & 1 deletion app/models/returns/ReturnDetails.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package models.returns

import models.checkAndSubmit.AdrTypeOfSpirit
import play.api.libs.json.{Json, OFormat}

import java.time.Instant
Expand All @@ -25,7 +26,8 @@ case class ReturnDetails(
alcoholDeclared: ReturnAlcoholDeclared,
adjustments: ReturnAdjustments,
totalDutyDue: ReturnTotalDutyDue,
netDutySuspension: Option[ReturnNetDutySuspension]
netDutySuspension: Option[ReturnNetDutySuspension],
spirits: Option[ReturnSpirits]
)

object ReturnDetails {
Expand Down Expand Up @@ -134,3 +136,23 @@ case class ReturnNetDutySuspension(
object ReturnNetDutySuspension {
implicit val returnTotalDutyDueFormat: OFormat[ReturnNetDutySuspension] = Json.format[ReturnNetDutySuspension]
}

case class ReturnSpiritsVolumes(
totalSpirits: BigDecimal,
scotchWhisky: BigDecimal,
irishWhiskey: BigDecimal
)

case object ReturnSpiritsVolumes {
implicit val returnSpiritsVolumesFormat: OFormat[ReturnSpiritsVolumes] = Json.format[ReturnSpiritsVolumes]
}

case class ReturnSpirits(
spiritsVolumes: ReturnSpiritsVolumes,
typesOfSpirit: Set[AdrTypeOfSpirit],
otherSpiritTypeName: Option[String]
)

case object ReturnSpirits {
implicit val returnSpiritsFormat: OFormat[ReturnSpirits] = Json.format[ReturnSpirits]
}
6 changes: 3 additions & 3 deletions app/services/checkAndSubmit/AdrReturnSubmissionService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,11 @@ class AdrReturnSubmissionServiceImpl @Inject() (
def declareSpiritsTotal(userAnswers: UserAnswers): EitherT[Future, String, AdrSpiritsVolumes] =
for {
totalSpirits <- getValue(userAnswers, DeclareSpiritsTotalPage)
whiskey <- getValue(userAnswers, WhiskyPage)
whisky <- getValue(userAnswers, WhiskyPage)
} yield AdrSpiritsVolumes(
totalSpirits = totalSpirits,
scotchWhiskey = whiskey.scotchWhisky,
irishWhisky = whiskey.irishWhiskey
scotchWhisky = whisky.scotchWhisky,
irishWhiskey = whisky.irishWhiskey
)

def getTypeOfSpirits(userAnswers: UserAnswers): EitherT[Future, String, Set[AdrTypeOfSpirit]] =
Expand Down
6 changes: 4 additions & 2 deletions app/viewmodels/TableViewModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import uk.gov.hmrc.govukfrontend.views.viewmodels.table.{HeadCell, TableRow}
case class TableViewModel(
head: Seq[HeadCell],
rows: Seq[TableRowViewModel],
total: Option[TableTotalViewModel] = None
total: Option[TableTotalViewModel] = None,
caption: Option[String] = None
)

object TableViewModel {
def empty(): TableViewModel = TableViewModel(Seq.empty, Seq.empty, None)
def empty(): TableViewModel = TableViewModel(Seq.empty, Seq.empty, None, None)
}

case class TableRowViewModel(cells: Seq[TableRow], actions: Seq[TableRowActionViewModel] = Seq.empty)

case class TableTotalViewModel(legend: HeadCell, total: HeadCell) {
Expand Down
109 changes: 109 additions & 0 deletions app/viewmodels/returns/ViewReturnViewModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package viewmodels.returns

import config.Constants.Css
import config.FrontendAppConfig
import models.checkAndSubmit.AdrTypeOfSpirit
import models.checkAndSubmit.AdrTypeOfSpirit._
import models.returns._
import models.{RateBand, ReturnPeriod}
import play.api.i18n.Messages
Expand Down Expand Up @@ -361,4 +363,111 @@ class ViewReturnViewModel @Inject() (appConfig: FrontendAppConfig) {
content = Text(messages("viewReturn.table.description.legend"))
)
)

def createSpiritsViewModels(
returnDetails: ReturnDetails
)(implicit messages: Messages): Seq[TableViewModel] =
returnDetails.spirits match {
case Some(spirits) =>
Seq(
TableViewModel(
head = spiritsDeclaredTableHeader(),
rows = spiritsDeclaredRows(spirits),
caption = Some(messages("viewReturn.spirits.caption"))
),
TableViewModel(
head = spiritsTypesDeclaredTableHeader(),
rows = spiritsTypesDeclaredRows(spirits)
)
)
case None =>
Seq(
TableViewModel(
head = spiritsNotDeclaredTableHeader(),
rows = spiritsNotDeclaredRow(),
caption = Some(messages("viewReturn.spirits.caption"))
)
)
}

private def spiritsDeclaredTableHeader()(implicit messages: Messages): Seq[HeadCell] =
Seq(
HeadCell(
content = Text(messages("viewReturn.table.description.legend"))
),
HeadCell(
content = Text(messages("viewReturn.table.totalVolume.lpa.legend")),
classes = Css.textAlignRightWrapCssClass
)
)

private def spiritsDeclaredRows(spirits: ReturnSpirits)(implicit messages: Messages): Seq[TableRowViewModel] =
Seq(
("viewReturn.spirits.totalVolume", spirits.spiritsVolumes.totalSpirits),
("viewReturn.spirits.scotchWhisky", spirits.spiritsVolumes.scotchWhisky),
("viewReturn.spirits.irishWhiskey", spirits.spiritsVolumes.irishWhiskey)
).map { case (legendKey, value) =>
TableRowViewModel(
cells = Seq(
TableRow(content = Text(messages(legendKey))),
TableRow(
content = Text(messages("site.2DP", value)),
classes = s"${Css.textAlignRightCssClass} ${Css.numericCellClass}"
)
)
)
}

private def spiritsTypesDeclaredTableHeader()(implicit messages: Messages): Seq[HeadCell] =
Seq(
HeadCell(
content = Text(messages("viewReturn.table.typesOfSpirits.legend"))
)
)

private val spiritsTypeToMessageKey: Map[AdrTypeOfSpirit, String] =
Map(
Malt -> "viewReturn.spirits.type.malt",
Grain -> "viewReturn.spirits.type.grain",
NeutralAgricultural -> "viewReturn.spirits.type.neutralAgricultural",
NeutralIndustrial -> "viewReturn.spirits.type.neutralIndustrial",
Beer -> "viewReturn.spirits.type.beer",
CiderOrPerry -> "viewReturn.spirits.type.cider",
WineOrMadeWine -> "viewReturn.spirits.type.wine"
)

private def spiritsTypesDeclaredRows(spirits: ReturnSpirits)(implicit messages: Messages): Seq[TableRowViewModel] = {
val typesOfSpirit = spirits.typesOfSpirit

val spiritsTypesDetails = AdrTypeOfSpirit.values
.flatMap {
case Other if typesOfSpirit.contains(Other) => spirits.otherSpiritTypeName
case typeOfSpirit if typesOfSpirit.contains(typeOfSpirit) =>
spiritsTypeToMessageKey.get(typeOfSpirit).map(messages(_))
case _ => None
}
.mkString(", ")

Seq(
TableRowViewModel(
cells = Seq(
TableRow(content = Text(spiritsTypesDetails))
)
)
)
}

private def spiritsNotDeclaredTableHeader()(implicit messages: Messages): Seq[HeadCell] = Seq(
HeadCell(
content = Text(messages("viewReturn.table.description.legend"))
)
)

private def spiritsNotDeclaredRow()(implicit messages: Messages) = Seq(
TableRowViewModel(
cells = Seq(
TableRow(content = Text(messages("viewReturn.spirits.noneDeclared")))
)
)
)
}
22 changes: 21 additions & 1 deletion app/views/returns/ViewReturnView.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@
printPage: PrintPage
)

@(period: String, submittedAtDate: String, submittedAtTime: String, dutyToDeclare: TableViewModel, adjustments: TableViewModel, totalDue: TableTotalViewModel, netDutySuspension: TableViewModel)(implicit request: Request[_], messages: Messages)
@(
period: String,
submittedAtDate: String,
submittedAtTime: String,
dutyToDeclare: TableViewModel,
adjustments: TableViewModel,
totalDue: TableTotalViewModel,
netDutySuspension: TableViewModel,
spirits: Seq[TableViewModel]
)(implicit request: Request[_], messages: Messages)

@layout(pageTitle = titleNoForm(messages("viewReturn.title", period)), fullWidth = true, withPrintCss = true) {

Expand Down Expand Up @@ -85,5 +94,16 @@

<br>

@spirits.map { spiritsTable =>
@govukTable(Table(
caption = spiritsTable.caption,
captionClasses = Css.tableCaptionMCssClass,
head = Some(spiritsTable.head),
rows = spiritsTable.rows.map(_.cells)
))
}

<br>

@printPage("print-past-payments-link", messages("viewReturn.printYourReturn"))
}
18 changes: 16 additions & 2 deletions conf/messages.en
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,30 @@ viewReturn.adjustments.type.spoilt = Spoilt
viewReturn.adjustments.type.drawback = Drawback
viewReturn.dutyDue.caption = Total
viewReturn.dutyDue.total.legend = Total duty value
viewReturn.netDutySuspension.caption = Duty suspended deliveries
viewReturn.netDutySuspension.noneDeclared = Nothing declared
viewReturn.spirits.caption = Spirits production in the last 3 months
viewReturn.spirits.totalVolume = Total volume of spirits
viewReturn.spirits.scotchWhisky = Scotch whisky
viewReturn.spirits.irishWhiskey = Irish whiskey
viewReturn.spirits.type.malt = Malt spirit
viewReturn.spirits.type.grain = Grain spirit
viewReturn.spirits.type.neutralAgricultural = Neutral spirit (agricultural origin)
viewReturn.spirits.type.neutralIndustrial = Neutral spirit (industrial origin)
viewReturn.spirits.type.beer = Beer-based spirit
viewReturn.spirits.type.wine = Wine or made-wine-based spirit
viewReturn.spirits.type.cider = Cider or perry-based spirit
viewReturn.spirits.noneDeclared = Nothing declared
viewReturn.table.adjustmentType.legend = Adjustment
viewReturn.table.description.legend = Description
viewReturn.table.totalVolume.legend = Total volume (litres)
viewReturn.table.lpa.legend = Litres of pure alcohol (LPA)
viewReturn.table.totalVolume.lpa.legend = Total volume (LPA)
viewReturn.table.dutyRate.legend = Duty rate (per litre)
viewReturn.table.dutyValue.legend = Duty value
viewReturn.table.dutyDue.legend = Duty value
viewReturn.table.typesOfSpirits.legend = Types of spirits produced
viewReturn.printYourReturn = Print your return
viewReturn.netDutySuspension.caption = Duty suspended deliveries
viewReturn.netDutySuspension.noneDeclared = Nothing declared

viewPastPayments.heading = Alcohol Duty payments
viewPastPayments.title = Alcohol Duty payments
Expand Down
Loading

0 comments on commit ceed575

Please sign in to comment.