diff --git a/app/models/return-requirement-point.model.js b/app/models/return-requirement-point.model.js new file mode 100644 index 0000000000..aadb602fd5 --- /dev/null +++ b/app/models/return-requirement-point.model.js @@ -0,0 +1,31 @@ +'use strict' + +/** + * Model for return_requirement_points (water.return_requirement_points) + * @module ReturnRequirementPointModel + */ + +const { Model } = require('objection') + +const BaseModel = require('./base.model.js') + +class ReturnRequirementPointModel extends BaseModel { + static get tableName () { + return 'returnRequirementPoints' + } + + static get relationMappings () { + return { + returnRequirements: { + relation: Model.BelongsToOneRelation, + modelClass: 'return-requirement.model', + join: { + from: 'returnRequirements.id', + to: 'returnRequirementPoints.returnRequirementId' + } + } + } + } +} + +module.exports = ReturnRequirementPointModel diff --git a/app/models/return-requirement.model.js b/app/models/return-requirement.model.js new file mode 100644 index 0000000000..fc00867bf1 --- /dev/null +++ b/app/models/return-requirement.model.js @@ -0,0 +1,39 @@ +'use strict' + +/** + * Model for return_requirements (water.return_requirements) + * @module ReturnRequirementModel + */ + +const { Model } = require('objection') + +const BaseModel = require('./base.model.js') + +class ReturnRequirementModel extends BaseModel { + static get tableName () { + return 'returnRequirements' + } + + static get relationMappings () { + return { + returnVersions: { + relation: Model.BelongsToOneRelation, + modelClass: 'return-version.model', + join: { + from: 'returnRequirements.returnVersionId', + to: 'returnVersions.id' + } + }, + returnRequirementPoints: { + relation: Model.ManyToManyRelation, + modelClass: 'return-requirement-point.model', + join: { + from: 'returnRequirements.id', + to: 'returnRequirementPoints.returnRequirementId' + } + } + } + } +} + +module.exports = ReturnRequirementModel diff --git a/app/models/return-version.model.js b/app/models/return-version.model.js new file mode 100644 index 0000000000..486f7a1d41 --- /dev/null +++ b/app/models/return-version.model.js @@ -0,0 +1,43 @@ +'use strict' + +/** + * Model for return_versions (water.return_versions) + * @module ReturnVersionModel + */ + +const { Model } = require('objection') + +const BaseModel = require('./base.model.js') + +class ReturnVersionModel extends BaseModel { + static get tableName () { + return 'returnVersions' + } + + static get relationMappings () { + return { + licence: { + relation: Model.BelongsToOneRelation, + modelClass: 'licence.model', + join: { + from: 'returnVersions.licenceId', + to: 'licences.id' + } + }, + returnRequirementPoints: { + relation: Model.ManyToManyRelation, + modelClass: 'return-requirement-point.model', + join: { + from: 'returnVersions.id', + through: { + from: 'returnRequirements.returnVersionId', + to: 'returnRequirements.id' + }, + to: 'returnRequirementPoints.returnRequirementId' + } + } + } + } +} + +module.exports = ReturnVersionModel diff --git a/app/presenters/return-requirements/setup.presenter.js b/app/presenters/return-requirements/setup.presenter.js index 8f7583af92..d5d7084daa 100644 --- a/app/presenters/return-requirements/setup.presenter.js +++ b/app/presenters/return-requirements/setup.presenter.js @@ -12,12 +12,36 @@ * * @returns {Object} - The data formatted for the view template */ -function go (session) { +function go (session, existingData) { const { id: sessionId, licence, setup } = session + const radioOptions = [{ + value: 'use-abstraction-data', + text: 'Start by using abstraction data', + checked: setup === 'use-abstraction-data' + }] + + if (existingData.length !== 0) { + radioOptions.push({ + value: 'use-existing-requirements', + text: 'Copy existing requirements', + checked: setup === 'use-existing-requirements' + }) + } + + radioOptions.push({ + divider: 'or' + }, + { + value: 'set-up-manually', + text: 'Set up manually', + checked: setup === 'set-up-manually' + }) + return { backLink: `/system/return-requirements/${sessionId}/reason`, licenceRef: licence.licenceRef, + radioOptions, sessionId, setup: setup ?? null } diff --git a/app/services/return-requirements/fetch-return-requirements.service.js b/app/services/return-requirements/fetch-return-requirements.service.js new file mode 100644 index 0000000000..c49d2f9c00 --- /dev/null +++ b/app/services/return-requirements/fetch-return-requirements.service.js @@ -0,0 +1,38 @@ +'use strict' + +/** + * Fetches existing return requirements needed for `/return-requirements/{sessionId}/setup` page + * @module FetchReturnRequirementsService + */ + +const ReturnVersionModel = require('../../models/return-version.model.js') + +/** + * Fetches existing return requirements needed for `/return-requirements/{sessionId}/setup` page + * + * @param {string} licenceId - The UUID for the licence to fetch + * + * @returns {Promise} The existing return requirements for the matching licenceId + */ +async function go (licenceId) { + const data = await _fetchReturnRequirements(licenceId) + + return data +} + +async function _fetchReturnRequirements (licenceId) { + const result = await ReturnVersionModel.query() + .where('licenceId', licenceId) + .withGraphFetched('returnRequirementPoints') + .modifyGraph('returnRequirementPoints', (builder) => { + builder.select([ + 'id' + ]) + }) + + return result +} + +module.exports = { + go +} diff --git a/app/services/return-requirements/setup.service.js b/app/services/return-requirements/setup.service.js index 87f3cea6e7..c46634b96a 100644 --- a/app/services/return-requirements/setup.service.js +++ b/app/services/return-requirements/setup.service.js @@ -5,14 +5,16 @@ * @module SetupService */ -const SetupPresenter = require('../../presenters/return-requirements/setup.presenter.js') +const FetchReturnRequirementsService = require('./fetch-return-requirements.service.js') const SessionModel = require('../../models/session.model.js') +const SetupPresenter = require('../../presenters/return-requirements/setup.presenter.js') /** * Orchestrates fetching and presenting the data for `/return-requirements/{sessionId}/setup` page * - * Supports generating the data needed for the select reason page in the return requirements setup journey. It - * fetches the current session record and combines it with the radio buttons and other information needed for the form. + * Supports generating the data needed for selecting how you want to set up the return in the return requirements setup + * 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 return requirement setup session record * @@ -20,8 +22,9 @@ const SessionModel = require('../../models/session.model.js') */ async function go (sessionId) { const session = await SessionModel.query().findById(sessionId) + const existingData = await FetchReturnRequirementsService.go(session.licence.id) - const formattedData = SetupPresenter.go(session) + const formattedData = SetupPresenter.go(session, existingData) return { activeNavBar: 'search', diff --git a/app/services/return-requirements/submit-setup.service.js b/app/services/return-requirements/submit-setup.service.js index 8203023805..67e3ec62a8 100644 --- a/app/services/return-requirements/submit-setup.service.js +++ b/app/services/return-requirements/submit-setup.service.js @@ -5,6 +5,7 @@ * @module SubmitSetupService */ +const FetchReturnRequirementsService = require('./fetch-return-requirements.service.js') const SessionModel = require('../../models/session.model.js') const SetupPresenter = require('../../presenters/return-requirements/setup.presenter.js') const SetupValidator = require('../../validators/return-requirements/setup.validator.js') @@ -21,11 +22,12 @@ const SetupValidator = require('../../validators/return-requirements/setup.valid * @param {string} sessionId - The UUID of the current session * @param {Object} payload - The submitted form data * - * @returns {Promise} If no errors a the url for where the user should be redirected else the page data for the + * @returns {Promise} If no errors return a url for where the user should be redirected else the page data for the * setup page including the validation error details */ async function go (sessionId, payload) { const session = await SessionModel.query().findById(sessionId) + const existingData = await FetchReturnRequirementsService.go(session.licence.id) const validationResult = _validate(payload) @@ -37,7 +39,7 @@ async function go (sessionId, payload) { } } - const formattedData = SetupPresenter.go(session, payload) + const formattedData = SetupPresenter.go(session, existingData) return { activeNavBar: 'search', @@ -54,6 +56,10 @@ function _redirect (setup) { endpoint = 'check' } + if (setup === 'use-existing-requirements') { + endpoint = 'existing' + } + if (setup === 'set-up-manually') { endpoint = 'purpose/0' } diff --git a/app/validators/return-requirements/setup.validator.js b/app/validators/return-requirements/setup.validator.js index 7c844bdd67..c6ec5f9432 100644 --- a/app/validators/return-requirements/setup.validator.js +++ b/app/validators/return-requirements/setup.validator.js @@ -9,6 +9,7 @@ const Joi = require('joi') const VALID_VALUES = [ 'use-abstraction-data', + 'use-existing-requirements', 'set-up-manually' ] diff --git a/app/views/return-requirements/setup.njk b/app/views/return-requirements/setup.njk index 8a9917a1a2..21fd4b8495 100644 --- a/app/views/return-requirements/setup.njk +++ b/app/views/return-requirements/setup.njk @@ -39,21 +39,7 @@ classes: "govuk-fieldset__legend--l govuk-!-margin-bottom-6" } }, - items: [ - { - value: "use-abstraction-data", - text: "Start by using abstraction data", - checked: "use-abstraction-data" === setup - }, - { - divider: "or" - }, - { - value: "set-up-manually", - text: "Set up manually", - checked: "set-up-manually" === setup - } - ] + items: radioOptions }) }} {{ govukButton({ text: "Continue", preventDoubleClick: true }) }} diff --git a/db/migrations/legacy/20221108007023-water-return-versions.js b/db/migrations/legacy/20221108007023-water-return-versions.js index 0e29f5f5ac..98701bcce8 100644 --- a/db/migrations/legacy/20221108007023-water-return-versions.js +++ b/db/migrations/legacy/20221108007023-water-return-versions.js @@ -15,8 +15,11 @@ exports.up = function (knex) { table.integer('version_number').notNullable() table.date('start_date').notNullable() table.date('end_date') - table.string('status water').notNullable() + table.string('status').notNullable() table.string('external_id') + table.string('reason') + table.boolean('multiple_upload') + table.text('notes') // Legacy timestamps table.timestamp('date_created', { useTz: false }).notNullable() diff --git a/db/migrations/legacy/20221108007024-water-return-requirements.js b/db/migrations/legacy/20221108007024-water-return-requirements.js index abfe78396b..a2c5e7d1c3 100644 --- a/db/migrations/legacy/20221108007024-water-return-requirements.js +++ b/db/migrations/legacy/20221108007024-water-return-requirements.js @@ -23,6 +23,11 @@ exports.up = function (knex) { table.string('description') table.integer('legacy_id') table.string('external_id') + table.string('collection_frequency') + table.boolean('gravity_fill') + table.boolean('reabstraction') + table.boolean('two_part_tariff') + table.boolean('fifty_six_exception') // Legacy timestamps table.timestamp('date_created', { useTz: false }).notNullable() diff --git a/db/migrations/legacy/20240530131706_water-return-requirement-points-table.js b/db/migrations/legacy/20240530131706_water-return-requirement-points-table.js new file mode 100644 index 0000000000..c5118f1c73 --- /dev/null +++ b/db/migrations/legacy/20240530131706_water-return-requirement-points-table.js @@ -0,0 +1,29 @@ +'use strict' + +const tableName = 'return_requirement_points' + +exports.up = function (knex) { + return knex + .schema + .withSchema('water') + .createTable(tableName, (table) => { + // Primary Key + table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()')) + + // Data + table.uuid('return_requirement_id').notNullable() + table.string('description').notNullable() + table.string('ngr_1').notNullable() + table.string('ngr_2') + table.string('ngr_3') + table.string('ngr_4') + table.string('external_id') + }) +} + +exports.down = function (knex) { + return knex + .schema + .withSchema('water') + .dropTableIfExists(tableName) +} diff --git a/db/migrations/public/20240527105914_water-return-version-view.js b/db/migrations/public/20240527105914_water-return-version-view.js new file mode 100644 index 0000000000..c48ef2988f --- /dev/null +++ b/db/migrations/public/20240527105914_water-return-version-view.js @@ -0,0 +1,30 @@ +'use strict' + +const viewName = 'return_versions' + +exports.up = function (knex) { + return knex + .schema + .createView(viewName, (view) => { + view.as(knex('return_versions').withSchema('water').select([ + 'return_version_id AS id', + 'licence_id', + 'version_number', + 'start_date', + 'end_date', + 'status', + 'external_id', + 'reason', + 'multiple_upload', + 'notes', + 'date_created AS created_at', + 'date_updated AS updated_at' + ])) + }) +} + +exports.down = function (knex) { + return knex + .schema + .dropViewIfExists(viewName) +} diff --git a/db/migrations/public/20240529120114_water-return-requirement-view.js b/db/migrations/public/20240529120114_water-return-requirement-view.js new file mode 100644 index 0000000000..9a322d880d --- /dev/null +++ b/db/migrations/public/20240529120114_water-return-requirement-view.js @@ -0,0 +1,37 @@ +'use strict' + +const viewName = 'return_requirements' + +exports.up = function (knex) { + return knex + .schema + .createView(viewName, (view) => { + view.as(knex('return_requirements').withSchema('water').select([ + 'return_requirement_id AS id', + 'return_version_id', + 'returns_frequency', + 'is_summer AS summer', + 'is_upload AS upload', + 'abstraction_period_start_day', + 'abstraction_period_start_month', + 'abstraction_period_end_day', + 'abstraction_period_end_month', + 'site_description', + 'legacy_id', + 'external_id', + 'collection_frequency', + 'gravity_fill', + 'reabstraction', + 'two_part_tariff', + 'fifty_six_exception', + 'date_created AS created_at', + 'date_updated AS updated_at' + ])) + }) +} + +exports.down = function (knex) { + return knex + .schema + .dropViewIfExists(viewName) +} diff --git a/db/migrations/public/20240529121208_water-return-requirement-point-view.js b/db/migrations/public/20240529121208_water-return-requirement-point-view.js new file mode 100644 index 0000000000..d3fc335784 --- /dev/null +++ b/db/migrations/public/20240529121208_water-return-requirement-point-view.js @@ -0,0 +1,26 @@ +'use strict' + +const viewName = 'return_requirement_points' + +exports.up = function (knex) { + return knex + .schema + .createView(viewName, (view) => { + view.as(knex('return_requirement_points').withSchema('water').select([ + 'id', + 'return_requirement_id', + 'description', + 'ngr_1', + 'ngr_2', + 'ngr_3', + 'ngr_4', + 'external_id' + ])) + }) +} + +exports.down = function (knex) { + return knex + .schema + .dropViewIfExists(viewName) +} diff --git a/test/models/return-requirement-point.model.test.js b/test/models/return-requirement-point.model.test.js new file mode 100644 index 0000000000..6e0076d5a9 --- /dev/null +++ b/test/models/return-requirement-point.model.test.js @@ -0,0 +1,70 @@ +'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 ReturnRequirementHelper = require('../support/helpers/return-requirement.helper.js') +const ReturnRequirementModel = require('../../app/models/return-requirement.model.js') +const ReturnRequirementPointHelper = require('../support/helpers/return-requirement-point.helper.js') + +// Thing under test +const ReturnRequirementPointModel = require('../../app/models/return-requirement-point.model.js') + +describe('Return Requirement Point model', () => { + let testRecord + + beforeEach(async () => { + await DatabaseSupport.clean() + }) + + describe('Basic query', () => { + beforeEach(async () => { + testRecord = await ReturnRequirementPointHelper.add() + }) + + it('can successfully run a basic query', async () => { + const result = await ReturnRequirementPointModel.query().findById(testRecord.id) + + expect(result).to.be.an.instanceOf(ReturnRequirementPointModel) + expect(result.id).to.equal(testRecord.id) + }) + }) + + describe('Relationships', () => { + describe('when linking to return requirement', () => { + let testReturnRequirement + + beforeEach(async () => { + testReturnRequirement = await ReturnRequirementHelper.add() + + const { id: returnRequirementId } = testReturnRequirement + testRecord = await ReturnRequirementPointHelper.add({ returnRequirementId }) + }) + + it('can successfully run a related query', async () => { + const query = await ReturnRequirementPointModel.query() + .innerJoinRelated('returnRequirements') + + expect(query).to.exist() + }) + + it('can eager load the return requirement', async () => { + const result = await ReturnRequirementPointModel.query() + .findById(testRecord.id) + .withGraphFetched('returnRequirements') + + expect(result).to.be.instanceOf(ReturnRequirementPointModel) + expect(result.id).to.equal(testRecord.id) + + expect(result.returnRequirements).to.be.an.instanceOf(ReturnRequirementModel) + expect(result.returnRequirements).to.equal(testReturnRequirement) + }) + }) + }) +}) diff --git a/test/models/return-requirement.model.test.js b/test/models/return-requirement.model.test.js new file mode 100644 index 0000000000..11f16d2639 --- /dev/null +++ b/test/models/return-requirement.model.test.js @@ -0,0 +1,70 @@ +'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 ReturnRequirementHelper = require('../support/helpers/return-requirement.helper.js') +const ReturnVersionHelper = require('../support/helpers/return-version.helper.js') +const ReturnVersionModel = require('../../app/models/return-version.model.js') + +// Thing under test +const ReturnRequirementModel = require('../../app/models/return-requirement.model.js') + +describe('Return Requirement model', () => { + let testRecord + + beforeEach(async () => { + await DatabaseSupport.clean() + }) + + describe('Basic query', () => { + beforeEach(async () => { + testRecord = await ReturnRequirementHelper.add() + }) + + it('can successfully run a basic query', async () => { + const result = await ReturnRequirementModel.query().findById(testRecord.id) + + expect(result).to.be.an.instanceOf(ReturnRequirementModel) + expect(result.id).to.equal(testRecord.id) + }) + }) + + describe('Relationships', () => { + describe('when linking to return version', () => { + let testReturnVersion + + beforeEach(async () => { + testReturnVersion = await ReturnVersionHelper.add() + testRecord = await ReturnRequirementHelper.add({ + returnVersionId: testReturnVersion.id + }) + }) + + it('can successfully run a related query', async () => { + const query = await ReturnRequirementModel.query() + .innerJoinRelated('returnVersions') + + expect(query).to.exist() + }) + + it('can eager load the return version', async () => { + const result = await ReturnRequirementModel.query() + .findById(testRecord.id) + .withGraphFetched('returnVersions') + + expect(result).to.be.instanceOf(ReturnRequirementModel) + expect(result.id).to.equal(testRecord.id) + + expect(result.returnVersions).to.be.an.instanceOf(ReturnVersionModel) + expect(result.returnVersions).to.equal(testReturnVersion) + }) + }) + }) +}) diff --git a/test/models/return-version-model.test.js b/test/models/return-version-model.test.js new file mode 100644 index 0000000000..61b5cdb9f3 --- /dev/null +++ b/test/models/return-version-model.test.js @@ -0,0 +1,106 @@ +'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 LicenceHelper = require('../support/helpers/licence.helper.js') +const LicenceModel = require('../../app/models/licence.model.js') +const ReturnRequirementHelper = require('../support/helpers/return-requirement.helper.js') +const ReturnRequirementPointHelper = require('../support/helpers/return-requirement-point.helper.js') +const ReturnRequirementPointModel = require('../../app/models/return-requirement-point.model.js') +const ReturnVersionHelper = require('../support/helpers/return-version.helper.js') + +// Thing under test +const ReturnVersionModel = require('../../app/models/return-version.model.js') + +describe('Return Version model', () => { + let testRecord + + beforeEach(async () => { + await DatabaseSupport.clean() + }) + + describe('Basic query', () => { + beforeEach(async () => { + testRecord = await ReturnVersionHelper.add() + }) + + it('can successfully run a basic query', async () => { + const result = await ReturnVersionModel.query().findById(testRecord.id) + + expect(result).to.be.an.instanceOf(ReturnVersionModel) + expect(result.id).to.equal(testRecord.id) + }) + }) + + describe('Relationships', () => { + describe('when linking to licence', () => { + let testLicence + + beforeEach(async () => { + testLicence = await LicenceHelper.add() + + const { id: licenceId } = testLicence + testRecord = await ReturnVersionHelper.add({ licenceId }) + }) + + it('can successfully run a related query', async () => { + const query = await ReturnVersionModel.query() + .innerJoinRelated('licence') + + expect(query).to.exist() + }) + + it('can eager load the licence', async () => { + const result = await ReturnVersionModel.query() + .findById(testRecord.id) + .withGraphFetched('licence') + + expect(result).to.be.instanceOf(ReturnVersionModel) + expect(result.id).to.equal(testRecord.id) + + expect(result.licence).to.be.an.instanceOf(LicenceModel) + expect(result.licence).to.equal(testLicence) + }) + }) + + describe('when linking to return requirement points', () => { + let testReturnRequirementPoint + + beforeEach(async () => { + testRecord = await ReturnVersionHelper.add() + const testReturnRequirement = await ReturnRequirementHelper.add({ + returnVersionId: testRecord.id + }) + testReturnRequirementPoint = await ReturnRequirementPointHelper.add({ + returnRequirementId: testReturnRequirement.id + }) + }) + + it('can successfully run a related query', async () => { + const query = await ReturnVersionModel.query() + .innerJoinRelated('returnRequirementPoints') + + expect(query).to.exist() + }) + + it('can eager load the return requirement points', async () => { + const result = await ReturnVersionModel.query() + .findById(testRecord.id) + .withGraphFetched('returnRequirementPoints') + + expect(result).to.be.instanceOf(ReturnVersionModel) + expect(result.id).to.equal(testRecord.id) + + expect(result.returnRequirementPoints[0]).to.be.an.instanceOf(ReturnRequirementPointModel) + expect(result.returnRequirementPoints[0]).to.equal(testReturnRequirementPoint) + }) + }) + }) +}) diff --git a/test/presenters/return-requirements/setup.presenter.test.js b/test/presenters/return-requirements/setup.presenter.test.js index 6f7a2edf00..a31b5bc369 100644 --- a/test/presenters/return-requirements/setup.presenter.test.js +++ b/test/presenters/return-requirements/setup.presenter.test.js @@ -34,33 +34,84 @@ describe('Return Requirements - Setup presenter', () => { describe('when provided with a session', () => { it('correctly presents the data', () => { - const result = SetupPresenter.go(session) + const result = SetupPresenter.go(session, []) expect(result).to.equal({ backLink: '/system/return-requirements/61e07498-f309-4829-96a9-72084a54996d/reason', licenceRef: '01/ABC', sessionId: '61e07498-f309-4829-96a9-72084a54996d', + radioOptions: [{ + checked: false, + text: 'Start by using abstraction data', + value: 'use-abstraction-data' + }, { + divider: 'or' + }, { + checked: false, + text: 'Set up manually', + value: 'set-up-manually' + }], setup: null }) }) }) - describe("the 'backLink' property", () => { + describe('the "backLink" property', () => { it("returns a link back to the 'start-date' page", () => { - const result = SetupPresenter.go(session) + const result = SetupPresenter.go(session, []) expect(result.backLink).to.equal('/system/return-requirements/61e07498-f309-4829-96a9-72084a54996d/reason') }) }) - describe("the 'setup' property", () => { + describe('the "radioOptions" property', () => { + it('when given an empty array returns a list of radio options excluding the copy-existing-data option', () => { + const result = SetupPresenter.go(session, []) + + expect(result.radioOptions).to.equal([{ + checked: false, + text: 'Start by using abstraction data', + value: 'use-abstraction-data' + }, { + divider: 'or' + }, { + checked: false, + text: 'Set up manually', + value: 'set-up-manually' + }]) + }) + }) + + describe('the "radioOptions" property', () => { + it('when given an array with data returns an array including all of the radio button options', () => { + const result = SetupPresenter.go(session, [{ id: '61e07498-f309-4829-96a9-72184a54996d' }]) + + expect(result.radioOptions).to.equal([{ + checked: false, + text: 'Start by using abstraction data', + value: 'use-abstraction-data' + }, { + value: 'use-existing-requirements', + text: 'Copy existing requirements', + checked: false + }, { + divider: 'or' + }, { + checked: false, + text: 'Set up manually', + value: 'set-up-manually' + }]) + }) + }) + + describe('the "setup" property', () => { describe('when the user has previously submitted a setup option', () => { beforeEach(() => { session.setup = 'set-up-manually' }) it('returns a populated setup', () => { - const result = SetupPresenter.go(session) + const result = SetupPresenter.go(session, []) expect(result.setup).to.equal('set-up-manually') }) @@ -68,7 +119,7 @@ describe('Return Requirements - Setup presenter', () => { describe('when the user has not previously submitted a setup option', () => { it('returns an empty setup', () => { - const result = SetupPresenter.go(session) + const result = SetupPresenter.go(session, []) expect(result.setup).to.be.null() }) diff --git a/test/services/return-requirements/setup.service.test.js b/test/services/return-requirements/setup.service.test.js index c8aeb61a96..6915906fe3 100644 --- a/test/services/return-requirements/setup.service.test.js +++ b/test/services/return-requirements/setup.service.test.js @@ -3,14 +3,18 @@ // Test framework dependencies const Lab = require('@hapi/lab') const Code = require('@hapi/code') +const Sinon = require('sinon') -const { describe, it, beforeEach } = exports.lab = Lab.script() +const { describe, it, beforeEach, afterEach } = exports.lab = Lab.script() const { expect } = Code // Test helpers const DatabaseSupport = require('../../support/database.js') const SessionHelper = require('../../support/helpers/session.helper.js') +// Things we need to stub +const FetchReturnRequirementsService = require('../../../app/services/return-requirements/fetch-return-requirements.service.js') + // Thing under test const SetupService = require('../../../app/services/return-requirements/setup.service.js') @@ -54,6 +58,62 @@ describe('Return Requirements - Setup service', () => { pageTitle: 'How do you want to set up the requirements for returns?', backLink: `/system/return-requirements/${session.id}/reason`, licenceRef: '01/ABC', + radioOptions: [{ + checked: false, + text: 'Start by using abstraction data', + value: 'use-abstraction-data' + }, { + divider: 'or' + }, { + checked: false, + text: 'Set up manually', + value: 'set-up-manually' + }], + setup: null + }, { skip: ['sessionId'] }) + }) + }) + + describe('when called with existing return requirements', () => { + beforeEach(async () => { + Sinon.stub(FetchReturnRequirementsService, 'go').resolves([ + { id: '14794d57-1acf-4c91-8b48-4b1ec68bfd6f' } + ]) + }) + + afterEach(() => { + Sinon.restore() + }) + + it('fetches the current setup session record', async () => { + const result = await SetupService.go(session.id) + + expect(result.sessionId).to.equal(session.id) + }) + + it('returns page data for the view', async () => { + const result = await SetupService.go(session.id) + + expect(result).to.equal({ + activeNavBar: 'search', + pageTitle: 'How do you want to set up the requirements for returns?', + backLink: `/system/return-requirements/${session.id}/reason`, + licenceRef: '01/ABC', + radioOptions: [{ + checked: false, + text: 'Start by using abstraction data', + value: 'use-abstraction-data' + }, { + value: 'use-existing-requirements', + text: 'Copy existing requirements', + checked: false + }, { + divider: 'or' + }, { + checked: false, + text: 'Set up manually', + value: 'set-up-manually' + }], setup: null }, { skip: ['sessionId'] }) }) diff --git a/test/services/return-requirements/submit-setup.service.test.js b/test/services/return-requirements/submit-setup.service.test.js index 532bbfe7f7..bcb243ec5f 100644 --- a/test/services/return-requirements/submit-setup.service.test.js +++ b/test/services/return-requirements/submit-setup.service.test.js @@ -92,6 +92,22 @@ describe('Return Requirements - Submit Setup service', () => { pageTitle: 'How do you want to set up the requirements for returns?', backLink: `/system/return-requirements/${session.id}/reason`, licenceRef: '01/ABC', + radioOptions: [ + { + checked: false, + text: 'Start by using abstraction data', + value: 'use-abstraction-data' + }, + { + divider: 'or' + }, + { + checked: false, + text: 'Set up manually', + value: 'set-up-manually' + } + ], + sessionId: session.id, setup: null }, { skip: ['sessionId', 'error'] }) }) diff --git a/test/support/helpers/return-requirement-point.helper.js b/test/support/helpers/return-requirement-point.helper.js new file mode 100644 index 0000000000..99b24772b3 --- /dev/null +++ b/test/support/helpers/return-requirement-point.helper.js @@ -0,0 +1,57 @@ +'use strict' + +/** + * @module ReturnRequirementPointHelper + */ + +const { generateUUID } = require('../../../app/lib/general.lib.js') +const ReturnRequirementPointModel = require('../../../app/models/return-requirement-point.model.js') + +/** + * Add a new return requirement point + * + * If no `data` is provided, default values will be used. These are + * + * - `id` - [random UUID] + * - `return_requirement_id` - [random UUID] + * - `description` - 'Point description' + * - `ngr_1` - 'TL 800 234' + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + * + * @returns {Promise} The instance of the newly created record + */ +function add (data = {}) { + const insertData = defaults(data) + + return ReturnRequirementPointModel.query() + .insert({ ...insertData }) + .returning('*') +} + +/** + * Returns the defaults used + * + * It will override or append to them any data provided. Mainly used by the `add()` method, we make it available + * for use in tests to avoid having to duplicate values. + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + */ +function defaults (data = {}) { + const defaults = { + id: generateUUID(), + returnRequirementId: generateUUID(), + description: 'A description of the point', + ngr1: 'TL 800 234' + } + + return { + ...defaults, + ...data + } +} + +module.exports = { + add, + defaults +} diff --git a/test/support/helpers/return-requirement.helper.js b/test/support/helpers/return-requirement.helper.js new file mode 100644 index 0000000000..8af02aa0ef --- /dev/null +++ b/test/support/helpers/return-requirement.helper.js @@ -0,0 +1,64 @@ +'use strict' + +/** + * @module ReturnRequirementHelper + */ + +const { generateUUID } = require('../../../app/lib/general.lib.js') +const ReturnRequirementModel = require('../../../app/models/return-requirement.model.js') + +/** + * Add a new return requirement + * + * If no `data` is provided, default values will be used. These are + * + * - `return_version_id` - [random UUID] + * - `returns_frequency` - day + * - `summer` - false + * - `upload` - false + * - `createdAt` - 2022-11-16 09:42:11.000 + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + * + * @returns {Promise} The instance of the newly created record + */ +function add (data = {}) { + const insertData = defaults(data) + + return ReturnRequirementModel.query() + .insert({ ...insertData }) + .returning('*') +} + +/** + * Returns the defaults used + * + * It will override or append to them any data provided. Mainly used by the `add()` method, we make it available + * for use in tests to avoid having to duplicate values. + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + */ +function defaults (data = {}) { + const defaults = { + returnVersionId: generateUUID(), + returnsFrequency: 'day', + summer: false, + upload: false, + // INFO: The table does not have a default for the date_created column. But it is set as 'not nullable'! So, we + // need to ensure we set it when creating a new record, something we'll never actually need to do because it's a + // static table. Also, we can't use Date.now() because Javascript returns the time since the epoch in milliseconds, + // whereas a PostgreSQL timestamp field can only hold the seconds since the epoch. Pass it an ISO string though + // ('2023-01-05T08:37:05.575Z') and PostgreSQL can do the conversion https://stackoverflow.com/a/61912776/6117745 + createdAt: new Date('2022-11-16 09:42:11.000').toISOString() + } + + return { + ...defaults, + ...data + } +} + +module.exports = { + add, + defaults +} diff --git a/test/support/helpers/return-version.helper.js b/test/support/helpers/return-version.helper.js new file mode 100644 index 0000000000..58994a9eac --- /dev/null +++ b/test/support/helpers/return-version.helper.js @@ -0,0 +1,67 @@ +'use strict' + +/** + * @module ReturnVersionHelper + */ + +const { generateUUID } = require('../../../app/lib/general.lib.js') +const ReturnVersionModel = require('../../../app/models/return-version.model.js') + +/** + * Add a new return version + * + * If no `data` is provided, default values will be used. These are + * + * - `id` - [random UUID] + * - `licence_id` - [random UUID] + * - `version_number` - 100 + * - `start_date` - 2022-11-16 + * - `status` - approved + * - `createdAt` - 2022-11-16 09:42:11.000 + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + * + * @returns {Promise} The instance of the newly created record + */ +function add (data = {}) { + const insertData = defaults(data) + + return ReturnVersionModel.query() + .insert({ ...insertData }) + .returning('*') +} + +/** + * Returns the defaults used + * + * It will override or append to them any data provided. Mainly used by the `add()` method, we make it available + * for use in tests to avoid having to duplicate values. + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + */ +function defaults (data = {}) { + const defaults = { + id: generateUUID(), + licenceId: generateUUID(), + versionNumber: 100, + startDate: new Date('2022-11-16'), + status: 'approved', + // INFO: The lines table does not have a default for the date_created column. But it is set as + // 'not nullable'! So, we need to ensure we set it when creating a new record, something we'll never actually need + // to do because it's a static table. Also, we can't use Date.now() because Javascript returns the time since the + // epoch in milliseconds, whereas a PostgreSQL timestamp field can only hold the seconds since the epoch. Pass it + // an ISO string though ('2023-01-05T08:37:05.575Z') and PostgreSQL can do the conversion + // https://stackoverflow.com/a/61912776/6117745 + createdAt: new Date('2022-11-16 09:42:11.000').toISOString() + } + + return { + ...defaults, + ...data + } +} + +module.exports = { + add, + defaults +}