diff --git a/app/controllers/returns/ViewReturnController.scala b/app/controllers/returns/ViewReturnController.scala index 429fff2a..29de50bf 100644 --- a/app/controllers/returns/ViewReturnController.scala +++ b/app/controllers/returns/ViewReturnController.scala @@ -58,6 +58,7 @@ class ViewReturnController @Inject() ( val dutyToDeclareViewModel = viewModel.createAlcoholDeclaredViewModel(returnDetails) val adjustmentsViewModel = viewModel.createAdjustmentsViewModel(returnDetails) 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) @@ -71,7 +72,8 @@ class ViewReturnController @Inject() ( submittedTimeStr, dutyToDeclareViewModel, adjustmentsViewModel, - totalDueViewModel + totalDueViewModel, + netDutySuspension ) ) } diff --git a/app/models/returns/ReturnDetails.scala b/app/models/returns/ReturnDetails.scala index 635f9b64..e04f2252 100644 --- a/app/models/returns/ReturnDetails.scala +++ b/app/models/returns/ReturnDetails.scala @@ -24,7 +24,8 @@ case class ReturnDetails( identification: ReturnDetailsIdentification, alcoholDeclared: ReturnAlcoholDeclared, adjustments: ReturnAdjustments, - totalDutyDue: ReturnTotalDutyDue + totalDutyDue: ReturnTotalDutyDue, + netDutySuspension: Option[ReturnNetDutySuspension] ) object ReturnDetails { @@ -109,3 +110,20 @@ case class ReturnTotalDutyDue(totalDue: BigDecimal) object ReturnTotalDutyDue { implicit val returnTotalDutyDueFormat: OFormat[ReturnTotalDutyDue] = Json.format[ReturnTotalDutyDue] } + +case class ReturnNetDutySuspension( + totalLtsBeer: Option[BigDecimal], + totalLtsWine: Option[BigDecimal], + totalLtsCider: Option[BigDecimal], + totalLtsSpirit: Option[BigDecimal], + totalLtsOtherFermented: Option[BigDecimal], + totalLtsPureAlcoholBeer: Option[BigDecimal], + totalLtsPureAlcoholWine: Option[BigDecimal], + totalLtsPureAlcoholCider: Option[BigDecimal], + totalLtsPureAlcoholSpirit: Option[BigDecimal], + totalLtsPureAlcoholOtherFermented: Option[BigDecimal] +) + +object ReturnNetDutySuspension { + implicit val returnTotalDutyDueFormat: OFormat[ReturnNetDutySuspension] = Json.format[ReturnNetDutySuspension] +} diff --git a/app/viewmodels/returns/ViewReturnViewModel.scala b/app/viewmodels/returns/ViewReturnViewModel.scala index c532aa54..e39fbd00 100644 --- a/app/viewmodels/returns/ViewReturnViewModel.scala +++ b/app/viewmodels/returns/ViewReturnViewModel.scala @@ -219,4 +219,106 @@ class ViewReturnViewModel @Inject() () { HeadCell(content = content, classes = s"${Constants.oneQuarterCssClass} ${Constants.textAlignRightCssClass}") ) } + + def createNetDutySuspensionViewModel( + returnDetails: ReturnDetails + )(implicit messages: Messages): TableViewModel = + returnDetails.netDutySuspension match { + case Some(netDutySuspension) => + TableViewModel( + head = netDutySuspensionTableHeader(), + rows = netDutySuspensionRows(netDutySuspension) + ) + case None => + TableViewModel( + head = noneDeclareDutySuspensionHeader(), + rows = noneDeclaredDutySuspension() + ) + } + + private def netDutySuspensionTableHeader()(implicit messages: Messages): Seq[HeadCell] = + Seq( + HeadCell( + content = Text(messages("viewReturn.table.description.legend")), + classes = Constants.oneHalfCssClass + ), + HeadCell( + content = Text(messages("viewReturn.table.totalVolume.legend")), + classes = s"${Constants.oneQuarterCssClass} ${Constants.textAlignRightCssClass}" + ), + HeadCell( + content = Text(messages("viewReturn.table.lpa.legend")), + classes = s"${Constants.oneQuarterCssClass} ${Constants.textAlignRightCssClass}" + ) + ) + + private def netDutySuspensionRows(netDutySuspension: ReturnNetDutySuspension)(implicit + messages: Messages + ): Seq[TableRowViewModel] = + Seq( + netDutySuspensionCell( + "return.regime.Beer", + netDutySuspension.totalLtsBeer, + netDutySuspension.totalLtsPureAlcoholBeer + ), + netDutySuspensionCell( + "return.regime.Cider", + netDutySuspension.totalLtsCider, + netDutySuspension.totalLtsPureAlcoholCider + ), + netDutySuspensionCell( + "return.regime.Spirits", + netDutySuspension.totalLtsSpirit, + netDutySuspension.totalLtsPureAlcoholSpirit + ), + netDutySuspensionCell( + "return.regime.Wine", + netDutySuspension.totalLtsWine, + netDutySuspension.totalLtsPureAlcoholWine + ), + netDutySuspensionCell( + "return.regime.OtherFermentedProduct", + netDutySuspension.totalLtsOtherFermented, + netDutySuspension.totalLtsPureAlcoholOtherFermented + ) + ).flatten + + private def netDutySuspensionCell( + messageKey: String, + totalLitres: Option[BigDecimal], + litresOfPureAlcohol: Option[BigDecimal] + )(implicit messages: Messages): Option[TableRowViewModel] = + for { + total <- totalLitres + lpa <- litresOfPureAlcohol + } yield TableRowViewModel( + cells = Seq( + TableRow( + content = Text(messages(messageKey).capitalize), + classes = Constants.oneHalfCssClass + ), + TableRow( + content = Text(messages("site.2DP", total)), + classes = s"${Constants.oneQuarterCssClass} ${Constants.textAlignRightCssClass}" + ), + TableRow( + content = Text(messages("site.4DP", lpa)), + classes = s"${Constants.oneQuarterCssClass} ${Constants.textAlignRightCssClass}" + ) + ) + ) + + private def noneDeclaredDutySuspension()(implicit messages: Messages) = Seq( + TableRowViewModel( + cells = Seq( + TableRow(content = Text(messages("viewReturn.netDutySuspension.noneDeclared"))) + ) + ) + ) + + private def noneDeclareDutySuspensionHeader()(implicit messages: Messages): Seq[HeadCell] = Seq( + HeadCell( + content = Text(messages("viewReturn.table.description.legend")) + ) + ) } diff --git a/app/views/returns/ViewReturnView.scala.html b/app/views/returns/ViewReturnView.scala.html index 338d3fe7..8acd5769 100644 --- a/app/views/returns/ViewReturnView.scala.html +++ b/app/views/returns/ViewReturnView.scala.html @@ -14,7 +14,7 @@ * limitations under the License. *@ -@import components.{PageHeading, Paragraph, PrintPage, SectionHeading} +@import components.{PageHeading, Paragraph, PrintPage} @import config.Constants @import viewmodels.{TableTotalViewModel, TableViewModel} @@ -22,14 +22,13 @@ layout: templates.Layout, formHelper: FormWithCSRF, govukErrorSummary: GovukErrorSummary, - sectionHeading: SectionHeading, pageHeading: PageHeading, paragraph: Paragraph, govukTable: GovukTable, printPage: PrintPage ) -@(period: String, submittedAtDate: String, submittedAtTime: String, dutyToDeclare: TableViewModel, adjustments: TableViewModel, totalDue: TableTotalViewModel)(implicit request: Request[_], messages: Messages) +@(period: String, submittedAtDate: String, submittedAtTime: String, dutyToDeclare: TableViewModel, adjustments: TableViewModel, totalDue: TableTotalViewModel, netDutySuspension: TableViewModel)(implicit request: Request[_], messages: Messages) @layout(pageTitle = titleNoForm(messages(s"viewReturn.title", period)), fullWidth = true, withPrintCss = true) { @@ -75,5 +74,16 @@ head = Some(totalDue.toHeadCells()) )) +
+ + @govukTable(Table( + caption = Some(messages("viewReturn.netDutySuspension.caption")), + captionClasses = Constants.tableCaptionMCssClass, + head = Some(netDutySuspension.head), + rows = netDutySuspension.rows.map(_.cells) + )) + +
+ @printPage("print-past-payments-link", messages("viewReturn.printYourReturn")) } \ No newline at end of file diff --git a/conf/messages.en b/conf/messages.en index cc6fbf47..fc29d55f 100644 --- a/conf/messages.en +++ b/conf/messages.en @@ -156,11 +156,14 @@ viewReturn.dutyDue.caption = Total viewReturn.dutyDue.total.legend = Total duty value 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.dutyRate.legend = Duty rate (per litre) viewReturn.table.dutyValue.legend = Duty value viewReturn.table.dutyDue.legend = Duty value 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 diff --git a/test-utils/common/TestData.scala b/test-utils/common/TestData.scala index 76732cfe..c873c4f7 100644 --- a/test-utils/common/TestData.scala +++ b/test-utils/common/TestData.scala @@ -234,7 +234,21 @@ trait TestData extends ModelGenerators { ), total = BigDecimal("-19434") ), - totalDutyDue = ReturnTotalDutyDue(totalDue = BigDecimal("55815")) + totalDutyDue = ReturnTotalDutyDue(totalDue = BigDecimal("55815")), + netDutySuspension = Some( + ReturnNetDutySuspension( + totalLtsBeer = Some(BigDecimal("0.15")), + totalLtsCider = Some(BigDecimal("0.38")), + totalLtsSpirit = Some(BigDecimal("0.02")), + totalLtsWine = Some(BigDecimal("0.44")), + totalLtsOtherFermented = Some(BigDecimal("0.02")), + totalLtsPureAlcoholBeer = Some(BigDecimal("0.4248")), + totalLtsPureAlcoholCider = Some(BigDecimal("0.0379")), + totalLtsPureAlcoholSpirit = Some(BigDecimal("0.2492")), + totalLtsPureAlcoholWine = Some(BigDecimal("0.5965")), + totalLtsPureAlcoholOtherFermented = Some(BigDecimal("0.1894")) + ) + ) ) def nilReturnDetails(periodKey: String, now: Instant): ReturnDetails = @@ -248,7 +262,8 @@ trait TestData extends ModelGenerators { adjustmentDetails = None, total = BigDecimal("0") ), - totalDutyDue = ReturnTotalDutyDue(totalDue = BigDecimal("0")) + totalDutyDue = ReturnTotalDutyDue(totalDue = BigDecimal("0")), + netDutySuspension = None ) def nilReturnDetailsWithEmptySections(periodKey: String, now: Instant): ReturnDetails = @@ -262,7 +277,8 @@ trait TestData extends ModelGenerators { adjustmentDetails = Some(Seq.empty), total = BigDecimal("0") ), - totalDutyDue = ReturnTotalDutyDue(totalDue = BigDecimal("0")) + totalDutyDue = ReturnTotalDutyDue(totalDue = BigDecimal("0")), + netDutySuspension = None ) val obligationDataSingleOpen = ObligationData( diff --git a/test/controllers/returns/ViewReturnControllerSpec.scala b/test/controllers/returns/ViewReturnControllerSpec.scala index 6e5bd856..5cc6c54e 100644 --- a/test/controllers/returns/ViewReturnControllerSpec.scala +++ b/test/controllers/returns/ViewReturnControllerSpec.scala @@ -46,6 +46,7 @@ class ViewReturnControllerSpec extends SpecBase { when(mockViewModel.createAlcoholDeclaredViewModel(returnDetails)).thenReturn(tableModel) when(mockViewModel.createAdjustmentsViewModel(returnDetails)).thenReturn(tableModel) when(mockViewModel.createTotalDueViewModel(returnDetails)).thenReturn(totalTableModel) + when(mockViewModel.createNetDutySuspensionViewModel(returnDetails)).thenReturn(tableModel) val request = FakeRequest(GET, controllers.returns.routes.ViewReturnController.onPageLoad(periodKey).url) val result = route(application, request).value @@ -59,7 +60,8 @@ class ViewReturnControllerSpec extends SpecBase { submittedAtTimeStr, tableModel, tableModel, - totalTableModel + totalTableModel, + tableModel )( request, messages diff --git a/test/viewmodels/returns/ViewReturnViewModelSpec.scala b/test/viewmodels/returns/ViewReturnViewModelSpec.scala index b99dcdee..d1deaf99 100644 --- a/test/viewmodels/returns/ViewReturnViewModelSpec.scala +++ b/test/viewmodels/returns/ViewReturnViewModelSpec.scala @@ -17,7 +17,7 @@ package viewmodels.returns import base.SpecBase -import models.returns.{ReturnAdjustments, ReturnAlcoholDeclared, ReturnTotalDutyDue} +import models.returns.{ReturnAdjustments, ReturnAlcoholDeclared, ReturnDetails, ReturnTotalDutyDue} import org.scalatest.matchers.should.Matchers.convertToAnyShouldWrapper import play.api.Application import play.api.i18n.Messages @@ -135,6 +135,45 @@ class ViewReturnViewModelSpec extends SpecBase { adjustmentsViewModel.rows.head.cells(1).content shouldBe Text(messages("site.nil")) } } + + "createNetDutySuspensionViewModel" - { + "should return a model with data when duty suspension is declared" in new SetUp { + val netDutySuspensionViewModel = viewModel.createNetDutySuspensionViewModel(returnDetails) + + netDutySuspensionViewModel.head.size shouldBe 3 + netDutySuspensionViewModel.rows.size shouldBe 5 + netDutySuspensionViewModel.rows.foreach { row => + row.cells.size shouldBe 3 + } + } + + "should return a model excluding the alcohol types not declared in the return" in new SetUp { + val returnDetailsWithoutCider: ReturnDetails = returnDetails + .copy(netDutySuspension = + Some( + returnDetails.netDutySuspension.get.copy( + totalLtsCider = None, + totalLtsPureAlcoholCider = None + ) + ) + ) + + val netDutySuspensionViewModel = viewModel.createNetDutySuspensionViewModel(returnDetailsWithoutCider) + + netDutySuspensionViewModel.head.size shouldBe 3 + netDutySuspensionViewModel.rows.size shouldBe 4 + + } + + "should return a model with the right label when nothing declared" in new SetUp { + val netDutySuspensionViewModel = viewModel.createNetDutySuspensionViewModel(nilReturn) + + netDutySuspensionViewModel.rows.size shouldBe 1 + netDutySuspensionViewModel.rows.head.cells.head.content shouldBe Text( + messages("viewReturn.netDutySuspension.noneDeclared") + ) + } + } } class SetUp {