Skip to content

Commit

Permalink
Create Reverse Billing Batch Licences service (#167)
Browse files Browse the repository at this point in the history
https://eaflood.atlassian.net/browse/WATER-3936

When we generate a supplementary bill run we are required to reverse the debits from the previous one. Essentially, when a licence is flagged for supplementary billing, for whatever reason, we reverse any previous bill runs and generate a new one.

With the exception of Annual billing, because we know any previous supplementary bill run will have reversed the debits on the one that came before, any new runs also just need to focus on the ones previous to them.

This is complicated, however, by the fact we then need to ignore transactions that cancel each other out. For example, Customer **ABC1** was billed 120 days for licence **LIC001** on the previous bill run. Our supplementary process calculates 120 days for the new charge version. These cancel each other out so should not be included.

So, for each charge version `FetchChargeVersionsService` returns we'll need to identify if they are linked to a previous `billing_batch`. From there we'll need to locate the transactions, determine if they cancel out what we've calculated and if not include them as credits in the supplementary bill run we're creating.

> Note - This PR originated to create a service to solve a part of this problem. It then became our 'SPIKE' branch and has culminated in all the changes needed. We _will_ be doing further cleanup and documentation after this change has been merged.
  • Loading branch information
StuAA78 authored Mar 24, 2023
1 parent 5a7768e commit a101846
Show file tree
Hide file tree
Showing 11 changed files with 643 additions and 230 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ async function _fetch (regionId, billingPeriod) {
.where('chargeVersions.status', 'current')
.where('chargeVersions.startDate', '>=', billingPeriod.startDate)
.where('chargeVersions.startDate', '<=', billingPeriod.endDate)
.orderBy([
{ column: 'chargeVersions.invoiceAccountId' },
{ column: 'chargeVersions.licenceId' }
])
.withGraphFetched('licence')
.modifyGraph('licence', builder => {
builder.select([
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict'

/**
* Fetches the previously billed transactions that match the invoice, licence and year provided
* @module FetchPreviousBillingTransactionsService
*/

const { db } = require('../../../db/db.js')

async function go (billingInvoice, billingInvoiceLicence, financialYearEnding) {
const billingTransactions = await _fetch(
billingInvoiceLicence.licenceId,
billingInvoice.invoiceAccountId,
financialYearEnding
)

return billingTransactions
}

async function _fetch (licenceId, invoiceAccountId, financialYearEnding) {
return db
.select(
'bt.authorisedDays',
'bt.billableDays',
'bt.isWaterUndertaker',
'bt.chargeElementId',
'bt.startDate',
'bt.endDate',
'bt.source',
'bt.season',
'bt.loss',
'bt.isCredit',
'bt.chargeType',
'bt.authorisedQuantity',
'bt.billableQuantity',
'bt.description',
'bt.volume',
'bt.section126Factor',
'bt.section127Agreement',
'bt.section130Agreement',
'bt.isTwoPartSecondPartCharge',
'bt.scheme',
'bt.aggregateFactor',
'bt.adjustmentFactor',
'bt.chargeCategoryCode',
'bt.chargeCategoryDescription',
'bt.isSupportedSource',
'bt.supportedSourceName',
'bt.isWaterCompanyCharge',
'bt.isWinterOnly',
'bt.purposes',
'validBillingInvoices.invoiceAccountId',
'validBillingInvoices.invoiceAccountNumber'
)
.from('water.billingTransactions as bt')
.innerJoin(
db
.select(
'bil.billingInvoiceLicenceId',
'bi.invoiceAccountId',
'bi.invoiceAccountNumber'
)
.max('bil.date_created as latest_date_created')
.from('water.billingInvoiceLicences as bil')
.innerJoin('water.billingInvoices as bi', 'bil.billingInvoiceId', 'bi.billingInvoiceId')
.innerJoin('water.billingBatches as bb', 'bi.billingBatchId', 'bb.billingBatchId')
.where({
'bil.licenceId': licenceId,
'bi.invoiceAccountId': invoiceAccountId,
'bi.financialYearEnding': financialYearEnding,
'bb.status': 'sent',
'bb.scheme': 'sroc'
})
.groupBy('bil.billingInvoiceLicenceId', 'bi.invoiceAccountId', 'bi.invoiceAccountNumber')
.as('validBillingInvoices'),
'bt.billingInvoiceLicenceId', 'validBillingInvoices.billingInvoiceLicenceId'
)
.where({
'bt.isCredit': false
})
}

module.exports = {
go
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,22 @@ const { randomUUID } = require('crypto')
* @returns {Object} A result object containing either the found or generated billing invoice licence object, and an
* array of generated billing invoice licences which includes the one being returned
*/
function go (generatedBillingInvoiceLicences, billingInvoiceId, licence) {
let billingInvoiceLicence = _existing(generatedBillingInvoiceLicences, billingInvoiceId, licence.licenceId)

if (billingInvoiceLicence) {
return {
billingInvoiceLicence,
billingInvoiceLicences: generatedBillingInvoiceLicences
}
function go (currentBillingInvoiceLicence, billingInvoiceId, licence) {
if (
currentBillingInvoiceLicence?.billingInvoiceId === billingInvoiceId &&
currentBillingInvoiceLicence?.licenceId === licence.licenceId
) {
return currentBillingInvoiceLicence
}

billingInvoiceLicence = {
const billingInvoiceLicence = {
billingInvoiceId,
billingInvoiceLicenceId: randomUUID({ disableEntropyCache: true }),
licenceRef: licence.licenceRef,
licenceId: licence.licenceId
}
const updatedBillingInvoiceLicences = [...generatedBillingInvoiceLicences, billingInvoiceLicence]

return {
billingInvoiceLicence,
billingInvoiceLicences: updatedBillingInvoiceLicences
}
}

function _existing (generatedBillingInvoiceLicences, billingInvoiceId, licenceId) {
return generatedBillingInvoiceLicences.find((invoiceLicence) => {
return (billingInvoiceId === invoiceLicence.billingInvoiceId && licenceId === invoiceLicence.licenceId)
})
return billingInvoiceLicence
}

module.exports = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,14 @@ const InvoiceAccountModel = require('../../models/crm-v2/invoice-account.model.j
* @returns {Object} A result object containing either the found or generated billing invoice object, and an array of
* generated billing invoices which includes the one being returned
*/
async function go (generatedBillingInvoices, invoiceAccountId, billingBatchId, financialYearEnding) {
let billingInvoice = _existing(generatedBillingInvoices, invoiceAccountId)

if (billingInvoice) {
return {
billingInvoice,
billingInvoices: generatedBillingInvoices
}
async function go (currentBillingInvoice, invoiceAccountId, billingBatchId, financialYearEnding) {
if (currentBillingInvoice?.invoiceAccountId === invoiceAccountId) {
return currentBillingInvoice
}

const invoiceAccount = await InvoiceAccountModel.query().findById(invoiceAccountId)

billingInvoice = {
const billingInvoice = {
billingBatchId,
financialYearEnding,
invoiceAccountId,
Expand All @@ -58,18 +53,8 @@ async function go (generatedBillingInvoices, invoiceAccountId, billingBatchId, f
invoiceAccountNumber: invoiceAccount.invoiceAccountNumber,
isCredit: false
}
const updatedBillingInvoices = [...generatedBillingInvoices, billingInvoice]

return {
billingInvoice,
billingInvoices: updatedBillingInvoices
}
}

function _existing (generatedBillingInvoices, invoiceAccountId) {
return generatedBillingInvoices.find((invoice) => {
return invoiceAccountId === invoice.invoiceAccountId
})
return billingInvoice
}

module.exports = {
Expand Down
Loading

0 comments on commit a101846

Please sign in to comment.