diff --git a/app/lib/general.lib.js b/app/lib/general.lib.js index c573002228..ab6351d644 100644 --- a/app/lib/general.lib.js +++ b/app/lib/general.lib.js @@ -180,11 +180,67 @@ function timestampForPostgres () { return new Date().toISOString() } +/** + * Compare key properties of 2 transactions and determine if they are a 'match' + * + * We compare those properties which determine the charge value calculated by the charging module. If the properties + * are the same we return true. Else we return false. + * + * This is used in the billing engines to determine 2 transactions within the same bill, often a debit and a credit, + * and whether they match. If they do we don't send either to the charge module or include them in the bill as they + * 'cancel' each other out. + * + * The key properties are charge type, category code, and billable days. But we also need to compare agreements and + * additional charges because if those have changed, we need to credit the previous transaction and calculate the + * new debit value. + * + * Because what we are checking does not match up to what you see in the UI we have this reference + * + * - Abatement agreement - section126Factor + * - Two-part tariff agreement - section127Agreement + * - Canal and River Trust agreement - section130Agreement + * - Aggregate - aggregateFactor + * - Charge Adjustment - adjustmentFactor + * - Winter discount - winterOnly + * + * - Additional charges - supportedSource + * - Additional charges - supportedSourceName + * - Additional charges - waterCompanyCharge + * + * @param {Object} left - First transaction to match + * @param {Object} right - Second transaction to match + * + * @returns {boolean} true if a match else false + */ +function transactionsMatch (left, right) { + // When we put together this matching logic our instincts were to try and do something 'better' than this long, + // chained `&&` statement. But whatever we came up with was + // + // - more complex + // - less performant + // + // We also believe this makes it easy to see what properties are being compared. Plus the moment something doesn't + // match we bail. So, much as it feels 'wrong', we are sticking with it! + return left.chargeType === right.chargeType && + left.chargeCategoryCode === right.chargeCategoryCode && + left.billableDays === right.billableDays && + left.section126Factor === right.section126Factor && + left.section127Agreement === right.section127Agreement && + left.section130Agreement === right.section130Agreement && + left.aggregateFactor === right.aggregateFactor && + left.adjustmentFactor === right.adjustmentFactor && + left.winterOnly === right.winterOnly && + left.supportedSource === right.supportedSource && + left.supportedSourceName === right.supportedSourceName && + left.waterCompanyCharge === right.waterCompanyCharge +} + module.exports = { calculateAndLogTimeTaken, currentTimeInNanoseconds, determineCurrentFinancialYear, generateUUID, periodsOverlap, - timestampForPostgres + timestampForPostgres, + transactionsMatch } diff --git a/app/services/bill-runs/supplementary/fetch-previous-transactions.service.js b/app/services/bill-runs/supplementary/fetch-previous-transactions.service.js index e46a4f3c00..73d138fffa 100644 --- a/app/services/bill-runs/supplementary/fetch-previous-transactions.service.js +++ b/app/services/bill-runs/supplementary/fetch-previous-transactions.service.js @@ -7,6 +7,7 @@ */ const { db } = require('../../../../db/db.js') +const { transactionsMatch } = require('../../../lib/general.lib.js') /** * Fetches the previously billed transactions that match the bill, licence and year provided, removing any debits @@ -42,7 +43,7 @@ function _cleanse (transactions) { credits.forEach((credit) => { const debitIndex = debits.findIndex((debit) => { - return _matchTransactions(debit, credit) + return transactionsMatch(debit, credit) }) if (debitIndex > -1) { @@ -53,55 +54,6 @@ function _cleanse (transactions) { return debits } -/** - * Compares a debit transaction to a credit transaction - * - * We compare those properties which determine the charge value calculated by the charging module. If the debit - * transaction's properties matches the credit's we return true. This will tell the calling method - * to remove the debit from the service's final results. - * - * The key properties are charge type, category code, and billable days. But we also need to compare agreements and - * additional charges because if those have changed, we'll need to credit the previous transaction and calculate the - * new debit value. Because what we are checking does not match up to what you see in the UI we have this reference - * - * - Abatement agreement - section126Factor - * - Two-part tariff agreement - section127Agreement - * - Canal and River Trust agreement - section130Agreement - * - Aggregate - aggregateFactor - * - Charge Adjustment - adjustmentFactor - * - Winter discount - winterOnly - * - * - Additional charges - supportedSource - * - Additional charges - supportedSourceName - * - Additional charges - waterCompanyCharge - */ -function _matchTransactions (debit, credit) { - // TODO: This logic is a duplicate of what we are doing in - // app/services/supplementary-billing/process-transactions.service.js. This also means we are running the - // same kind of unit tests on 2 places. We need to refactor this duplication in the code and the tests out. - - // When we put together this matching logic our instincts was to try and do something 'better' than this long, - // chained && statement. But whatever we came up with was - // - // - more complex - // - less performant - // - // We found this easy to see what properties are being compared. Plus the moment something doesn't match we bail. So, - // much as it feels 'wrong', we are sticking with it! - return debit.chargeType === credit.chargeType && - debit.chargeCategoryCode === credit.chargeCategoryCode && - debit.billableDays === credit.billableDays && - debit.section126Factor === credit.section126Factor && - debit.section127Agreement === credit.section127Agreement && - debit.section130Agreement === credit.section130Agreement && - debit.aggregateFactor === credit.aggregateFactor && - debit.adjustmentFactor === credit.adjustmentFactor && - debit.winterOnly === credit.winterOnly && - debit.supportedSource === credit.supportedSource && - debit.supportedSourceName === credit.supportedSourceName && - debit.waterCompanyCharge === credit.waterCompanyCharge -} - async function _fetch (licenceId, billingAccountId, financialYearEnding) { return db .select( diff --git a/app/services/bill-runs/supplementary/process-transactions.service.js b/app/services/bill-runs/supplementary/process-transactions.service.js index f8f8a04222..203653ac69 100644 --- a/app/services/bill-runs/supplementary/process-transactions.service.js +++ b/app/services/bill-runs/supplementary/process-transactions.service.js @@ -8,6 +8,7 @@ */ const FetchPreviousTransactionsService = require('./fetch-previous-transactions.service.js') +const { transactionsMatch } = require('../../../lib/general.lib.js') const ReverseTransactionsService = require('./reverse-transactions.service.js') /** @@ -44,50 +45,13 @@ async function go (calculatedTransactions, bill, billLicence, billingPeriod) { * Takes a single calculated debit transaction and checks to see if the provided array of reversed (credit) transactions * contains a transaction that will cancel it out, returning `true` or `false` to indicate if it does or doesn't. * - * We compare those properties which determine the charge value calculated by the charging module. If the calculated - * transaction's properties matches one in reversedTransactions we return true. This will tell the calling method - * to not include the calculated transaction in the bill run. We also remove the matched transaction from - * reversedTransactions. - * - * The key properties are charge type, category code, and billable days. But we also need to compare agreements and - * additional charges because if those have changed, we'll need to credit the previous transaction and calculate the - * new debit value. Because what we are checking does not match up to what you see in the UI we have this reference - * - * - Abatement agreement - section126Factor - * - Two-part tariff agreement - section127Agreement - * - Canal and River Trust agreement - section130Agreement - * - Aggregate - aggregateFactor - * - Charge Adjustment - adjustmentFactor - * - Winter discount - winterOnly - * - * - Additional charges - supportedSource - * - Additional charges - supportedSourceName - * - Additional charges - waterCompanyCharge - * * NOTE: This function will mutate the provided array of reversed transactions if one of the transactions in it will * cancel the calculated transaction; in this case, we remove the reversed transaction from the array as it can only * cancel one calculated transaction. */ function _cancelCalculatedTransaction (calculatedTransaction, reversedTransactions) { - // When we put together this matching logic our instincts were to try and do something 'better' than this long, - // chained && statement. But whatever we came up with was - // - more complex - // - less performant - // We found this easy to see what properties are being compared. Plus the moment something doesn't match we bail. So, - // much as it feels 'wrong', we are sticking with it! const result = reversedTransactions.findIndex((reversedTransaction) => { - return reversedTransaction.chargeType === calculatedTransaction.chargeType && - reversedTransaction.chargeCategoryCode === calculatedTransaction.chargeCategoryCode && - reversedTransaction.billableDays === calculatedTransaction.billableDays && - reversedTransaction.section126Factor === calculatedTransaction.section126Factor && - reversedTransaction.section127Agreement === calculatedTransaction.section127Agreement && - reversedTransaction.section130Agreement === calculatedTransaction.section130Agreement && - reversedTransaction.aggregateFactor === calculatedTransaction.aggregateFactor && - reversedTransaction.adjustmentFactor === calculatedTransaction.adjustmentFactor && - reversedTransaction.winterOnly === calculatedTransaction.winterOnly && - reversedTransaction.supportedSource === calculatedTransaction.supportedSource && - reversedTransaction.supportedSourceName === calculatedTransaction.supportedSourceName && - reversedTransaction.waterCompanyCharge === calculatedTransaction.waterCompanyCharge + return transactionsMatch(reversedTransaction, calculatedTransaction) }) if (result === -1) { diff --git a/test/lib/general.lib.test.js b/test/lib/general.lib.test.js index 4819eaa1e2..3dcb22b7f7 100644 --- a/test/lib/general.lib.test.js +++ b/test/lib/general.lib.test.js @@ -8,6 +8,9 @@ const Sinon = require('sinon') const { describe, it, beforeEach, afterEach } = exports.lab = Lab.script() const { expect } = Code +// Test helpers +const TransactionHelper = require('../support/helpers/transaction.helper.js') + // Thing under test const GeneralLib = require('../../app/lib/general.lib.js') @@ -277,4 +280,185 @@ describe('GeneralLib', () => { expect(result).to.equal('2015-10-21T20:31:57.000Z') }) }) + + describe('#transactionsMatch', () => { + let leftTransaction + let rightTransaction + + beforeEach(() => { + // NOTE: The properties the function is comparing are; chargeType, chargeCategoryCode, billableDays, + // section126Factor, section127Agreement, section130Agreement, aggregateFactor, adjustmentFactor, winterOnly, + // supportedSource, supportedSourceName, waterCompanyCharge. + // + // We add IDs just so we can tell them apart! + leftTransaction = { + ...TransactionHelper.defaults(), + id: 'cba29373-d9a2-423e-8f36-83c13b07d925', + adjustmentFactor: 1, + aggregateFactor: 1, + chargeCategoryCode: '4.3.2', + section126Factor: 1, + section127Agreement: false, + supportedSource: false, + supportedSourceName: 'Severn', + waterCompanyCharge: false, + winterOnly: false + } + rightTransaction = { ...leftTransaction, id: '164eb779-4d2d-4578-bfbb-f07347e68171' } + }) + + describe('when the transactions match', () => { + it('returns true', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.true() + }) + }) + + describe('when the transactions do not match', () => { + describe('because the abatement agreement (section 126) is different', () => { + beforeEach(() => { + rightTransaction.section126Factor = 0.5 + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the aggregate is different', () => { + beforeEach(() => { + rightTransaction.aggregateFactor = 0.5 + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the billable days are different', () => { + beforeEach(() => { + rightTransaction.billableDays = 10 + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the canal and river trust agreement (section 130) is different', () => { + beforeEach(() => { + rightTransaction.section130Agreement = true + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the charge adjustment is different', () => { + beforeEach(() => { + rightTransaction.adjustmentFactor = 0.5 + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the charge category code is different', () => { + beforeEach(() => { + rightTransaction.chargeCategoryCode = '4.3.3' + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the charge type is different', () => { + beforeEach(() => { + rightTransaction.chargeType = 'compensation' + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the supported source differs (additional charge) is different', () => { + beforeEach(() => { + rightTransaction.supportedSource = true + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the supported source name differs (additional charge) is different', () => { + beforeEach(() => { + rightTransaction.supportedSourceName = 'source name' + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the two-part tariff agreement (section 127) is different', () => { + beforeEach(() => { + rightTransaction.section127Agreement = true + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the water company flag differs (additional charge) is different', () => { + beforeEach(() => { + rightTransaction.waterCompanyCharge = true + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + + describe('because the winter discount is different', () => { + beforeEach(() => { + rightTransaction.adjustmentFactor = true + }) + + it('returns false', () => { + const result = GeneralLib.transactionsMatch(leftTransaction, rightTransaction) + + expect(result).to.be.false() + }) + }) + }) + }) }) diff --git a/test/services/bill-runs/supplementary/fetch-previous-transactions.service.test.js b/test/services/bill-runs/supplementary/fetch-previous-transactions.service.test.js index eff20e0cf0..2d90261599 100644 --- a/test/services/bill-runs/supplementary/fetch-previous-transactions.service.test.js +++ b/test/services/bill-runs/supplementary/fetch-previous-transactions.service.test.js @@ -92,267 +92,24 @@ describe('Fetch Previous Transactions service', () => { }) describe("that does not match the first bill run's debit", () => { - describe('because the billable days are different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - billableDays: 30, - chargeCategoryCode, - credit: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the charge type is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode, - chargeType: 'compensation', - credit: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the charge category code is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode: '4.3.2', - credit: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the abatement agreement (section 126) is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode, - credit: true, - section126Factor: 0.5 - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the two-part tariff agreement (section 127) is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode, - credit: true, - section127Agreement: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the canal and river trust agreement (section 130) is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode, - credit: true, - section130Agreement: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the aggregate is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - aggregateFactor: 0.5, - chargeCategoryCode, - credit: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the charge adjustment is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - adjustmentFactor: 0.5, - chargeCategoryCode, - credit: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the winter discount is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode, - credit: true, - winterOnly: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the supported source differs (additional charge) is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode, - credit: true, - supportedSource: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) - }) - - describe('because the supported source name differs (additional charge) is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode, - credit: true, - supportedSourceName: 'source name' - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) - - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() + beforeEach(async () => { + await TransactionHelper.add({ + billLicenceId: followUpBillLicenceId, + billableDays: 30, + chargeCategoryCode, + credit: true }) }) - describe('because the water company flag differs (additional charge) is different', () => { - beforeEach(async () => { - await TransactionHelper.add({ - billLicenceId: followUpBillLicenceId, - chargeCategoryCode, - credit: true, - waterCompanyCharge: true - }) - }) - - it('returns the debits', async () => { - const results = await FetchPreviousTransactionsService.go( - { billingAccountId }, - { licenceId }, - financialYearEnding - ) + it('returns the debits', async () => { + const results = await FetchPreviousTransactionsService.go( + { billingAccountId }, + { licenceId }, + financialYearEnding + ) - expect(results).to.have.length(1) - expect(results[0].credit).to.be.false() - }) + expect(results).to.have.length(1) + expect(results[0].credit).to.be.false() }) }) }) diff --git a/test/services/bill-runs/supplementary/process-transactions.service.test.js b/test/services/bill-runs/supplementary/process-transactions.service.test.js index 33dfda7175..8cc7a5bdd0 100644 --- a/test/services/bill-runs/supplementary/process-transactions.service.test.js +++ b/test/services/bill-runs/supplementary/process-transactions.service.test.js @@ -39,10 +39,10 @@ describe('Process Transactions service', () => { }) describe('match to transactions on a previous bill run', () => { - describe('and the calculated transactions provided', () => { + describe('and some of the calculated transactions provided', () => { let previousTransactions - describe('match all the previous transactions from the last bill run', () => { + describe('match to all the previous transactions from the last bill run', () => { beforeEach(() => { previousTransactions = [ _generatePreviousTransaction('4.10.1', 365, 'I_WILL_BE_REMOVED_1'), @@ -52,7 +52,7 @@ describe('Process Transactions service', () => { Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) }) - it('returns the matched calculated transactions', async () => { + it('returns the unmatched calculated transactions', async () => { const result = await ProcessTransactionsService.go( calculatedTransactions, bill, @@ -160,307 +160,6 @@ describe('Process Transactions service', () => { }) }) }) - - describe('the service matches calculated to previous transactions', () => { - let calculatedTransactions - - beforeEach(() => { - calculatedTransactions = [_generateCalculatedTransaction('4.10.1', 365, 'CALCULATED_TRANSACTION')] - }) - - describe('when the charge type differs', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction( - '4.10.1', 365, 'PREVIOUS_TRANSACTION', { chargeType: 'compensation' } - ) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the charge category code differs', () => { - beforeEach(() => { - const previousTransactions = [_generatePreviousTransaction('5.10.1', 365, 'PREVIOUS_TRANSACTION')] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the billable days differ', () => { - beforeEach(() => { - const previousTransactions = [_generatePreviousTransaction('4.10.1', 5, 'PREVIOUS_TRANSACTION')] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the abatement agreement (section 126) differs', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { section126Factor: 0.5 }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the two-part tariff agreement (section 127) differs', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { section127Agreement: true }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the canal and river trust agreement (section 130) differs', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { section130Agreement: true }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the aggregate differs', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { aggregateFactor: 0.5 }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the charge adjustment differs', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { adjustmentFactor: 0.5 }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the winter discount differs', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { winterOnly: true }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when if it is a supported source differs (additional charge)', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { supportedSource: true }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the supported source name differs (additional charge)', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { supportedSourceName: 'source name' }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when the water company flag differs (additional charge)', () => { - beforeEach(() => { - const previousTransactions = [ - _generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION', { waterCompanyCharge: true }) - ] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('does not match the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.have.length(2) - expect(result[0].purposes).to.equal('CALCULATED_TRANSACTION') - expect(result[1].purposes).to.equal('PREVIOUS_TRANSACTION') - }) - }) - - describe('when nothing differs', () => { - beforeEach(() => { - const previousTransactions = [_generatePreviousTransaction('4.10.1', 365, 'PREVIOUS_TRANSACTION')] - - Sinon.stub(FetchPreviousTransactionsService, 'go').resolves(previousTransactions) - }) - - it('matches the transactions', async () => { - const result = await ProcessTransactionsService.go( - calculatedTransactions, - bill, - billLicence, - billingPeriod - ) - - expect(result).to.be.empty() - }) - }) - }) }) function _generateCalculatedTransaction (chargeCategoryCode, billableDays, testReference, changes = {}) {