From c76d25c69b280b760e9ac48473990769c5acd99b Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Mon, 6 Nov 2023 22:49:18 +0000 Subject: [PATCH] https://eaflood.atlassian.net/browse/WATER-4156 (#508) https://eaflood.atlassian.net/browse/WATER-4156 Related to WATER-4155 and the work to replace the legacy bill page with our own we're now starting to build the page it will link to; a view of the bill licence and all its transactions. We added presenters for each of the transaction types - [Minimum Charge Transaction presenter](https://github.com/DEFRA/water-abstraction-system/pull/505) - [Compensation Charge Transaction presenter](https://github.com/DEFRA/water-abstraction-system/pull/506) - [Standard Charge Transaction presenter](https://github.com/DEFRA/water-abstraction-system/pull/507) Now we add a presenter that brings those together with details about the bill and bill run. --- .../view-bill-licence.presenter.js | 101 +++++++++++ .../view-bill-licence.presenter.test.js | 169 ++++++++++++++++++ 2 files changed, 270 insertions(+) create mode 100644 app/presenters/bill-licences/view-bill-licence.presenter.js create mode 100644 test/presenters/bill-licences/view-bill-licence.presenter.test.js diff --git a/app/presenters/bill-licences/view-bill-licence.presenter.js b/app/presenters/bill-licences/view-bill-licence.presenter.js new file mode 100644 index 0000000000..30c85abfac --- /dev/null +++ b/app/presenters/bill-licences/view-bill-licence.presenter.js @@ -0,0 +1,101 @@ +'use strict' + +/** + * Formats data for a bill licence including its transactions into what is needed for the bill-licence page + * @module BillLicencesTransactionsPresenter + */ + +const { formatMoney } = require('../base.presenter.js') +const CompensationChargeTransactionPresenter = require('./compensation-charge-transaction.presenter.js') +const MinimumChargeTransactionPresenter = require('./minimum-charge-transaction.presenter.js') +const StandardChargeTransactionPresenter = require('./standard-charge-transaction.presenter.js') + +/** + * Formats data for a bill licence including its transactions into what is needed for the bill-licence page + * + * @param {module:BillLicenceModel} billLicence an instance of `BillLicence` with linked bill, bill run and + * transactions + * + * @returns {Object} a formatted representation of the bill licence and its transactions specifically for the + * view bill-licence page + */ +function go (billLicence) { + const { bill, licenceId, licenceRef, transactions } = billLicence + + const displayCreditDebitTotals = _displayCreditDebitTotals(bill.billRun) + + const { creditTotal, debitTotal, total } = _totals(transactions) + + return { + accountNumber: bill.invoiceAccountNumber, + billingInvoiceId: bill.billingInvoiceId, + creditTotal, + debitTotal, + displayCreditDebitTotals, + licenceId, + licenceRef, + scheme: bill.billRun.scheme, + tableCaption: _tableCaption(transactions), + total, + transactions: _transactions(transactions) + } +} + +function _displayCreditDebitTotals (billRun) { + const { batchType, source } = billRun + + return batchType === 'supplementary' && source === 'wrls' +} + +function _tableCaption (transactions) { + const numberOfTransactions = transactions.length + + if (numberOfTransactions === 1) { + return '1 transaction' + } + + return `${numberOfTransactions} transactions` +} + +function _totals (transactions) { + let creditTotal = 0 + let debitTotal = 0 + let total = 0 + + transactions.forEach((transaction) => { + const { isCredit, netAmount } = transaction + + total += netAmount + if (isCredit) { + creditTotal += netAmount + } else { + debitTotal += netAmount + } + }) + + return { + creditTotal: formatMoney(creditTotal), + debitTotal: formatMoney(debitTotal), + total: formatMoney(total) + } +} + +function _transactions (transactions) { + return transactions.map((transaction) => { + const { chargeType } = transaction + + if (chargeType === 'minimum_charge') { + return MinimumChargeTransactionPresenter.go(transaction) + } + + if (chargeType === 'compensation') { + return CompensationChargeTransactionPresenter.go(transaction) + } + + return StandardChargeTransactionPresenter.go(transaction) + }) +} + +module.exports = { + go +} diff --git a/test/presenters/bill-licences/view-bill-licence.presenter.test.js b/test/presenters/bill-licences/view-bill-licence.presenter.test.js new file mode 100644 index 0000000000..c346f96385 --- /dev/null +++ b/test/presenters/bill-licences/view-bill-licence.presenter.test.js @@ -0,0 +1,169 @@ +'use strict' + +// Test framework dependencies +const Lab = require('@hapi/lab') +const Code = require('@hapi/code') +const Sinon = require('sinon') + +const { describe, it, beforeEach, afterEach } = exports.lab = Lab.script() +const { expect } = Code + +// Things we need to stub +const CompensationChargeTransactionPresenter = require('../../../app/presenters/bill-licences/compensation-charge-transaction.presenter.js') +const MinimumChargeTransactionPresenter = require('../../../app/presenters/bill-licences/minimum-charge-transaction.presenter.js') +const StandardChargeTransactionPresenter = require('../../../app/presenters/bill-licences/standard-charge-transaction.presenter.js') + +// Thing under test +const ViewBillLicencePresenter = require('../../../app/presenters/bill-licences/view-bill-licence.presenter.js') + +describe('View Bill Licence presenter', () => { + let billLicence + + afterEach(() => { + Sinon.restore() + }) + + describe('when provided with a populated bill licence', () => { + beforeEach(() => { + billLicence = _testBillLicence() + + Sinon.stub(CompensationChargeTransactionPresenter, 'go').returns({ chargeType: 'compensation' }) + Sinon.stub(MinimumChargeTransactionPresenter, 'go').returns({ chargeType: 'minimum_charge' }) + Sinon.stub(StandardChargeTransactionPresenter, 'go').returns({ chargeType: 'standard' }) + }) + + describe("the 'displayCreditDebitTotals' property", () => { + describe('when the linked bill run is not supplementary', () => { + beforeEach(() => { + billLicence.bill.billRun.batchType = 'annual' + }) + + it('returns false', () => { + const result = ViewBillLicencePresenter.go(billLicence) + + expect(result.displayCreditDebitTotals).to.be.false() + }) + }) + + describe('when the bill run is supplementary', () => { + describe('and was created in WRLS', () => { + beforeEach(() => { + billLicence.bill.billRun.batchType = 'supplementary' + }) + + it('returns true', () => { + const result = ViewBillLicencePresenter.go(billLicence) + + expect(result.displayCreditDebitTotals).to.be.true() + }) + }) + + describe('but was created in NALD', () => { + beforeEach(() => { + billLicence.bill.billRun.batchType = 'supplementary' + billLicence.bill.billRun.source = 'nald' + }) + + it('returns false', () => { + const result = ViewBillLicencePresenter.go(billLicence) + + expect(result.displayCreditDebitTotals).to.be.false() + }) + }) + }) + }) + + describe("the 'tableCaption' property", () => { + describe('when there is only 1 transaction', () => { + beforeEach(() => { + billLicence.transactions = [billLicence.transactions[0]] + }) + + it('returns the count and caption singular', () => { + const result = ViewBillLicencePresenter.go(billLicence) + + expect(result.tableCaption).to.equal('1 transaction') + }) + }) + + describe('when there are multiple transactions', () => { + it('returns the count and caption pluralised', () => { + const result = ViewBillLicencePresenter.go(billLicence) + + expect(result.tableCaption).to.equal('4 transactions') + }) + }) + }) + + it('correctly presents the data', () => { + const result = ViewBillLicencePresenter.go(billLicence) + + // NOTE: The transaction details we pass in and what we get back is not what would actually happen. Our + // transaction presenter tests exhaust what we expect back for all scenarios. What we are confirming though is + // that depending on a transaction's charge type ViewBillLicencePresenter will call the relevant transaction + // presenter. Plus, that things like the totals and the table caption is returned as expected. + expect(result).to.equal({ + accountNumber: 'W88898987A', + billingInvoiceId: '5a5b313b-e707-490a-a693-799339941e4f', + creditTotal: '£10.00', + debitTotal: '£298.37', + displayCreditDebitTotals: true, + licenceId: '2eaa831d-7bd6-4b0a-aaf1-3aacafec6bf2', + licenceRef: 'WA/055/0017/013', + scheme: 'alcs', + tableCaption: '4 transactions', + total: '£288.37', + transactions: [ + { chargeType: 'standard' }, + { chargeType: 'standard' }, + { chargeType: 'compensation' }, + { chargeType: 'minimum_charge' } + ] + }) + }) + }) +}) + +function _testBillLicence () { + return { + billingInvoiceLicenceId: 'a4fbaa27-a91c-4328-a1b8-774ade11027b', + licenceId: '2eaa831d-7bd6-4b0a-aaf1-3aacafec6bf2', + licenceRef: 'WA/055/0017/013', + bill: { + billingInvoiceId: '5a5b313b-e707-490a-a693-799339941e4f', + invoiceAccountNumber: 'W88898987A', + billRun: { + billingBatchId: '0e61c36f-f22f-4534-8247-b73a97f551b5', + batchType: 'supplementary', + scheme: 'alcs', + source: 'wrls' + } + }, + transactions: [ + { + billingTransactionId: '5858e36f-5a8e-4f5c-84b3-cbca26624d67', + chargeType: 'standard', + isCredit: false, + netAmount: 29837 + }, + { + billingTransactionId: '5858e36f-5a8e-4f5c-84b3-cbca26624d67', + chargeType: 'standard', + isCredit: true, + netAmount: -1000 + }, + { + billingTransactionId: '14d6d530-7f07-4e4b-ac17-b8ade9f5b21a', + chargeType: 'compensation', + isCredit: false, + netAmount: 0 + }, + { + billingTransactionId: '23f43d51-8880-4e30-89da-40231cb8dea2', + chargeType: 'minimum_charge', + isCredit: false, + netAmount: 0 + } + ] + } +}