Skip to content

Commit

Permalink
Update existing return versions when a new one is added (#1216)
Browse files Browse the repository at this point in the history
https://eaflood.atlassian.net/browse/WATER-4568

Having worked out how to convert the information in the return requirements setup session into the various records in the DB (see #1137) we now need to deal with how the new record impacts the existing ones.

This PR will extend the persisting logic to deal with changes to the existing return versions.
  • Loading branch information
Jozzey authored Aug 9, 2024
1 parent 504788b commit 927a1d7
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

const GenerateReturnVersionRequirementsService = require('./generate-return-version-requirements.service.js')
const ProcessExistingReturnVersionsService = require('./process-existing-return-versions.service.js')
const ReturnVersionModel = require('../../models/return-version.model.js')

/**
Expand All @@ -20,7 +21,9 @@ const ReturnVersionModel = require('../../models/return-version.model.js')
* @returns {Promise<Object>} The new return version and requirement data for a licence
*/
async function go (sessionData, userId) {
const returnVersion = await _generateReturnVersion(sessionData, userId)
const returnVersionsExist = sessionData.licence.returnVersions.length > 0

const returnVersion = await _generateReturnVersion(returnVersionsExist, sessionData, userId)
const returnRequirements = await _generateReturnRequirements(sessionData)

return {
Expand All @@ -36,7 +39,7 @@ function _calculateStartDate (sessionData) {
return new Date(sessionData.startDateYear, sessionData.startDateMonth - 1, sessionData.startDateDay)
}

return sessionData.licence.currentVersionStartDate
return new Date(sessionData.licence.currentVersionStartDate)
}

async function _generateReturnRequirements (sessionData) {
Expand All @@ -53,15 +56,22 @@ async function _generateReturnRequirements (sessionData) {
return returnRequirements
}

async function _generateReturnVersion (sessionData, userId) {
async function _generateReturnVersion (returnVersionsExist, sessionData, userId) {
const startDate = _calculateStartDate(sessionData)
let endDate = null

if (returnVersionsExist) {
endDate = await ProcessExistingReturnVersionsService.go(sessionData.licence.id, startDate)
}

return {
createdBy: userId,
endDate: null,
endDate,
licenceId: sessionData.licence.id,
multipleUpload: _multipleUpload(sessionData?.additionalSubmissionOptions),
notes: sessionData?.note?.content,
reason: sessionData.reason,
startDate: _calculateStartDate(sessionData),
startDate,
status: 'current',
version: await _nextVersionNumber(sessionData.licence.id)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const ReturnVersionModel = require('../../models/return-version.model.js')
async function go (returnVersionData) {
const { returnRequirements, returnVersion } = returnVersionData

const { id: returnVersionId } = await ReturnVersionModel.query().insert(returnVersion).returning('id')
const { id: returnVersionId } = await ReturnVersionModel.query().insert(returnVersion)

await _persistReturnRequirements(returnRequirements, returnVersionId)
}
Expand All @@ -48,7 +48,6 @@ async function _persistReturnRequirements (returnRequirements, returnVersionId)
summer: returnRequirement.summer,
twoPartTariff: returnRequirement.twoPartTariff
})
.returning('id')

await _persistReturnRequirementsPoints(returnRequirement.returnRequirementPoints, returnRequirementId)
await _persistReturnRequirementsPurposes(returnRequirement.returnRequirementPurposes, returnRequirementId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
'use strict'

/**
* Processes existing return versions to update the their `status` and `endDate` when a new return version is created
* @module ProcessExistingReturnVersionsService
*/

const ReturnVersionModel = require('../../models/return-version.model.js')

/**
* Processes existing return versions to update the their `status` and `endDate` when a new return version is created
*
* Depending on the `startDate` of the new return version that is to be inserted. An existing return version may need
* its `status` or `endDate` to be updated. An `endDate` may also need to be calculated for the new return version if
* it is to be inserted between existing return versions, or is superseding an existing one that has an `endDate`.
*
* @param {String} licenceId - The UUID of the licence the requirements are for
* @param {Date} newVersionStartDate - The date that the new return version starts
*
* @returns {Promise<Date>} The calculated `endDate` for the new return version if there is one. Null will be returned
* if there is no `endDate`
*/
async function go (licenceId, newVersionStartDate) {
const previousVersions = await _previousVersions(licenceId)
const previousVersionEndDate = _previousVersionEndDate(newVersionStartDate)

let result

result = await _endLatestVersion(previousVersions, newVersionStartDate, previousVersionEndDate)
if (result) {
return null
}

result = await _insertBetweenVersions(previousVersions, newVersionStartDate, previousVersionEndDate)
if (result) {
return result
}

result = await _replaceLatestVersion(previousVersions, newVersionStartDate)
if (result) {
return null
}

result = await _replacePreviousVersion(previousVersions, newVersionStartDate)
if (result) {
return result
}

return null
}

/**
* Update the end date of the latest return version whose start data is less than the new one and whose end date is null
*
* For example, imagine these are the existing return versions
*
* | Id | Start date | End date | Status |
* |----|------------|------------|---------|
* | 1 | 2008-04-01 | 2019-05-12 | current |
* | 2 | 2019-05-13 | 2022-03-31 | current |
* | 3 | 2022-04-01 | | current |
*
* The user adds a new return version staring 2024-08-01. The end result would be
*
* | Id | Start date | End date | Status |
* |----|------------|------------|---------|
* | 1 | 2008-04-01 | 2019-05-12 | current |
* | 2 | 2019-05-13 | 2022-03-31 | current |
* | 3 | 2022-04-01 | 2024-07-31 | current |
* | 4 | 2022-08-01 | | current |
*
* This function finds return version **3** and updates its end date to the start date of the new return version minus
* 1 day. We don't care about the end date because the new return version doesn't need one.
*/
async function _endLatestVersion (previousVersions, newVersionStartDate, endDate) {
const matchedReturnVersion = previousVersions.find((previousVersion) => {
return previousVersion.startDate < newVersionStartDate &&
previousVersion.endDate === null
})

if (!matchedReturnVersion) {
return null
}

return matchedReturnVersion.$query().patch({ endDate })
}

/**
* Update the end date of a previous version whose start date is less than the new one and whose end date is greater
*
* For example, imagine these are the existing return versions
*
* | Id | Start date | End date | Status |
* |----|------------|------------|---------|
* | 1 | 2008-04-01 | 2019-05-12 | current |
* | 2 | 2019-05-13 | 2022-03-31 | current |
* | 3 | 2022-04-01 | | current |
*
* The user adds a new return version staring 2021-07-01. The end result would be
*
* | Id | Start date | End date | Status |
* |----|------------|------------|---------|
* | 1 | 2008-04-01 | 2019-05-12 | current |
* | 2 | 2019-05-13 | 2021-06-30 | current |
* | 4 | 2021-07-01 | 2022-03-31 | current |
* | 3 | 2022-04-01 | | current |
*
* This function finds return version **2** and updates its end date to the start date of the new return version minus
* 1 day. We also return the end date from version **2** as this needs to be applied to the new return version. Hence,
* we are _inserting between versions_.
*/
async function _insertBetweenVersions (previousVersions, newVersionStartDate, endDate) {
const matchedReturnVersion = previousVersions.find((previousVersion) => {
return previousVersion.startDate < newVersionStartDate &&
previousVersion.endDate > newVersionStartDate
})

if (!matchedReturnVersion) {
return null
}

const newVersionEndDate = matchedReturnVersion.endDate

await matchedReturnVersion.$query().patch({ endDate })

return newVersionEndDate
}

function _previousVersionEndDate (newVersionStartDate) {
// NOTE: You have to create a new date from newVersionStartDate else when we call setDate we amend the source
// newVersionStartDate passed to the service.
const previousVersionEndDate = new Date(newVersionStartDate)

previousVersionEndDate.setDate(previousVersionEndDate.getDate() - 1)

return previousVersionEndDate
}

function _previousVersions (licenceId) {
return ReturnVersionModel.query()
.select(['endDate', 'id', 'startDate'])
.where('licenceId', licenceId)
.where('status', 'current')
.orderBy('startDate', 'desc')
}

/**
* Update the status of a previous version whose start date is equal to the new one and whose end date is null
*
* For example, imagine these are the existing return versions
*
* | Id | Start date | End date | Status |
* |----|------------|------------|---------|
* | 1 | 2008-04-01 | 2019-05-12 | current |
* | 2 | 2019-05-13 | 2022-03-31 | current |
* | 3 | 2022-04-01 | | current |
*
* The user adds a new return version staring 2022-04-01. The end result would be
*
* | Id | Start date | End date | Status |
* |----|------------|------------|------------|
* | 1 | 2008-04-01 | 2019-05-12 | current |
* | 2 | 2019-05-13 | 2022-03-31 | current |
* | 3 | 2022-04-01 | | superseded |
* | 4 | 2022-04-01 | | current |
*
* This function finds return version **3** and updates its status to `superseded`. We don't care about the end date
* because the new return version doesn't need one.
*/
async function _replaceLatestVersion (previousVersions, newVersionStartDate) {
const matchedReturnVersion = previousVersions.find((previousVersion) => {
// NOTE: When you use the equality operator JavaScript will check for reference equality. Dates being objects this
// will always return false, even though they refer to the exact same time. This means you need to convert them to
// a more primitive value like a string or number first. `getTime()` seems to be the winner according to
// stack overflow https://stackoverflow.com/a/4587089/6117745
return previousVersion.startDate.getTime() === newVersionStartDate.getTime() &&
previousVersion.endDate === null
})

if (!matchedReturnVersion) {
return null
}

return matchedReturnVersion.$query().patch({ status: 'superseded' })
}

/**
* Update the status of a previous version whose start date is equal to the new one and whose end date is not null
*
* For example, imagine these are the existing return versions
*
* | Id | Start date | End date | Status |
* |----|------------|------------|---------|
* | 1 | 2008-04-01 | 2019-05-12 | current |
* | 2 | 2019-05-13 | 2022-03-31 | current |
* | 3 | 2022-04-01 | | current |
*
* The user adds a new return version staring 2019-05-13. The end result would be
*
* | Id | Start date | End date | Status |
* |----|------------|------------|------------|
* | 1 | 2008-04-01 | 2019-05-12 | current |
* | 2 | 2019-05-13 | 2022-03-31 | superseded |
* | 4 | 2019-05-13 | 2022-03-31 | current |
* | 3 | 2022-04-01 | | current |
*
* This function finds return version **2** and updates its status to `superseded`. We also return the end date from
* version **2** as this needs to be applied to the new return version as its end date. Hence, we are _replacing a
* previous version_.
*/
async function _replacePreviousVersion (previousVersions, newVersionStartDate) {
const matchedReturnVersion = previousVersions.find((previousVersion) => {
return previousVersion.startDate.getTime() === newVersionStartDate.getTime() &&
previousVersion.endDate > newVersionStartDate
})

if (!matchedReturnVersion) {
return null
}

const newVersionEndDate = matchedReturnVersion.endDate

await matchedReturnVersion.$query().patch({ status: 'superseded' })

return newVersionEndDate
}

module.exports = {
go
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const ReturnVersionHelper = require('../../support/helpers/return-version.helper

// Things we need to stub
const GenerateReturnVersionRequirementsService = require('../../../app/services/return-requirements/generate-return-version-requirements.service.js')
const ProcessExistingReturnVersionsService = require('../../../app/services/return-requirements/process-existing-return-versions.service.js')

// Thing under test
const GenerateReturnVersionService = require('../../../app/services/return-requirements/generate-return-version.service.js')
Expand All @@ -35,10 +36,6 @@ describe('Generate Return Version service', () => {
describe('when called with the minimum possible session data and previous return versions exist', () => {
beforeEach(async () => {
licenceId = generateUUID()

await ReturnVersionHelper.add({ licenceId, version: 100 })
await ReturnVersionHelper.add({ licenceId, version: 102 })

sessionData = {
setup: 'use-existing-requirements',
reason: 'minor-change',
Expand Down Expand Up @@ -67,19 +64,24 @@ describe('Generate Return Version service', () => {
checkPageVisited: true,
startDateOptions: 'licenceStartDate'
}

await ReturnVersionHelper.add({ licenceId, version: 100 })
await ReturnVersionHelper.add({ licenceId, version: 102 })

Sinon.stub(ProcessExistingReturnVersionsService, 'go').resolves('2024-04-01T00:00:00.000Z')
})

it('generates the data required to populate a record in the "return_version" table', async () => {
const result = await GenerateReturnVersionService.go(sessionData, userId)

expect(result.returnRequirements).to.equal('return requirements data')
expect(result.returnVersion.createdBy).to.equal(userId)
expect(result.returnVersion.endDate).to.be.null()
expect(result.returnVersion.endDate).to.equal('2024-04-01T00:00:00.000Z')
expect(result.returnVersion.licenceId).to.equal(licenceId)
expect(result.returnVersion.multipleUpload).to.be.false()
expect(result.returnVersion.notes).to.be.undefined()
expect(result.returnVersion.reason).to.equal(sessionData.reason)
expect(result.returnVersion.startDate).to.equal(sessionData.licence.currentVersionStartDate)
expect(result.returnVersion.startDate).to.equal(new Date(sessionData.licence.currentVersionStartDate))
expect(result.returnVersion.status).to.equal('current')
// Version number is 103 because this is the next version number after the previous version
expect(result.returnVersion.version).to.equal(103)
Expand All @@ -89,7 +91,6 @@ describe('Generate Return Version service', () => {
describe('when called with the maximum possible session data and no previous return versions exist', () => {
beforeEach(async () => {
licenceId = generateUUID()

sessionData = {
note: {
content: 'This is a test note',
Expand Down Expand Up @@ -140,7 +141,6 @@ describe('Generate Return Version service', () => {
describe('when called with session data from the "no-returns-required" journey', () => {
beforeEach(async () => {
licenceId = generateUUID()

sessionData = {
reason: 'returns-exception',
journey: 'no-returns-required',
Expand Down Expand Up @@ -169,7 +169,7 @@ describe('Generate Return Version service', () => {
expect(result.returnVersion.multipleUpload).to.be.false()
expect(result.returnVersion.notes).to.be.undefined()
expect(result.returnVersion.reason).to.equal(sessionData.reason)
expect(result.returnVersion.startDate).to.equal(sessionData.licence.currentVersionStartDate)
expect(result.returnVersion.startDate).to.equal(new Date(sessionData.licence.currentVersionStartDate))
expect(result.returnVersion.status).to.equal('current')
expect(result.returnVersion.version).to.equal(1)
})
Expand Down
Loading

0 comments on commit 927a1d7

Please sign in to comment.