From 6df4b1884b06ef4a55845d1574f88fc150f89d51 Mon Sep 17 00:00:00 2001 From: Jason Claxton <30830544+Jozzey@users.noreply.github.com> Date: Tue, 11 Apr 2023 12:02:37 +0100 Subject: [PATCH] Reverse previous SROC billing batches in supplementary bill run process (#186) https://eaflood.atlassian.net/browse/WATER-3936 When we generate a supplementary bill run, we must reverse the previous ones in the same financial period. Essentially, when a licence is flagged for supplementary billing, for whatever reason, we reverse any previous bill runs and generate a new one. This marks a change to our previous approach where only the last bill run was reversed. This is due to a scenario being found during testing which needed more than just the last bill run to be reversed. We therefore needed to change how we do this. The way we decided to approach this is to get all credits and debits then remove any debits which have a matching credit, based on the value of billable days and the charge type. The debits that remain are the ones that we will then turn into credits in order to reverse the previous batches. --- ...h-previous-billing-transactions.service.js | 32 ++++-- ...vious-billing-transactions.service.test.js | 104 +++++++++++++----- 2 files changed, 100 insertions(+), 36 deletions(-) diff --git a/app/services/supplementary-billing/fetch-previous-billing-transactions.service.js b/app/services/supplementary-billing/fetch-previous-billing-transactions.service.js index b24cb33b28..6e82c8f025 100644 --- a/app/services/supplementary-billing/fetch-previous-billing-transactions.service.js +++ b/app/services/supplementary-billing/fetch-previous-billing-transactions.service.js @@ -1,7 +1,8 @@ 'use strict' /** - * Fetches the previously billed transactions that match the invoice, licence and year provided + * Fetches the previously billed transactions that match the invoice, licence and year provided, removing any debits + * which are cancelled out by previous credits. * @module FetchPreviousBillingTransactionsService */ @@ -14,7 +15,29 @@ async function go (billingInvoice, billingInvoiceLicence, financialYearEnding) { financialYearEnding ) - return billingTransactions + return _cleanse(billingTransactions) +} + +/** + * Cleanse the billing transactions by cancelling out matching pairs of debits and credits, and return the remaining + * debits. We judge a pair of credits and debits to be matching if they have the same number of billable days and the + * same charge type. + */ +function _cleanse (billingTransactions) { + const credits = billingTransactions.filter((transaction) => transaction.isCredit) + const debits = billingTransactions.filter((transaction) => !transaction.isCredit) + + for (const credit of credits) { + const debitIndex = debits.findIndex((debit) => { + return debit.billableDays === credit.billableDays && debit.chargeType === credit.chargeType + }) + + if (debitIndex > -1) { + debits.splice(debitIndex, 1) + } + } + + return debits } async function _fetch (licenceId, invoiceAccountId, financialYearEnding) { @@ -70,14 +93,9 @@ async function _fetch (licenceId, invoiceAccountId, financialYearEnding) { 'bb.status': 'sent', 'bb.scheme': 'sroc' }) - .orderBy('bb.dateCreated', 'desc') - .limit(1) .as('validBillingInvoices'), 'bt.billingInvoiceLicenceId', 'validBillingInvoices.billingInvoiceLicenceId' ) - .where({ - 'bt.isCredit': false - }) } module.exports = { diff --git a/test/services/supplementary-billing/fetch-previous-billing-transactions.service.test.js b/test/services/supplementary-billing/fetch-previous-billing-transactions.service.test.js index 19faeddcc8..bdfcb7e720 100644 --- a/test/services/supplementary-billing/fetch-previous-billing-transactions.service.test.js +++ b/test/services/supplementary-billing/fetch-previous-billing-transactions.service.test.js @@ -41,13 +41,7 @@ describe('Fetch Previous Billing Transactions service', () => { describe('when there is a bill run', () => { describe('for the same licence and invoice account', () => { beforeEach(async () => { - const { billingBatchId } = await BillingBatchHelper.add({ status: 'sent' }) - const { billingInvoiceId } = await BillingInvoiceHelper.add({ invoiceAccountId }, { billingBatchId }) - const { billingInvoiceLicenceId } = await BillingInvoiceLicenceHelper.add( - {}, - { licenceId }, - { billingInvoiceId } - ) + const billingInvoiceLicenceId = await _createBillingBatchInvoiceAndLicence(invoiceAccountId, licenceId) await BillingTransactionHelper.add({ billingInvoiceLicenceId }) }) @@ -63,14 +57,35 @@ describe('Fetch Previous Billing Transactions service', () => { describe('followed by another run which credits the previous', () => { beforeEach(async () => { - const { billingBatchId } = await BillingBatchHelper.add({ status: 'sent' }) - const { billingInvoiceId } = await BillingInvoiceHelper.add({ invoiceAccountId }, { billingBatchId }) - const { billingInvoiceLicenceId } = await BillingInvoiceLicenceHelper.add( - {}, + const billingInvoiceLicenceId = await _createBillingBatchInvoiceAndLicence(invoiceAccountId, licenceId) + await BillingTransactionHelper.add({ billingInvoiceLicenceId, isCredit: true }) + }) + + it('returns no results', async () => { + const result = await FetchPreviousBillingTransactionsService.go( + { invoiceAccountId }, { licenceId }, - { billingInvoiceId } + financialYearEnding ) - await BillingTransactionHelper.add({ billingInvoiceLicenceId, isCredit: true }) + + expect(result).to.be.empty() + }) + }) + + describe('followed by more runs with equal credits and debits', () => { + beforeEach(async () => { + const creditsBillingInvoiceLicenceId = await _createBillingBatchInvoiceAndLicence(invoiceAccountId, licenceId) + await BillingTransactionHelper.add({ + billingInvoiceLicenceId: creditsBillingInvoiceLicenceId, + isCredit: true + }) + await BillingTransactionHelper.add({ + billingInvoiceLicenceId: creditsBillingInvoiceLicenceId, + isCredit: true + }) + + const debitBillingInvoiceLicenceId = await _createBillingBatchInvoiceAndLicence(invoiceAccountId, licenceId) + await BillingTransactionHelper.add({ billingInvoiceLicenceId: debitBillingInvoiceLicenceId }) }) it('returns no results', async () => { @@ -83,17 +98,42 @@ describe('Fetch Previous Billing Transactions service', () => { expect(result).to.be.empty() }) }) + + describe('followed by more runs with unequal credits and debits', () => { + beforeEach(async () => { + const unmatchedBillingInvoiceLicenceId = await _createBillingBatchInvoiceAndLicence( + invoiceAccountId, + licenceId + ) + await BillingTransactionHelper.add({ + billingInvoiceLicenceId: unmatchedBillingInvoiceLicenceId, + billableDays: 30, + isCredit: true + }) + + const matchedBillingInvoiceLicenceId = await _createBillingBatchInvoiceAndLicence(invoiceAccountId, licenceId) + await BillingTransactionHelper.add({ + billingInvoiceLicenceId: matchedBillingInvoiceLicenceId, + isCredit: true + }) + await BillingTransactionHelper.add({ billingInvoiceLicenceId: matchedBillingInvoiceLicenceId }) + }) + + it('returns results', async () => { + const result = await FetchPreviousBillingTransactionsService.go( + { invoiceAccountId }, + { licenceId }, + financialYearEnding + ) + + expect(result).to.have.length(1) + }) + }) }) describe('but for a different licence', () => { beforeEach(async () => { - const { billingBatchId } = await BillingBatchHelper.add({ status: 'sent' }) - const { billingInvoiceId } = await BillingInvoiceHelper.add({ invoiceAccountId }, { billingBatchId }) - const { billingInvoiceLicenceId } = await BillingInvoiceLicenceHelper.add( - {}, - { licenceId: '66498337-e6a6-4a2a-9fb7-e39f43410f80' }, - { billingInvoiceId } - ) + const billingInvoiceLicenceId = await _createBillingBatchInvoiceAndLicence(invoiceAccountId, '66498337-e6a6-4a2a-9fb7-e39f43410f80') await BillingTransactionHelper.add({ billingInvoiceLicenceId }) }) @@ -110,15 +150,9 @@ describe('Fetch Previous Billing Transactions service', () => { describe('but for a different invoice account', () => { beforeEach(async () => { - const { billingBatchId } = await BillingBatchHelper.add({ status: 'sent' }) - const { billingInvoiceId } = await BillingInvoiceHelper.add( - { invoiceAccountId: 'b0b75e7a-e80a-4c28-9ac9-33b3a850722b' }, - { billingBatchId } - ) - const { billingInvoiceLicenceId } = await BillingInvoiceLicenceHelper.add( - {}, - { licenceId }, - { billingInvoiceId } + const billingInvoiceLicenceId = await _createBillingBatchInvoiceAndLicence( + 'b0b75e7a-e80a-4c28-9ac9-33b3a850722b', + licenceId ) await BillingTransactionHelper.add({ billingInvoiceLicenceId }) }) @@ -135,3 +169,15 @@ describe('Fetch Previous Billing Transactions service', () => { }) }) }) + +async function _createBillingBatchInvoiceAndLicence (invoiceAccountId, licenceId) { + const { billingBatchId } = await BillingBatchHelper.add({ status: 'sent' }) + const { billingInvoiceId } = await BillingInvoiceHelper.add({ invoiceAccountId }, { billingBatchId }) + const { billingInvoiceLicenceId } = await BillingInvoiceLicenceHelper.add( + {}, + { licenceId }, + { billingInvoiceId } + ) + + return billingInvoiceLicenceId +}