Skip to content

Commit

Permalink
Merge pull request #8 from hmrc/ADR-744
Browse files Browse the repository at this point in the history
ADR-744 Alcohol by volume page
  • Loading branch information
JemmaEdwards authored Nov 28, 2023
2 parents 4ef63a2 + cfe870b commit 5b56ff6
Show file tree
Hide file tree
Showing 18 changed files with 760 additions and 52 deletions.
75 changes: 75 additions & 0 deletions app/controllers/AlcoholByVolumeQuestionController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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 controllers

import connectors.CacheConnector
import controllers.actions._
import forms.AlcoholByVolumeQuestionFormProvider

import javax.inject.Inject
import models.Mode
import navigation.Navigator
import pages.AlcoholByVolumeQuestionPage
import play.api.i18n.{I18nSupport, MessagesApi}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController
import views.html.AlcoholByVolumeQuestionView

import scala.concurrent.{ExecutionContext, Future}

class AlcoholByVolumeQuestionController @Inject() (
override val messagesApi: MessagesApi,
cacheConnector: CacheConnector,
navigator: Navigator,
identify: IdentifierAction,
getData: DataRetrievalAction,
requireData: DataRequiredAction,
formProvider: AlcoholByVolumeQuestionFormProvider,
val controllerComponents: MessagesControllerComponents,
view: AlcoholByVolumeQuestionView
)(implicit ec: ExecutionContext)
extends FrontendBaseController
with I18nSupport {

val form = formProvider()

def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) { implicit request =>
val preparedForm = request.userAnswers.get(AlcoholByVolumeQuestionPage) match {
case None => form
case Some(value) => form.fill(value)
}

Ok(view(preparedForm, mode))
}

def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async {
implicit request =>
form
.bindFromRequest()
.fold(
formWithErrors => Future.successful(BadRequest(view(formWithErrors, mode))),
value =>
for {
updatedAnswers <-
Future.fromTry(
request.userAnswers.set(AlcoholByVolumeQuestionPage, value)
)
_ <- cacheConnector.set(updatedAnswers)
} yield Redirect(navigator.nextPage(AlcoholByVolumeQuestionPage, mode, updatedAnswers))
)
}
}
36 changes: 19 additions & 17 deletions app/controllers/DraughtReliefQuestionController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import controllers.actions._
import forms.DraughtReliefQuestionFormProvider

import javax.inject.Inject
import models.{Mode, UserAnswers}
import models.Mode
import navigation.Navigator
import pages.DraughtReliefQuestionPage
import play.api.i18n.{I18nSupport, MessagesApi}
Expand All @@ -37,6 +37,7 @@ class DraughtReliefQuestionController @Inject() (
navigator: Navigator,
identify: IdentifierAction,
getData: DataRetrievalAction,
requireData: DataRequiredAction,
formProvider: DraughtReliefQuestionFormProvider,
val controllerComponents: MessagesControllerComponents,
view: DraughtReliefQuestionView
Expand All @@ -46,28 +47,29 @@ class DraughtReliefQuestionController @Inject() (

val form = formProvider()

def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData) { implicit request =>
val preparedForm = request.userAnswers.flatMap(_.get(DraughtReliefQuestionPage)) match {
def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) { implicit request =>
val preparedForm = request.userAnswers.get(DraughtReliefQuestionPage) match {
case None => form
case Some(value) => form.fill(value)
}

Ok(view(preparedForm, mode))
}

def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData).async { implicit request =>
form
.bindFromRequest()
.fold(
formWithErrors => Future.successful(BadRequest(view(formWithErrors, mode))),
value =>
for {
updatedAnswers <-
Future.fromTry(
request.userAnswers.getOrElse(UserAnswers(request.userId)).set(DraughtReliefQuestionPage, value)
)
_ <- cacheConnector.set(updatedAnswers)
} yield Redirect(navigator.nextPage(DraughtReliefQuestionPage, mode, updatedAnswers))
)
def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async {
implicit request =>
form
.bindFromRequest()
.fold(
formWithErrors => Future.successful(BadRequest(view(formWithErrors, mode))),
value =>
for {
updatedAnswers <-
Future.fromTry(
request.userAnswers.set(DraughtReliefQuestionPage, value)
)
_ <- cacheConnector.set(updatedAnswers)
} yield Redirect(navigator.nextPage(DraughtReliefQuestionPage, mode, updatedAnswers))
)
}
}
36 changes: 36 additions & 0 deletions app/forms/AlcoholByVolumeQuestionFormProvider.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 forms

import forms.mappings.Mappings
import javax.inject.Inject
import play.api.data.Form

class AlcoholByVolumeQuestionFormProvider @Inject() extends Mappings {

def apply(): Form[BigDecimal] =
Form(
"value" -> bigDecimal(
"alcoholByVolumeQuestion.error.required",
"alcoholByVolumeQuestion.error.nonNumeric",
"alcoholByVolumeQuestion.error.twoDecimalPlaces"
)
.verifying(minimumValue(BigDecimal(0.01), "alcoholByVolumeQuestion.error.minimumRequired"))
.verifying(maximumValue(BigDecimal(100), "alcoholByVolumeQuestion.error.maximumRequired"))
)

}
33 changes: 33 additions & 0 deletions app/forms/mappings/Formatters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,39 @@ trait Formatters {
Map(key -> value)
}

private[mappings] def bigDecimalFormatter(
requiredKey: String,
nonNumericKey: String,
twoDecimalPlacesKey: String,
args: Seq[String] = Seq.empty
): Formatter[BigDecimal] =
new Formatter[BigDecimal] {
val decimalRegexp = """^[+-]?[0-9]*(\.[0-9]{0,2})?$"""

private val baseFormatter = stringFormatter(requiredKey, args)

override def bind(key: String, data: Map[String, String]) =
baseFormatter
.bind(key, data)
.map(_.replace(",", ""))
.flatMap { s =>
nonFatalCatch
.either(BigDecimal(s))
.left
.map(_ => Seq(FormError(key, nonNumericKey, args)))
.flatMap { res =>
if (res.toString().matches(decimalRegexp)) {
Right(res)
} else {
Left(Seq(FormError(key, twoDecimalPlacesKey, args)))
}
}
}

override def unbind(key: String, value: BigDecimal) =
baseFormatter.unbind(key, value.toString)
}

private[mappings] def booleanFormatter(
requiredKey: String,
invalidKey: String,
Expand Down
8 changes: 8 additions & 0 deletions app/forms/mappings/Mappings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ trait Mappings extends Formatters with Constraints {
): FieldMapping[Int] =
of(intFormatter(requiredKey, wholeNumberKey, nonNumericKey, args))

protected def bigDecimal(
requiredKey: String = "error.required",
nonNumericKey: String = "error.nonNumeric",
twoDecimalPlacesKey: String = "error.twoDecimalPlaces",
args: Seq[String] = Seq.empty
): FieldMapping[BigDecimal] =
of(bigDecimalFormatter(requiredKey, nonNumericKey, twoDecimalPlacesKey, args))

protected def boolean(
requiredKey: String = "error.required",
invalidKey: String = "error.boolean",
Expand Down
6 changes: 4 additions & 2 deletions app/navigation/Navigator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import models._
class Navigator @Inject() () {

private val normalRoutes: Page => UserAnswers => Call = {
case DraughtReliefQuestionPage => _ => routes.SmallProducerReliefQuestionController.onPageLoad(NormalMode)
case _ =>
case ProductNamePage => _ => routes.AlcoholByVolumeQuestionController.onPageLoad(NormalMode)
case AlcoholByVolumeQuestionPage => _ => routes.DraughtReliefQuestionController.onPageLoad(NormalMode)
case DraughtReliefQuestionPage => _ => routes.SmallProducerReliefQuestionController.onPageLoad(NormalMode)
case _ =>
_ => routes.IndexController.onPageLoad

}
Expand Down
26 changes: 26 additions & 0 deletions app/pages/AlcoholByVolumeQuestionPage.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 pages

import play.api.libs.json.JsPath

case object AlcoholByVolumeQuestionPage extends QuestionPage[BigDecimal] {

override def path: JsPath = JsPath \ toString

override def toString: String = "alcoholByVolumeQuestion"
}
40 changes: 40 additions & 0 deletions app/viewmodels/checkAnswers/AlcoholByVolumeQuestionSummary.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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 viewmodels.checkAnswers

import controllers.routes
import models.{CheckMode, UserAnswers}
import pages.AlcoholByVolumeQuestionPage
import play.api.i18n.Messages
import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow
import viewmodels.govuk.summarylist._
import viewmodels.implicits._

object AlcoholByVolumeQuestionSummary {

def row(answers: UserAnswers)(implicit messages: Messages): Option[SummaryListRow] =
answers.get(AlcoholByVolumeQuestionPage).map { answer =>
SummaryListRowViewModel(
key = "alcoholByVolumeQuestion.checkYourAnswersLabel",
value = ValueViewModel(answer.toString),
actions = Seq(
ActionItemViewModel("site.change", routes.AlcoholByVolumeQuestionController.onPageLoad(CheckMode).url)
.withVisuallyHiddenText(messages("alcoholByVolumeQuestion.change.hidden"))
)
)
}
}
60 changes: 60 additions & 0 deletions app/views/AlcoholByVolumeQuestionView.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@*
* 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.
*@

@import viewmodels.InputWidth._
@import components.{Link, SectionHeading, PageHeading}
@import uk.gov.hmrc.govukfrontend.views.viewmodels.content.Empty


@this(
layout: templates.Layout,
formHelper: FormWithCSRF,
govukErrorSummary: GovukErrorSummary,
govukInput: GovukInput,
govukButton: GovukButton,
sectionHeading: SectionHeading,
pageHeading: PageHeading
)

@(form: Form[_], mode: Mode)(implicit request: Request[_], messages: Messages)

@layout(pageTitle = title(form, messages("alcoholByVolumeQuestion.title"))) {

@formHelper(action = routes.AlcoholByVolumeQuestionController.onSubmit(mode), Symbol("autoComplete") -> "off") {

@if(form.errors.nonEmpty) {
@govukErrorSummary(ErrorSummaryViewModel(form))
}
@sectionHeading(
id = "draught-relief-question-section",
text = messages("section.alcoholDutyReturn"),
)

@govukInput(
InputViewModel(
field = form("value"),
label = LabelViewModel(messages("alcoholByVolumeQuestion.heading")).asPageHeading()
)
.withSuffix(PrefixOrSuffix(content = "%"))
.asNumeric()
.withWidth(Fixed10)
)

@govukButton(
ButtonViewModel("saveAndContinueButton",messages("site.saveAndContinue"))
)
}
}
4 changes: 4 additions & 0 deletions conf/app.routes
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ POST /smallProducerReliefQuestion controllers.Small
GET /changeSmallProducerReliefQuestion controllers.SmallProducerReliefQuestionController.onPageLoad(mode: Mode = CheckMode)
POST /changeSmallProducerReliefQuestion controllers.SmallProducerReliefQuestionController.onSubmit(mode: Mode = CheckMode)

GET /alcoholByVolumeQuestion controllers.AlcoholByVolumeQuestionController.onPageLoad(mode: Mode = NormalMode)
POST /alcoholByVolumeQuestion controllers.AlcoholByVolumeQuestionController.onSubmit(mode: Mode = NormalMode)
GET /changeAlcoholByVolumeQuestion controllers.AlcoholByVolumeQuestionController.onPageLoad(mode: Mode = CheckMode)
POST /changeAlcoholByVolumeQuestion controllers.AlcoholByVolumeQuestionController.onSubmit(mode: Mode = CheckMode)
Loading

0 comments on commit 5b56ff6

Please sign in to comment.