Skip to content

Commit

Permalink
Add bill runs setup region page to journey (#804)
Browse files Browse the repository at this point in the history
https://eaflood.atlassian.net/browse/WATER-4375

> Part of a series of changes related to replacing the create bill run journey to incorporate changes for two-part tariff

This adds the second page to the setup bill run journey; select a region. This has a bit more complexity than [Add bill runs setup type page to journey](#802) because depending on the bill run type selected, we are either done or need to go to another page in the journey.
  • Loading branch information
Cruikshanks authored Mar 12, 2024
1 parent 5dde8aa commit f84d03c
Show file tree
Hide file tree
Showing 16 changed files with 718 additions and 3 deletions.
36 changes: 36 additions & 0 deletions app/controllers/bill-runs-setup.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,49 @@
*/

const InitiateSessionService = require('../services/bill-runs/setup/initiate-session.service.js')
const RegionService = require('../services/bill-runs/setup/region.service.js')
const SubmitRegionService = require('../services/bill-runs/setup/submit-region.service.js')
const SubmitTypeService = require('../services/bill-runs/setup/submit-type.service.js')
const TypeService = require('../services/bill-runs/setup/type.service.js')

async function region (request, h) {
const { sessionId } = request.params

const pageData = await RegionService.go(sessionId)

return h.view('bill-runs/setup/region.njk', {
activeNavBar: 'bill-runs',
pageTitle: 'Select the region',
...pageData
})
}

async function setup (_request, h) {
const session = await InitiateSessionService.go()

return h.redirect(`/system/bill-runs/setup/${session.id}/type`)
}

async function submitRegion (request, h) {
const { sessionId } = request.params

const pageData = await SubmitRegionService.go(sessionId, request.payload)

if (pageData.error) {
return h.view('bill-runs/setup/region.njk', {
activeNavBar: 'bill-runs',
pageTitle: 'Select a region',
...pageData
})
}

if (pageData.setupComplete) {
return h.redirect(`/system/bill-runs/setup/${sessionId}/generate`)
}

return h.redirect(`/system/bill-runs/setup/${sessionId}/year`)
}

async function submitType (request, h) {
const { sessionId } = request.params

Expand Down Expand Up @@ -44,7 +78,9 @@ async function type (request, h) {
}

module.exports = {
region,
setup,
submitRegion,
submitType,
type
}
26 changes: 26 additions & 0 deletions app/presenters/bill-runs/setup/region.presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict'

/**
* Formats data for the `/bill-runs/setup/{sessionId}/region` page
* @module RegionPresenter
*/

/**
* Formats data for the `/bill-runs/setup/{sessionId}/region` page
*
* @param {module:SessionModel} session - The session instance to format
* @param {module:RegionsModel[]} regions - UUID and display name of all regions
*
* @returns {Object} - The data formatted for the view template
*/
function go (session, regions) {
return {
sessionId: session.id,
regions,
selectedRegion: session.data.region ? session.data.region : null
}
}

module.exports = {
go
}
26 changes: 26 additions & 0 deletions app/routes/bill-runs-setup.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,32 @@
const BillRunsSetupController = require('../controllers/bill-runs-setup.controller.js')

const routes = [
{
method: 'GET',
path: '/bill-runs/setup/{sessionId}/region',
handler: BillRunsSetupController.region,
options: {
auth: {
access: {
scope: ['billing']
}
},
description: 'Select the region for the bill run'
}
},
{
method: 'POST',
path: '/bill-runs/setup/{sessionId}/region',
handler: BillRunsSetupController.submitRegion,
options: {
auth: {
access: {
scope: ['billing']
}
},
description: 'Submit the region for the bill run'
}
},
{
method: 'GET',
path: '/bill-runs/setup',
Expand Down
28 changes: 28 additions & 0 deletions app/services/bill-runs/setup/fetch-regions.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict'

/**
* Fetches just the ID and display name for all regions
* @module FetchRegionsService
*/

const RegionModel = require('../../../models/region.model.js')

/**
* Fetches just the ID and display name for all regions
*
* @returns {Promise<Object[]>} The display name and ID for all regions in the service ordered by display name
*/
async function go () {
return RegionModel.query()
.select([
'id',
'displayName'
])
.orderBy([
{ column: 'displayName', order: 'asc' }
])
}

module.exports = {
go
}
35 changes: 35 additions & 0 deletions app/services/bill-runs/setup/region.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict'

/**
* Orchestrates fetching and presenting the data for `/bill-runs/setup/{sessionId}/region` page
* @module RegionService
*/

const FetchRegionsService = require('./fetch-regions.service.js')
const RegionPresenter = require('../../../presenters/bill-runs/setup/region.presenter.js')
const SessionModel = require('../../../models/session.model.js')

/**
* Orchestrates fetching and presenting the data for `/bill-runs/setup/{sessionId}/region` page
*
* Supports generating the data needed for the region page in the setup bill run journey. It fetches the current
* session record and combines it with the radio buttons and other information needed for the form.
*
* @param {string} sessionId - The UUID for setup bill run session record
*
* @returns {Promise<Object>} The view data for the region page
*/
async function go (sessionId) {
const session = await SessionModel.query().findById(sessionId)
const regions = await FetchRegionsService.go()

const formattedData = RegionPresenter.go(session, regions)

return {
...formattedData
}
}

module.exports = {
go
}
78 changes: 78 additions & 0 deletions app/services/bill-runs/setup/submit-region.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use strict'

/**
* Orchestrates validating the data for `/bill-runs/setup/{sessionId}/region` page
* @module SubmitRegionService
*/

const FetchRegionsService = require('./fetch-regions.service.js')
const RegionPresenter = require('../../../presenters/bill-runs/setup/region.presenter.js')
const RegionValidator = require('../../../validators/bill-runs/setup/region.validator.js')
const SessionModel = require('../../../models/session.model.js')

/**
* Orchestrates validating the data for `/bill-runs/setup/{sessionId}/region` page
*
* It first retrieves the session instance for the setup bill run journey in progress. It then validates the payload of
* the submitted request.
*
* If there is no validation error it will save the selected value to the session then return an object with a
* `setupComplete:` property. If the bill run type was not two-part tariff this will be true. Else it will be false.
* The fact the property exists will tell the controller the submission was successful. Whether the property
* `setupComplete:` is true or false will be used to determine which page to direct the user to next.
*
* If there is a validation error it is combined with the output of the presenter to generate the page data needed to
* re-render the view with an error message.
*
* @param {string} sessionId - The UUID of the current session
* @param {Object} payload - The submitted form data
*
* @returns {Promise<Object>} An object with a `setupComplete:` property if there are no errors else the page data for
* the region page including the validation error details
*/
async function go (sessionId, payload) {
const session = await SessionModel.query().findById(sessionId)
const regions = await FetchRegionsService.go()

const validationResult = _validate(payload, regions)

if (!validationResult) {
await _save(session, payload)

// The journey is complete (we don't need any details) if the bill run type is not 2PT
return { setupComplete: !session.data.type.startsWith('two_part') }
}

const formattedData = RegionPresenter.go(session, regions)

return {
error: validationResult,
...formattedData
}
}

async function _save (session, payload) {
const currentData = session.data

currentData.region = payload.region

return session.$query().patch({ data: currentData })
}

function _validate (payload, regions) {
const validation = RegionValidator.go(payload, regions)

if (!validation.error) {
return null
}

const { message } = validation.error.details[0]

return {
text: message
}
}

module.exports = {
go
}
2 changes: 1 addition & 1 deletion app/services/bill-runs/setup/submit-type.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const TypeValidator = require('../../../validators/bill-runs/setup/type.validato
* It first retrieves the session instance for the setup bill run journey in progress. It then validates the payload of
* the submitted request.
*
* If there is validation error it will save the selected value to the session then return an empty object. This will
* If there is no validation error it will save the selected value to the session then return an empty object. This will
* indicate to the controller that the submission was successful triggering it to redirect to the next page in the
* journey.
*
Expand Down
39 changes: 39 additions & 0 deletions app/validators/bill-runs/setup/region.validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict'

/**
* Validates data submitted for the `/bill-runs/setup/{sessionId}/region` page
* @module BillRunsCreateRegionValidator
*/

const Joi = require('joi')

/**
* Validates data submitted for the `/bill-runs/setup/{sessionId}/region` page
*
* @param {Object} payload - The payload from the request to be validated
*
* @returns {Object} the result from calling Joi's schema.validate(). It will be an object with a `value:` property. If
* any errors are found the `error:` property will also exist detailing what the issues were
*/
function go (data, regions) {
const validValues = regions.map((region) => {
return region.id
})

const schema = Joi.object({
region: Joi.string()
.required()
.valid(...validValues)
.messages({
'any.required': 'Select the region',
'any.only': 'Select the region',
'string.empty': 'Select the region'
})
})

return schema.validate(data, { abortEarly: false })
}

module.exports = {
go
}
59 changes: 59 additions & 0 deletions app/views/bill-runs/setup/region.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{% extends 'layout.njk' %}
{% from "govuk/components/back-link/macro.njk" import govukBackLink %}
{% from "govuk/components/button/macro.njk" import govukButton %}
{% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %}
{% from "govuk/components/radios/macro.njk" import govukRadios %}

{% block breadcrumbs %}
{# Back link #}
{{
govukBackLink({
text: 'Back',
href: '/system/bill-runs/setup/' + sessionId + '/type'
})
}}
{% endblock %}

{% block content %}
{% if error %}
{{ govukErrorSummary({
titleText: "There is a problem",
errorList: [
{
text: error.text,
href: '#region-error'
}
]
}) }}
{% endif %}

<div class="govuk-body">
<form method="post">
{% set regionItems = [] %}
{% for region in regions %}
{% set regionItem = { text: region.displayName, value: region.id, checked: region.id == selectedRegion } %}

{# Push our item into the region items array #}
{% set regionItems = (regionItems.push(regionItem), regionItems) %}
{% endfor %}

{{ govukRadios({
attributes: {
'data-test': 'bill-run-region'
},
name: 'region',
errorMessage: error,
fieldset: {
legend: {
text: pageTitle,
isPageHeading: true,
classes: 'govuk-fieldset__legend--l govuk-!-margin-bottom-6'
}
},
items: regionItems
}) }}

{{govukButton({ text: 'Continue', preventDoubleClick: true }) }}
</form>
</div>
{% endblock %}
Loading

0 comments on commit f84d03c

Please sign in to comment.