-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Return Logs setup received date (#1602)
https://eaflood.atlassian.net/browse/WATER-4844 As part of the work to migrate the return logs setup journey, this PR is focused on adding the 'When was this return received' page. NOTE: This work is only for a due return. The pages will be updated in a separate PR to handle the editing of existing return logs.
- Loading branch information
1 parent
5996791
commit 1eccd0f
Showing
12 changed files
with
1,023 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
'use strict' | ||
|
||
/** | ||
* Format data for the `/return-log/setup/{sessionId}/received` page | ||
* @module ReceivedPresenter | ||
*/ | ||
|
||
/** | ||
* Format data for the `/return-log/setup/{sessionId}/received` page | ||
* | ||
* @param {module:SessionModel} session - The return log setup session instance | ||
* | ||
* @returns {object} page data needed by the view template | ||
*/ | ||
function go(session) { | ||
const { | ||
id: sessionId, | ||
data: { returnReference }, | ||
receivedDateOptions, | ||
receivedDateDay, | ||
receivedDateMonth, | ||
receivedDateYear | ||
} = session | ||
|
||
return { | ||
receivedDateOption: receivedDateOptions ?? null, | ||
receivedDateDay: receivedDateDay ?? null, | ||
receivedDateMonth: receivedDateMonth ?? null, | ||
receivedDateYear: receivedDateYear ?? null, | ||
sessionId, | ||
returnReference, | ||
backLink: `/system/return-logs/setup/${session.id}/start` | ||
} | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
'use strict' | ||
|
||
/** | ||
* Orchestrates fetching and presenting the data for `/return-logs/setup/{sessionId}/received` page | ||
* @module ReceivedService | ||
*/ | ||
|
||
const ReceivedPresenter = require('../../../presenters/return-logs/setup/received.presenter.js') | ||
const SessionModel = require('../../../models/session.model.js') | ||
|
||
/** | ||
* Orchestrates fetching and presenting the data for `/return-logs/setup/{sessionId}/received` page | ||
* | ||
* Supports generating the data needed for the received page in the return log setup journey. It fetches the | ||
* current session record and formats the data needed for the page. | ||
* | ||
* @param {string} sessionId - The UUID of the current session | ||
* | ||
* @returns {Promise<object>} The view data for the received page | ||
*/ | ||
async function go(sessionId) { | ||
const session = await SessionModel.query().findById(sessionId) | ||
|
||
const formattedData = ReceivedPresenter.go(session) | ||
|
||
return { | ||
pageTitle: 'When was the return received?', | ||
activeNavBar: 'search', | ||
...formattedData | ||
} | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
106 changes: 106 additions & 0 deletions
106
app/services/return-logs/setup/submit-received.service.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
'use strict' | ||
|
||
/** | ||
* Orchestrates validating the data for `/return-logs/setup/{sessionId}/received` page | ||
* @module SubmitReceivedService | ||
*/ | ||
|
||
const ReceivedDateValidator = require('../../../validators/return-logs/setup/received-date.validator.js') | ||
const ReceivedPresenter = require('../../../presenters/return-logs/setup/received.presenter.js') | ||
const SessionModel = require('../../../models/session.model.js') | ||
|
||
/** | ||
* Orchestrates validating the data for `/return-logs/setup/{sessionId}/received` page | ||
* | ||
* It first retrieves the session instance for the return log setup session in progress. The session has details about | ||
* the return log that are needed to validate that the chosen date is valid. | ||
* | ||
* The validation 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>} If no errors the page data for the received page else the validation error details | ||
*/ | ||
async function go(sessionId, payload) { | ||
const session = await SessionModel.query().findById(sessionId) | ||
|
||
const { startDate } = session | ||
const validationResult = _validate(payload, startDate) | ||
|
||
if (!validationResult) { | ||
await _save(session, payload) | ||
|
||
return {} | ||
} | ||
|
||
const formattedData = _submittedSessionData(session, payload) | ||
|
||
return { | ||
pageTitle: 'When was the return received?', | ||
activeNavBar: 'search', | ||
error: validationResult, | ||
...formattedData | ||
} | ||
} | ||
|
||
async function _save(session, payload) { | ||
const selectedOption = payload['received-date-options'] | ||
session.receivedDateOptions = selectedOption | ||
const todaysDate = new Date(new Date().setHours(0, 0, 0, 0)) | ||
|
||
if (selectedOption === 'today') { | ||
session.receivedDate = todaysDate | ||
} else if (selectedOption === 'yesterday') { | ||
// The setDate method updates the date object in place and returns a timestamp, | ||
// not the updated date object itself. To ensure we store the correct date, | ||
// we first modify the 'yesterday' variable and then assign it to session.receivedDate. | ||
todaysDate.setDate(todaysDate.getDate() - 1) | ||
session.receivedDate = todaysDate | ||
} else { | ||
session.receivedDateDay = payload['received-date-day'] | ||
session.receivedDateMonth = payload['received-date-month'] | ||
session.receivedDateYear = payload['received-date-year'] | ||
session.receivedDate = new Date( | ||
`${payload['received-date-year']}-${payload['received-date-month']}-${payload['received-date-day']}` | ||
) | ||
} | ||
|
||
return session.$update() | ||
} | ||
|
||
function _submittedSessionData(session, payload) { | ||
session.receivedDateDay = payload['received-date-day'] ?? null | ||
session.receivedDateMonth = payload['received-date-month'] ?? null | ||
session.receivedDateYear = payload['received-date-year'] ?? null | ||
session.receivedDateOptions = payload['received-date-options'] ?? null | ||
|
||
const data = ReceivedPresenter.go(session) | ||
return data | ||
} | ||
|
||
function _validate(payload, startDate) { | ||
const validation = ReceivedDateValidator.go(payload, startDate) | ||
|
||
if (!validation.error) { | ||
return null | ||
} | ||
|
||
const { message, type } = validation.error.details[0] | ||
|
||
// There are only two possible error scenarios: either a radio button has not been selected, in which case the date | ||
// isn't visible so there cannot be an "invalid date" error; or an invalid date has been entered, in which case the | ||
// date *is* visible so there cannot be a "radio button not selected" error. We identify the former by checking if the | ||
// error type is `any.required`; and so if an error is present which isn't of this type, it must be a date error. | ||
return { | ||
message, | ||
radioFormElement: type === 'any.required' ? { text: message } : null, | ||
dateInputFormElement: type === 'any.required' ? null : { text: message } | ||
} | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
80 changes: 80 additions & 0 deletions
80
app/validators/return-logs/setup/received-date.validator.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
'use strict' | ||
|
||
/** | ||
* Validates data submitted for the `/return-logs/setup/{sessionId}/received` page | ||
* @module ReceivedDateValidator | ||
*/ | ||
|
||
const Joi = require('joi').extend(require('@joi/date')) | ||
|
||
const { leftPadZeroes } = require('../../../presenters/base.presenter.js') | ||
|
||
/** | ||
* Validates data submitted for the `/return-logs/setup/{sessionId}/received` page | ||
* | ||
* When entering a return log users must specify a received date for when the return was received by. The page | ||
* allows them to select todays date, yesterdays date or enter a custom date. | ||
* | ||
* The custom date uses a {@link https://design-system.service.gov.uk/components/date-input/ | GOV.UK date input} | ||
* which is 3 text fields for day, month and year. Users can enter what they like or omit a value completely which is | ||
* why date validation can become quite complex. | ||
* | ||
* Also, the date they enter cannot be before the start date of the return or a date in the future. | ||
* | ||
* Finally, we also need to validate that the user selected one of the options; todays, yesterday or a custom | ||
* date. | ||
* | ||
* @param {object} payload - The payload from the request to be validated | ||
* @param {string} startDate - The date the return log starts from | ||
* | ||
* @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(payload, startDate) { | ||
const { 'received-date-options': selectedOption } = payload | ||
|
||
if (selectedOption === 'custom-date') { | ||
payload.fullDate = _fullDate(payload) | ||
|
||
return _validateCustomReceivedDate(payload, startDate) | ||
} | ||
return _validateReceivedDate(payload) | ||
} | ||
|
||
function _fullDate(payload) { | ||
const { 'received-date-day': day, 'received-date-month': month, 'received-date-year': year } = payload | ||
|
||
const paddedMonth = month ? leftPadZeroes(month, 2) : '' | ||
const paddedDay = day ? leftPadZeroes(day, 2) : '' | ||
|
||
return `${year}-${paddedMonth}-${paddedDay}` | ||
} | ||
|
||
function _validateCustomReceivedDate(payload, startDate) { | ||
const schema = Joi.object({ | ||
fullDate: Joi.date().format(['YYYY-MM-DD']).required().min(startDate).less('now').messages({ | ||
'date.base': 'Enter a return received date', | ||
'date.format': 'Enter a real received date', | ||
'date.min': 'Received date must be the return period start date or after it', | ||
'date.less': "Received date must be either today's date or in the past" | ||
}), | ||
otherwise: Joi.forbidden() | ||
}) | ||
|
||
return schema.validate(payload, { abortEarly: false, allowUnknown: true }) | ||
} | ||
|
||
function _validateReceivedDate(payload) { | ||
const schema = Joi.object({ | ||
'received-date-options': Joi.string().required().messages({ | ||
'any.required': 'Select the return received date', | ||
'string.empty': 'Select the return received date' | ||
}) | ||
}) | ||
|
||
return schema.validate(payload, { abortEarly: false, allowUnknown: true }) | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
Oops, something went wrong.