Skip to content

Commit

Permalink
Merge branch 'main' into periods-overlap-helper
Browse files Browse the repository at this point in the history
  • Loading branch information
Beckyrose200 authored Nov 27, 2023
2 parents fc12d95 + 9e83895 commit 9708417
Show file tree
Hide file tree
Showing 9 changed files with 1,344 additions and 357 deletions.
135 changes: 135 additions & 0 deletions app/presenters/bill-runs/view-bill-run.presenter.js
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
}
129 changes: 129 additions & 0 deletions app/services/bill-runs/fetch-bill-run.service.js
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
}
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
}
28 changes: 0 additions & 28 deletions app/views/home.njk

This file was deleted.

11 changes: 0 additions & 11 deletions app/views/includes/back-link.njk

This file was deleted.

Loading

0 comments on commit 9708417

Please sign in to comment.