Skip to content

Commit 7967091

Browse files
authored
Move reusable supplementary billing services (#1760)
https://eaflood.atlassian.net/browse/WATER-4201 As we write this description, the long outstanding annual SROC two-part tariff bill runs are being 'sent' in production. The final part of two-part tariff is to add support for supplementary billing; when changes are made to a licence or the returns previously submitted and billed, we need to generate supplementary two-part tariff bill runs. We've already made several changes to support it, off the back of a [spike](#1412). That spike has shown us there are existing billing services, especially in supplementary billing, that we can reuse for two-part supplementary. This change moves them to make their reuse more transparent, while tweaking a few things to allow reuse.
1 parent 6f11e96 commit 7967091

14 files changed

+267
-226
lines changed

app/lib/general.lib.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,15 @@ function timestampForPostgres() {
200200
/**
201201
* Compare key properties of 2 transactions and determine if they are a 'match'
202202
*
203-
* We compare those properties which determine the charge value calculated by the charging module. If the properties
204-
* are the same we return true. Else we return false.
203+
* We compare those properties which determine the charge value calculated by the charging module. If the properties are
204+
* the same we return true. Else we return false.
205205
*
206-
* This is used in the billing engines to determine 2 transactions within the same bill, often a debit and a credit,
207-
* and whether they match. If they do we don't send either to the charge module or include them in the bill as they
208-
* 'cancel' each other out.
206+
* This is used in the billing engines to determine 2 transactions within the same bill, often a debit and a credit, and
207+
* whether they match. If they do we don't send either to the charge module or include them in the bill as they 'cancel'
208+
* each other out.
209209
*
210-
* The key properties are charge type, category code, and billable days. But we also need to compare agreements and
211-
* additional charges because if those have changed, we need to credit the previous transaction and calculate the
210+
* The key properties are charge type, category code, billable days, and volume. But we also need to compare agreements
211+
* and additional charges because if those have changed, we need to credit the previous transaction and calculate the
212212
* new debit value.
213213
*
214214
* Because what we are checking does not match up to what you see in the UI we have this reference
@@ -242,6 +242,7 @@ function transactionsMatch(left, right) {
242242
left.chargeType === right.chargeType &&
243243
left.chargeCategoryCode === right.chargeCategoryCode &&
244244
left.billableDays === right.billableDays &&
245+
left.volume === right.volume &&
245246
left.section126Factor === right.section126Factor &&
246247
left.section127Agreement === right.section127Agreement &&
247248
left.section130Agreement === right.section130Agreement &&

app/services/bill-runs/supplementary/fetch-previous-transactions.service.js renamed to app/services/bill-runs/fetch-previous-transactions.service.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
* @module FetchPreviousTransactionsService
66
*/
77

8-
const { db } = require('../../../../db/db.js')
9-
const { transactionsMatch } = require('../../../lib/general.lib.js')
10-
const TransactionModel = require('../../../models/transaction.model.js')
8+
const { db } = require('../../../db/db.js')
9+
const { transactionsMatch } = require('../../lib/general.lib.js')
10+
const TransactionModel = require('../../models/transaction.model.js')
1111

1212
/**
1313
* Fetches the previously billed transactions that match, removing any debits which cancelled out by previous credits

app/services/bill-runs/two-part-tariff/fetch-billing-accounts.service.js renamed to app/services/bill-runs/fetch-two-part-tariff-billing-accounts.service.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
/**
44
* Fetches all billing accounts linked to a bill run to be processed as part of two-part tariff billing
5-
* @module FetchBillingAccountsService
5+
* @module FetchTwoPartTariffBillingAccountsService
66
*/
77

88
const { ref } = require('objection')
99

10-
const BillingAccountModel = require('../../../models/billing-account.model.js')
11-
const ChargeVersionModel = require('../../../models/charge-version.model.js')
10+
const BillingAccountModel = require('../../models/billing-account.model.js')
11+
const ChargeVersionModel = require('../../models/charge-version.model.js')
1212

1313
/**
1414
* Fetches all billing accounts linked to a bill run to be processed as part of two-part tariff billing

app/services/bill-runs/supplementary/process-transactions.service.js renamed to app/services/bill-runs/process-supplementary-transactions.service.js

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,56 @@
11
'use strict'
22

33
/**
4-
* Fetches the matching debit transactions from a previous bill run and reverses them as credits
5-
* @module ProcessTransactionsService
4+
* Fetches matching debit transactions from previous bill runs, then compares them as credits to those just generated
5+
* @module ProcessSupplementaryTransactionsService
66
*/
77

88
const FetchPreviousTransactionsService = require('./fetch-previous-transactions.service.js')
9-
const { transactionsMatch } = require('../../../lib/general.lib.js')
10-
const ReverseTransactionsService = require('./reverse-transactions.service.js')
9+
const { transactionsMatch } = require('../../lib/general.lib.js')
10+
const ReverseTransactionsService = require('./reverse-supplementary-transactions.service.js')
1111

1212
/**
13-
* Fetches debit-only transactions from the previous bill run for the billing account and licence provided and reverses
14-
* them as credits.
13+
* Fetches matching debit transactions from previous bill runs, then compares them as credits to those just generated
1514
*
16-
* These credits are compared with the supplied calculated debit transactions (ie. debit transactions which are to be
17-
* sent to the Charging Module) and any matching pairs of transactions which would cancel each other out are removed.
18-
* Any remaining reversed credits and calculated debits are returned.
15+
* The `FetchPreviousTransactionsService` finds _all_ previous debit and credit transactions. It then compares the
16+
* debits to the credits. If they match, the debit transaction is dropped. This is because it must have been dealt with
17+
* by a previous supplementary bill run.
1918
*
20-
* @param {object[]} calculatedTransactions - The calculated transactions to be processed
21-
* @param {string} billingAccountId - The UUID that identifies the billing account we are processing transactions for
22-
* @param {object} billLicence - A generated bill licence that identifies the licence we need to match against
23-
* @param {object} billingPeriod - Object with a `startDate` and `endDate` property representing the period being billed
19+
* Any debits that remain are then reversed as credits. These are then compared against the ones we've generated for the
20+
* bill run in progress.
2421
*
25-
* @returns {Promise<object[]>} An array of the remaining calculated transactions (ie. those which were not cancelled
26-
* out by a previous matching credit)
22+
* If any matches occur here, both transactions are dropped. There is no point in sending them to the Charging Module
23+
* API and including them in the bill, as the result will just be £0, and we've been asked to limit zero-value bills
24+
* where we can.
25+
*
26+
* Any generated transactions and reversed debits (now credits) that didn't match are returned as the transactions to be
27+
* sent to the Charging Module API, and included in the bill.
28+
*
29+
* @param {object[]} generatedTransactions - The generated transactions for the bill licence being processed
30+
* @param {string} billLicenceId - The UUID that will be used for the bill licence we are processing transactions for
31+
* @param {string} billingAccountId - The UUID for the billing account we are processing transactions for and for which
32+
* we need to fetch previous transactions
33+
* @param {string} licenceId - The UUID for the licence we are processing transactions for and and for which we need to
34+
* fetch previous transactions
35+
* @param {number} financialYearEnding - Which financial year to look for previous transactions in
36+
*
37+
* @returns {Promise<object[]>} An array of the remaining generated transactions and reversed debits from previous
38+
* transactions (ie. those which were not cancelled out when the generated and reversed were compared)
2739
*/
28-
async function go(calculatedTransactions, billingAccountId, billLicence, billingPeriod) {
29-
const { id: billLicenceId, licenceId } = billLicence
30-
const previousTransactions = await _fetchPreviousTransactions(billingAccountId, licenceId, billingPeriod)
40+
async function go(generatedTransactions, billLicenceId, billingAccountId, licenceId, financialYearEnding) {
41+
const previousTransactions = await FetchPreviousTransactionsService.go(
42+
billingAccountId,
43+
licenceId,
44+
financialYearEnding
45+
)
3146

3247
if (previousTransactions.length === 0) {
33-
return calculatedTransactions
48+
return generatedTransactions
3449
}
3550

3651
const reversedTransactions = ReverseTransactionsService.go(previousTransactions, billLicenceId)
3752

38-
return _cleanseTransactions(calculatedTransactions, reversedTransactions)
53+
return _cleanseTransactions(generatedTransactions, reversedTransactions)
3954
}
4055

4156
/**
@@ -91,14 +106,6 @@ function _cleanseTransactions(calculatedTransactions, reverseTransactions) {
91106
return cleansedTransactionLines
92107
}
93108

94-
async function _fetchPreviousTransactions(billingAccountId, licenceId, billingPeriod) {
95-
const financialYearEnding = billingPeriod.endDate.getFullYear()
96-
97-
const transactions = await FetchPreviousTransactionsService.go(billingAccountId, licenceId, financialYearEnding)
98-
99-
return transactions
100-
}
101-
102109
module.exports = {
103110
go
104111
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict'
2+
3+
/**
4+
* Takes previously billed transactions and returns reversed and cleansed versions of them for supplementary billing
5+
* @module ReverseSupplementaryTransactionsService
6+
*/
7+
8+
const { generateUUID } = require('../../lib/general.lib.js')
9+
10+
/**
11+
* Takes previously billed transactions and returns reversed and cleansed versions of them for supplementary billing
12+
*
13+
* In supplementary we need to "reverse" transactions. We start by identifying previous debit transactions that have not
14+
* been cancelled out by a previous credit transaction.
15+
*
16+
* We then "reverse" these previous debits as credits and compare them to the transactions generated as part of the
17+
* bill run currently in progress.
18+
*
19+
* If the "reversed" transaction matches the generated transaction, we know including both will just result in £0.
20+
* If like the legacy service we included them, it could lead to a zero value bill which our users hate! So, we drop\
21+
* both from the bill run.
22+
*
23+
* Those reversed and generated transactions that don't match, are what gets included and eventually billed.
24+
*
25+
* This service takes the previous debit transactions and a bill licence ID, and returns a copy of them, but with
26+
*
27+
* - `credit` set to `true`
28+
* - `status` set to `candidate`
29+
* - `billLicenceId` set to the supplied ID
30+
* - `id` set to a new UUID
31+
*
32+
* @param {module:TransactionModel[]} transactions - The transactions to be reversed
33+
* @param {string} billLicenceId - The bill licence UUID these transactions are to be added to
34+
*
35+
* @returns {object[]} The "reversed" transactions
36+
*/
37+
function go(transactions, billLicenceId) {
38+
return transactions.map((transaction) => {
39+
return {
40+
...transaction,
41+
id: generateUUID(),
42+
billLicenceId,
43+
credit: true,
44+
status: 'candidate'
45+
}
46+
})
47+
}
48+
49+
module.exports = {
50+
go
51+
}

app/services/bill-runs/supplementary/process-billing-period.service.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const DetermineChargePeriodService = require('../determine-charge-period.service
1313
const DetermineMinimumChargeService = require('../determine-minimum-charge.service.js')
1414
const GenerateTransactionsService = require('../generate-transactions.service.js')
1515
const PreGenerateBillingDataService = require('./pre-generate-billing-data.service.js')
16-
const ProcessTransactionsService = require('./process-transactions.service.js')
16+
const ProcessSupplementaryTransactionsService = require('../process-supplementary-transactions.service.js')
1717
const SendTransactionsService = require('../send-transactions.service.js')
1818
const TransactionModel = require('../../../models/transaction.model.js')
1919

@@ -179,11 +179,12 @@ async function _cleanseTransactions(currentBillingData, billingPeriod) {
179179
return []
180180
}
181181

182-
const cleansedTransactions = await ProcessTransactionsService.go(
182+
const cleansedTransactions = await ProcessSupplementaryTransactionsService.go(
183183
currentBillingData.calculatedTransactions,
184+
currentBillingData.billLicence.id,
184185
currentBillingData.bill.billingAccountId,
185-
currentBillingData.billLicence,
186-
billingPeriod
186+
currentBillingData.billLicence.licenceId,
187+
billingPeriod.endDate.getFullYear()
187188
)
188189

189190
return cleansedTransactions

app/services/bill-runs/supplementary/reverse-transactions.service.js

Lines changed: 0 additions & 47 deletions
This file was deleted.

app/services/bill-runs/two-part-tariff/generate-bill-run.service.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const {
1414
currentTimeInNanoseconds,
1515
timestampForPostgres
1616
} = require('../../../lib/general.lib.js')
17-
const FetchBillingAccountsService = require('./fetch-billing-accounts.service.js')
17+
const FetchTwoPartTariffBillingAccountsService = require('../fetch-two-part-tariff-billing-accounts.service.js')
1818
const HandleErroredBillRunService = require('../handle-errored-bill-run.service.js')
1919
const LegacyRefreshBillRunRequest = require('../../../requests/legacy/refresh-bill-run.request.js')
2020
const ProcessBillingPeriodService = require('./process-billing-period.service.js')
@@ -77,7 +77,7 @@ function _billingPeriod(billRun) {
7777

7878
async function _fetchBillingAccounts(billRunId) {
7979
try {
80-
return await FetchBillingAccountsService.go(billRunId)
80+
return await FetchTwoPartTariffBillingAccountsService.go(billRunId)
8181
} catch (error) {
8282
// We know we're saying we failed to process charge versions. But we're stuck with the legacy error codes and this
8383
// is the closest one related to what stage we're at in the process

test/lib/general.lib.test.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ describe('GeneralLib', () => {
332332
let rightTransaction
333333

334334
beforeEach(() => {
335-
// NOTE: The properties the function is comparing are; chargeType, chargeCategoryCode, billableDays,
335+
// NOTE: The properties the function is comparing are; chargeType, chargeCategoryCode, billableDays, volume,
336336
// section126Factor, section127Agreement, section130Agreement, aggregateFactor, adjustmentFactor, winterOnly,
337337
// supportedSource, supportedSourceName, waterCompanyCharge.
338338
//
@@ -398,6 +398,18 @@ describe('GeneralLib', () => {
398398
})
399399
})
400400

401+
describe('because the volume is different', () => {
402+
beforeEach(() => {
403+
rightTransaction.volume = 10
404+
})
405+
406+
it('returns false', () => {
407+
const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction)
408+
409+
expect(result).to.be.false()
410+
})
411+
})
412+
401413
describe('because the canal and river trust agreement (section 130) is different', () => {
402414
beforeEach(() => {
403415
rightTransaction.section130Agreement = true

test/services/bill-runs/supplementary/fetch-previous-transactions.service.test.js renamed to test/services/bill-runs/fetch-previous-transactions.service.test.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ const { describe, it, beforeEach } = (exports.lab = Lab.script())
88
const { expect } = Code
99

1010
// Test helpers
11-
const BillHelper = require('../../../support/helpers/bill.helper.js')
12-
const BillingAccountHelper = require('../../../support/helpers/billing-account.helper.js')
13-
const BillLicenceHelper = require('../../../support/helpers/bill-licence.helper.js')
14-
const BillRunHelper = require('../../../support/helpers/bill-run.helper.js')
15-
const { generateUUID } = require('../../../../app/lib/general.lib.js')
16-
const LicenceHelper = require('../../../support/helpers/licence.helper.js')
17-
const TransactionHelper = require('../../../support/helpers/transaction.helper.js')
11+
const BillHelper = require('../../support/helpers/bill.helper.js')
12+
const BillingAccountHelper = require('../../support/helpers/billing-account.helper.js')
13+
const BillLicenceHelper = require('../../support/helpers/bill-licence.helper.js')
14+
const BillRunHelper = require('../../support/helpers/bill-run.helper.js')
15+
const { generateUUID } = require('../../../app/lib/general.lib.js')
16+
const LicenceHelper = require('../../support/helpers/licence.helper.js')
17+
const TransactionHelper = require('../../support/helpers/transaction.helper.js')
1818

1919
// Thing under test
20-
const FetchPreviousTransactionsService = require('../../../../app/services/bill-runs/supplementary/fetch-previous-transactions.service.js')
20+
const FetchPreviousTransactionsService = require('../../../app/services/bill-runs/fetch-previous-transactions.service.js')
2121

22-
describe('Fetch Previous Transactions service', () => {
22+
describe('Bill Runs - Fetch Previous Transactions service', () => {
2323
const chargeCategoryCode = '4.3.1'
2424
const financialYearEnding = 2023
2525

0 commit comments

Comments
 (0)