-
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.
Review Result of 2PT Matching for selected Region (#663)
https://eaflood.atlassian.net/browse/WATER-4188 Create a screen to display the overview of the results of the 2PT matching process. It will be displayed at the end of the matching process, and when a 2PT Bill Run is clicked from the Bill runs page and is in the REVIEW state.
- Loading branch information
Showing
21 changed files
with
1,244 additions
and
17 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
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
77 changes: 77 additions & 0 deletions
77
app/presenters/bill-runs/two-part-tariff/review-bill-run.presenter.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,77 @@ | ||
'use strict' | ||
|
||
/** | ||
* Formats the two part tariff review data ready for presenting in the review page | ||
* @module ReviewBillRunPresenter | ||
*/ | ||
|
||
const { formatLongDate } = require('../../base.presenter.js') | ||
|
||
/** | ||
* Prepares and processes bill run and licence data for presentation | ||
* | ||
* @param {module:BillRunModel} billRun the data from the bill run | ||
* @param {module:LicenceModel} licences the licences data asociated with the bill run | ||
* | ||
* @returns {Object} the prepared bill run and licence data to be passed to the review page | ||
*/ | ||
function go (billRun, licences) { | ||
const { licencesToReviewCount, preparedLicences } = _prepareLicences(licences) | ||
|
||
const preparedBillRun = _prepareBillRun(billRun, preparedLicences, licencesToReviewCount) | ||
|
||
return { ...preparedBillRun, preparedLicences } | ||
} | ||
|
||
function _prepareLicences (licences) { | ||
let licencesToReviewCount = 0 | ||
const preparedLicences = [] | ||
|
||
for (const licence of licences) { | ||
if (licence.status === 'review') { | ||
licencesToReviewCount++ | ||
} | ||
|
||
preparedLicences.push({ | ||
licenceRef: licence.licenceRef, | ||
licenceHolder: licence.licenceHolder, | ||
status: licence.status, | ||
issue: _getIssueOnLicence(licence.issues) | ||
}) | ||
} | ||
|
||
return { preparedLicences, licencesToReviewCount } | ||
} | ||
|
||
function _prepareBillRun (billRun, billRunLicences, licencesToReviewCount) { | ||
return { | ||
region: billRun.region.displayName, | ||
status: billRun.status, | ||
dateCreated: formatLongDate(billRun.createdAt), | ||
financialYear: _financialYear(billRun.toFinancialYearEnding), | ||
billRunType: 'two-part tariff', | ||
numberOfLicences: billRunLicences.length, | ||
licencesToReviewCount | ||
} | ||
} | ||
|
||
function _financialYear (financialYearEnding) { | ||
const startYear = financialYearEnding - 1 | ||
const endYear = financialYearEnding | ||
|
||
return `${startYear} to ${endYear}` | ||
} | ||
|
||
function _getIssueOnLicence (issues) { | ||
if (issues.length > 1) { | ||
return 'Multiple Issues' | ||
} else if (issues.length === 1) { | ||
return issues[0] | ||
} else { | ||
return '' | ||
} | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
206 changes: 206 additions & 0 deletions
206
app/services/bill-runs/two-part-tariff/determine-bill-run-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,206 @@ | ||
'use strict' | ||
|
||
/** | ||
* Determines the issues on the licences for a two-part tariff bill run | ||
* @module DetermineBillRunIssuesService | ||
*/ | ||
|
||
const FetchReviewResultsService = require('./fetch-review-results.service.js') | ||
|
||
// 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' | ||
] | ||
|
||
/** | ||
* Fetches the review data on a licence and determines all the issues and licence status | ||
* | ||
* @param {module:LicenceModel} licences the two-part tariff licence included in the bill run | ||
*/ | ||
async function go (licences) { | ||
for (const licence of licences) { | ||
const licenceReviewResults = await FetchReviewResultsService.go(licence.id) | ||
const { issues, status } = _determineIssues(licenceReviewResults) | ||
|
||
licence.issues = issues | ||
licence.status = status | ||
} | ||
} | ||
|
||
function _abstractionOutsidePeriod (issues, licenceReviewResults) { | ||
const abstractionOutsidePeriod = licenceReviewResults.some((licenceReviewResult) => { | ||
return licenceReviewResult.reviewReturnResults?.abstractionOutsidePeriod | ||
}) | ||
|
||
if (abstractionOutsidePeriod) { | ||
issues.push('Abstraction outside period') | ||
} | ||
} | ||
|
||
function _determineIssues (licenceReviewResults) { | ||
const issues = [] | ||
|
||
_abstractionOutsidePeriod(issues, licenceReviewResults) | ||
|
||
_hasAggregate(issues, licenceReviewResults) | ||
|
||
_underQuery(issues, licenceReviewResults) | ||
|
||
_noReturnsReceived(issues, licenceReviewResults) | ||
|
||
_overAbstracted(issues, licenceReviewResults) | ||
|
||
_hasChargeDatesOverlap(issues, licenceReviewResults) | ||
|
||
_notProcessed(issues, licenceReviewResults) | ||
|
||
_receivedLate(issues, licenceReviewResults) | ||
|
||
_returnsSplitOverChargeReference(issues, licenceReviewResults) | ||
|
||
_returnsNotReceived(issues, licenceReviewResults) | ||
|
||
_matchingReturns(issues, licenceReviewResults) | ||
|
||
const status = _determineIssueStatus(issues) | ||
|
||
return { issues, status } | ||
} | ||
|
||
function _determineIssueStatus (issues) { | ||
const hasReviewIssue = issues.some((issue) => { | ||
return REVIEW_STATUSES.includes(issue) | ||
}) | ||
|
||
if (hasReviewIssue) { | ||
return 'review' | ||
} | ||
|
||
return 'ready' | ||
} | ||
|
||
function _hasAggregate (issues, licenceReviewResults) { | ||
const hasAggregate = licenceReviewResults.some((licenceReviewResult) => { | ||
return licenceReviewResult.reviewChargeElementResults?.aggregate !== 1 | ||
}) | ||
|
||
if (hasAggregate) { | ||
issues.push('Aggregate factor') | ||
} | ||
} | ||
|
||
function _hasChargeDatesOverlap (issues, licenceReviewResults) { | ||
const hasChargeDatesOverlap = licenceReviewResults.some((licenceReviewResult) => { | ||
return licenceReviewResult.reviewChargeElementResults?.chargeDatesOverlap | ||
}) | ||
|
||
if (hasChargeDatesOverlap) { | ||
issues.push('Overlap of charge dates') | ||
} | ||
} | ||
|
||
function _matchingReturns (issues, licenceReviewResults) { | ||
const matchingReturns = licenceReviewResults.some((licenceReviewResult) => { | ||
return !(licenceReviewResult?.reviewChargeElementResultId && licenceReviewResult?.reviewReturnResultId) | ||
}) | ||
|
||
if (matchingReturns) { | ||
issues.push('Unable to match returns') | ||
} | ||
} | ||
|
||
function _notProcessed (issues, licenceReviewResults) { | ||
const notProcessed = licenceReviewResults.some((licenceReviewResult) => { | ||
return licenceReviewResult.reviewReturnResults?.status === 'received' | ||
}) | ||
|
||
if (notProcessed) { | ||
issues.push('Returns received but not processed') | ||
} | ||
} | ||
|
||
function _noReturnsReceived (issues, licenceReviewResults) { | ||
const noReturnsReceived = licenceReviewResults.some((licenceReviewResult) => { | ||
return licenceReviewResult.reviewReturnResults?.status === 'due' || licenceReviewResult.reviewReturnResults?.status === 'overdue' | ||
}) | ||
|
||
if (noReturnsReceived) { | ||
issues.push('No returns received') | ||
} | ||
} | ||
|
||
function _overAbstracted (issues, licenceReviewResults) { | ||
const overAbstracted = licenceReviewResults.some((licenceReviewResult) => { | ||
return licenceReviewResult.reviewReturnResults?.quantity > licenceReviewResult.reviewReturnResults?.allocated | ||
}) | ||
|
||
if (overAbstracted) { | ||
issues.push('Over abstraction') | ||
} | ||
} | ||
|
||
function _receivedLate (issues, licenceReviewResults) { | ||
const receivedLate = licenceReviewResults.some((licenceReviewResult) => { | ||
return licenceReviewResult.reviewReturnResults?.receivedDate > licenceReviewResult.reviewReturnResults?.dueDate | ||
}) | ||
|
||
if (receivedLate) { | ||
issues.push('Returns received late') | ||
} | ||
} | ||
|
||
function _underQuery (issues, licenceReviewResults) { | ||
const underQuery = licenceReviewResults.some((licenceReviewResult) => { | ||
return licenceReviewResult.reviewReturnResults?.underQuery | ||
}) | ||
|
||
if (underQuery) { | ||
issues.push('Checking query') | ||
} | ||
} | ||
|
||
/** | ||
* Checks if any of the licence review results indicate that a charge element has not received its returns | ||
*/ | ||
function _returnsNotReceived (issues, licenceReviewResults) { | ||
const returnsNotReceived = licenceReviewResults.some((licenceReviewResult) => { | ||
return (licenceReviewResult.reviewReturnResultId && | ||
licenceReviewResult.reviewChargeElementResultId && | ||
(licenceReviewResult.reviewReturnResults.status === 'due' || | ||
licenceReviewResult.reviewReturnResults.status === 'overdue') | ||
) | ||
}) | ||
|
||
if (returnsNotReceived) { | ||
issues.push('Some returns not received') | ||
} | ||
} | ||
|
||
/** | ||
* Determines if there are multiple charge references associated with a matched return | ||
*/ | ||
function _returnsSplitOverChargeReference (issues, licenceReviewResults) { | ||
const seenReviewReturnResults = {} | ||
let returnsSplitOverChargeReference | ||
|
||
for (const result of licenceReviewResults) { | ||
const { chargeReferenceId, reviewReturnResultId } = result | ||
|
||
if (seenReviewReturnResults[reviewReturnResultId]) { | ||
if (seenReviewReturnResults[reviewReturnResultId] !== chargeReferenceId) { | ||
returnsSplitOverChargeReference = true | ||
} | ||
} else { | ||
seenReviewReturnResults[reviewReturnResultId] = chargeReferenceId | ||
} | ||
} | ||
|
||
if (returnsSplitOverChargeReference) { | ||
issues.push('Returns split over charge references') | ||
} | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
Oops, something went wrong.