diff --git a/app/controllers/return-requirements.controller.js b/app/controllers/return-requirements.controller.js index 4426f90673..f42e327551 100644 --- a/app/controllers/return-requirements.controller.js +++ b/app/controllers/return-requirements.controller.js @@ -6,6 +6,7 @@ */ const AbstractionPeriodService = require('../services/return-requirements/abstraction-period.service.js') +const AddService = require('../services/return-requirements/add.service.js') const AgreementsExceptionsService = require('../services/return-requirements/agreements-exceptions.service.js') const CancelService = require('../services/return-requirements/cancel.service.js') const CheckService = require('../services/return-requirements/check.service.js') @@ -50,6 +51,14 @@ async function abstractionPeriod (request, h) { }) } +async function add (request, h) { + const { sessionId } = request.params + + const requirementIndex = await AddService.go(sessionId) + + return h.redirect(`/system/return-requirements/${sessionId}/purpose/${requirementIndex}`) +} + async function agreementsExceptions (request, h) { const { requirementIndex, sessionId } = request.params @@ -458,6 +467,7 @@ async function submitStartDate (request, h) { module.exports = { abstractionPeriod, + add, agreementsExceptions, approved, cancel, diff --git a/app/presenters/return-requirements/check.presenter.js b/app/presenters/return-requirements/check.presenter.js index ce3f4c5929..c0deeeff03 100644 --- a/app/presenters/return-requirements/check.presenter.js +++ b/app/presenters/return-requirements/check.presenter.js @@ -18,6 +18,7 @@ function go (session) { pageTitle: `Check the return requirements for ${licence.licenceHolder}`, reason: returnRequirementReasons[reason], reasonLink: _reasonLink(sessionId, journey), + requirements: _requirements(session), sessionId, startDate: _startDate(session), userEmail: note ? note.userEmail : 'No notes added' @@ -32,6 +33,24 @@ function _reasonLink (sessionId, journey) { return `/system/return-requirements/${sessionId}/no-returns-required` } +function _requirements (session) { + const { requirements } = session + + const completedRequirements = [] + + for (const requirement of requirements) { + const { siteDescription, agreementsExceptions } = requirement + + // NOTE: We determine a requirement is complete because agreement exceptions is populated and it is the last step in + // the journey + if (agreementsExceptions) { + completedRequirements.push({ siteDescription }) + } + } + + return completedRequirements +} + function _startDate (session) { const { licence, startDateOptions, startDateDay, startDateMonth, startDateYear } = session diff --git a/app/presenters/return-requirements/purpose.presenter.js b/app/presenters/return-requirements/purpose.presenter.js index 5a955fb129..9a05afb576 100644 --- a/app/presenters/return-requirements/purpose.presenter.js +++ b/app/presenters/return-requirements/purpose.presenter.js @@ -29,9 +29,18 @@ function go (session, requirementIndex, purposesData) { } function _backLink (session) { - const { checkPageVisited, id } = session - - if (checkPageVisited) { + const { checkPageVisited, id, requirements } = session + + // NOTE: Purpose is the first page in the manual setup journey. So, when a user first comes through, we want to allow + // them to go back to `/setup`. Once they've got to the `/check` page they may return because they clicked the + // 'Change' link for the purpose. When this happens, `checkPageVisited` will be true and 'Back' needs to take them + // back there. + // + // But if they click 'Add requirement' on the `/check` page they'll also be directed here. When that happens + // `checkPageVisited` will have been reset to false to allow the user to progress through the setup journey. In this + // scenario 'Back' also needs to take them back to `/check`. Hence, the logic is different in this presenter when + // compared with the other setup pages. + if (checkPageVisited || requirements.length > 1) { return `/system/return-requirements/${id}/check` } diff --git a/app/routes/return-requirement.routes.js b/app/routes/return-requirement.routes.js index c7671689be..94b2674e83 100644 --- a/app/routes/return-requirement.routes.js +++ b/app/routes/return-requirement.routes.js @@ -29,6 +29,20 @@ const routes = [ description: 'Submit the abstraction period for the return requirement' } }, + { + method: 'POST', + path: '/return-requirements/{sessionId}/add', + handler: ReturnRequirementsController.add, + options: { + auth: { + access: { + scope: ['billing'] + } + }, + description: 'Adds another object to the requirements array in the session' + } + + }, { method: 'GET', path: '/return-requirements/{sessionId}/agreements-exceptions/{requirementIndex}', diff --git a/app/services/return-requirements/add.service.js b/app/services/return-requirements/add.service.js new file mode 100644 index 0000000000..e3c90d715e --- /dev/null +++ b/app/services/return-requirements/add.service.js @@ -0,0 +1,38 @@ +'use strict' + +/** + * Orchestrates adding an empty object to the requirements array in the session + * @module AddService +*/ + +const SessionModel = require('../../models/session.model.js') + +/** + * Orchestrates adding an empty object to the requirements array in the session + * + * Supports adding another object to the requirements array in the session when a user hits the 'Add another + * requirement' button. + * + * @param {string} sessionId - The UUID of the current session + * + * @returns {number} - The index of the new requirement. Needed by the setup pages so they know which requirement to + * display and update +*/ +async function go (sessionId) { + const session = await SessionModel.query().findById(sessionId) + + await _save(session) + + return session.requirements.length - 1 +} + +async function _save (session) { + session.requirements.push({}) + + session.checkPageVisited = false + + return session.$update() +} +module.exports = { + go +} diff --git a/app/views/return-requirements/check.njk b/app/views/return-requirements/check.njk index 1ae9b30885..a63271bba9 100644 --- a/app/views/return-requirements/check.njk +++ b/app/views/return-requirements/check.njk @@ -19,86 +19,108 @@ -
-
-
- {{ govukSummaryList({ - classes: 'govuk-!-margin-bottom-2', - rows: [ - { - classes: 'govuk-summary-list govuk-summary-list__row--no-border', - key: { - text: "Start date" - }, - value: { - text: startDate - }, - actions: { - items: [ - { - href: "/system/return-requirements/" + sessionId + "/start-date", - text: "Change", - visuallyHiddenText: "the start date for the return requirement" - } - ] - } +
+
+ {{ govukSummaryList({ + classes: 'govuk-!-margin-bottom-2', + rows: [ + { + classes: 'govuk-summary-list govuk-summary-list__row--no-border', + key: { + text: "Start date" + }, + value: { + text: startDate }, - { - classes: 'govuk-summary-list govuk-summary-list__row--no-border', - key: { - text: "Reason" - }, - value: { - text: reason - }, - actions: { - items: [ - { - href: reasonLink, - text: "Change", - visuallyHiddenText: "the reason for the return requirement" - } - ] - } + actions: { + items: [ + { + href: "/system/return-requirements/" + sessionId + "/start-date", + text: "Change", + visuallyHiddenText: "the start date for the return requirement" + } + ] } - ] - }) }} -
-
+ }, + { + classes: 'govuk-summary-list govuk-summary-list__row--no-border', + key: { + text: "Reason" + }, + value: { + text: reason + }, + actions: { + items: [ + { + href: reasonLink, + text: "Change", + visuallyHiddenText: "the reason for the return requirement" + } + ] + } + } + ] + }) }} +
+
-
-

Notes

-
- {{ govukSummaryList({ - classes: 'govuk-!-margin-bottom-2', - rows: [ - { - classes: 'govuk-summary-list govuk-summary-list__row--no-border', - key: { - text: userEmail, - classes: "govuk-body govuk-!-font-weight-regular" - }, - value: { - text: note | escape | nl2br - }, - actions: { - items: [ - { - href: "note", - text: "Change" if note else "Add a note" - }, - { - href: "delete-note", - text: "Delete" - } if note - ] - } +
+

Notes

+
+ {{ govukSummaryList({ + classes: 'govuk-!-margin-bottom-2', + rows: [ + { + classes: 'govuk-summary-list govuk-summary-list__row--no-border', + key: { + text: userEmail, + classes: "govuk-body govuk-!-font-weight-regular" + }, + value: { + text: note | escape | nl2br + }, + actions: { + items: [ + { + href: "note", + text: "Change" if note else "Add a note" + }, + { + href: "delete-note", + text: "Delete" + } if note + ] } - ] - }) }} -
+ } + ] + }) }} +
+
+ + {% if journey == 'returns-required' %} +
+

Requirements for returns

+ + {{ govukButton({ + text: "Add another requirement", + classes: "govuk-button--secondary", + preventDoubleClick: true + }) }} + + + {% for requirement in requirements %} + {# Set an easier to use index #} + {% set rowIndex = loop.index0 %} +
+

{{requirement.siteDescription}}

+

Remove {{rowIndex}}

+
+ {% endfor %}
+ {% endif %} +
{% if journey == 'no-returns-required' %}

Returns are not required for this licence

diff --git a/test/presenters/return-requirements/check.presenter.test.js b/test/presenters/return-requirements/check.presenter.test.js index b422a33ee3..81b73087ae 100644 --- a/test/presenters/return-requirements/check.presenter.test.js +++ b/test/presenters/return-requirements/check.presenter.test.js @@ -43,6 +43,7 @@ describe('Return Requirements - Check presenter', () => { pageTitle: 'Check the return requirements for Turbo Kid', reason: 'Major change', reasonLink: '/system/return-requirements/61e07498-f309-4829-96a9-72084a54996d/reason', + requirements: [], sessionId: '61e07498-f309-4829-96a9-72084a54996d', startDate: '1 January 2023', userEmail: 'No notes added' diff --git a/test/services/return-requirements/add.service.test.js b/test/services/return-requirements/add.service.test.js new file mode 100644 index 0000000000..ff1e54a4c4 --- /dev/null +++ b/test/services/return-requirements/add.service.test.js @@ -0,0 +1,58 @@ +'use strict' + +// Test framework dependencies +const Lab = require('@hapi/lab') +const Code = require('@hapi/code') + +const { describe, it, beforeEach } = exports.lab = Lab.script() +const { expect } = Code + +// Test helpers +const DatabaseSupport = require('../../support/database.js') +const SessionHelper = require('../../support/helpers/session.helper.js') + +// Thing under test +const AddService = require('../../../app/services/return-requirements/add.service.js') + +describe('Return Requirements - Add service', () => { + let session + + beforeEach(async () => { + await DatabaseSupport.clean() + + session = await SessionHelper.add({ + data: { + checkPageVisited: false, + licence: { + id: '8b7f78ba-f3ad-4cb6-a058-78abc4d1383d', + currentVersionStartDate: '2023-01-01T00:00:00.000Z', + endDate: null, + licenceRef: '01/ABC', + licenceHolder: 'Turbo Kid', + startDate: '2022-04-01T00:00:00.000Z' + }, + journey: 'returns-required', + requirements: [{}], + startDateOptions: 'licenceStartDate', + reason: 'major-change' + } + }) + }) + + describe('when called', () => { + it('adds another empty object to the requirement array in the current setup session record', async () => { + await AddService.go(session.id) + + const refreshedSession = await session.$query() + + expect(refreshedSession.data.requirements.length).to.equal(2) + expect(refreshedSession.data.requirements).to.equal([{}, {}]) + }) + + it('returns the index of the new requirement', async () => { + const result = await AddService.go(session.id) + + expect(result).to.equal(1) + }) + }) +}) diff --git a/test/services/return-requirements/check.service.test.js b/test/services/return-requirements/check.service.test.js index f6320bdd43..47d9f8b172 100644 --- a/test/services/return-requirements/check.service.test.js +++ b/test/services/return-requirements/check.service.test.js @@ -66,6 +66,7 @@ describe('Return Requirements - Check service', () => { pageTitle: 'Check the return requirements for Turbo Kid', reason: 'Major change', reasonLink: `/system/return-requirements/${session.id}/reason`, + requirements: [], startDate: '1 January 2023', userEmail: 'No notes added' }, { skip: ['sessionId'] })