Skip to content

Commit

Permalink
Determine if licence is 'billed' (#101)
Browse files Browse the repository at this point in the history
https://eaflood.atlassian.net/browse/WATER-3868

We need to know whether a licence has already been ‘billed’ in the current period. If so we’ll have to ensure it’s credited as part of the bill run before we recalculate the debit.

For each licence that is a candidate for billing, we need to determine if it was included in a billing_batch for the current period.
  • Loading branch information
Jozzey authored Jan 31, 2023
1 parent 82c68c8 commit 1a52bf3
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 68 deletions.
12 changes: 1 addition & 11 deletions app/presenters/check/supplementary-data.presenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@

function go (data) {
const licences = data.licences.map((licence) => {
const licenceExistsInBilling = _licenceExistsInBilling(licence.billingInvoiceLicences)

return {
licenceId: licence.licenceId,
licenceRef: licence.licenceRef,
licenceExistsInBilling
licenceExistsInBilling: licence.numberOfTimesBilled > 0
}
})
const chargeVersions = data.chargeVersions
Expand All @@ -24,14 +22,6 @@ function go (data) {
}
}

function _licenceExistsInBilling (billingInvoiceLicences) {
if (billingInvoiceLicences) {
return billingInvoiceLicences.some((billingInvoiceLicence) => billingInvoiceLicence.billingInvoice)
}

return false
}

module.exports = {
go
}
41 changes: 23 additions & 18 deletions app/services/supplementary-billing/fetch-licences.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @module FetchLicencesService
*/

const LicenceModel = require('../../models/water/licence.model.js')
const { db } = require('../../../db/db.js')

/**
* Fetches licences flagged for supplementary billing that are linked to the selected region
Expand All @@ -24,24 +24,29 @@ async function go (region, billingPeriodFinancialYearEnding) {
}

async function _fetch (region, billingPeriodFinancialYearEnding) {
const result = await LicenceModel.query()
.distinctOn('licenceId')
.innerJoinRelated('chargeVersions')
.where('regionId', region.regionId)
.where('includeInSupplementaryBilling', 'yes')
.where('chargeVersions.scheme', 'sroc')
.withGraphFetched('billingInvoiceLicences.billingInvoice')
.modifyGraph('billingInvoiceLicences', builder => {
builder.select(
'billingInvoiceLicenceId'
)
})
.modifyGraph('billingInvoiceLicences.billingInvoice', builder => {
builder.select(
'financialYearEnding'
)
.where('financialYearEnding', billingPeriodFinancialYearEnding)
const result = db
.select('l.licenceId', 'l.licenceRef')
.count('billed.licenceId as numberOfTimesBilled')
.distinctOn('l.licenceId')
.from('water.licences as l')
.leftOuterJoin(
db
.select('bil.licenceId')
.from('water.billingInvoiceLicences as bil')
.innerJoin('water.billingInvoices as bi', 'bil.billingInvoiceId', 'bi.billingInvoiceId')
.innerJoin('water.billingBatches as bb', 'bi.billingBatchId', 'bb.billingBatchId')
.where({
'bi.financialYearEnding': billingPeriodFinancialYearEnding,
'bb.status': 'sent',
'bb.scheme': 'sroc'
}).as('billed'),
'l.licenceId', 'billed.licenceId'
)
.where({
'l.includeInSupplementaryBilling': 'yes',
'l.regionId': region.regionId
})
.groupBy('l.licenceId')

return result
}
Expand Down
22 changes: 2 additions & 20 deletions test/presenters/check/supplementary-data.presenter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,12 @@ describe('Supplementary presenter', () => {
{
licenceId: 'f1288f6c-8503-4dc1-b114-75c408a14bd0',
licenceRef: 'AT/SROC/SUPB/01',
billingInvoiceLicences: [
{
billingInvoiceLicenceId: '585135da-0879-400a-b329-4d94b214ca66',
billingInvoice: null
},
{
billingInvoiceLicenceId: '585135da-0879-400a-b329-4d94b214ca66',
billingInvoice: { financialYearEnding: 2023 }
}
]
numberOfTimesBilled: 1
},
{
licenceId: '81b50b35-459a-43f0-a48a-262028a34493',
licenceRef: 'AT/SROC/SUPB/02',
billingInvoiceLicences: [
{
billingInvoiceLicenceId: 'f2a4689f-1c6f-4388-8a42-c1daddcc7f2f',
billingInvoice: null
},
{
billingInvoiceLicenceId: '5846052d-2acf-43c3-8c5a-353debb1b8ed',
billingInvoice: null
}
]
numberOfTimesBilled: 0
}
],
chargeVersions: [
Expand Down
79 changes: 61 additions & 18 deletions test/services/supplementary-billing/fetch-licences.service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const { expect } = Code
// Test helpers
const BillingInvoiceHelper = require('../../support/helpers/water/billing-invoice.helper.js')
const BillingInvoiceLicenceHelper = require('../../support/helpers/water/billing-invoice-licence.helper.js')
const ChargeVersionHelper = require('../../support/helpers/water/charge-version.helper.js')
const DatabaseHelper = require('../../support/helpers/database.helper.js')
const LicenceHelper = require('../../support/helpers/water/licence.helper.js')

Expand All @@ -33,23 +32,38 @@ describe('Fetch Licences service', () => {
testLicence = await LicenceHelper.add({ includeInSupplementaryBilling: 'yes' })
})

describe('and that have an SROC charge version. Licence not previosly billed', () => {
describe('and that have not been previously billed', () => {
it('returns the expected results', async () => {
const result = await FetchLicencesService.go(region, billingPeriodFinancialYearEnding)

expect(result.length).to.equal(1)
expect(result[0].licenceId).to.equal(testLicence.licenceId)
expect(result[0].licenceRef).to.equal(testLicence.licenceRef)
expect(result[0].numberOfTimesBilled).to.equal(0)
})
})

describe('and that have been billed in the current period', () => {
beforeEach(async () => {
await ChargeVersionHelper.add({}, testLicence)
billingInvoice = await BillingInvoiceHelper.add(
{ financialYearEnding: billingPeriodFinancialYearEnding },
{ status: 'sent' }
)
await BillingInvoiceLicenceHelper.add({}, testLicence, billingInvoice)
})

it('returns results', async () => {
it('returns the expected results', async () => {
const result = await FetchLicencesService.go(region, billingPeriodFinancialYearEnding)

expect(result.length).to.equal(1)
expect(result[0].licenceId).to.equal(testLicence.licenceId)
expect(result[0].billingInvoiceLicences).to.equal([])
expect(result[0].licenceRef).to.equal(testLicence.licenceRef)
expect(result[0].numberOfTimesBilled).to.equal(1)
})
})

describe('and that have an SROC charge version. Licence previosly billed in current period 2023', () => {
describe("and that are included in an 'unsent' billing batch in the current period", () => {
beforeEach(async () => {
await ChargeVersionHelper.add({}, testLicence)
billingInvoice = await BillingInvoiceHelper.add({ financialYearEnding: billingPeriodFinancialYearEnding })
await BillingInvoiceLicenceHelper.add({}, testLicence, billingInvoice)
})
Expand All @@ -59,14 +73,17 @@ describe('Fetch Licences service', () => {

expect(result.length).to.equal(1)
expect(result[0].licenceId).to.equal(testLicence.licenceId)
expect(result[0].billingInvoiceLicences[0].billingInvoice.financialYearEnding).to.equal(billingPeriodFinancialYearEnding)
expect(result[0].licenceRef).to.equal(testLicence.licenceRef)
expect(result[0].numberOfTimesBilled).to.equal(0)
})
})

describe('and that have an SROC charge version. Licence previosly billed in previous period 2022', () => {
describe('and that have been billed in the previous period', () => {
beforeEach(async () => {
await ChargeVersionHelper.add({}, testLicence)
billingInvoice = await BillingInvoiceHelper.add({ financialYearEnding: 2022 })
billingInvoice = await BillingInvoiceHelper.add(
{ financialYearEnding: 2022 },
{ status: 'sent' }
)
await BillingInvoiceLicenceHelper.add({}, testLicence, billingInvoice)
})

Expand All @@ -75,29 +92,52 @@ describe('Fetch Licences service', () => {

expect(result.length).to.equal(1)
expect(result[0].licenceId).to.equal(testLicence.licenceId)
expect(result[0].billingInvoiceLicences[0].billingInvoice).to.equal(null)
expect(result[0].licenceRef).to.equal(testLicence.licenceRef)
expect(result[0].numberOfTimesBilled).to.equal(0)
})
})

describe('and that have multiple SROC charge versions', () => {
describe('and that have been billed twice in the current period', () => {
beforeEach(async () => {
await ChargeVersionHelper.add({}, testLicence)
await ChargeVersionHelper.add({}, testLicence)
billingInvoice = await BillingInvoiceHelper.add(
{ financialYearEnding: billingPeriodFinancialYearEnding },
{ status: 'sent' }
)
await BillingInvoiceLicenceHelper.add({}, testLicence, billingInvoice)
billingInvoice = await BillingInvoiceHelper.add(
{ financialYearEnding: billingPeriodFinancialYearEnding },
{ status: 'sent' }
)
await BillingInvoiceLicenceHelper.add({}, testLicence, billingInvoice)
})

it('returns a licence only once in the results', async () => {
const result = await FetchLicencesService.go(region, billingPeriodFinancialYearEnding)

expect(result.length).to.equal(1)
expect(result[0].licenceId).to.equal(testLicence.licenceId)
expect(result[0].licenceRef).to.equal(testLicence.licenceRef)
expect(result[0].numberOfTimesBilled).to.equal(2)
})
})

describe('but do not have an SROC charge version', () => {
// NOTE: This situation will not occur normally. But as the billing batch is filtered on `scheme` we wanted a test to ensure the filter worked
describe('and that have been billed in the current period under a different scheme', () => {
beforeEach(async () => {
billingInvoice = await BillingInvoiceHelper.add(
{ financialYearEnding: billingPeriodFinancialYearEnding },
{ status: 'sent', scheme: 'alcs' }
)
await BillingInvoiceLicenceHelper.add({}, testLicence, billingInvoice)
})

it('returns no results', async () => {
const result = await FetchLicencesService.go(region, billingPeriodFinancialYearEnding)

expect(result.length).to.equal(0)
expect(result.length).to.equal(1)
expect(result[0].licenceId).to.equal(testLicence.licenceId)
expect(result[0].licenceRef).to.equal(testLicence.licenceRef)
expect(result[0].numberOfTimesBilled).to.equal(0)
})
})
})
Expand All @@ -117,7 +157,10 @@ describe('Fetch Licences service', () => {

describe('when there are no licences for the matching region', () => {
beforeEach(async () => {
await LicenceHelper.add({ regionId: '000446bd-182a-4340-be6b-d719855ace1a' })
await LicenceHelper.add({
regionId: '000446bd-182a-4340-be6b-d719855ace1a',
includeInSupplementaryBilling: 'yes'
})
})

it('returns no results', async () => {
Expand Down
9 changes: 8 additions & 1 deletion test/support/helpers/water/billing-invoice.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const BillingBatchHelper = require('./billing-batch.helper.js')
* Add a new billing invoice
*
* A billing invoice is always linked to a billing batch. So, creating a billing invoice will automatically
* create a new billing batch and handle linking the two together by `billingBatchId`.
* create a new billing batch and handle linking the two together by `billingBatchId`. If a `financialYearEnding` has
* been provided for the billing invoice, but not for the billing batch. The billing batch will use the 'financialYearEnding'
* from the billing invoice to populate it's `fromFinancialYearEnding` & `toFinancialYearEnding` items.
*
* If no `data` is provided, default values will be used. These are
*
Expand All @@ -25,6 +27,11 @@ const BillingBatchHelper = require('./billing-batch.helper.js')
* @returns {module:BillingInvoiceModel} The instance of the newly created record
*/
async function add (data = {}, billingBatch = {}) {
if (data.financialYearEnding && !billingBatch.fromFinancialYearEnding) {
billingBatch.fromFinancialYearEnding = data.financialYearEnding
billingBatch.toFinancialYearEnding = data.financialYearEnding
}

const billingBatchId = await _billingBatchId(billingBatch)

const insertData = defaults({ ...data, billingBatchId })
Expand Down

0 comments on commit 1a52bf3

Please sign in to comment.