Skip to content

Commit

Permalink
Select agreements and exceptions for requirements (#912)
Browse files Browse the repository at this point in the history
* Select agreements and exceptions for requirements

https://eaflood.atlassian.net/browse/WATER-4301

This is PR is focused on adding functionality to page 7 of 11 in the return requirement pages.

The pages were previously setup as part of a separate PR to create the user journey.
  • Loading branch information
rvsiyad authored and jonathangoulding committed Apr 29, 2024
1 parent 7f3cd4b commit 3236030
Show file tree
Hide file tree
Showing 15 changed files with 597 additions and 14 deletions.
14 changes: 10 additions & 4 deletions app/controllers/return-requirements.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

const AddNoteService = require('../services/return-requirements/add-note.service.js')
const AbstractionPeriodService = require('../services/return-requirements/abstraction-period.service.js')
const AgreementsExceptionsService = require('../services/return-requirements/agreements-exceptions.service.js')
const CheckYourAnswersService = require('../services/return-requirements/check-your-answers.service.js')
const FrequencyCollectedService = require('../services/return-requirements/frequency-collected.service.js')
const FrequencyReportedService = require('../services/return-requirements/frequency-reported.service.js')
Expand All @@ -21,6 +22,7 @@ const SiteDescriptionService = require('../services/return-requirements/site-des
const StartDateService = require('../services/return-requirements/start-date.service.js')
const SubmitAddNoteService = require('../services/return-requirements/submit-add-note.service.js')
const SubmitAbstractionPeriod = require('../services/return-requirements/submit-abstraction-period.service.js')
const SubmitAgreementsExceptions = require('../services/return-requirements/submit-agreements-exceptions.service.js')
const SubmitCheckYourAnswersService = require('../services/return-requirements/submit-check-your-answers.service.js')
const SubmitFrequencyCollectedService = require('../services/return-requirements/submit-frequency-collected.service.js')
const SubmitFrequencyReportedService = require('../services/return-requirements/submit-frequency-reported.service.js')
Expand Down Expand Up @@ -56,12 +58,10 @@ async function addNote (request, h) {
async function agreementsExceptions (request, h) {
const { sessionId } = request.params

const session = await SessionModel.query().findById(sessionId)
const pageData = await AgreementsExceptionsService.go(sessionId)

return h.view('return-requirements/agreements-exceptions.njk', {
activeNavBar: 'search',
pageTitle: 'Select agreements and exceptions for the return requirement',
...session
...pageData
})
}

Expand Down Expand Up @@ -223,6 +223,12 @@ async function submitAddNote (request, h) {
async function submitAgreementsExceptions (request, h) {
const { sessionId } = request.params

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

if (pageData.error) {
return h.view('return-requirements/agreements-exceptions.njk', pageData)
}

return h.redirect(`/system/return-requirements/${sessionId}/check-your-answers`)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict'

/**
* Formats data for the `/return-requirements/{sessionId}/agreements-exceptions` page
* @module AgreementsExceptionsPresenter
*/

/**
* Formats data for the `/return-requirements/{sessionId}/agreements-exceptions` page
*
* @param {module:SessionModel} session - The returns requirements session instance
* @param {Object} [payload] - The payload from the request
*
* @returns {Object} - The data formatted for the view template
*/
function go (session) {
const data = {
id: session.id,
licenceId: session.data.licence.id,
licenceRef: session.data.licence.licenceRef,
agreementsExceptions: session.data.agreementsExceptions ? session.data.agreementsExceptions : ''
}

return data
}

module.exports = {
go
}
34 changes: 34 additions & 0 deletions app/services/return-requirements/agreements-exceptions.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict'

/**
* Orchestrates fetching and presenting the data for `/return-requirements/{sessionId}/agreements-exceptions` page
* @module AgreementExceptionService
*/

const AgreementsExceptionsPresenter = require('../../presenters/return-requirements/agreements-exceptions.presenter.js')
const SessionModel = require('../../models/session.model.js')

/**
* Orchestrates fetching and presenting the data for `/return-requirements/{sessionId}/agreements-exceptions` page
*
* Supports generating the data needed for the agreements and exceptions page in the return requirements setup journey.
* It fetches the current session record and combines it with the date fields and other information needed for the form.
*
* @param {string} sessionId - The UUID of the current session
*
* @returns {Promise<Object>} The view data for the agreements and exceptions page
*/
async function go (sessionId) {
const session = await SessionModel.query().findById(sessionId)
const formattedData = AgreementsExceptionsPresenter.go(session)

return {
activeNavBar: 'search',
pageTitle: 'Select agreements and exceptions for the requirements for returns',
...formattedData
}
}

module.exports = {
go
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async function go (sessionId, payload) {
return {
activeNavBar: 'search',
error: validationResult,
pageTitle: 'Select the abstraction period for the requirements for returns',
pageTitle: 'Enter the abstraction period for the requirements for returns',
...formattedData
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'use strict'

/**
* Orchestrates validating the data for `/return-requirements/{sessionId}/agreements-exceptions` page
* @module SubmitAgreementsExceptions
*/

const AgreementsExceptionsPresenter = require('../../presenters/return-requirements/agreements-exceptions.presenter.js')
const AgreementsExceptionsValidator = require('../../validators/return-requirements/agreements-exceptions.validator.js')
const SessionModel = require('../../models/session.model.js')

/**
* Orchestrates validating the data for `/return-requirements/{sessionId}/agreements-exceptions` page
*
* It first retrieves the session instance for the returns requirements journey in progress.
*
* The user input is then validated and the result is then combined with the output of the presenter to generate the
* page data needed by the view. If there was a validation error the controller will re-render the page so needs this
* information. If all is well the controller will redirect to the next page in the journey.
*
* @param {string} sessionId - The UUID of the current session
* @param {Object} payload - The submitted form data
*
* @returns {Promise<Object>} The page data for the agreements and exceptions page
*/
async function go (sessionId, payload) {
const session = await SessionModel.query().findById(sessionId)

_handleOneOptionSelected(payload)

const validationResult = _validate(payload)

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

return {}
}

const formattedData = AgreementsExceptionsPresenter.go(session, payload)

return {
activeNavBar: 'search',
error: validationResult,
pageTitle: 'Select agreements and exceptions for the requirements for returns',
...formattedData
}
}

/**
* When a single agreement and exception is checked by the user, it returns as a string. When multiple agreements and
* exceptions are checked, the 'agreementsExceptions' is returned as an array. This function works to make those single
* selected string 'agreementsExceptions' into an array for uniformity.
*/
function _handleOneOptionSelected (payload) {
if (!Array.isArray(payload.agreementsExceptions)) {
payload.agreementsExceptions = [payload.agreementsExceptions]
}
}

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

currentData.agreementsExceptions = payload.agreementsExceptions

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

function _validate (payload) {
const validation = AgreementsExceptionsValidator.go(payload)

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/return-requirements/submit-reason.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/**
* Orchestrates validating the data for `/return-requirements/{sessionId}/reason` page
* @module StartDateService
* @module SubmitReasonService
*/

const ReasonPresenter = require('../../presenters/return-requirements/reason.presenter.js')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict'

/**
* Validates data submitted for the `/return-requirements/{sessionId}/agreements-exceptions` page
* @module AgreementsExceptionsValidator
*/

const Joi = require('joi')

/**
* Validates data submitted for the `/return-requirements/{sessionId}/agreements-exceptions` page
*
* When setting up a requirement users must specify an agreement and exception for the return requirement.
* Users must select one or more agreements and exceptions linked to the licence. If these requirements are not met
* the validation will return an error.
*
* @param {Object} payload - The payload from the request to be validated
*
* @returns {Object} The result from calling Joi's schema.validate(). If any errors are found the
* `error:` property will also exist detailing what the issue is.
*/
function go (payload) {
const agreementsExceptions = payload.agreementsExceptions

const errorMessage = 'Select if there are any agreements and exceptions needed for the return requirements'

const schema = Joi.object({
agreementsExceptions: Joi.array()
.items(Joi.string().valid(...VALID_VALUES))
.required()
.messages({
'any.required': errorMessage,
'any.only': errorMessage,
'array.includesOne': errorMessage,
'array.includes': errorMessage,
'array.sparse': errorMessage
})
})

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

const VALID_VALUES = [
'gravity-fill',
'transfer-re-abstraction-scheme',
'two-part-tariff',
'56-returns-exception',
'none'
]

module.exports = {
go
}
1 change: 1 addition & 0 deletions app/views/return-requirements/abstraction-period.njk
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@

{# Main heading #}
<div class="govuk-body">
<span class="govuk-caption-l"> Licence {{ licenceRef }} </span>
<h1 class="govuk-heading-xl govuk-!-margin-bottom-3">{{ pageTitle }}</h1>
</div>

Expand Down
64 changes: 62 additions & 2 deletions app/views/return-requirements/agreements-exceptions.njk
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{% extends 'layout.njk' %}
{% from "govuk/components/back-link/macro.njk" import govukBackLink %}
{% from "govuk/components/button/macro.njk" import govukButton %}
{% from "govuk/components/checkboxes/macro.njk" import govukCheckboxes %}
{% from "govuk/components/error-message/macro.njk" import govukErrorMessage %}
{% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %}

{% set rootLink = "/system/return-requirements/" + id %}

Expand All @@ -15,12 +18,69 @@
{% endblock %}

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

{# Main heading #}
<div class="govuk-body">
<h1 class="govuk-heading-xl govuk-!-margin-bottom-3">{{ pageTitle }}</h1>
<div>
<span class="govuk-caption-l"> Licence {{ licenceRef }} </span>
<h1 class="govuk-heading-l govuk-!-margin-bottom-3">{{ pageTitle }}</h1>

</div>

<form method="post">
{{ govukCheckboxes({
name: "agreementsExceptions",
hint: {
text: "Select all that apply"
},
errorMessage: error,
items: [
{
value: "gravity-fill",
text: "Gravity fill",
checked: agreementsExceptions and "gravity-fill" in agreementsExceptions
},
{
value: "transfer-re-abstraction-scheme",
text: "Transfer re-abstraction scheme",
checked: agreementsExceptions and "transfer-re-abstraction-scheme" in agreementsExceptions
},
{
value: "two-part-tariff",
text: "Two-part tarrif",
checked: agreementsExceptions and "two-part-tariff" in agreementsExceptions,
hint: {
text: "requires daily collection"
}
},
{
value: "56-returns-exception",
text: "56 returns exception",
checked: agreementsExceptions and "56-returns-exception" in agreementsExceptions
},
{
divider: "or"
},
{
value: "none",
text: "None",
checked: "none" === agreementsExceptions,
behaviour: "exclusive"
}
]
}) }}

<div class="govuk-body">
{{ govukButton({ text: "Continue" }) }}
</div>
Expand Down
8 changes: 8 additions & 0 deletions test/controllers/return-requirements.controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const { expect } = Code
// Things we need to stub
const AbstractionPeriodService = require('../../app/services/return-requirements/abstraction-period.service.js')
const AddNoteService = require('../../app/services/return-requirements/add-note.service.js')
const AgreementsExceptionService = require('../../app/services/return-requirements/agreements-exceptions.service.js')
const CheckYourAnswersService = require('../../app/services/return-requirements/check-your-answers.service.js')
const FrequencyCollectedService = require('../../app/services/return-requirements/frequency-collected.service.js')
const FrequencyReportedService = require('../../app/services/return-requirements/frequency-reported.service.js')
Expand Down Expand Up @@ -81,6 +82,13 @@ describe('Return requirements controller', () => {
})

describe('GET /return-requirements/{sessionId}/agreements-exceptions', () => {
beforeEach(async () => {
Sinon.stub(AgreementsExceptionService, 'go').resolves({
id: '8702b98f-ae51-475d-8fcc-e049af8b8d38',
pageTitle: 'Select agreements and exceptions for the return requirement'
})
})

describe('when the request succeeds', () => {
it('returns the page successfully', async () => {
const response = await server.inject(_options('agreements-exceptions'))
Expand Down
Loading

0 comments on commit 3236030

Please sign in to comment.