From d3f815a5d7acbd33f9549e929196f44dfc0a30a2 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 29 Sep 2023 16:11:04 +0100 Subject: [PATCH] Add new bill run volume model https://eaflood.atlassian.net/browse/WATER-4152 We are currently working on the SROC two part tariff bill. The first step of the process in the UI is to review how the returns have been matched. Our superstar design team are working on an improved design for the returns review screen but to do that they need data. Enter our mock data service! We previously [Create endpoint to generate fake data](https://github.com/DEFRA/water-abstraction-system/pull/347) which was focused on a bill run. The design team would love it if we could do the same for the data the current returns review page uses. The page relies on data in the `water.billing_volumes` table. So, first step in generating the mock data is to add that model. --- app/models/water/bill-run-volume.model.js | 73 ++++++++++++ app/models/water/bill-run.model.js | 8 ++ app/models/water/charge-reference.model.js | 8 ++ .../mock/generate-returns-review.service.js | 19 +++ app/services/data/mock/mock.service.js | 8 +- ...0929085159_create-water-billing-volumes.js | 33 ++++++ .../water/bill-run-volume.model.test.js | 112 ++++++++++++++++++ test/models/water/bill-run.model.test.js | 38 ++++++ .../water/charge-reference.model.test.js | 38 ++++++ .../helpers/water/bill-run-volume.helper.js | 57 +++++++++ 10 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 app/models/water/bill-run-volume.model.js create mode 100644 app/services/data/mock/generate-returns-review.service.js create mode 100644 db/migrations/20230929085159_create-water-billing-volumes.js create mode 100644 test/models/water/bill-run-volume.model.test.js create mode 100644 test/support/helpers/water/bill-run-volume.helper.js diff --git a/app/models/water/bill-run-volume.model.js b/app/models/water/bill-run-volume.model.js new file mode 100644 index 0000000000..b2f0b0bb9b --- /dev/null +++ b/app/models/water/bill-run-volume.model.js @@ -0,0 +1,73 @@ +'use strict' + +/** + * Model for billing_batches + * @module BillRunModel + */ + +const { Model } = require('objection') + +const WaterBaseModel = require('./water-base.model.js') + +class BillRunVolumeModel extends WaterBaseModel { + static get tableName () { + return 'billingVolumes' + } + + static get idColumn () { + return 'billingVolumeId' + } + + static get translations () { + return [] + } + + static get relationMappings () { + return { + billRun: { + relation: Model.BelongsToOneRelation, + modelClass: 'bill-run.model', + join: { + from: 'billingVolumes.billingBatchId', + to: 'billingBatches.billingBatchId' + } + }, + chargeReference: { + relation: Model.BelongsToOneRelation, + modelClass: 'charge-reference.model', + join: { + from: 'billingVolumes.chargeElementId', + to: 'chargeElements.chargeElementId' + } + } + } + } + + // NOTE: When we checked the live data the only statuses we could find in use were; 10, 40, 50, 60, 70, 90 and 100 + static get twoPartTariffStatuses () { + return { + noReturnsSubmitted: 10, + underQuery: 20, + received: 30, + someReturnsDue: 40, + lateReturns: 50, + overAbstraction: 60, + noReturnsForMatching: 70, + notDueForBilling: 80, + returnLineOverlapsChargePeriod: 90, + noMatchingChargeElement: 100 + } + } + + $twoPartTariffStatus () { + Object.entries(BillRunVolumeModel.twoPartTariffStatuses).forEach(([key, value]) => { + if (value === this.twoPartTariffStatus) { + return key + } + }) + + return null + } +} + +module.exports = BillRunVolumeModel diff --git a/app/models/water/bill-run.model.js b/app/models/water/bill-run.model.js index e489bbea01..f314ba31c0 100644 --- a/app/models/water/bill-run.model.js +++ b/app/models/water/bill-run.model.js @@ -42,6 +42,14 @@ class BillRunModel extends WaterBaseModel { from: 'billingBatches.billingBatchId', to: 'billingInvoices.billingBatchId' } + }, + billRunVolumes: { + relation: Model.HasManyRelation, + modelClass: 'bill-run-volume.model', + join: { + from: 'billingBatches.billingBatchId', + to: 'billingVolumes.billingBatchId' + } } } } diff --git a/app/models/water/charge-reference.model.js b/app/models/water/charge-reference.model.js index 4fc2712b60..4a4a8aec1e 100644 --- a/app/models/water/charge-reference.model.js +++ b/app/models/water/charge-reference.model.js @@ -31,6 +31,14 @@ class ChargeReferenceModel extends WaterBaseModel { static get relationMappings () { return { + billRunVolumes: { + relation: Model.HasManyRelation, + modelClass: 'bill-run-volume.model', + join: { + from: 'chargeElements.chargeElementId', + to: 'billingVolumes.chargeElementId' + } + }, chargeVersion: { relation: Model.BelongsToOneRelation, modelClass: 'charge-version.model', diff --git a/app/services/data/mock/generate-returns-review.service.js b/app/services/data/mock/generate-returns-review.service.js new file mode 100644 index 0000000000..cb0268b878 --- /dev/null +++ b/app/services/data/mock/generate-returns-review.service.js @@ -0,0 +1,19 @@ +'use strict' + +async function go (id) { + const mockedReturnReviewData = { + id: '83a67627-f523-4a0b-8875-24e990d1c89c', + name: 'boo', + otherId: id + } + + return _response(mockedReturnReviewData) +} + +function _response (mockedReturnReviewData) { + return mockedReturnReviewData +} + +module.exports = { + go +} diff --git a/app/services/data/mock/mock.service.js b/app/services/data/mock/mock.service.js index 4441dfd52a..752fad4ec6 100644 --- a/app/services/data/mock/mock.service.js +++ b/app/services/data/mock/mock.service.js @@ -7,9 +7,11 @@ const ExpandedError = require('../../../errors/expanded.error.js') const GenerateBillRunService = require('./generate-bill-run.service.js') +const GenerateReturnsReviewService = require('./generate-returns-review.service.js') const types = { - 'bill-run': _billRun + 'bill-run': _billRun, + 'returns-review': _returnsReview } async function go (type, id) { @@ -34,6 +36,10 @@ async function _billRun (id) { return GenerateBillRunService.go(id) } +async function _returnsReview (id) { + return GenerateReturnsReviewService.go(id) +} + module.exports = { go } diff --git a/db/migrations/20230929085159_create-water-billing-volumes.js b/db/migrations/20230929085159_create-water-billing-volumes.js new file mode 100644 index 0000000000..b02c5f4671 --- /dev/null +++ b/db/migrations/20230929085159_create-water-billing-volumes.js @@ -0,0 +1,33 @@ +'use strict' + +const tableName = 'billing_volumes' + +exports.up = function (knex) { + return knex + .schema + .withSchema('water') + .createTable(tableName, (table) => { + // Primary Key + table.uuid('billing_volume_id').primary().defaultTo(knex.raw('gen_random_uuid()')) + + // Data + table.uuid('charge_element_id').notNullable() + table.integer('financial_year').notNullable() + table.boolean('is_summer').notNullable() + table.decimal('calculated_volume') + table.boolean('two_part_tariff_error').notNullable().defaultTo(false) + table.integer('two_part_tariff_status') + table.jsonb('two_part_tariff_review') + table.boolean('is_approved').notNullable().defaultTo(false) + table.uuid('billing_batch_id').notNullable() + table.decimal('volume') + table.timestamp('errored_on') + }) +} + +exports.down = function (knex) { + return knex + .schema + .withSchema('water') + .dropTableIfExists(tableName) +} diff --git a/test/models/water/bill-run-volume.model.test.js b/test/models/water/bill-run-volume.model.test.js new file mode 100644 index 0000000000..c2b573f3fe --- /dev/null +++ b/test/models/water/bill-run-volume.model.test.js @@ -0,0 +1,112 @@ +'use strict' + +// Test framework dependencies +const Lab = require('@hapi/lab') +const Code = require('@hapi/code') + +const { describe, it, beforeEach } = exports.lab = Lab.script() +const { expect } = Code + +// Test helpers +const BillRunHelper = require('../../support/helpers/water/bill-run.helper.js') +const BillRunModel = require('../../../app/models/water/bill-run.model.js') +const BillRunVolumeHelper = require('../../support/helpers/water/bill-run-volume.helper.js') +const ChargeReferenceHelper = require('../../support/helpers/water/charge-reference.helper.js') +const ChargeReferenceModel = require('../../../app/models/water/charge-reference.model.js') +const DatabaseHelper = require('../../support/helpers/database.helper.js') + +// Thing under test +const BillRunVolumeModel = require('../../../app/models/water/bill-run-volume.model.js') + +describe('Bill Run Volume model', () => { + let testRecord + + beforeEach(async () => { + await DatabaseHelper.clean() + }) + + describe('Basic query', () => { + beforeEach(async () => { + testRecord = await BillRunVolumeHelper.add() + }) + + it('can successfully run a basic query', async () => { + const result = await BillRunVolumeModel.query().findById(testRecord.billingVolumeId) + + expect(result).to.be.an.instanceOf(BillRunVolumeModel) + expect(result.billingVolumeId).to.equal(testRecord.billingVolumeId) + }) + }) + + describe('Relationships', () => { + describe('when linking to bill run', () => { + let testBillRun + + beforeEach(async () => { + testBillRun = await BillRunHelper.add() + + const { billingBatchId } = testBillRun + testRecord = await BillRunVolumeHelper.add({ billingBatchId }) + }) + + it('can successfully run a related query', async () => { + const query = await BillRunVolumeModel.query() + .innerJoinRelated('billRun') + + expect(query).to.exist() + }) + + it('can eager load the bill run', async () => { + const result = await BillRunVolumeModel.query() + .findById(testRecord.billingVolumeId) + .withGraphFetched('billRun') + + expect(result).to.be.instanceOf(BillRunVolumeModel) + expect(result.billingVolumeId).to.equal(testRecord.billingVolumeId) + + expect(result.billRun).to.be.an.instanceOf(BillRunModel) + expect(result.billRun).to.equal(testBillRun) + }) + }) + + describe('when linking to charge reference', () => { + let testChargeReference + + beforeEach(async () => { + testChargeReference = await ChargeReferenceHelper.add() + + const { chargeElementId } = testChargeReference + testRecord = await BillRunVolumeHelper.add({ chargeElementId }) + }) + + it('can successfully run a related query', async () => { + const query = await BillRunVolumeModel.query() + .innerJoinRelated('chargeReference') + + expect(query).to.exist() + }) + + it('can eager load the charge reference', async () => { + const result = await BillRunVolumeModel.query() + .findById(testRecord.billingVolumeId) + .withGraphFetched('chargeReference') + + expect(result).to.be.instanceOf(BillRunVolumeModel) + expect(result.billingVolumeId).to.equal(testRecord.billingVolumeId) + + expect(result.chargeReference).to.be.an.instanceOf(ChargeReferenceModel) + expect(result.chargeReference).to.equal(testChargeReference) + }) + }) + }) + + describe('Static getters', () => { + describe('Two Part Tariff status codes', () => { + it('returns the requested status code', async () => { + const result = BillRunVolumeModel.twoPartTariffStatuses.noReturnsSubmitted + + expect(result).to.equal(10) + }) + }) + }) +}) diff --git a/test/models/water/bill-run.model.test.js b/test/models/water/bill-run.model.test.js index ef693443be..514a8adcf6 100644 --- a/test/models/water/bill-run.model.test.js +++ b/test/models/water/bill-run.model.test.js @@ -11,6 +11,8 @@ const { expect } = Code const BillHelper = require('../../support/helpers/water/bill.helper.js') const BillModel = require('../../../app/models/water/bill.model.js') const BillRunHelper = require('../../support/helpers/water/bill-run.helper.js') +const BillRunVolumeHelper = require('../../support/helpers/water/bill-run-volume.helper.js') +const BillRunVolumeModel = require('../../../app/models/water/bill-run-volume.model.js') const DatabaseHelper = require('../../support/helpers/database.helper.js') const RegionHelper = require('../../support/helpers/water/region.helper.js') const RegionModel = require('../../../app/models/water/region.model.js') @@ -102,6 +104,42 @@ describe('Bill Run model', () => { expect(result.bills).to.include(testBills[1]) }) }) + + describe('when linking to bill run volumes', () => { + let testBillRunVolumes + + beforeEach(async () => { + testRecord = await BillRunHelper.add() + const { billingBatchId } = testRecord + + testBillRunVolumes = [] + for (let i = 0; i < 2; i++) { + const billRunVolume = await BillRunVolumeHelper.add({ billingBatchId }) + testBillRunVolumes.push(billRunVolume) + } + }) + + it('can successfully run a related query', async () => { + const query = await BillRunModel.query() + .innerJoinRelated('billRunVolumes') + + expect(query).to.exist() + }) + + it('can eager load the bills', async () => { + const result = await BillRunModel.query() + .findById(testRecord.billingBatchId) + .withGraphFetched('billRunVolumes') + + expect(result).to.be.instanceOf(BillRunModel) + expect(result.billingBatchId).to.equal(testRecord.billingBatchId) + + expect(result.billRunVolumes).to.be.an.array() + expect(result.billRunVolumes[0]).to.be.an.instanceOf(BillRunVolumeModel) + expect(result.billRunVolumes).to.include(testBillRunVolumes[0]) + expect(result.billRunVolumes).to.include(testBillRunVolumes[1]) + }) + }) }) describe('Static getters', () => { diff --git a/test/models/water/charge-reference.model.test.js b/test/models/water/charge-reference.model.test.js index 55aaca3440..9a60c95eb1 100644 --- a/test/models/water/charge-reference.model.test.js +++ b/test/models/water/charge-reference.model.test.js @@ -8,6 +8,8 @@ const { describe, it, beforeEach } = exports.lab = Lab.script() const { expect } = Code // Test helpers +const BillRunVolumeHelper = require('../../support/helpers/water/bill-run-volume.helper.js') +const BillRunVolumeModel = require('../../../app/models/water/bill-run-volume.model.js') const ChargeCategoryHelper = require('../../support/helpers/water/charge-category.helper.js') const ChargeCategoryModel = require('../../../app/models/water/charge-category.model.js') const ChargeElementHelper = require('../../support/helpers/water/charge-element.helper.js') @@ -41,6 +43,42 @@ describe('Charge Reference model', () => { }) describe('Relationships', () => { + describe('when linking to bill run volumes', () => { + let testBillRunVolumes + + beforeEach(async () => { + testRecord = await ChargeReferenceHelper.add() + const { chargeElementId } = testRecord + + testBillRunVolumes = [] + for (let i = 0; i < 2; i++) { + const billRunVolume = await BillRunVolumeHelper.add({ chargeElementId }) + testBillRunVolumes.push(billRunVolume) + } + }) + + it('can successfully run a related query', async () => { + const query = await ChargeReferenceModel.query() + .innerJoinRelated('billRunVolumes') + + expect(query).to.exist() + }) + + it('can eager load the bills', async () => { + const result = await ChargeReferenceModel.query() + .findById(testRecord.chargeElementId) + .withGraphFetched('billRunVolumes') + + expect(result).to.be.instanceOf(ChargeReferenceModel) + expect(result.chargeElementId).to.equal(testRecord.chargeElementId) + + expect(result.billRunVolumes).to.be.an.array() + expect(result.billRunVolumes[0]).to.be.an.instanceOf(BillRunVolumeModel) + expect(result.billRunVolumes).to.include(testBillRunVolumes[0]) + expect(result.billRunVolumes).to.include(testBillRunVolumes[1]) + }) + }) + describe('when linking to charge category', () => { let testChargeCategory diff --git a/test/support/helpers/water/bill-run-volume.helper.js b/test/support/helpers/water/bill-run-volume.helper.js new file mode 100644 index 0000000000..8250c35710 --- /dev/null +++ b/test/support/helpers/water/bill-run-volume.helper.js @@ -0,0 +1,57 @@ +'use strict' + +/** + * @module BillRunVolumeHelper + */ + +const BillRunVolumeModel = require('../../../../app/models/water/bill-run-volume.model.js') +const { generateUUID } = require('../../../../app/lib/general.lib.js') + +/** + * Add a new bill run volume + * + * If no `data` is provided, default values will be used. These are + * + * - `chargeElementId` - [random UUID] + * - `financialYear` - 2023 + * - `isSummer` - false + * - `billingBatchId` - [random UUID] + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + * + * @returns {module:BillRunModel} The instance of the newly created record + */ +function add (data = {}) { + const insertData = defaults(data) + + return BillRunVolumeModel.query() + .insert({ ...insertData }) + .returning('*') +} + +/** + * Returns the defaults used + * + * It will override or append to them any data provided. Mainly used by the `add()` method, we make it available + * for use in tests to avoid having to duplicate values. + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + */ +function defaults (data = {}) { + const defaults = { + chargeElementId: generateUUID(), + financialYear: 2023, + isSummer: false, + billingBatchId: generateUUID() + } + + return { + ...defaults, + ...data + } +} + +module.exports = { + add, + defaults +}