Skip to content

Commit

Permalink
Adding multiple return requirements to the session (#1014)
Browse files Browse the repository at this point in the history
* Adding multiple return requirements to the session

This PR is focused on allowing users to add another return requirement
to the current session once they have reached the check page in the journey.
Users will be redirected to the purpose page for the new requirement.
  • Loading branch information
rvsiyad authored May 16, 2024
1 parent 5b8572d commit d1ec9fa
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 77 deletions.
10 changes: 10 additions & 0 deletions app/controllers/return-requirements.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -458,6 +467,7 @@ async function submitStartDate (request, h) {

module.exports = {
abstractionPeriod,
add,
agreementsExceptions,
approved,
cancel,
Expand Down
19 changes: 19 additions & 0 deletions app/presenters/return-requirements/check.presenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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

Expand Down
15 changes: 12 additions & 3 deletions app/presenters/return-requirements/purpose.presenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}

Expand Down
14 changes: 14 additions & 0 deletions app/routes/return-requirement.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}',
Expand Down
38 changes: 38 additions & 0 deletions app/services/return-requirements/add.service.js
Original file line number Diff line number Diff line change
@@ -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
}
170 changes: 96 additions & 74 deletions app/views/return-requirements/check.njk
Original file line number Diff line number Diff line change
Expand Up @@ -19,86 +19,108 @@
</h1>
</div>

<form method="post">
<div class="govuk-!-margin-bottom-9">
<hr class="govuk-section-break govuk-!-margin-bottom-2 govuk-section-break--visible">
{{ 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"
}
]
}
<div class="govuk-!-margin-bottom-9">
<hr class="govuk-section-break govuk-!-margin-bottom-2 govuk-section-break--visible">
{{ 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"
}
]
}
]
}) }}
<hr class="govuk-section-break govuk-!-margin-bottom-2 govuk-section-break--visible">
</div>
},
{
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"
}
]
}
}
]
}) }}
<hr class="govuk-section-break govuk-!-margin-bottom-2 govuk-section-break--visible">
</div>

<div class="govuk-!-margin-bottom-9">
<h2 class="govuk-heading-l govuk-!-margin-bottom-4" >Notes</h2>
<hr class="govuk-section-break govuk-!-margin-bottom-2 govuk-section-break--visible">
{{ 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
]
}
<div class="govuk-!-margin-bottom-9">
<h2 class="govuk-heading-l govuk-!-margin-bottom-4" >Notes</h2>
<hr class="govuk-section-break govuk-!-margin-bottom-2 govuk-section-break--visible">
{{ 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
]
}
]
}) }}
<hr class="govuk-section-break govuk-!-margin-bottom-2 govuk-section-break--visible">
}
]
}) }}
<hr class="govuk-section-break govuk-!-margin-bottom-2 govuk-section-break--visible">
</div>

{% if journey == 'returns-required' %}
<div class="govuk-!-margin-bottom-9">
<h2 class="govuk-heading-l govuk-!-margin-bottom-4" >Requirements for returns</h2>
<form method= "post" action= "/system/return-requirements/{{sessionId}}/add">
{{ govukButton({
text: "Add another requirement",
classes: "govuk-button--secondary",
preventDoubleClick: true
}) }}
</form>

{% for requirement in requirements %}
{# Set an easier to use index #}
{% set rowIndex = loop.index0 %}
<div>
<h3>{{requirement.siteDescription}}</h3>
<p>Remove {{rowIndex}} </p>
</div>
{% endfor %}
</div>
{% endif %}

<form method="post">
<div class="govuk-body">
{% if journey == 'no-returns-required' %}
<h3 class="govuk-heading-m">Returns are not required for this licence</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
58 changes: 58 additions & 0 deletions test/services/return-requirements/add.service.test.js
Original file line number Diff line number Diff line change
@@ -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)
})
})
})
Loading

0 comments on commit d1ec9fa

Please sign in to comment.