Skip to content

Commit

Permalink
Licence import flagging for supplementary billing (#1338)
Browse files Browse the repository at this point in the history
https://eaflood.atlassian.net/browse/WATER-4591

During the licence import process, we need to check if a licence has been lapsed/revoked or ended. A licence can end at any time, either in the past or the future. If the licence has ended in the past then the licence bills will be incorrect. This means the licence will need to be flagged for either pre-sroc supplementary billing, sroc supplementary billing or two-part tariff supplementary billing (depending on what is affected). This PR is building the service needed to check the licence lapsed/revoked or ended date and flagging if necessary.
  • Loading branch information
Beckyrose200 authored Oct 21, 2024
1 parent 488fc86 commit ebab844
Show file tree
Hide file tree
Showing 10 changed files with 970 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
'use strict'

/**
* Determines if an imported licence has a new end date
* @module DetermineSupplementaryBillingFlagsService
*/

const LicenceModel = require('../../models/licence.model.js')
const ProcessImportedLicenceService = require('../licences/supplementary/process-imported-licence.service.js')

/**
* Determines if an imported licence has a new end date.
*
* This service is responsible for determining whether a licence imported has a new end day and therefore should be
* flagged for supplementary billing.
*
* It compares the licences end dates (such as lapsed, revoked or expired dates) between WRLS licence and the imported
* data, and if there is a change in the dates allows the licence to go on to determining the flags.
*
* @param {object} importedLicence - The imported licence
* @param {string} licenceId - The UUID of the licence being updated by the import
*
* @returns {Promise} A promise is returned but it does not resolve to anything we expect the caller to use
*/
async function go (importedLicence, licenceId) {
try {
const licenceChanged = await _licenceChanged(importedLicence, licenceId)

if (!licenceChanged) {
return
}

return ProcessImportedLicenceService.go(importedLicence, licenceId)
} catch (error) {
global.GlobalNotifier.omfg('Determine supplementary billing flags on import failed ', { licenceId }, error)
}
}

async function _licenceChanged (importedLicence, licenceId) {
const query = LicenceModel.query()
.select(['id'])
.where('id', licenceId)

_whereClauses(query, importedLicence)

const result = await query

return result.length === 0
}

/**
* Adds where clauses to compare the end dates (expired, revoked, lapsed) of the imported licence with those stored
* in the database. It handles where the end dates can be null.
*
* In SQL, comparing `null` values using a regular `where` clause does not work as expected because
* `null` represents the absence of a value and `null = null` returns false. To address this, we use
* `whereNull` to explicitly check for null values in the database.
*
* If an end date is present on the imported licence, the query uses a standard `where` clause to check
* for a match. If the end date is null, the query uses `whereNull` to compare against the null values.
*
* This ensures that value types (dates and null) can be correctly compared, allowing us to detect changes
* between the imported licence and the existing WRLS licence data.
*
* @private
*/
function _whereClauses (query, importedLicence) {
const { expiredDate, lapsedDate, revokedDate } = importedLicence

if (expiredDate) {
query.where('expiredDate', expiredDate)
} else {
query.whereNull('expiredDate')
}

if (revokedDate) {
query.where('revokedDate', revokedDate)
} else {
query.whereNull('revokedDate')
}

if (lapsedDate) {
query.where('lapsedDate', lapsedDate)
} else {
query.whereNull('lapsedDate')
}
}

module.exports = {
go
}
12 changes: 8 additions & 4 deletions app/services/import/legacy/process-licence.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* @module ImportLegacyProcessLicenceService
*/

const DetermineSupplementaryBillingFlagsService = require('../determine-supplementary-billing-flags.service.js')
const LicenceStructureValidator = require('../../../validators/import/licence-structure.validator.js')
const PersistImportService = require('../persist-import.service.js')
const ProcessLicenceReturnLogsService = require('../../jobs/return-logs/process-licence-return-logs.service.js')
Expand Down Expand Up @@ -35,6 +36,13 @@ async function go (licenceRef) {
const { naldLicenceId, regionCode, transformedLicence, wrlsLicenceId } =
await TransformLicenceService.go(licenceRef)

// We have other services that need to know when a licence has been imported. However, they only care about changes
// to existing licences. So, if wrlsLicenceId is populated it means the import is updating an existing licence.
if (wrlsLicenceId) {
DetermineSupplementaryBillingFlagsService.go(transformedLicence, wrlsLicenceId)
await ProcessLicenceReturnLogsService.go(wrlsLicenceId)
}

// Pass the transformed licence through each transformation step, building the licence as we go
await TransformLicenceVersionsService.go(regionCode, naldLicenceId, transformedLicence)
await TransformLicenceVersionPurposesService.go(regionCode, naldLicenceId, transformedLicence)
Expand All @@ -58,10 +66,6 @@ async function go (licenceRef) {
// Either insert or update the licence in WRLS
const licenceId = await PersistImportService.go(transformedLicence, transformedCompanies)

if (wrlsLicenceId) {
await ProcessLicenceReturnLogsService.go(wrlsLicenceId)
}

calculateAndLogTimeTaken(startTime, 'Legacy licence import complete', { licenceId, licenceRef })
} catch (error) {
global.GlobalNotifier.omfg('Legacy licence import errored', { licenceRef }, error)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict'

/**
* Fetches existing supplementary details about a licence being updated during import
* @module FetchExistingLicenceDetailsService
*/

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

/**
* Fetches existing supplementary details about a licence being updated during import
*
* We need to get the existing end dates so we can compare with those being imported to determine which have been
* changed (it could be more than one).
*
* The query also determines what relevant charge versions exist for the licence. When processing the licence we have
* to work out whether to set the include in presroc, include in sroc, and include in two-part tariff (by way of
* supplementary billing years) flags on the licence.
*
* We can skip those steps if we know the licence being updated doesn't have the relevant charge versions. If it doesn't
* have them, then it could never have been previously billed for them!
*
* @param {string} licenceId - The UUID of the licence details being fetched
*
* @returns {Promise<object>} - The data needed to determine which supplementary flags the licence needs
*/
async function go (licenceId) {
const query = _query()

const { rows: [row] } = await db.raw(query, [licenceId])

return row
}

function _query () {
return `
SELECT
l.id,
l.expired_date,
l.lapsed_date,
l.revoked_date,
(CASE l.include_in_presroc_billing
WHEN 'yes' THEN TRUE
ELSE FALSE
END) AS flagged_for_presroc,
l.include_in_sroc_billing AS flagged_for_sroc,
EXISTS(
SELECT
1
FROM
public.charge_versions cv
WHERE
cv.licence_id = l.id
AND cv.start_date < '2022-04-01'
) AS pre_sroc_charge_versions,
EXISTS(
SELECT
1
FROM
public.charge_versions cv
WHERE
cv.licence_id = l.id
AND cv.start_date > '2022-03-31'
) AS sroc_charge_versions,
EXISTS(
SELECT
1
FROM
public.charge_versions cv
INNER JOIN
public.charge_references cr ON cr.charge_version_id = cv.id
INNER JOIN
public.charge_elements ce ON ce.charge_reference_id = cr.id
WHERE
cv.licence_id = l.id
AND cv.start_date > '2022-03-31'
AND cr.adjustments->>'s127' = 'true'
AND ce.section_127_Agreement = TRUE
) AS two_part_tariff_charge_versions
FROM
public.licences l
WHERE
l.id = ?;
`
}

module.exports = {
go
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict'

/**
* Persists the supplementary billing flags for a licence
* @module PersistSupplementaryBillingFlagsService
*/

const CreateLicenceSupplementaryYearService = require('./create-licence-supplementary-year.service.js')
const LicenceModel = require('../../../models/licence.model.js')

/**
* Persists the supplementary billing flags for a licence
*
* Updates the licences includeInPresrocBilling and includeInSrocBilling flags.
* Adds financial years related to two-part tariff billing into the LicenceSupplementaryYears table.
*
* NOTE: Due to the column data type of the includeInPresrocBilling & includeInSrocBilling, one is a string value and
* one is a boolean.
*
* @param {object[]} twoPartTariffBillingYears - The years that need persisting in the LicenceSupplementaryYears table
* @param {boolean} flagForPreSrocSupplementary - `true` or `false` depending on if the licence needs to be flagged
* for pre sroc billing
* @param {boolean} flagForSrocSupplementary - `true` or `false` depending on if the licence needs to be flagged for
* sroc billing
* @param {string} licenceId - The UUID of the licence that needs the flags persisting for
*
* @returns {Promise<object>} - Resolves with the result of persisting two-part tariff billing years,
*/
async function go (twoPartTariffBillingYears, flagForPreSrocSupplementary, flagForSrocSupplementary, licenceId) {
const includeInPresrocBilling = flagForPreSrocSupplementary ? 'yes' : 'no'

await _updateLicenceFlags(includeInPresrocBilling, flagForSrocSupplementary, licenceId)

return _flagForLicenceSupplementaryYears(twoPartTariffBillingYears, licenceId)
}

/**
* Persists two-part tariff financial years in the LicenceSupplementaryYears table.
* @private
*/
async function _flagForLicenceSupplementaryYears (twoPartTariffBillingYears, licenceId) {
if (twoPartTariffBillingYears.length === 0) {
return
}

const twoPartTariff = true

return CreateLicenceSupplementaryYearService.go(licenceId, twoPartTariffBillingYears, twoPartTariff)
}

async function _updateLicenceFlags (includeInPresrocBilling, flagForSrocSupplementary, licenceId) {
return LicenceModel.query()
.patch({ includeInPresrocBilling, includeInSrocBilling: flagForSrocSupplementary })
.where('id', licenceId)
}

module.exports = {
go
}
Loading

0 comments on commit ebab844

Please sign in to comment.