From 540a9986ac13aa3b9234a5314897700f30296b7e Mon Sep 17 00:00:00 2001 From: Jason Claxton <30830544+Jozzey@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:07:52 +0000 Subject: [PATCH] New charge category model for SROC supplementary bill run (#57) https://eaflood.atlassian.net/browse/WATER-3861 Behind the scenes, the charge references are held in the billing_charge_categories table. It is a lookup table as the references are static. They are linked to a charge version via the charge_elements table. --- app/models/billing-charge-category.model.js | 31 +++++++++ app/models/charge-element.model.js | 8 +++ ...112650_create_billing_charge_categories.js | 43 ++++++++++++ .../billing-charge-category.model.test.js | 30 ++++++++ test/models/charge-element.model.test.js | 9 +++ .../helpers/billing-charge-category.helper.js | 69 +++++++++++++++++++ 6 files changed, 190 insertions(+) create mode 100644 app/models/billing-charge-category.model.js create mode 100755 db/migrations/20221216112650_create_billing_charge_categories.js create mode 100644 test/models/billing-charge-category.model.test.js create mode 100644 test/support/helpers/billing-charge-category.helper.js diff --git a/app/models/billing-charge-category.model.js b/app/models/billing-charge-category.model.js new file mode 100644 index 0000000000..847aa2c25c --- /dev/null +++ b/app/models/billing-charge-category.model.js @@ -0,0 +1,31 @@ +'use strict' + +/** + * Model for water.billing_charge_categories + * @module BillingChargeCategoryModel + */ + +const { Model } = require('objection') + +const BaseModel = require('./base.model.js') + +class BillingChargeCategoryModel extends BaseModel { + static get tableName () { + return 'water.billing_charge_categories' + } + + static get relationMappings () { + return { + chargeElement: { + relation: Model.HasManyRelation, + modelClass: 'charge-element.model', + join: { + from: 'water.billing_charge_categories.billing_charge_category_id', + to: 'water.charge_elements.billing_charge_category_id' + } + } + } + } +} + +module.exports = BillingChargeCategoryModel diff --git a/app/models/charge-element.model.js b/app/models/charge-element.model.js index 7fc0b884f5..19599449c1 100644 --- a/app/models/charge-element.model.js +++ b/app/models/charge-element.model.js @@ -23,6 +23,14 @@ class ChargeElementModel extends BaseModel { from: 'water.charge_elements.charge_version_id', to: 'water.charge_versions.charge_version_id' } + }, + billingChargeCategory: { + relation: Model.BelongsToOneRelation, + modelClass: 'billing-charge-category.model', + join: { + from: 'water.charge_elements.billing_charge_category_id', + to: 'water.billing_charge_categories.billing_charge_category_id' + } } } } diff --git a/db/migrations/20221216112650_create_billing_charge_categories.js b/db/migrations/20221216112650_create_billing_charge_categories.js new file mode 100755 index 0000000000..dd2d1ce69d --- /dev/null +++ b/db/migrations/20221216112650_create_billing_charge_categories.js @@ -0,0 +1,43 @@ +'use strict' + +const tableName = 'billing_charge_categories' + +exports.up = async function (knex) { + await knex + .schema + .withSchema('water') + .createTable(tableName, table => { + // Primary Key + table.uuid('billing_charge_category_id').primary().defaultTo(knex.raw('gen_random_uuid()')) + + // Data + table.string('reference') + table.integer('subsistence_charge') + table.string('description') + table.string('short_description') + table.boolean('is_tidal') + table.string('loss_factor') + table.string('model_tier') + table.boolean('is_restricted_source') + table.bigInteger('min_volume') + table.bigInteger('max_volume') + + // Automatic timestamps + table.timestamps(false, true) + }) + + await knex.raw(` + CREATE TRIGGER update_timestamp + BEFORE UPDATE + ON water.${tableName} + FOR EACH ROW + EXECUTE PROCEDURE update_timestamp(); + `) +} + +exports.down = function (knex) { + return knex + .schema + .withSchema('water') + .dropTableIfExists(tableName) +} diff --git a/test/models/billing-charge-category.model.test.js b/test/models/billing-charge-category.model.test.js new file mode 100644 index 0000000000..76ab9b79dd --- /dev/null +++ b/test/models/billing-charge-category.model.test.js @@ -0,0 +1,30 @@ +'use strict' + +// Test framework dependencies +const Lab = require('@hapi/lab') +const Code = require('@hapi/code') + +const { describe, it } = exports.lab = Lab.script() +const { expect } = Code + +// Thing under test +const BillingChargeCategory = require('../../app/models/billing-charge-category.model.js') + +describe('Billing Charge Category model', () => { + it('can successfully run a query', async () => { + const query = await BillingChargeCategory.query() + + expect(query).to.exist() + }) + + describe('Relationships', () => { + describe('when linking to charge element', () => { + it('can successfully run a query', async () => { + const query = await BillingChargeCategory.query() + .innerJoinRelated('chargeElement') + + expect(query).to.exist() + }) + }) + }) +}) diff --git a/test/models/charge-element.model.test.js b/test/models/charge-element.model.test.js index e4299bb4da..2d3010b5bc 100644 --- a/test/models/charge-element.model.test.js +++ b/test/models/charge-element.model.test.js @@ -26,5 +26,14 @@ describe('Charge Element model', () => { expect(query).to.exist() }) }) + + describe('when linking to billing charge category', () => { + it('can successfully run a query', async () => { + const query = await ChargeElement.query() + .innerJoinRelated('billingChargeCategory') + + expect(query).to.exist() + }) + }) }) }) diff --git a/test/support/helpers/billing-charge-category.helper.js b/test/support/helpers/billing-charge-category.helper.js new file mode 100644 index 0000000000..34dbde2279 --- /dev/null +++ b/test/support/helpers/billing-charge-category.helper.js @@ -0,0 +1,69 @@ +'use strict' + +/** + * @module BillingChargeCategoryHelper + */ + +const { db } = require('../../../db/db.js') + +/** + * Add a new billing charge category + * + * If no `data` is provided, default values will be used. These are + * + * - `reference` - 4.4.5 + * - `subsistence_charge` - 12000 + * - `description` - Low loss non-tidal abstraction of restricted water up to and including 5,000 megalitres a year, where a Tier 1 model applies. + * - `short_description` - Low loss, non-tidal, restricted water, up to and including 5,000 ML/yr, Tier 1 model + * - `is_tidal` - false + * - `loss_factor` - low + * - `model_tier` - tier 1 + * - `is_restricted_source` - true + * - `min_volume` - 0 + * - `max_volume` - 5000 + * + * @param {Object} [data] Any data you want to use instead of the defaults used here or in the database + * + * @returns {string} The ID of the newly created record + */ +async function add (data = {}) { + const insertData = defaults(data) + + const result = await db.table('water.billing_charge_categories') + .insert(insertData) + .returning('billing_charge_category_id') + + return result +} + +/** + * Returns the defaults used when creating a new billing charge category + * + * 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 = { + reference: '4.4.5', + subsistence_charge: 12000, + description: 'Low loss non-tidal abstraction of restricted water up to and including 5,000 megalitres a year, where a Tier 1 model applies.', + short_description: 'Low loss, non-tidal, restricted water, up to and including 5,000 ML/yr, Tier 1 model', + is_tidal: false, + loss_factor: 'low', + model_tier: 'tier 1', + is_restricted_source: true, + min_volume: 0, + max_volume: 5000 + } + + return { + ...defaults, + ...data + } +} + +module.exports = { + add +}