Skip to content

Commit

Permalink
Merge branch 'main' into new-view-completed-monthly-return-logs-page
Browse files Browse the repository at this point in the history
  • Loading branch information
StuAA78 authored Jan 30, 2025
2 parents 13e6f71 + 6c8f297 commit aed49b2
Show file tree
Hide file tree
Showing 13 changed files with 212 additions and 35 deletions.
8 changes: 8 additions & 0 deletions app/controllers/jobs.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const ProcessLicenceUpdates = require('../services/jobs/licence-updates/process-
const ProcessReturnLogsService = require('../services/jobs/return-logs/process-return-logs.service.js')
const ProcessSessionStorageCleanupService = require('../services/jobs/session-cleanup/process-session-storage-cleanup.service.js')
const ProcessTimeLimitedLicencesService = require('../services/jobs/time-limited/process-time-limited-licences.service.js')
const ReturnVersionMigrationService = require('../services/jobs/return-logs/return-version-migration.service.js')

const NO_CONTENT_STATUS_CODE = 204
const NOT_FOUND_STATUS_CODE = 404
Expand Down Expand Up @@ -59,10 +60,17 @@ async function returnLogs(request, h) {
return h.response().code(NO_CONTENT_STATUS_CODE)
}

async function returnVersionMigration(request, h) {
ReturnVersionMigrationService.go()

return h.response().code(NO_CONTENT_STATUS_CODE)
}

module.exports = {
exportDb,
licenceUpdates,
returnLogs,
returnVersionMigration,
sessionCleanup,
timeLimited
}
4 changes: 2 additions & 2 deletions app/models/licence-document-header.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ class LicenceDocumentHeaderModel extends BaseModel {
to: 'licences.licenceRef'
}
},
licenceEntityRole: {
relation: Model.HasOneRelation,
licenceEntityRoles: {
relation: Model.HasManyRelation,
modelClass: 'licence-entity-role.model',
join: {
from: 'licenceDocumentHeaders.companyEntityId',
Expand Down
21 changes: 14 additions & 7 deletions app/models/licence.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,16 +235,16 @@ class LicenceModel extends BaseModel {
.modifyGraph('licenceDocumentHeader', (builder) => {
builder.select(['id'])
})
.withGraphFetched('licenceDocumentHeader.licenceEntityRole')
.modifyGraph('licenceDocumentHeader.licenceEntityRole', (builder) => {
.withGraphFetched('licenceDocumentHeader.licenceEntityRoles')
.modifyGraph('licenceDocumentHeader.licenceEntityRoles', (builder) => {
builder.select(['id']).where('role', 'primary_user')
})
.withGraphFetched('licenceDocumentHeader.licenceEntityRole.licenceEntity')
.modifyGraph('licenceDocumentHeader.licenceEntityRole.licenceEntity', (builder) => {
.withGraphFetched('licenceDocumentHeader.licenceEntityRoles.licenceEntity')
.modifyGraph('licenceDocumentHeader.licenceEntityRoles.licenceEntity', (builder) => {
builder.select(['id'])
})
.withGraphFetched('licenceDocumentHeader.licenceEntityRole.licenceEntity.user')
.modifyGraph('licenceDocumentHeader.licenceEntityRole.licenceEntity.user', (builder) => {
.withGraphFetched('licenceDocumentHeader.licenceEntityRoles.licenceEntity.user')
.modifyGraph('licenceDocumentHeader.licenceEntityRoles.licenceEntity.user', (builder) => {
builder.select(['id', 'username'])
})
}
Expand Down Expand Up @@ -411,11 +411,18 @@ class LicenceModel extends BaseModel {
*
* Within the UI this what determines whether you see the "Registered to" link in the view licence page's top section.
*
* This query will find the `licenceEntityRole` with the role 'primary_user'. As we can expect multiple
* `licenceEntityRoles` we need to take the first (and expected to be only) `licenceEntityRole` from the received
* array of `licenceEntityRoles`.
*
* > We understand that `licenceEntityRoles` can be associated with the same `company_entity_id`. A common example
* of this is having a 'Primary user' and a 'Returns agent' (known as `user_agent` in the database)
*
* @returns {(module:UserModel|null)} the primary user if the licence has one and the additional properties needed to
* to determine it have been set, else `null`
*/
$primaryUser() {
const primaryUser = this?.licenceDocumentHeader?.licenceEntityRole?.licenceEntity?.user
const primaryUser = this?.licenceDocumentHeader?.licenceEntityRoles?.[0]?.licenceEntity?.user

return primaryUser || null
}
Expand Down
14 changes: 14 additions & 0 deletions app/routes/jobs.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,20 @@ const routes = [
crumb: false
}
}
},
{
method: 'POST',
path: '/jobs/return-version-migration',
options: {
handler: JobsController.returnVersionMigration,
app: {
plainOutput: true
},
auth: false,
plugins: {
crumb: false
}
}
}
]

Expand Down
74 changes: 74 additions & 0 deletions app/services/jobs/return-logs/fetch-water-undertakers.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
'use strict'

/**
* Fetches all water undertakers that have a valid licence after 1/04/2025
* @module FetchWaterUndertakersService
*/

const { db } = require('../../../../db/db.js')
const LicenceModel = require('../../../models/licence.model.js')
const ReturnRequirementModel = require('../../../models/return-requirement.model.js')
const ReturnVersionModel = require('../../../models/return-version.model.js')

/**
* Fetches all water undertaker licences that will have not been ended before 1/04/2025
*
* @returns {Promise<module:LicenceModel>} the matching licences
*/
async function go() {
const quarterlyStartDate = new Date('2025-04-01')

return await LicenceModel.query()
.select([
'id',
'expiredDate',
'lapsedDate',
'licenceRef',
'revokedDate',
'startDate',
'waterUndertaker',
db.raw("regions->>'regionalChargeArea' as regionalChargeArea")
])
.withGraphFetched('returnVersions')
.modifyGraph('returnVersions', (builder) => {
builder
.select(['id', 'startDate', 'reason'])
.where('startDate', '<', quarterlyStartDate)
.where('status', 'current')
// A return version must include return requirements in order for us to be able to copy from it
.whereExists(
ReturnRequirementModel.query()
.select(1)
.whereColumn('returnVersions.id', 'returnRequirements.returnVersionId')
)
.orderBy('startDate', 'desc')
})
.where('waterUndertaker', true)
.where((builder) => {
builder.whereNull('expiredDate').orWhere('expiredDate', '>=', quarterlyStartDate)
})
.where((builder) => {
builder.whereNull('lapsedDate').orWhere('lapsedDate', '>=', quarterlyStartDate)
})
.where((builder) => {
builder.whereNull('revokedDate').orWhere('revokedDate', '>=', quarterlyStartDate)
})
.whereNotExists(
ReturnVersionModel.query()
.where('returnVersions.status', 'current')
.where('startDate', '>=', quarterlyStartDate)
.whereColumn('returnVersions.licenceId', 'licences.id')
)
.whereExists(
ReturnVersionModel.query()
.where('returnVersions.status', 'current')
.where('quarterlyReturns', false)
.where('startDate', '<', quarterlyStartDate)
.whereColumn('returnVersions.licenceId', 'licences.id')
)
.whereIn('id', ['5599e2b2-4047-48e6-84aa-f6fc23741e05', '95de4f0f-f13f-4ea6-9460-b7f5d6ce8381'])
}

module.exports = {
go
}
65 changes: 65 additions & 0 deletions app/services/jobs/return-logs/return-version-migration.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict'

/**
* Determines which licences need new return versions created for quarterly returns
* @module ReturnVersionMigrationService
*/

const { calculateAndLogTimeTaken, currentTimeInNanoseconds } = require('../../../lib/general.lib.js')
const FetchWaterUndertakersService = require('./fetch-water-undertakers.service.js')
const GenerateFromExisintRequirementsService = require('../../return-versions/setup/existing/generate-from-existing-requirements.service.js')
const GenerateReturnVersionService = require('../../return-versions/setup/check/generate-return-version.service.js')
const LicencesConfig = require('../../../../config/licences.config.js')
const PersistReturnVersionService = require('../../return-versions/setup/check/persist-return-version.service.js')
const UserModel = require('../../../models/user.model.js')

/**
* Determines which licences need new return versions created for quarterly returns
*
* This service will generate new return versions for each current licence that is a water undertaker (a water company).
* The new return version will start on 1/04/2025.
*/
async function go() {
try {
const startTime = currentTimeInNanoseconds()

const usernames = ['[email protected]']

if (LicencesConfig.returnVersionBatchUser) {
usernames.unshift(LicencesConfig.returnVersionBatchUser)
}

const user = await UserModel.query().select(['id']).whereIn('username', usernames).first()

const licences = await FetchWaterUndertakersService.go()

for (const licence of licences) {
const returnRequirements = await GenerateFromExisintRequirementsService.go(licence.returnVersions[0].id)

const data = {
licence: {
id: licence.id
},
multipleUpload: returnRequirements.multipleUpload,
note: {
content: 'Changed due to water company licences moving to quarterly returns'
},
reason: 'change-to-return-requirements',
returnVersionStartDate: new Date('2025-04-01'),
requirements: returnRequirements.requirements,
quarterlyReturns: true
}

const returnVersionData = await GenerateReturnVersionService.go(data, user.id)
await PersistReturnVersionService.go(returnVersionData)
}

calculateAndLogTimeTaken(startTime, 'Return version migration job complete')
} catch (error) {
global.GlobalNotifier.omfg('Return version migration job failed', null, error)
}
}

module.exports = {
go
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function go(returnVersionId) {
async function _fetch(returnVersionId) {
return ReturnVersionModel.query()
.findById(returnVersionId)
.select(['id', 'multipleUpload', 'quarterlyReturns'])
.select(['id', 'multipleUpload', 'startDate', 'quarterlyReturns'])
.withGraphFetched('returnRequirements')
.modifyGraph('returnRequirements', (returnRequirementsBuilder) => {
returnRequirementsBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ async function go(returnVersionId) {
return {
multipleUpload: returnVersion.multipleUpload,
quarterlyReturns: returnVersion.quarterlyReturns,
requirements: _transformForSetup(returnVersion)
requirements: _transformForSetup(returnVersion),
startDate: returnVersion.startDate
}
}

Expand Down
3 changes: 2 additions & 1 deletion config/licences.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ require('dotenv').config()
const config = {
endDates: {
batchSize: parseInt(process.env.LICENCE_END_DATES_BATCH_SIZE) || 10
}
},
returnVersionBatchUser: process.env.RETURN_VERSION_BATCH_USER
}

module.exports = config
14 changes: 8 additions & 6 deletions test/models/licence-document-header.model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,25 @@ describe('Licence Document Header model', () => {
})
})

describe('when linking to licence entity role', () => {
describe('when linking to licence entity roles', () => {
it('can successfully run a related query', async () => {
const query = await LicenceDocumentHeaderModel.query().innerJoinRelated('licenceEntityRole')
const query = await LicenceDocumentHeaderModel.query().innerJoinRelated('licenceEntityRoles')

expect(query).to.exist()
})

it('can eager load the licence entity role', async () => {
it('can eager load the licence entity roles', async () => {
const result = await LicenceDocumentHeaderModel.query()
.findById(testRecord.id)
.withGraphFetched('licenceEntityRole')
.withGraphFetched('licenceEntityRoles')

expect(result).to.be.instanceOf(LicenceDocumentHeaderModel)
expect(result.id).to.equal(testRecord.id)

expect(result.licenceEntityRole).to.be.an.instanceOf(LicenceEntityRoleModel)
expect(result.licenceEntityRole).to.equal(testLicenceEntityRole)
const [licenceEntityRole] = result.licenceEntityRoles

expect(licenceEntityRole).to.be.an.instanceOf(LicenceEntityRoleModel)
expect(licenceEntityRole).to.equal(testLicenceEntityRole)
})
})
})
Expand Down
20 changes: 11 additions & 9 deletions test/presenters/licences/view-licence.presenter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('View Licence presenter', () => {

describe('when the licence does not have a primary user (registered user)', () => {
beforeEach(() => {
licence.licenceDocumentHeader.licenceEntityRole = null
licence.licenceDocumentHeader.licenceEntityRoles = []
})

it('returns "Unregistered licence"', () => {
Expand Down Expand Up @@ -347,16 +347,18 @@ function _licence() {
licenceDocumentHeader: {
id: 'e8f491f0-0c60-4083-9d41-d2be69f17a1e',
licenceName: 'Between two ferns',
licenceEntityRole: {
id: 'd7eecfc1-7afa-49f7-8bef-5dc477696a2d',
licenceEntity: {
id: 'ba7702cf-cd87-4419-a04c-8cea4e0cfdc2',
user: {
id: 10036,
username: '[email protected]'
licenceEntityRoles: [
{
id: 'd7eecfc1-7afa-49f7-8bef-5dc477696a2d',
licenceEntity: {
id: 'ba7702cf-cd87-4419-a04c-8cea4e0cfdc2',
user: {
id: 10036,
username: '[email protected]'
}
}
}
}
]
},
licenceSupplementaryYears: [],
workflows: [{ id: 'b6f44c94-25e4-4ca8-a7db-364534157ba7', status: 'to_setup' }]
Expand Down
18 changes: 10 additions & 8 deletions test/services/licences/view-licence.service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,18 @@ function _licence() {
licenceDocumentHeader: {
id: 'e8f491f0-0c60-4083-9d41-d2be69f17a1e',
licenceName: 'Between two ferns',
licenceEntityRole: {
id: 'd7eecfc1-7afa-49f7-8bef-5dc477696a2d',
licenceEntity: {
id: 'ba7702cf-cd87-4419-a04c-8cea4e0cfdc2',
user: {
id: 10036,
username: '[email protected]'
licenceEntityRoles: [
{
id: 'd7eecfc1-7afa-49f7-8bef-5dc477696a2d',
licenceEntity: {
id: 'ba7702cf-cd87-4419-a04c-8cea4e0cfdc2',
user: {
id: 10036,
username: '[email protected]'
}
}
}
}
]
},
licenceSupplementaryYears: [],
workflows: [{ id: 'b6f44c94-25e4-4ca8-a7db-364534157ba7', status: 'to_setup' }]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('Return Versions Setup - Fetch Existing Requirements service', () => {
id: seededReturnVersion.id,
multipleUpload: false,
quarterlyReturns: false,
startDate: seededReturnVersion.startDate,
returnRequirements: [
{
abstractionPeriodEndDay: 31,
Expand Down

0 comments on commit aed49b2

Please sign in to comment.