Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate return logs from requirements #1244

Merged
merged 42 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b74109b
initial work creating the return logs
robertparkinson Aug 2, 2024
068e47c
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 2, 2024
a73ae41
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 2, 2024
4ef3f4d
add new job to create return logs
robertparkinson Aug 8, 2024
1815a30
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 8, 2024
e29aa64
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 8, 2024
90c0d23
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 9, 2024
b7be7ff
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 12, 2024
3cad678
add option to provide a licence reference to create return logs
robertparkinson Aug 12, 2024
0b94e94
Merge branch 'generate-return-logs-from-requirements' of github.com:D…
robertparkinson Aug 12, 2024
b3fde00
add unit tests and refactor some of the code
robertparkinson Aug 19, 2024
dc60dbc
clean up unit test
robertparkinson Aug 19, 2024
563d6d7
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 19, 2024
8a6d59d
Merge branch 'generate-return-logs-from-requirements' of github.com:D…
robertparkinson Aug 19, 2024
bac6e4e
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 20, 2024
40243b1
change end of file character
robertparkinson Aug 20, 2024
8d4947c
Merge branch 'generate-return-logs-from-requirements' of github.com:D…
robertparkinson Aug 20, 2024
bc1820b
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 20, 2024
3d38d55
fix failing tests
robertparkinson Aug 20, 2024
2b0f878
Merge branch 'generate-return-logs-from-requirements' of github.com:D…
robertparkinson Aug 20, 2024
444e4b7
refactor code to make it easier to read
robertparkinson Aug 20, 2024
008eac8
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 20, 2024
cf71541
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 21, 2024
e447664
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 21, 2024
6bf6408
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 21, 2024
a6c9f92
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 21, 2024
409bceb
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 22, 2024
f70f10c
refactor insert and add timing of process
robertparkinson Aug 23, 2024
16dd4af
Merge branch 'generate-return-logs-from-requirements' of github.com:D…
robertparkinson Aug 23, 2024
ddaeb78
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 23, 2024
c408643
fix linting issues
robertparkinson Aug 23, 2024
957ec2c
fix failing tests
robertparkinson Aug 23, 2024
13afd44
fix unit tests
robertparkinson Aug 23, 2024
3ad30ff
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 23, 2024
010316b
fix linting issues
robertparkinson Aug 23, 2024
f30513a
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 27, 2024
4c471ea
address review comments
robertparkinson Aug 27, 2024
259d1f5
sonarqube refactor suggestion
robertparkinson Aug 27, 2024
2607c46
Merge branch 'main' into generate-return-logs-from-requirements
robertparkinson Aug 27, 2024
d6c6fe5
address review comments
robertparkinson Aug 27, 2024
aeceb68
Merge branch 'generate-return-logs-from-requirements' of github.com:D…
robertparkinson Aug 27, 2024
830f8bf
address review comments
robertparkinson Aug 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions app/controllers/jobs.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const ExportService = require('../services/jobs/export/export.service.js')
const ProcessLicenceUpdates = require('../services/jobs/licence-updates/process-licence-updates.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 ProcessReturnLogsService = require('../services/jobs/return-logs/process-return-logs.service.js')

const redirectStatusCode = 204
const notFoundStatusCode = 404

/**
* Triggers export of all relevant tables to CSV and then uploads them to S3
Expand All @@ -20,30 +24,49 @@ const ProcessTimeLimitedLicencesService = require('../services/jobs/time-limited
async function exportDb (_request, h) {
ExportService.go()

return h.response().code(204)
return h.response().code(redirectStatusCode)
}

async function licenceUpdates (_request, h) {
ProcessLicenceUpdates.go()

return h.response().code(204)
return h.response().code(redirectStatusCode)
}

async function sessionCleanup (_request, h) {
ProcessSessionStorageCleanupService.go()

return h.response().code(204)
return h.response().code(redirectStatusCode)
}

async function timeLimited (_request, h) {
ProcessTimeLimitedLicencesService.go()

return h.response().code(204)
return h.response().code(redirectStatusCode)
}

async function returnLogs (request, h) {
const { cycle } = request.params

if (!['summer', 'all-year'].includes(cycle)) {
return h.response().code(notFoundStatusCode)
}

let licenceReference

if (h.request.payload !== null && h.request.payload.licenceReference) {
licenceReference = h.request.payload.licenceReference
}

ProcessReturnLogsService.go(cycle, licenceReference)

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

module.exports = {
exportDb,
licenceUpdates,
returnLogs,
sessionCleanup,
timeLimited
}
14 changes: 14 additions & 0 deletions app/routes/jobs.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ const routes = [
crumb: false
}
}
},
{
method: 'POST',
path: '/jobs/return-logs/{cycle}',
options: {
handler: JobsController.returnLogs,
app: {
plainOutput: true
},
auth: false,
plugins: {
crumb: false
}
}
}
]

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

/**
* Fetches data needed for the generating return logs
* @module FetchLicenceWithoutReturnsService
*/

const ReturnLogModel = require('../../../models/return-log.model.js')
const ReturnRequirementModel = require('../../../models/return-requirement.model.js')
const ReturnVersionModel = require('../../../models/return-version.model.js')

const { db } = require('../../../../db/db.js')

const allYearDueDateDay = 28
const allYearDueDateMonth = 3
const allYearEndDay = 31
const allYearEndMonth = 2
const allYearStartDay = 1
const allYearStartMonth = 3

const summerDueDateDay = 28
const summerDueDateMonth = 10
const summerEndDay = 31
const summerEndMonth = 9
const summerStartDay = 1
const summerStartMonth = 10

const endOfSummerCycle = new Date(new Date().getFullYear() + 1, summerEndMonth, summerEndDay)
const endOfWinterAndAllYearCycle = new Date(new Date().getFullYear() + 1, allYearEndMonth, allYearEndDay)

/**
* Fetch all return requirements that need return logs created.
*
* @returns {Promise<Array>} the list of return requirement ids
*/
async function go (isSummer, licenceReference) {
const requirementsForReturns = await _fetchReturnRequirements(isSummer, licenceReference)
const data = await _generateReturnLogPayload(isSummer, requirementsForReturns)

return data
}

async function _createMetaData (isSummer, endDate, requirements) {
return {
description: requirements.siteDescription,
isCurrent: requirements.returnVersion.reason !== 'succession-or-transfer-of-licence',
isFinal: _isFinal(endDate, isSummer),
isSummer,
isTwoPartTariff: requirements.twoPartTariff,
isUpload: requirements.upload,
nald: {
regionCode: requirements.returnVersion.licence.region.naldRegionId,
areaCode: requirements.returnVersion.licence.areacode,
formatId: requirements.legacyId,
periodStartDay: requirements.abstractionPeriodStartDay,
periodStartMonth: requirements.abstractionPeriodStartMonth,
periodEndDay: requirements.abstractionPeriodEndDay,
periodEndMonth: requirements.abstractionPeriodEndMonth
},
points: requirements.returnRequirementPoints,
purposes: requirements.returnRequirementPurposes,
version: 1
}
}

function _createReturnLogId (requirements, startDate, endDate) {
const regionCode = requirements.returnVersion.licence.region.naldRegionId
const licenceReference = requirements.returnVersion.licence.licenceRef
const legacyId = requirements.legacyId

return `v1:${regionCode}:${licenceReference}:${legacyId}:${startDate}:${endDate}`
}

async function _fetchExternalIds (cycleStartDate) {
const externalIds = await ReturnLogModel.query()
.select(['licenceRef',
db.raw("concat(ret.metadata->'nald'->>'regionCode', ':', ret.return_requirement) as externalid")
])
.from('returns.returns as ret')
.where('startDate', '>=', cycleStartDate)

const externalIdsArray = externalIds.map((item) => {
return item.externalid
})

return externalIdsArray
}

async function _fetchReturnRequirements (isSummer, licenceReference) {
const cycleStartDate = _getCycleStartDate(isSummer)
const externalIds = await _fetchExternalIds(cycleStartDate)

const results = await ReturnRequirementModel.query()
.whereNotIn('returnRequirements.externalId', externalIds)
.whereExists(_whereExistsClause(licenceReference, cycleStartDate))
.where('returnRequirements.summer', isSummer)
.withGraphFetched('returnVersion')
.modifyGraph('returnVersion', (builder) => {
builder.select(['endDate',
'id',
'startDate',
'reason'])
})
.withGraphFetched('returnVersion.licence')
.modifyGraph('returnVersion.licence', (builder) => {
builder.select(['expiredDate',
'id',
'lapsedDate',
'licenceRef',
'revokedDate',
db.raw('regions->>\'historicalAreaCode\' as areacode')])
})
.withGraphFetched('returnVersion.licence.region')
.modifyGraph('returnVersion.licence.region', (builder) => {
builder.select(['id', 'naldRegionId'])
})
.withGraphFetched('returnRequirementPoints')
.withGraphFetched('returnRequirementPurposes')

return results
}

function _formatDate (date) {
return date.toISOString().split('T')[0]
}

async function _generateReturnLogPayload (isSummer, requirementsForReturns) {
const returnLogs = requirementsForReturns.map(async (requirements) => {
const startDate = _getCycleStartDate(isSummer)
const endDate = _getCycleEndDate(isSummer, requirements.returnVersion)
const id = _createReturnLogId(requirements, startDate, endDate)
const metadata = await _createMetaData(isSummer, endDate, requirements)

return {
createdAt: new Date(),
dueDate: _getCycleDueDate(isSummer),
endDate,
id,
licenceRef: requirements.returnVersion.licence.licenceRef,
metadata,
returnsFrequency: requirements.reportingFrequency,
startDate,
status: 'due',
source: 'WRLS',
returnReference: requirements.legacyId.toString()
}
})

const results = await Promise.all(returnLogs)

return results
}

function _getCycleDueDate (isSummer) {
return isSummer
? _formatDate(new Date(new Date().getFullYear() + 1, summerDueDateMonth, summerDueDateDay))
: _formatDate(new Date(new Date().getFullYear() + 1, allYearDueDateMonth, allYearDueDateDay))
}

function _getCycleEndDate (isSummer, returnVersion) {
const dates = [returnVersion.licence.expiredDate,
returnVersion.licence.lapsedDate,
returnVersion.licence.revokedDate,
returnVersion.endDate]
.filter((date) => { return date !== null })
.map((date) => { return new Date(date) })

if (dates.length === 0) {
return isSummer
? _formatDate(endOfSummerCycle)
: _formatDate(endOfWinterAndAllYearCycle)
}

dates.map((date) => { return date.getTime() })
const earliestEndDate = new Date(Math.min(...dates))

if (isSummer) {
if (earliestEndDate < endOfSummerCycle) {
return _formatDate(earliestEndDate)
}

return _formatDate(endOfSummerCycle)
}

if (earliestEndDate < endOfWinterAndAllYearCycle) {
return _formatDate(earliestEndDate)
}

return _formatDate(endOfWinterAndAllYearCycle)
}

function _getCycleStartDate (isSummer) {
return isSummer
? _formatDate(new Date(new Date().getFullYear(), summerStartMonth, summerStartDay))
: _formatDate(new Date(new Date().getFullYear(), allYearStartMonth, allYearStartDay))
}

function _isFinal (endDateString, isSummer) {
const endDate = new Date(endDateString)

return ((isSummer && endDate < endOfSummerCycle) || (!isSummer && endDate < endOfWinterAndAllYearCycle))
}

function _whereExistsClause (licenceReference, cycleStartDate) {
const query = ReturnVersionModel.query().select(1)

query.select(1)
.innerJoinRelated('licence')
.where('returnVersions.startDate', '<=', cycleStartDate)
.where('returnVersions.status', 'current')
.where((builder) => {
builder
.whereNull('returnVersions.endDate')
.orWhere('returnVersions.endDate', '>=', cycleStartDate)
})
.where((builder) => {
builder
.whereNull('licence.expiredDate')
.orWhere('licence.expiredDate', '>=', cycleStartDate)
})
.where((builder) => {
builder
.whereNull('licence.lapsedDate')
.orWhere('licence.lapsedDate', '>=', cycleStartDate)
})
.where((builder) => {
builder
.whereNull('licence.revokedDate')
.orWhere('licence.revokedDate', '>=', cycleStartDate)
})

query.whereColumn('returnVersions.id', 'returnRequirements.returnVersionId')

if (licenceReference) {
query.where('licence.licenceRef', licenceReference)
}

return query
}

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

/**
* Process the return logs for the next cycle
* @module ProcessReturnLogsService
*/

const { calculateAndLogTimeTaken, currentTimeInNanoseconds } = require('../../../lib/general.lib.js')
const FetchReturnLogsService = require('./fetch-return-logs.service.js')
const ReturnLogModel = require('../../../models/return-log.model.js')

/**
* Creates the return logs for the next cycle
*/
async function go (cycle, licenceReference) {
try {
const isSummer = cycle === 'summer'
robertparkinson marked this conversation as resolved.
Show resolved Hide resolved
const startTime = currentTimeInNanoseconds()
const returnLogs = await FetchReturnLogsService.go(isSummer, licenceReference)

if (returnLogs.length > 0) {
await _createReturnLogs(returnLogs)
}
calculateAndLogTimeTaken(startTime, 'Create return logs complete', { isSummer, licenceReference })
robertparkinson marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
global.GlobalNotifier.omfg('Create return logs job failed', null, error)
robertparkinson marked this conversation as resolved.
Show resolved Hide resolved
}
}

async function _createReturnLogs (returnLogs) {
for (const returnLog of returnLogs) {
await ReturnLogModel.query()
.insert(returnLog)
}
}

module.exports = {
go
}
Loading
Loading