-
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.
Merge branch 'main' into periods-overlap-helper
- Loading branch information
Showing
9 changed files
with
1,344 additions
and
357 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
'use strict' | ||
|
||
/** | ||
* Formats bill run data ready for presenting in the view bill run page | ||
* @module ViewBillRunPresenter | ||
*/ | ||
|
||
const { | ||
capitalize, | ||
formatLongDate, | ||
formatMoney | ||
} = require('../base.presenter.js') | ||
|
||
function go (billRun) { | ||
const { | ||
batchType, | ||
billingBatchId, | ||
billRunNumber, | ||
createdAt, | ||
creditNoteCount, | ||
creditNoteValue, | ||
invoiceCount, | ||
invoiceValue, | ||
isSummer, | ||
netTotal, | ||
region, | ||
scheme, | ||
status, | ||
transactionFileReference, | ||
toFinancialYearEnding | ||
} = billRun | ||
|
||
const billRunType = _billRunType(batchType, isSummer, scheme) | ||
const regionName = capitalize(region.displayName) | ||
|
||
return { | ||
billsCount: _billsCount(creditNoteCount, invoiceCount, billRunType), | ||
billRunId: billingBatchId, | ||
billRunNumber, | ||
billRunStatus: status, | ||
billRunTotal: _billRunTotal(netTotal), | ||
billRunType, | ||
chargeScheme: _chargeScheme(scheme), | ||
creditsCount: _creditsCount(creditNoteCount), | ||
creditsTotal: formatMoney(creditNoteValue), | ||
dateCreated: formatLongDate(createdAt), | ||
debitsCount: _debitsCount(invoiceCount), | ||
debitsTotal: formatMoney(invoiceValue), | ||
displayCreditDebitTotals: _displayCreditDebitTotals(billRun), | ||
financialYear: _financialYear(toFinancialYearEnding), | ||
pageTitle: _pageTitle(regionName, billRunType), | ||
region: regionName, | ||
transactionFile: transactionFileReference | ||
} | ||
} | ||
|
||
function _billsCount (creditsCount, debitsCount, billRunType) { | ||
const total = creditsCount + debitsCount | ||
|
||
if (total === 1) { | ||
return `1 ${billRunType} bill` | ||
} | ||
|
||
return `${total} ${billRunType} bills` | ||
} | ||
|
||
function _billRunTotal (valueInPence) { | ||
const valueAsMoney = formatMoney(valueInPence) | ||
|
||
if (valueInPence < 0) { | ||
return `${valueAsMoney} credit` | ||
} | ||
|
||
return valueAsMoney | ||
} | ||
|
||
function _billRunType (batchType, isSummer, scheme) { | ||
if (batchType !== 'two_part_tariff') { | ||
return capitalize(batchType) | ||
} | ||
|
||
if (scheme === 'sroc') { | ||
return 'Two-part tariff' | ||
} | ||
|
||
if (isSummer) { | ||
return 'Two-part tariff summer' | ||
} | ||
|
||
return 'Two-part tariff winter and all year' | ||
} | ||
|
||
function _chargeScheme (scheme) { | ||
if (scheme === 'sroc') { | ||
return 'Current' | ||
} | ||
|
||
return 'Old' | ||
} | ||
|
||
function _creditsCount (count) { | ||
if (count === 1) { | ||
return '1 credit note' | ||
} | ||
|
||
return `${count} credit notes` | ||
} | ||
|
||
function _debitsCount (count) { | ||
if (count === 1) { | ||
return '1 invoice' | ||
} | ||
|
||
return `${count} invoices` | ||
} | ||
|
||
function _displayCreditDebitTotals (billRun) { | ||
const { batchType } = billRun | ||
|
||
return batchType === 'supplementary' | ||
} | ||
|
||
function _financialYear (financialYearEnding) { | ||
return `${financialYearEnding - 1} to ${financialYearEnding}` | ||
} | ||
|
||
function _pageTitle (regionName, billRunType) { | ||
const lowercaseBillRunType = billRunType.toLowerCase() | ||
|
||
return `${regionName} ${lowercaseBillRunType}` | ||
} | ||
|
||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
'use strict' | ||
|
||
/** | ||
* Fetches data needed for the bill run page which includes a summary for each bill linked to the bill run | ||
* @module FetchBillRunService | ||
*/ | ||
|
||
const BillRunModel = require('../../models/water/bill-run.model.js') | ||
const { db } = require('../../../db/db.js') | ||
|
||
/** | ||
* Fetch the matching Bill Run plus a summary for each bill linked to it | ||
* | ||
* Was built to provide the data needed for the '/bill-runs/{id}' page | ||
* | ||
* @param {string} id The UUID for the bill run to fetch | ||
* | ||
* @returns {Object} the matching instance of BillRunModel plus a summary (Billing account number and contact, licence, | ||
* numbers, financial year and total net amount) for each bill linked to the bill run | ||
*/ | ||
async function go (id) { | ||
const billRun = await _fetchBillRun(id) | ||
const billSummaries = await _fetchBillSummaries(id) | ||
|
||
return { | ||
billRun, | ||
billSummaries | ||
} | ||
} | ||
|
||
async function _fetchBillRun (id) { | ||
const result = BillRunModel.query() | ||
.findById(id) | ||
.select([ | ||
'billingBatchId', | ||
'batchType', | ||
'billRunNumber', | ||
'creditNoteCount', | ||
'creditNoteValue', | ||
'dateCreated', | ||
'invoiceCount', | ||
'invoiceValue', | ||
'isSummer', | ||
'netTotal', | ||
'scheme', | ||
'source', | ||
'status', | ||
'toFinancialYearEnding', | ||
'transactionFileReference' | ||
]) | ||
.withGraphFetched('region') | ||
.modifyGraph('region', (builder) => { | ||
builder.select([ | ||
'regionId', | ||
'displayName' | ||
]) | ||
}) | ||
|
||
return result | ||
} | ||
|
||
/** | ||
* Query the DB and generate a summary for each bill linked to the bill run | ||
* | ||
* On the page we need to show the following in a table for every bill linked to the bill run | ||
* | ||
* - billing account number | ||
* - billing contact | ||
* - a list of licences references included in the bill | ||
* - financial year (displayed in supplementary bill runs) | ||
* - total net amount for the bill | ||
* | ||
* We also need to work out if any of the licences linked to a bill are flagged as 'water undertaker'. Historically, | ||
* annual bill runs display the bills grouped by 'Water companies' and 'Other abstractors'. So, if a bill is linked to | ||
* a licence that is flagged as a water undertaker that bill needs to be shown under 'Water companies'. | ||
* | ||
* Because we have to dip into a different schema to get billing account information Objection.js not available to us. | ||
* So, immediately we are required to use Knex. But then we have 2 sub-queries to perform | ||
* | ||
* - **get a list of linked licences as a single result** - Because a bill can be linked to 1 or more licences we can | ||
* not use a JOIN. So, instead we use a function built into PostgreSQL that allows you to aggregate multiple results | ||
* into a single value. (We can easily split them out again using JavaScripts `split()` method) | ||
* - **flag the bill as a water company** - Again we have to avoid joining to the `licences` table via | ||
* `billing_invoice_licences` as we'll get duplicate results. So, again we use a PostgreSQL function that will return | ||
* true or false based on the query provided to it. | ||
* | ||
* We could have made things simpler by performing separate queries and then transforming all the results into what we | ||
* need this service to return. But we opted to go for performance this time, avoiding multiple requests to the DB and | ||
* getting the query to provide the data we need without having to transform the result. | ||
*/ | ||
async function _fetchBillSummaries (id) { | ||
const results = await db | ||
.select( | ||
'bi.billing_invoice_id', | ||
'bi.invoice_account_id', | ||
'bi.invoice_account_number', | ||
'bi.net_amount', | ||
'bi.financial_year_ending', | ||
db.raw('(c."name") AS company_name'), | ||
db.raw('(ac."name") AS agent_name'), | ||
db.raw( | ||
"(SELECT string_agg(bil.licence_ref, ',' ORDER BY bil.licence_ref) FROM water.billing_invoice_licences bil WHERE bil.billing_invoice_id = bi.billing_invoice_id GROUP BY bil.billing_invoice_id) AS all_licences" | ||
), | ||
db.raw( | ||
'(EXISTS(SELECT 1 FROM water.licences l WHERE l.is_water_undertaker AND l.licence_id IN (SELECT bil2.licence_id FROM water.billing_invoice_licences bil2 WHERE bil2.billing_invoice_id = bi.billing_invoice_id))) AS water_company' | ||
) | ||
) | ||
.from('water.billing_invoices AS bi') | ||
.innerJoin('crm_v2.invoice_accounts AS ia', 'ia.invoice_account_id', 'bi.invoice_account_id') | ||
.innerJoin('crm_v2.companies AS c', 'c.company_id', 'ia.company_id') | ||
.innerJoin( | ||
'crm_v2.invoice_account_addresses AS iaa', | ||
function () { | ||
this.on('iaa.invoice_account_id', '=', 'ia.invoice_account_id').andOnNull('iaa.end_date') | ||
} | ||
) | ||
.leftJoin('crm_v2.companies AS ac', 'ac.company_id', 'iaa.agent_company_id') | ||
.where('bi.billing_batch_id', '=', id) | ||
.orderBy([ | ||
{ column: 'bi.invoice_account_number' }, | ||
{ column: 'bi.financial_year_ending', order: 'desc' } | ||
]) | ||
|
||
return results | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
78 changes: 78 additions & 0 deletions
78
app/services/bill-runs/two-part-tariff/fetch-returns-for-licence.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,78 @@ | ||
'use strict' | ||
|
||
/** | ||
* Fetches SROC Returns linked to licences flagged for inclusion in next SROC 2PT billing | ||
* @module FetchReturnsForLicenceService | ||
*/ | ||
|
||
const { ref } = require('objection') | ||
|
||
const ReturnModel = require('../../../models/returns/return.model') | ||
|
||
/** | ||
* Fetch all SROC returns to be processed as part of two-part-tariff billing | ||
* * | ||
* @param {String} licenceRef The reference of the licence that the return relates to | ||
* @param {Object} billingPeriod Object with a `startDate` and `endDate` property representing the period being billed | ||
* | ||
* @returns {Object} Contains an array of Returns and the associated current Version, and Lines if they exist | ||
*/ | ||
async function go (licenceRef, billingPeriod) { | ||
return _fetch(licenceRef, billingPeriod) | ||
} | ||
|
||
async function _fetch (licenceRef, billingPeriod) { | ||
const returns = await ReturnModel.query() | ||
.select([ | ||
'returnId', | ||
'returnRequirement', | ||
ref('metadata:description').castText().as('description'), | ||
'startDate', | ||
'endDate', | ||
'receivedDate', | ||
'dueDate', | ||
'status', | ||
'underQuery', | ||
ref('metadata:nald.periodStartDay').castInt().as('periodStartDay'), | ||
ref('metadata:nald.periodStartMonth').castInt().as('periodStartMonth'), | ||
ref('metadata:nald.periodEndDay').castInt().as('periodEndDay'), | ||
ref('metadata:nald.periodEndMonth').castInt().as('periodEndMonth'), | ||
ref('metadata:purposes').as('purposes') | ||
]) | ||
.where('licenceRef', licenceRef) | ||
// water-abstraction-service filters out old returns in this way: see `src/lib/services/returns/api-connector.js` | ||
.where('startDate', '>=', '2008-04-01') | ||
.where('startDate', '<=', billingPeriod.endDate) | ||
.where('endDate', '>=', billingPeriod.startDate) | ||
.whereJsonPath('metadata', '$.isTwoPartTariff', '=', true) | ||
.orderBy('startDate', 'ASC') | ||
.orderBy('returnRequirement', 'ASC') | ||
.withGraphFetched('versions') | ||
.modifyGraph('versions', builder => { | ||
builder | ||
.select([ | ||
'versionId', | ||
'nilReturn' | ||
]) | ||
.where('versions.current', true) | ||
}) | ||
.withGraphFetched('versions.lines') | ||
.modifyGraph('versions.lines', builder => { | ||
builder | ||
.select([ | ||
'lineId', | ||
'startDate', | ||
'endDate', | ||
'quantity' | ||
]) | ||
.where('lines.quantity', '>', 0) | ||
.where('lines.startDate', '<=', billingPeriod.endDate) | ||
.where('lines.endDate', '>=', billingPeriod.startDate) | ||
}) | ||
|
||
return returns | ||
} | ||
|
||
module.exports = { | ||
go | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.