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

Create error bill run when CM fails #104

Merged
merged 16 commits into from
Jan 31, 2023
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
14 changes: 14 additions & 0 deletions app/models/water/billing-batch.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ class BillingBatchModel extends WaterBaseModel {
}
}
}

static get errorCodes () {
return {
failedToPopulateChargeVersions: 10,
failedToProcessChargeVersions: 20,
failedToPrepareTransactions: 30,
failedToCreateCharge: 40,
failedToCreateBillRun: 50,
failedToDeleteInvoice: 60,
failedToProcessTwoPartTariff: 70,
failedToGetChargeModuleBillRunSummary: 80,
failedToProcessRebilling: 90
}
}
}

module.exports = BillingBatchModel
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ function go (billingBatch) {
scheme,
batchType,
status,
externalId
externalId,
errorCode
} = billingBatch

return {
Expand All @@ -21,7 +22,8 @@ function go (billingBatch) {
scheme,
batchType,
status,
externalId
externalId,
errorCode
}
}

Expand Down
38 changes: 28 additions & 10 deletions app/services/supplementary-billing/create-billing-batch.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,49 @@ const BillingBatchModel = require('../../models/water/billing-batch.model.js')
*
* @param {Object} regionId The regionId for the selected region
* @param {Object} billingPeriod The billing period in the format { startDate: 2022-04-01, endDate: 2023-03-31 }
* @param {string} [batchType=supplementary] The type of billing batch to create. Defaults to 'supplementary'
* @param {string} [scheme=sroc] The applicable charging scheme. Defaults to 'sroc'
* @param {string} [source=wrls] Where the billing batch originated from. Records imported from NALD have the source 'nald'. Those created in the service use 'wrls'. Defaults to 'wrls'
* @param {string} [externalId=null] The id of the bill run as created in the Charging Module
* @param {Object} options Optional params to be overridden
* @param {string} [options.batchType=supplementary] The type of billing batch to create. Defaults to 'supplementary'
* @param {string} [options.scheme=sroc] The applicable charging scheme. Defaults to 'sroc'
* @param {string} [options.source=wrls] Where the billing batch originated from. Records imported from NALD have the
* source 'nald'. Those created in the service use 'wrls'. Defaults to 'wrls'
* @param {string} [options.externalId=null] The id of the bill run as created in the Charging Module
* @param {string} [options.status=queued] The status that the bill run should be created with
* @param {number} [options.errorCode=null] Numeric error code
*
* @returns {module:BillingBatchModel} The newly created billing batch instance with the `.region` property populated
*/
async function go (regionId, billingPeriod, batchType = 'supplementary', scheme = 'sroc', source = 'wrls', externalId = null) {
async function go (regionId, billingPeriod, options) {
const optionsData = optionsDefaults(options)

const billingBatch = await BillingBatchModel.query()
.insert({
regionId,
batchType,
fromFinancialYearEnding: billingPeriod.endDate.getFullYear(),
toFinancialYearEnding: billingPeriod.endDate.getFullYear(),
status: 'queued',
scheme,
source,
externalId
...optionsData
})
.returning('*')
.withGraphFetched('region')

return billingBatch
}

function optionsDefaults (data) {
const defaults = {
batchType: 'supplementary',
scheme: 'sroc',
source: 'wrls',
externalId: null,
status: 'queued',
errorCode: null
}

return {
...defaults,
...data
}
}

module.exports = {
go
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* @module InitiateBillingBatchService
*/

const BillingBatchModel = require('../../models/water/billing-batch.model.js')
const BillingPeriodService = require('./billing-period.service.js')
const ChargingModuleCreateBillRunService = require('../charging-module/create-bill-run.service.js')
const CheckLiveBillRunService = require('./check-live-bill-run.service.js')
Expand All @@ -15,8 +16,8 @@ const CreateBillingBatchEventService = require('./create-billing-batch-event.ser
/**
* Initiate a new billing batch
*
* Initiating a new billing batch means creating both the `billing_batch` and `event` record with the appropriate data.
* In the future it will also encompass creating the bill run record in the SROC Charging Module API.
* Initiating a new billing batch means creating both the `billing_batch` and `event` record with the appropriate data,
* along with a bill run record in the SROC Charging Module API.
*
* @param {Object} billRunRequestData Validated version of the data sent in the request to create the new billing batch
*
Expand All @@ -38,24 +39,27 @@ async function go (billRunRequestData) {

const chargingModuleBillRun = await ChargingModuleCreateBillRunService.go(region, 'sroc')

// A failed response will be due to a failed `RequestLib` request so format an error message accordingly and throw it
if (!chargingModuleBillRun.succeeded) {
// We always get the status code and error
const errorHead = `${chargingModuleBillRun.response.statusCode} ${chargingModuleBillRun.response.error}`
const billingBatchOptions = _billingBatchOptions(type, scheme, chargingModuleBillRun)
const billingBatch = await CreateBillingBatchService.go(region, billingPeriod, billingBatchOptions)

// We should always get an additional error messge but if not then simply throw the status code and error
if (!chargingModuleBillRun.response.message) {
throw Error(errorHead)
}
await CreateBillingBatchEventService.go(billingBatch, user)

throw Error(errorHead + ` - ${chargingModuleBillRun.response.message}`)
}
return _response(billingBatch)
}

const billingBatch = await CreateBillingBatchService.go(region, billingPeriod, type, scheme, undefined, chargingModuleBillRun.response.id)
function _billingBatchOptions (type, scheme, chargingModuleBillRun) {
const options = {
scheme,
batchType: type,
externalId: chargingModuleBillRun.response.id
}

await CreateBillingBatchEventService.go(billingBatch, user)
if (!chargingModuleBillRun.succeeded) {
options.status = 'error'
options.errorCode = BillingBatchModel.errorCodes.failedToCreateBillRun
}

return _response(billingBatch)
return options
}

function _response (billingBatch) {
Expand Down
21 changes: 21 additions & 0 deletions db/migrations/20230131152030_alter-water-billing-batches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict'

const tableName = 'billing_batches'

exports.up = async function (knex) {
await knex
.schema
.withSchema('water')
.alterTable(tableName, table => {
table.integer('error_code')
})
}

exports.down = async function (knex) {
await knex
.schema
.withSchema('water')
.alterTable(tableName, table => {
table.dropColumn('error_code')
})
}
10 changes: 10 additions & 0 deletions test/models/water/billing-batch.model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,14 @@ describe('Billing Batch model', () => {
})
})
})

describe('Static getters', () => {
describe('Error codes', () => {
it('returns the requested error code', async () => {
const result = BillingBatchModel.errorCodes.failedToCreateBillRun

expect(result).to.equal(50)
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ describe('Create Billing Batch presenter', () => {
regionId: '6a472535-145c-4170-ab59-f555783fa6e7',
scheme: 'sroc',
status: 'processing',
externalId: '2bbbe459-966e-4026-b5d2-2f10867bdddd'
externalId: '2bbbe459-966e-4026-b5d2-2f10867bdddd',
errorCode: 123
}
})

Expand All @@ -32,6 +33,7 @@ describe('Create Billing Batch presenter', () => {
expect(result.scheme).to.equal(data.scheme)
expect(result.status).to.equal(data.status)
expect(result.externalId).to.equal(data.externalId)
expect(result.errorCode).to.equal(data.errorCode)
})
})

Expand All @@ -42,7 +44,8 @@ describe('Create Billing Batch presenter', () => {
regionId: null,
scheme: null,
status: null,
externalId: null
externalId: null,
errorCode: null
}
})

Expand All @@ -54,6 +57,7 @@ describe('Create Billing Batch presenter', () => {
expect(result.scheme).to.be.null()
expect(result.status).to.be.null()
expect(result.externalId).to.be.null()
expect(result.errorCode).to.be.null()
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -32,37 +32,63 @@ describe('Create Billing Batch service', () => {

expect(result).to.be.an.instanceOf(BillingBatchModel)

expect(result.status).to.equal('queued')
expect(result.fromFinancialYearEnding).to.equal(2023)
expect(result.toFinancialYearEnding).to.equal(2023)
expect(result.batchType).to.equal('supplementary')
expect(result.scheme).to.equal('sroc')
expect(result.source).to.equal('wrls')
expect(result.externalId).to.be.null()
expect(result.status).to.equal('queued')
expect(result.errorCode).to.be.null()

expect(result.region).to.be.an.instanceOf(RegionModel)
expect(result.region.regionId).to.equal(region.regionId)
})
})

describe('when the defaults are overridden', () => {
describe('when all defaults are overridden', () => {
const batchType = 'annual'
const scheme = 'wrls'
const source = 'nald'
const externalId = '2bbbe459-966e-4026-b5d2-2f10867bdddd'
const status = 'error'
const errorCode = 50

it('returns the new billing batch instance containing the provided values', async () => {
const result = await CreateBillingBatchService.go(region.regionId, billingPeriod, batchType, scheme, source, externalId)
const result = await CreateBillingBatchService.go(region.regionId, billingPeriod, { batchType, scheme, source, externalId, status, errorCode })

expect(result).to.be.an.instanceOf(BillingBatchModel)

expect(result.status).to.equal('queued')
expect(result.fromFinancialYearEnding).to.equal(2023)
expect(result.toFinancialYearEnding).to.equal(2023)
expect(result.batchType).to.equal(batchType)
expect(result.scheme).to.equal(scheme)
expect(result.source).to.equal(source)
expect(result.externalId).to.equal(externalId)
expect(result.status).to.equal('error')
expect(result.errorCode).to.equal(errorCode)

expect(result.region).to.be.an.instanceOf(RegionModel)
expect(result.region.regionId).to.equal(region.regionId)
})
})

describe('when some defaults are overridden', () => {
const externalId = '2bbbe459-966e-4026-b5d2-2f10867bdddd'
const status = 'error'

it('returns the new billing batch instance containing the provided values', async () => {
const result = await CreateBillingBatchService.go(region.regionId, billingPeriod, { externalId, status })

expect(result).to.be.an.instanceOf(BillingBatchModel)

expect(result.fromFinancialYearEnding).to.equal(2023)
expect(result.toFinancialYearEnding).to.equal(2023)
expect(result.batchType).to.equal('supplementary')
expect(result.scheme).to.equal('sroc')
expect(result.source).to.equal('wrls')
expect(result.externalId).to.equal(externalId)
expect(result.status).to.equal('error')

expect(result.region).to.be.an.instanceOf(RegionModel)
expect(result.region.regionId).to.equal(region.regionId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe('Initiate Billing Batch service', () => {
expect(result.scheme).to.equal('sroc')
expect(result.batchType).to.equal('supplementary')
expect(result.status).to.equal('queued')
expect(result.errorCode).to.equal(null)
})
})

Expand All @@ -101,11 +102,17 @@ describe('Initiate Billing Batch service', () => {
})
})

it('rejects with an appropriate error', async () => {
const err = await expect(InitiateBillingBatchService.go(validatedRequestData)).to.reject()
it('creates a bill run with `error` status and error code 50', async () => {
const result = await InitiateBillingBatchService.go(validatedRequestData)

expect(err).to.be.an.error()
expect(err.message).to.equal("403 Forbidden - Unauthorised for regime 'wrls'")
const billingBatch = await BillingBatchModel.query().first()

expect(result.id).to.equal(billingBatch.billingBatchId)
expect(result.region).to.equal(billingBatch.regionId)
expect(result.scheme).to.equal('sroc')
expect(result.batchType).to.equal('supplementary')
expect(result.status).to.equal('error')
expect(result.errorCode).to.equal(50)
})
})

Expand All @@ -121,24 +128,5 @@ describe('Initiate Billing Batch service', () => {
expect(err.message).to.equal(`Batch already live for region ${validatedRequestData.region}`)
})
})

describe('and the error doesn\'t include a message', () => {
beforeEach(() => {
Sinon.stub(ChargingModuleCreateBillRunService, 'go').resolves({
succeeded: false,
response: {
statusCode: 403,
error: 'Forbidden'
}
})
})

it('rejects with an appropriate error', async () => {
const err = await expect(InitiateBillingBatchService.go(validatedRequestData)).to.reject()

expect(err).to.be.an.error()
expect(err.message).to.equal('403 Forbidden')
})
})
})
})