-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Determine match and allocate issues and status
https://eaflood.atlassian.net/browse/WATER-4378 During the refinement of future two-part tariff tickets, it was noted that the status of a licence could be changed by the user from a 'review' status to a 'ready' status. Before this was noted we were working out the licence status based on the issues the licence has with its returns and charge elements. This is done on every page where the licence status is being shown. Now that we know a status can be overwritten regardless of its issues, we now need to persist the licence status during the first time the engine works it out and show the persisted status instead of working it out each time. This will allow users to manually change the status. The same thing applies to the charge elements. The status of the element is determined by the issues on it. This status at the moment is worked out every time it is displayed on a page. We have since learnt through refinement that a user can allocate volumes on the element and this will change the status from 'review' to 'ready'. This is regardless of the issues that remain on a charge element. So with this new information, it has made it clear that we now need to persist the initial status of the charge element to allow this to be overwritten when needed. This opened up a lot of discussions about how we could refactor the review tables to work more effectively with how the two-part tariff engine works now. When we first built the tables we did so with the knowledge we had at the time. Now that we have worked on more tickets and started building actual pages we realised we could persist the data better. TLDR: Review tables updated to add status and issues
- Loading branch information
1 parent
561cb4f
commit 875f478
Showing
8 changed files
with
532 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
190 changes: 190 additions & 0 deletions
190
app/services/bill-runs/two-part-tariff/determine-licence-issues.service.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
'use strict' | ||
|
||
/** | ||
* Determines the issues on a licences for a two-part tariff bill run | ||
* @module DetermineLicenceIssuesService | ||
*/ | ||
|
||
// A list of issues that would put a licence into a status of 'review' | ||
const REVIEW_STATUSES = [ | ||
'Aggregate factor', 'Checking query', 'Overlap of charge dates', 'Returns received but not processed', | ||
'Returns split over charge references', 'Unable to match returns' | ||
] | ||
|
||
/** | ||
* Determines the issues on a licence charge elements and return logs and sets the status based on them | ||
* | ||
* @param {module:LicenceModel} licence - the two-part tariff licence included in the bill run | ||
*/ | ||
async function go (licence) { | ||
const { returnLogs: licenceReturnLogs, chargeVersions } = licence | ||
|
||
const allReturnIssues = _determineReturnLogsIssues(licenceReturnLogs, licence) | ||
const allElementIssues = _determineChargeElementsIssues(chargeVersions, licenceReturnLogs) | ||
|
||
licence.status = _determineLicenceStatus(allElementIssues, allReturnIssues) | ||
} | ||
|
||
function _determineChargeElementsIssues (chargeVersions, licenceReturnLogs) { | ||
const allElementIssues = [] | ||
|
||
chargeVersions.forEach((chargeVersion) => { | ||
const { chargeReferences } = chargeVersion | ||
|
||
chargeReferences.forEach((chargeReference) => { | ||
const { chargeElements } = chargeReference | ||
|
||
chargeElements.forEach((chargeElement) => { | ||
const { returnLogs } = chargeElement | ||
|
||
const { elementIssues, status } = _elementIssues(chargeReference, chargeElement, licenceReturnLogs, returnLogs) | ||
|
||
chargeElement.issues = elementIssues | ||
chargeElement.status = status | ||
allElementIssues.push(...elementIssues) | ||
}) | ||
}) | ||
}) | ||
|
||
return allElementIssues | ||
} | ||
|
||
function _determineLicenceStatus (allElementIssues, allReturnIssues) { | ||
const allLicenceIssues = [...allElementIssues, ...allReturnIssues] | ||
|
||
// If a licence has more than one issue, or has 1 issue that is in the `REVIEW_STATUSES` array the licence status is | ||
// set to 'Review' otherwise its 'Ready' | ||
if (allLicenceIssues.length > 1 || REVIEW_STATUSES.includes(allLicenceIssues[0])) { | ||
return 'review' | ||
} else { | ||
return 'ready' | ||
} | ||
} | ||
|
||
function _determineReturnLogsIssues (returnLogs, licence) { | ||
const allReturnsIssues = [] | ||
|
||
returnLogs.forEach((returnLog) => { | ||
const returnLogIssues = _returnLogIssues(returnLog, licence) | ||
|
||
returnLog.issues = returnLogIssues | ||
allReturnsIssues.push(...returnLogIssues) | ||
}) | ||
|
||
return allReturnsIssues | ||
} | ||
|
||
function _determineReturnSplitOverChargeReference (licence, returnLog) { | ||
let chargeReferenceCounter = 0 | ||
const { chargeVersions } = licence | ||
|
||
chargeVersions.forEach((chargeVersion) => { | ||
const { chargeReferences } = chargeVersion | ||
|
||
chargeReferences.forEach((chargeReference) => { | ||
const { chargeElements } = chargeReference | ||
|
||
// We do a .some here as we only care if the returnLog is present in the chargeReference at least once. If the | ||
// return is present we increase our chargeReference counter by 1 to tally up how many unique chargeReference have | ||
// matched to the return | ||
const returnLogInChargeReference = chargeElements.some((chargeElement) => { | ||
const { returnLogs: chargeElementReturnLogs } = chargeElement | ||
|
||
return chargeElementReturnLogs.some((chargeElementReturnLog) => { | ||
return chargeElementReturnLog.returnId === returnLog.id | ||
}) | ||
}) | ||
|
||
if (returnLogInChargeReference) { | ||
chargeReferenceCounter++ | ||
} | ||
}) | ||
}) | ||
|
||
return chargeReferenceCounter > 1 | ||
} | ||
|
||
function _elementIssues (chargeReference, chargeElement, licenceReturnLogs, returnLogs) { | ||
let status = 'ready' | ||
const elementIssues = [] | ||
|
||
// Issue Aggregate factor | ||
if (chargeReference.aggregate !== 1) { | ||
elementIssues.push('Aggregate factor') | ||
status = 'review' | ||
} | ||
|
||
// Issue Overlap of charge dates | ||
if (chargeElement.chargeDatesOverlap) { | ||
elementIssues.push('Overlap of charge dates') | ||
status = 'review' | ||
} | ||
|
||
// Issue Some returns not received | ||
if (_someReturnsNotReceived(returnLogs, licenceReturnLogs)) { | ||
elementIssues.push('Some returns not received') | ||
} | ||
|
||
// Unable to match return | ||
if (returnLogs.length < 1) { | ||
elementIssues.push('Unable to match return') | ||
status = 'review' | ||
} | ||
|
||
return { elementIssues, status } | ||
} | ||
|
||
function _returnLogIssues (returnLog, licence) { | ||
const returnLogIssues = [] | ||
|
||
// Abstraction outside period issue | ||
if (returnLog.abstractionOutsidePeriod) { | ||
returnLogIssues.push('Abstraction outside period') | ||
} | ||
|
||
// Checking query issue | ||
if (returnLog.underQuery) { | ||
returnLogIssues.push('Checking query') | ||
} | ||
|
||
// No returns received | ||
if (returnLog.status === 'due') { | ||
returnLogIssues.push('No returns received') | ||
} | ||
|
||
// Over abstraction | ||
if (returnLog.quantity > returnLog.allocatedQuantity) { | ||
returnLogIssues.push('Over abstraction') | ||
} | ||
|
||
// Returns received but not processed | ||
if (returnLog.status === 'received') { | ||
returnLogIssues.push('Returns received but not processed') | ||
} | ||
|
||
// Returns received late | ||
if (returnLog.receivedDate > returnLog.dueDate) { | ||
returnLogIssues.push('Returns received late') | ||
} | ||
|
||
// Returns split over charge references | ||
if (_determineReturnSplitOverChargeReference(licence, returnLog)) { | ||
returnLogIssues.push('Return split over charge references') | ||
} | ||
|
||
return returnLogIssues | ||
} | ||
|
||
function _someReturnsNotReceived (returnLogs, licenceReturnLogs) { | ||
const returnLogIds = returnLogs.map((returnLog) => { | ||
return returnLog.returnId | ||
}) | ||
|
||
return licenceReturnLogs.some((licenceReturnLog) => { | ||
return returnLogIds.includes(licenceReturnLog.id) && licenceReturnLog.status === 'due' | ||
}) | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.