From c626cda5eec78293f603faae291ac51649d09518 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 1 Mar 2024 10:06:02 +0000 Subject: [PATCH 1/8] Add Bill Run Charge Version Years model https://eaflood.atlassian.net/browse/WATER-4387 We need to add this to support work we are doing to improve the way bill runs are cancelled. Basically, we're replace the crummy legacy one! The driving reason is to make it visible to users when a bill run is being cancelled as this impacts performance. If we can make cancelling bill runs visible then users can make more informed decisions about when to start creating a new one. In support of that we need to add the `BillRunChargeVersionYearModel` as its the one table we don't have a model for and which we need to delete stuff from when cancelling a bill run. From 22569ece1b1a328ade19954effd1f321b913765d Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 1 Mar 2024 10:44:39 +0000 Subject: [PATCH 2/8] Create BillRunChargeVersionYear legacy migration --- ...ater-billing-batch-charge-version-years.js | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 db/migrations/legacy/20221108007018-water-billing-batch-charge-version-years.js diff --git a/db/migrations/legacy/20221108007018-water-billing-batch-charge-version-years.js b/db/migrations/legacy/20221108007018-water-billing-batch-charge-version-years.js new file mode 100644 index 0000000000..3d4e3e385d --- /dev/null +++ b/db/migrations/legacy/20221108007018-water-billing-batch-charge-version-years.js @@ -0,0 +1,37 @@ +'use strict' + +const tableName = 'billing_batch_charge_version_years' + +exports.up = function (knex) { + return knex + .schema + .withSchema('water') + .createTable(tableName, (table) => { + // Primary Key + table.uuid('billing_batch_charge_version_year_id').primary().defaultTo(knex.raw('gen_random_uuid()')) + + // Data + table.uuid('billing_batch_id').notNullable() + table.uuid('charge_version_id').notNullable() + table.integer('financial_year_ending').notNullable() + table.string('status').notNullable() + table.string('transaction_type').notNullable() + table.boolean('is_summer').notNullable() + table.boolean('has_two_part_agreement').defaultTo(false) + table.boolean('is_chargeable').defaultTo(true) + + // Legacy timestamps + table.timestamp('date_created', { useTz: false }).notNullable().defaultTo(knex.fn.now()) + table.timestamp('date_updated', { useTz: false }).notNullable().defaultTo(knex.fn.now()) + + // Constraints + table.unique(['billing_batch_id', 'charge_version_id', 'financial_year_ending', 'transaction_type', 'is_summer'], { useConstraint: true }) + }) +} + +exports.down = function (knex) { + return knex + .schema + .withSchema('water') + .dropTableIfExists(tableName) +} From fb577bc103674c3583c5e7b458bdfd2bf3b75cf2 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 1 Mar 2024 10:47:17 +0000 Subject: [PATCH 3/8] Create BillRunChargeVersionYear view migration --- ...32_create-bill-run-charge-version-years.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 db/migrations/public/20240229170332_create-bill-run-charge-version-years.js diff --git a/db/migrations/public/20240229170332_create-bill-run-charge-version-years.js b/db/migrations/public/20240229170332_create-bill-run-charge-version-years.js new file mode 100644 index 0000000000..916f3d1de3 --- /dev/null +++ b/db/migrations/public/20240229170332_create-bill-run-charge-version-years.js @@ -0,0 +1,29 @@ +'use strict' + +const viewName = 'bill_run_charge_version_years' + +exports.up = function (knex) { + return knex + .schema + .createView(viewName, (view) => { + view.as(knex('billing_batch_charge_version_years').withSchema('water').select([ + 'billing_batch_charge_version_year_id AS id', + 'billing_batch_id AS bill_run_id', + 'charge_version_id', + 'financial_year_ending', + 'status', + 'transaction_type AS batch_type', + 'is_summer AS summer', + 'has_two_part_agreement AS two_part_agreement', + 'is_chargeable AS chargeable', + 'date_created AS created_at', + 'date_updated AS updated_at' + ])) + }) +} + +exports.down = function (knex) { + return knex + .schema + .dropViewIfExists(viewName) +} From 8f85fe805cef856e4f8305991e5e99c54459e687 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 1 Mar 2024 10:48:24 +0000 Subject: [PATCH 4/8] Create BillRunChargeVersionYear model --- .../bill-run-charge-version-year.model.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/models/bill-run-charge-version-year.model.js diff --git a/app/models/bill-run-charge-version-year.model.js b/app/models/bill-run-charge-version-year.model.js new file mode 100644 index 0000000000..41779abd0d --- /dev/null +++ b/app/models/bill-run-charge-version-year.model.js @@ -0,0 +1,39 @@ +'use strict' + +/** + * Model for bill_run_charge_version_years (water.billing_batch_charge_version_years) + * @module BillRunChargeVersionYearModel + */ + +const { Model } = require('objection') + +const BaseModel = require('./base.model.js') + +class BillRunChargeVersionYearModel extends BaseModel { + static get tableName () { + return 'billRunChargeVersionYears' + } + + static get relationMappings () { + return { + billRun: { + relation: Model.BelongsToOneRelation, + modelClass: 'bill-run.model', + join: { + from: 'billRunChargeVersionYears.billRunId', + to: 'billRuns.id' + } + }, + chargeVersion: { + relation: Model.BelongsToOneRelation, + modelClass: 'charge-version.model', + join: { + from: 'billRunChargeVersionYears.chargeVersionId', + to: 'chargeVersions.id' + } + } + } + } +} + +module.exports = BillRunChargeVersionYearModel From aa5572d55da97a3ec642002b4cf33a5019da5108 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 1 Mar 2024 10:50:15 +0000 Subject: [PATCH 5/8] Create BillRunChargeVersionYear helper --- .../bill-run-charge-version-year.helper.js | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/support/helpers/bill-run-charge-version-year.helper.js diff --git a/test/support/helpers/bill-run-charge-version-year.helper.js b/test/support/helpers/bill-run-charge-version-year.helper.js new file mode 100644 index 0000000000..02024d3503 --- /dev/null +++ b/test/support/helpers/bill-run-charge-version-year.helper.js @@ -0,0 +1,60 @@ +'use strict' + +/** + * @module BillRunChargeVersionYearHelper + */ + +const BillRunChargeVersionYearModel = require('../../../app/models/bill-run-charge-version-year.model.js') +const { generateUUID } = require('../../../app/lib/general.lib.js') + +/** + * Add a new bill run charge version year record + * + * If no `data` is provided, default values will be used. These are + * + * - `billRunId` - [random UUID] + * - `chargeVersionId` - [random UUID] + * - `financialYearEnding` - 2024 + * - `batchType` - annual + * - `summer` - false + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + * + * @returns {Promise} The instance of the newly created record + */ +function add (data = {}) { + const insertData = defaults(data) + + return BillRunChargeVersionYearModel.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 = { + billRunId: generateUUID(), + chargeVersionId: generateUUID(), + financialYearEnding: 2024, + status: 'ready', + batchType: 'annual', + summer: false + } + + return { + ...defaults, + ...data + } +} + +module.exports = { + add, + defaults +} From 23f66c5349429c9ee9e1ea664d398c4927533ce1 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 1 Mar 2024 10:51:23 +0000 Subject: [PATCH 6/8] Create BillRunChargeVersionYear unit test --- ...bill-run-charge-version-year.model.test.js | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 test/models/bill-run-charge-version-year.model.test.js diff --git a/test/models/bill-run-charge-version-year.model.test.js b/test/models/bill-run-charge-version-year.model.test.js new file mode 100644 index 0000000000..c2ac36e44d --- /dev/null +++ b/test/models/bill-run-charge-version-year.model.test.js @@ -0,0 +1,102 @@ +'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/bill-run.helper.js') +const BillRunModel = require('../../app/models/bill-run.model.js') +const BillRunChargeVersionYearHelper = require('../support/helpers/bill-run-charge-version-year.helper.js') +const ChargeVersionHelper = require('../support/helpers/charge-version.helper.js') +const ChargeVersionModel = require('../../app/models/charge-version.model.js') +const DatabaseHelper = require('../support/helpers/database.helper.js') + +// Thing under test +const BillRunChargeVersionYearModel = require('../../app/models/bill-run-charge-version-year.model.js') + +describe('Bill Run Charge Version Year model', () => { + let testRecord + + beforeEach(async () => { + await DatabaseHelper.clean() + }) + + describe('Basic query', () => { + beforeEach(async () => { + testRecord = await BillRunChargeVersionYearHelper.add() + }) + + it('can successfully run a basic query', async () => { + const result = await BillRunChargeVersionYearModel.query().findById(testRecord.id) + + expect(result).to.be.an.instanceOf(BillRunChargeVersionYearModel) + expect(result.id).to.equal(testRecord.id) + }) + }) + + describe('Relationships', () => { + describe('when linking to bill run', () => { + let testBillRun + + beforeEach(async () => { + testBillRun = await BillRunHelper.add() + + const { id: billRunId } = testBillRun + testRecord = await BillRunChargeVersionYearHelper.add({ billRunId }) + }) + + it('can successfully run a related query', async () => { + const query = await BillRunChargeVersionYearModel.query() + .innerJoinRelated('billRun') + + expect(query).to.exist() + }) + + it('can eager load the bill run', async () => { + const result = await BillRunChargeVersionYearModel.query() + .findById(testRecord.id) + .withGraphFetched('billRun') + + expect(result).to.be.instanceOf(BillRunChargeVersionYearModel) + expect(result.id).to.equal(testRecord.id) + + expect(result.billRun).to.be.an.instanceOf(BillRunModel) + expect(result.billRun).to.equal(testBillRun) + }) + }) + + describe('when linking to charge version', () => { + let testChargeVersion + + beforeEach(async () => { + testChargeVersion = await ChargeVersionHelper.add() + + const { id: chargeVersionId } = testChargeVersion + testRecord = await BillRunChargeVersionYearHelper.add({ chargeVersionId }) + }) + + it('can successfully run a related query', async () => { + const query = await BillRunChargeVersionYearModel.query() + .innerJoinRelated('chargeVersion') + + expect(query).to.exist() + }) + + it('can eager load the charge version', async () => { + const result = await BillRunChargeVersionYearModel.query() + .findById(testRecord.id) + .withGraphFetched('chargeVersion') + + expect(result).to.be.instanceOf(BillRunChargeVersionYearModel) + expect(result.id).to.equal(testRecord.id) + + expect(result.chargeVersion).to.be.an.instanceOf(ChargeVersionModel) + expect(result.chargeVersion).to.equal(testChargeVersion) + }) + }) + }) +}) From 3152d10c1f8c055e7af20e319e852b6f68b252c9 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 1 Mar 2024 10:53:18 +0000 Subject: [PATCH 7/8] Link BillRunChargeVersionYear to ChargeVersion --- app/models/charge-version.model.js | 8 +++++ test/models/charge-version.model.test.js | 37 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/app/models/charge-version.model.js b/app/models/charge-version.model.js index bb8df7f515..31229191ca 100644 --- a/app/models/charge-version.model.js +++ b/app/models/charge-version.model.js @@ -24,6 +24,14 @@ class ChargeVersionModel extends BaseModel { to: 'billingAccounts.id' } }, + billRunChargeVersionYears: { + relation: Model.HasManyRelation, + modelClass: 'bill-run-charge-version-year.model', + join: { + from: 'chargeVersions.id', + to: 'billRunChargeVersionYears.chargeVersionId' + } + }, licence: { relation: Model.BelongsToOneRelation, modelClass: 'licence.model', diff --git a/test/models/charge-version.model.test.js b/test/models/charge-version.model.test.js index 49df96a5a2..e5307a5f8f 100644 --- a/test/models/charge-version.model.test.js +++ b/test/models/charge-version.model.test.js @@ -10,6 +10,8 @@ const { expect } = Code // Test helpers const BillingAccountHelper = require('../support/helpers/billing-account.helper.js') const BillingAccountModel = require('../../app/models/billing-account.model.js') +const BillRunChargeVersionYearHelper = require('../support/helpers/bill-run-charge-version-year.helper.js') +const BillRunChargeVersionYearModel = require('../../app/models/bill-run-charge-version-year.model.js') const ChangeReasonHelper = require('../support/helpers/change-reason.helper.js') const ChangeReasonModel = require('../../app/models/change-reason.model.js') const ChargeReferenceHelper = require('../support/helpers/charge-reference.helper.js') @@ -71,6 +73,41 @@ describe('Charge Version model', () => { }) }) + describe('when linking to bill run charge version years', () => { + let testBillRunChargeVersionYears + + beforeEach(async () => { + const { id: chargeVersionId } = testRecord + + testBillRunChargeVersionYears = [] + for (let i = 0; i < 2; i++) { + const billRunChargeVersionYear = await BillRunChargeVersionYearHelper.add({ chargeVersionId }) + testBillRunChargeVersionYears.push(billRunChargeVersionYear) + } + }) + + it('can successfully run a related query', async () => { + const query = await ChargeVersionModel.query() + .innerJoinRelated('billRunChargeVersionYears') + + expect(query).to.exist() + }) + + it('can eager load the charge references', async () => { + const result = await ChargeVersionModel.query() + .findById(testRecord.id) + .withGraphFetched('billRunChargeVersionYears') + + expect(result).to.be.instanceOf(ChargeVersionModel) + expect(result.id).to.equal(testRecord.id) + + expect(result.billRunChargeVersionYears).to.be.an.array() + expect(result.billRunChargeVersionYears[0]).to.be.an.instanceOf(BillRunChargeVersionYearModel) + expect(result.billRunChargeVersionYears).to.include(testBillRunChargeVersionYears[0]) + expect(result.billRunChargeVersionYears).to.include(testBillRunChargeVersionYears[1]) + }) + }) + describe('when linking to licence', () => { let testLicence From 19ad11263575554b01f811771d6cac43e5e1f994 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Fri, 1 Mar 2024 10:58:38 +0000 Subject: [PATCH 8/8] Add comment to model --- app/models/bill-run-charge-version-year.model.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/models/bill-run-charge-version-year.model.js b/app/models/bill-run-charge-version-year.model.js index 41779abd0d..a1fe1bd43e 100644 --- a/app/models/bill-run-charge-version-year.model.js +++ b/app/models/bill-run-charge-version-year.model.js @@ -9,6 +9,19 @@ const { Model } = require('objection') const BaseModel = require('./base.model.js') +/** + * Represents an instance of a bill run charge version year record + * + * For reference, the bill run charge version year record is a 'nothing' record! Certainly as far as we are concerned. + * + * They use this table when generating pre-sroc bill runs. We think it was mainly to support supplementary bill runs + * and the fact they cover a range of years rather than a single period. + * + * We haven't needed to do anything like this in our engine and nothing else appears to use this table. The only reason + * we reference it is to delete stuff when a bill run gets cancelled. + * + * Welcome to dealing with the legacy database schema! ¯\_(ツ)_/¯ + */ class BillRunChargeVersionYearModel extends BaseModel { static get tableName () { return 'billRunChargeVersionYears'