Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move reusable two-part tariff billing services #1765

Merged
merged 6 commits into from
Mar 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/controllers/bill-runs.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

const Boom = require('@hapi/boom')

const GenerateBillRunService = require('../services/bill-runs/two-part-tariff/generate-bill-run.service.js')
const GenerateTwoPartTariffBillRunService = require('../services/bill-runs/generate-two-part-tariff-bill-run.service.js')
const IndexBillRunsService = require('../services/bill-runs/index-bill-runs.service.js')
const SubmitCancelBillRunService = require('../services/bill-runs/cancel/submit-cancel-bill-run.service.js')
const SubmitSendBillRunService = require('../services/bill-runs/send/submit-send-bill-run.service.js')
Expand Down Expand Up @@ -66,7 +66,7 @@ async function twoPartTariff(request, h) {
try {
// NOTE: What we are awaiting here is for the GenerateBillRunService to update the status of the bill run to
// `processing'.
await GenerateBillRunService.go(id)
await GenerateTwoPartTariffBillRunService.go(id)

// Redirect to the bill runs page
return h.redirect('/system/bill-runs')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@
* @module GenerateBillRunService
*/

const BillRunError = require('../../../errors/bill-run.error.js')
const BillRunModel = require('../../../models/bill-run.model.js')
const ChargingModuleGenerateBillRunRequest = require('../../../requests/charging-module/generate-bill-run.request.js')
const ExpandedError = require('../../../errors/expanded.error.js')
const {
calculateAndLogTimeTaken,
currentTimeInNanoseconds,
timestampForPostgres
} = require('../../../lib/general.lib.js')
const FetchTwoPartTariffBillingAccountsService = require('../fetch-two-part-tariff-billing-accounts.service.js')
const HandleErroredBillRunService = require('../handle-errored-bill-run.service.js')
const LegacyRefreshBillRunRequest = require('../../../requests/legacy/refresh-bill-run.request.js')
const ProcessBillingPeriodService = require('./process-billing-period.service.js')
const BillRunError = require('../../errors/bill-run.error.js')
const BillRunModel = require('../../models/bill-run.model.js')
const ChargingModuleGenerateBillRunRequest = require('../../requests/charging-module/generate-bill-run.request.js')
const ExpandedError = require('../../errors/expanded.error.js')
const { calculateAndLogTimeTaken, currentTimeInNanoseconds, timestampForPostgres } = require('../../lib/general.lib.js')
const FetchTwoPartTariffBillingAccountsService = require('./fetch-two-part-tariff-billing-accounts.service.js')
const HandleErroredBillRunService = require('./handle-errored-bill-run.service.js')
const LegacyRefreshBillRunRequest = require('../../requests/legacy/refresh-bill-run.request.js')
const ProcessAnnualBillingPeriodService = require('./two-part-tariff/process-billing-period.service.js')
const ProcessSupplementaryBillingPeriodService = require('./tpt-supplementary/process-billing-period.service.js')

/**
* Generates a two-part tariff bill run after the users have completed reviewing its match & allocate results
Expand Down Expand Up @@ -136,7 +133,12 @@ async function _processBillingPeriod(billingPeriod, billRun) {

const billingAccounts = await _fetchBillingAccounts(billRunId)

const billRunPopulated = await ProcessBillingPeriodService.go(billRun, billingPeriod, billingAccounts)
let billRunPopulated = false
if (billRun.batchType === 'two_part_tariff') {
billRunPopulated = await ProcessAnnualBillingPeriodService.go(billRun, billingPeriod, billingAccounts)
} else {
billRunPopulated = await ProcessSupplementaryBillingPeriodService.go(billRun, billingPeriod, billingAccounts)
}

await _finaliseBillRun(billRun, billRunPopulated)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

/**
* Generate a two-part tariff transaction data from the the charge reference and other information passed in
* @module GenerateTransactionService
* @module GenerateTwoPartTariffTransactionService
*/

const { generateUUID } = require('../../../lib/general.lib.js')
const { generateUUID } = require('../../lib/general.lib.js')

/**
* Generate a two-part tariff transaction data from the the charge reference and other information passed in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict'

/**
* Process the billing accounts for a given billing period and creates their supplementary two-part tariff bills
* @module ProcessBillingPeriodService
*/

/**
* Process the billing accounts for a given billing period and creates their supplementary two-part tariff bills
*
* @param {module:BillRunModel} _billRun - The two-part tariff supplementary bill run we need to process
* @param {object} _billingPeriod - An object representing the financial year the bills will be for
* @param {module:BillingAccountModel[]} _billingAccounts - The billing accounts to create bills for
*
* @returns {Promise<boolean>} true if the bill run is not empty (there are transactions to bill) else false
*/
async function go(_billRun, _billingPeriod, _billingAccounts) {
throw Error('Not implemented')
}

module.exports = {
go
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const BillLicenceModel = require('../../../models/bill-licence.model.js')
const DetermineChargePeriodService = require('../determine-charge-period.service.js')
const DetermineMinimumChargeService = require('../determine-minimum-charge.service.js')
const { generateUUID } = require('../../../lib/general.lib.js')
const GenerateTransactionService = require('./generate-transaction.service.js')
const GenerateTwoPartTariffTransactionService = require('../generate-two-part-tariff-transaction.service.js')
const SendTransactionsService = require('../send-transactions.service.js')
const TransactionModel = require('../../../models/transaction.model.js')

Expand All @@ -21,7 +21,7 @@ const BillingConfig = require('../../../../config/billing.config.js')
/**
* Process the billing accounts for a given billing period and creates their annual two-part tariff bills
*
* @param {module:BillRunModel} billRun - The two-part tariff bill run we need to process
* @param {module:BillRunModel} billRun - The two-part tariff annual bill run we need to process
* @param {object} billingPeriod - An object representing the financial year the bills will be for
* @param {module:BillingAccountModel[]} billingAccounts - The billing accounts to create bills for
*
Expand Down Expand Up @@ -227,7 +227,7 @@ function _generateTransactionData(billLicenceId, chargePeriod, chargeVersion) {
const transactions = []

chargeVersion.chargeReferences.forEach((chargeReference) => {
const transaction = GenerateTransactionService.go(
const transaction = GenerateTwoPartTariffTransactionService.go(
billLicenceId,
chargeReference,
chargePeriod,
Expand Down
6 changes: 3 additions & 3 deletions test/controllers/bill-runs.controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const { postRequestOptions } = require('../support/general.js')

// Things we need to stub
const Boom = require('@hapi/boom')
const GenerateBillRunService = require('../../app/services/bill-runs/two-part-tariff/generate-bill-run.service.js')
const GenerateTwoPartTariffBillRunService = require('../../app/services/bill-runs/generate-two-part-tariff-bill-run.service.js')
const IndexBillRunsService = require('../../app/services/bill-runs/index-bill-runs.service.js')
const SubmitCancelBillRunService = require('../../app/services/bill-runs/cancel/submit-cancel-bill-run.service.js')
const SubmitSendBillRunService = require('../../app/services/bill-runs/send/submit-send-bill-run.service.js')
Expand Down Expand Up @@ -276,7 +276,7 @@ describe('Bill Runs controller', () => {

describe('when a request is valid', () => {
beforeEach(() => {
Sinon.stub(GenerateBillRunService, 'go').resolves('97db1a27-8308-4aba-b463-8a6af2558b28')
Sinon.stub(GenerateTwoPartTariffBillRunService, 'go').resolves('97db1a27-8308-4aba-b463-8a6af2558b28')
})

it('redirects to the bill runs page', async () => {
Expand All @@ -291,7 +291,7 @@ describe('Bill Runs controller', () => {
describe('because the generate service threw an error', () => {
beforeEach(async () => {
Sinon.stub(Boom, 'badImplementation').returns(new Boom.Boom('Bang', { statusCode: 500 }))
Sinon.stub(GenerateBillRunService, 'go').rejects()
Sinon.stub(GenerateTwoPartTariffBillRunService, 'go').rejects()
})

it('returns the error page', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,20 @@ const { expect } = Code
// Test helpers
const { setTimeout } = require('timers/promises')

const BillRunError = require('../../../../app/errors/bill-run.error.js')
const ExpandedErrorError = require('../../../../app/errors/expanded.error.js')
const BillRunError = require('../../../app/errors/bill-run.error.js')
const ExpandedErrorError = require('../../../app/errors/expanded.error.js')

// Things we need to stub
const BillRunModel = require('../../../../app/models/bill-run.model.js')
const ChargingModuleGenerateRequest = require('../../../../app/requests/charging-module/generate-bill-run.request.js')
const FetchTwoPartTariffBillingAccountsService = require('../../../../app/services/bill-runs/fetch-two-part-tariff-billing-accounts.service.js')
const HandleErroredBillRunService = require('../../../../app/services/bill-runs/handle-errored-bill-run.service.js')
const LegacyRefreshBillRunRequest = require('../../../../app/requests/legacy/refresh-bill-run.request.js')
const ProcessBillingPeriodService = require('../../../../app/services/bill-runs/two-part-tariff/process-billing-period.service.js')
const BillRunModel = require('../../../app/models/bill-run.model.js')
const ChargingModuleGenerateRequest = require('../../../app/requests/charging-module/generate-bill-run.request.js')
const FetchTwoPartTariffBillingAccountsService = require('../../../app/services/bill-runs/fetch-two-part-tariff-billing-accounts.service.js')
const HandleErroredBillRunService = require('../../../app/services/bill-runs/handle-errored-bill-run.service.js')
const LegacyRefreshBillRunRequest = require('../../../app/requests/legacy/refresh-bill-run.request.js')
const ProcessAnnualBillingPeriodService = require('../../../app/services/bill-runs/two-part-tariff/process-billing-period.service.js')
const ProcessSupplementaryBillingPeriodService = require('../../../app/services/bill-runs/tpt-supplementary/process-billing-period.service.js')

// Thing under test
const GenerateBillRunService = require('../../../../app/services/bill-runs/two-part-tariff/generate-bill-run.service.js')
const GenerateTwoPartTariffBillRunService = require('../../../app/services/bill-runs/generate-two-part-tariff-bill-run.service.js')

describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
// NOTE: introducing a delay in the tests is not ideal. But the service is written such that the generating happens in
Expand All @@ -41,6 +42,8 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {

let billRunPatchStub
let billRunSelectStub
let processAnnualStub
let processSupplementaryStub
let notifierStub

beforeEach(async () => {
Expand All @@ -53,6 +56,9 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
select: billRunSelectStub
})

processAnnualStub = Sinon.stub(ProcessAnnualBillingPeriodService, 'go')
processSupplementaryStub = Sinon.stub(ProcessSupplementaryBillingPeriodService, 'go')

// BaseRequest depends on the GlobalNotifier to have been set. This happens in app/plugins/global-notifier.plugin.js
// when the app starts up and the plugin is registered. As we're not creating an instance of Hapi server in this
// test we recreate the condition by setting it directly with our own stub
Expand All @@ -79,21 +85,56 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
})

it('throws an error', async () => {
const error = await expect(GenerateBillRunService.go(billRunDetails.id)).to.reject()
const error = await expect(GenerateTwoPartTariffBillRunService.go(billRunDetails.id)).to.reject()

expect(error).to.be.an.instanceOf(ExpandedErrorError)
expect(error.message).to.equal('Cannot process a two-part tariff bill run that is not in review')
})
})

describe('and the bill run is in review', () => {
describe('and is a "two part tariff annual"', () => {
beforeEach(async () => {
billRunSelectStub.resolves({ ...billRunDetails })
processAnnualStub.resolves(false)
processSupplementaryStub.resolves(false)
})

it('triggers the "process annual" service', async () => {
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

expect(processAnnualStub.calledOnce).to.be.true()
expect(processSupplementaryStub.called).to.be.false()
})
})

describe('and is a "two part tariff annual"', () => {
beforeEach(async () => {
billRunSelectStub.resolves({ ...billRunDetails, batchType: 'two_part_supplementary' })
processAnnualStub.resolves(false)
processSupplementaryStub.resolves(false)
})

it('triggers the "process supplementary" service', async () => {
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

expect(processSupplementaryStub.calledOnce).to.be.true()
expect(processAnnualStub.called).to.be.false()
})
})

describe('but there is nothing to bill', () => {
beforeEach(async () => {
billRunSelectStub.resolves({ ...billRunDetails })
processAnnualStub.resolves(false)
})

it('sets the bill run status first to "processing" and then to "empty"', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

Expand All @@ -113,26 +154,26 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
chargingModuleGenerateRequestStub = Sinon.stub(ChargingModuleGenerateRequest, 'send')
legacyRefreshBillRunRequestStub = Sinon.stub(LegacyRefreshBillRunRequest, 'send')

Sinon.stub(ProcessBillingPeriodService, 'go').resolves(true)
processAnnualStub.resolves(true)
})

it('sets the bill run status to "processing"', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

expect(billRunPatchStub.calledOnce).to.be.true()
expect(billRunPatchStub.firstCall.firstArg).to.equal({ status: 'processing' }, { skip: ['updatedAt'] })
})

it('tells the charging module API to "generate" the bill run', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

expect(chargingModuleGenerateRequestStub.called).to.be.true()
})

it('tells the legacy service to start its refresh job', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

Expand Down Expand Up @@ -160,7 +201,7 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
})

it('calls HandleErroredBillRunService with appropriate error code', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

Expand All @@ -170,7 +211,7 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
})

it('logs the error', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

Expand All @@ -191,11 +232,11 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
thrownError = new BillRunError(new Error(), BillRunModel.errorCodes.failedToPrepareTransactions)

Sinon.stub(FetchTwoPartTariffBillingAccountsService, 'go').resolves([])
Sinon.stub(ProcessBillingPeriodService, 'go').rejects(thrownError)
processAnnualStub.rejects(thrownError)
})

it('calls HandleErroredBillRunService with appropriate error code', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

Expand All @@ -205,7 +246,7 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
})

it('logs the error', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

Expand All @@ -226,12 +267,12 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
thrownError = new Error('ERROR')

Sinon.stub(FetchTwoPartTariffBillingAccountsService, 'go').resolves([])
Sinon.stub(ProcessBillingPeriodService, 'go').resolves(true)
processAnnualStub.resolves(true)
Sinon.stub(ChargingModuleGenerateRequest, 'send').rejects(thrownError)
})

it('calls HandleErroredBillRunService with appropriate error code', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

Expand All @@ -241,7 +282,7 @@ describe('Bill Runs - Two Part Tariff - Generate Bill Run Service', () => {
})

it('logs the error', async () => {
await GenerateBillRunService.go(billRunDetails.id)
await GenerateTwoPartTariffBillRunService.go(billRunDetails.id)

await setTimeout(delay)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ const { describe, it, beforeEach } = (exports.lab = Lab.script())
const { expect } = Code

// Thing under test
const GenerateTransactionService = require('../../../../app/services/bill-runs/two-part-tariff/generate-transaction.service.js')
const GenerateTwoPartTariffTransactionService = require('../../../app/services/bill-runs/generate-two-part-tariff-transaction.service.js')

describe('Generate Transaction service', () => {
describe('Bill Runs - Generate Two Part Tariff Transaction service', () => {
const billLicenceId = '5e2afb53-ca92-4515-ad71-36a7cefbcebb'

let chargePeriod
Expand All @@ -32,7 +32,7 @@ describe('Generate Transaction service', () => {
describe('when called', () => {
describe('with a charge reference that has volume to be billed', () => {
it('returns a two-part tariff transaction ready to be persisted', () => {
const result = GenerateTransactionService.go(
const result = GenerateTwoPartTariffTransactionService.go(
billLicenceId,
chargeReference,
chargePeriod,
Expand Down Expand Up @@ -90,7 +90,7 @@ describe('Generate Transaction service', () => {
})

it('returns the two-part tariff prefixed description', () => {
const result = GenerateTransactionService.go(
const result = GenerateTwoPartTariffTransactionService.go(
billLicenceId,
chargeReference,
chargePeriod,
Expand All @@ -112,7 +112,7 @@ describe('Generate Transaction service', () => {
})

it('returns null', () => {
const result = GenerateTransactionService.go(
const result = GenerateTwoPartTariffTransactionService.go(
billLicenceId,
chargeReference,
chargePeriod,
Expand Down
Loading
Loading