From 623d033af35968b6a4080a8e5aa9603357611a25 Mon Sep 17 00:00:00 2001 From: Alan Cruikshanks Date: Sun, 3 Mar 2024 23:00:18 +0000 Subject: [PATCH] wip --- .../charging-module/send-bill-run.service.js | 45 ++++++++++++ .../wait-for-status.service.js | 70 +++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 app/services/charging-module/send-bill-run.service.js create mode 100644 app/services/charging-module/wait-for-status.service.js diff --git a/app/services/charging-module/send-bill-run.service.js b/app/services/charging-module/send-bill-run.service.js new file mode 100644 index 0000000000..4e73a2adce --- /dev/null +++ b/app/services/charging-module/send-bill-run.service.js @@ -0,0 +1,45 @@ +'use strict' + +/** + * Connects with the Charging Module to send a bill run + * @module ChargingModuleSendBillRunService + */ + +const ChargingModuleRequestLib = require('../../lib/charging-module-request.lib.js') + +/** + * Approve then send a bill run in the Charging Module API + * + * Our service does in one step what the CHA does in two; approve the bill run then 'send' it. Approving a CHA bill run + * doesn't actually do anything. It just changes the state to `approved` which the bill run has to be in before the bill + * run can be sent. + * + * Sending a bill run is the final step. Once sent a bill run cannot be changed or deleted. Sending involves the CHA + * generating a bill number for every bill in the bill run. It then generates a transaction reference for the bill run + * itself. This reference is used to name the transaction import file which the CHA also generates at this time. This + * is the file that will make it's way to SOP and be used to generate the invoice and credit notes that customers + * receive. + * + * For small bill runs the process is near instantaneous. Larger bill runs however it can take a number of seconds. + * Because of this when the request is first made the CHA switches the bill run's status to `sending`. Only when the + * process is complete does the status get set to `sent`. + * + * It's this we are waiting for because then we can extract the generated bill numbers and transaction file reference + * and apply them to our bill run records. + * + * See {@link https://defra.github.io/sroc-charging-module-api-docs/#/bill-run/SendBillRun | CHA API docs} for more + * details + * @param {string} billRunId - UUID of the charging module API bill run to send + * + * @returns {Promise} The result of the request; whether it succeeded and the response or error returned + */ +async function go (billRunId) { + const path = `v3/wrls/bill-runs/${billRunId}/generate` + const result = await ChargingModuleRequestLib.patch(path) + + return result +} + +module.exports = { + go +} diff --git a/app/services/charging-module/wait-for-status.service.js b/app/services/charging-module/wait-for-status.service.js new file mode 100644 index 0000000000..6c21013f02 --- /dev/null +++ b/app/services/charging-module/wait-for-status.service.js @@ -0,0 +1,70 @@ +'use strict' + +/** + * Connects with the Charging Module to send a bill run + * @module ChargingModuleSendBillRunService + */ + +const { setTimeout } = require('node:timers/promises') + +const BillRunStatusService = require('./bill-run-status.service.js') +const ExpandedError = require('../../errors/expanded.error.js') + +const ONE_SECOND = 1000 + +async function go (billRunId, statusToWaitFor, maximumAttempts = 120) { + let attempts = 0 + let status + + // + do { + await _pause(attempts) + + const result = await BillRunStatusService.go(billRunId) + + if (!result.succeeded) { + _requestFailed(billRunId, result) + } + + status = result.response.body.status + attempts += 1 + } while (status !== statusToWaitFor || attempts >= maximumAttempts) + + if (status !== statusToWaitFor) { + _statusNotAchieved(billRunId, statusToWaitFor, status, attempts, maximumAttempts) + } +} + +/** + * Pause for one second between requests so that we are not bombarding the Charging Module + */ +function _pause (attempts) { + // If attempts is 0 then this is the first attempt so don't pause + if (attempts === 0) { + return + } + + return setTimeout(ONE_SECOND) +} + +function _requestFailed (billRunId, result) { + const error = new ExpandedError( + 'Charging Module status request failed', + { billRunExternalId: billRunId, responseBody: result.response.body } + ) + + throw error +} + +function _statusNotAchieved (billRunId, statusToWaitFor, status, attempts, maximumAttempts) { + const error = new ExpandedError( + 'Charging Module wait for status failed', + { billRunExternalId: billRunId, statusToWaitFor, lastStatus: status, attempts, maximumAttempts } + ) + + throw error +} + +module.exports = { + go +}