Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add comments to AbstractionBillingPeriodService #93

Merged
merged 2 commits into from
Jan 20, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,42 @@
* @module AbstractionBillingPeriodService
*/

/**
* Calculate for a given charge purpose and billing period the 'billable' abstraction periods
*
* In WRLS the charge purpose, linked to a charge version via the charge element, holds the abstraction period
* information. The abstraction period is the time when a licensee is permitted to abstract water. They are held as a
* start and end day and month, for example 1 Apr to 31 Oct. They do not have years because the intent is they are the
* same period no matter what year it is.
*
* The charge a licensee is expected to pay is based on how much of an abstraction period is 'billable' within a given
* billing period, for example, 2022-04-01 to 2023-03-31.
*
* The problem is abstraction periods do not intersect nicely with billing periods. The abstraction might start or end
* outside of the billing period, and apply twice! For example, 1 Jan to 30 Jun intersects twice with a billing period
* of 2022-04-01 to 2023-03-31.
*
* - 1 Jan 2022 to 30 Jun 2022
* - 1 Jan 2023 to 30 Jun 2023
*
* This is why we always return 2 abstraction periods for each charge purpose. For each period, we calculate the
* 'billable days'. This is how much of the period intersects with the billing period. Finally, we add a 'consider'
* flag. This is a boolean which identifies whether a period intersects the billing period. For example, if the
* abstraction period was 1 Jan to 31 Mar, only one of the periods we create would intersect with a billing period of
* 2022-04-01 to 2023-03-31
*
* - 1 Jan 2022 to 31 Mar 2022 - `false`
* - 1 Jan 2023 to 31 Mar 2023 - `true`
*
* @param {Object} billingPeriod Object that has a `startDate` and `endDate` that defines the billing period
* @param {module:ChargePurposeModel} chargePurpose The abstraction period to be checked
*
* @returns {Object[]} An array of abstraction periods
* @returns {Date} periods[].startDate
* @returns {Date} periods[].endDate
* @returns {boolean} periods[].consider
* @returns {number} periods[].billableDays
*/
function go (billingPeriod, chargePurpose) {
const abstractionPeriods = _abstractionPeriods(billingPeriod, chargePurpose)

Expand All @@ -15,6 +51,54 @@ function go (billingPeriod, chargePurpose) {
return abstractionPeriods
}

/**
* Create the abstraction billing periods
*
* Before we can calculate the billable days and whether a period should be considered, we first have to assign a year
* to our abstraction start and end values, for example 1 Apr to 31 Oct.
*
* ## In-year
*
* If the abstraction period end month is _after_ the start month, for example 01-Jan to 31-May, then we assign the
* billing period's end year year to both the abstraction start and end dates.
*
* - **Billing period** 01-Apr-2022 to 31-Mar-2023
* - **Abstraction period** 01-Jan to 31-May
*
* **Result:** Abstraction period is 01-Jan-2023 to 31-May-2023
*
* We then create a previous period by deducting 1 year from the calculated abstraction period.
*
* - 01-Jan-2023 to 31-May-2023
* - 01-Jan-2022 to 31-May-2022
*
* ## Out-year
*
* If the abstraction period end month is _before_ the start month, for example 01-Nov to 31-Mar, then we assign the
* billing period's end year year to the end date, and start year to the start date.
*
* - **Billing period** 01-Apr-2022 to 31-Mar-2023
* - **Abstraction period** 01-Nov to 31-Mar
*
* **Result:** Abstraction period is 01-Nov-2022 to 31-Mar-2023
*
* We then create a previous period by deducting 1 year from the calculated abstraction period.
*
* - 01-Nov-2022 to 31-Mar-2023
* - 01-Nov-2021 to 31-Mar-2022
*
* ## Months are equal
*
* We also handle the edge case of the months being equal. If this is the case we still treat the abstraction period as
* in-year and out-year, only we use the days in the comparison instead of the months.
*
* @param {Object} billingPeriod Object that has a `startDate` and `endDate` that defines the billing period
* @param {module:ChargePurposeModel} chargePurpose The abstraction period to be checked
*
* @returns {Object[]} An array of abstraction periods
* @returns {Date} periods[].startDate
* @returns {Date} periods[].endDate
*/
function _abstractionPeriods (billingPeriod, chargePurpose) {
const billingPeriodStartYear = billingPeriod.startDate.getFullYear()
const billingPeriodEndYear = billingPeriod.endDate.getFullYear()
Expand All @@ -26,6 +110,8 @@ function _abstractionPeriods (billingPeriod, chargePurpose) {
} = chargePurpose
const firstPeriod = {}

// Reminder! Because of the unique qualities of Javascript, Year and Day are literal values, month is an index! So,
// January is actually 0, February is 1 etc. This is why we are always deducting 1 from the months.
if (endMonth === startMonth) {
if (endDay >= startDay) {
firstPeriod.startDate = new Date(billingPeriodEndYear, startMonth - 1, startDay)
Expand All @@ -50,6 +136,17 @@ function _abstractionPeriods (billingPeriod, chargePurpose) {
return [previousPeriod, firstPeriod]
}

/**
* Calculates the 'billable days' for each abstraction billing period
*
* @param {Object[]} abstractionPeriods An array of abstraction billing periods
*
* @returns {Object[]} The array abstraction periods each with a new `billableDays` property
* @returns {Date} periods[].startDate
* @returns {Date} periods[].endDate
* @returns {boolean} periods[].consider
* @returns {number} periods[].billableDays
*/
function _calculateBillableDays (abstractionPeriods) {
let difference
let billableDays
Expand All @@ -60,6 +157,23 @@ function _calculateBillableDays (abstractionPeriods) {
}
}

/**
* Adds the `consider` flag to each abstraction billing period as to whether it should be included in billing
*
* The flag is determined by calculating whether the abstraction period intersects with the billing period. For example,
* if the billing period is 2022-04-01 to 2023-03-31 and the abstraction period was 01-Nov to 31-Mar:
*
* - 01-Nov-2022 to 31-Mar-2023 = `true`
* - 01-Nov-2021 to 31-Mar-2022 = `false`
*
* @param {Object} billingPeriod Object that has a `startDate` and `endDate` that defines the billing period
* @param {Object[]} abstractionPeriods An array of abstraction billing periods
*
* @returns {Object[]} The array abstraction periods each with a new `consider` property
* @returns {Date} periods[].startDate
* @returns {Date} periods[].endDate
* c
*/
function _flagPeriodsForConsideration (billingPeriod, abstractionPeriods) {
for (const abstractionPeriod of abstractionPeriods) {
if (abstractionPeriod.startDate > billingPeriod.endDate) {
Expand Down