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

Adding a periods overlap helper #548

Merged
merged 6 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
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
48 changes: 48 additions & 0 deletions app/lib/general.lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,53 @@ function generateUUID () {
return randomUUID({ disableEntropyCache: true })
}

/**
* Tests if one set of periods (represented by a start and end date) overlaps with another
*
* Added as part of two-part tariff and the need to match returns and lines to charge elements. A common complication in
* WRLS is the need to convert an abstract period, for example '1 Nov to 31 Mar' to a concrete period (2023-11-01 to
* 2024-03-31). It gets even more complex when the period crosses over another, for example the start or end of a
* billing period. Then the only way to represent the abstract period in a usable way is as 2 separate periods. Hence
* this service deals with arrays of periods.
*
* > See the comments for `DetermineAbstractionPeriodService` to better understand the complexity of going from
* > abstract to concrete periods
*
* Then there are times we need to test if the periods of one thing overlap with another. In two-part tariff that's the
* abstraction periods of a charge element with those of a return. If _any_ of the periods overlap then the return is
* 'matched' and can be allocated to the charge element.
*
* This method iterates through the `referencePeriods`. It then filters the `checkPeriods` by testing if any of them
* overlap with the `referencePeriod`. If any do `checkPeriods.filter()` will return a non-empty array at which point
* `periodsOverlap()` will return `true`.
*
* Else, having compared all the `checkPeriods` against each `referencePeriod` and finding no overlaps the function will
* return false.
*
* @param {Object[]} referencePeriods Each period is an object containing a `startDate` and `endDate` property
* @param {Object[]} checkPeriods Each period is an object containing a `startDate` and `endDate` property. These
* periods will be checked against the `referencePeriods for any overlaps
*
* @returns {boolean} Returns true if there _any_ check period overlaps with a reference period, else false
*/
function periodsOverlap (referencePeriods, checkPeriods) {
for (const referencePeriod of referencePeriods) {
const overLappingPeriods = checkPeriods.filter((checkPeriod) => {
if (checkPeriod.startDate > referencePeriod.endDate || referencePeriod.startDate > checkPeriod.endDate) {
return false
}

return true
})

if (overLappingPeriods.length) {
return true
}
}

return false
}

/**
* Returns the current date and time as an ISO string
*
Expand All @@ -44,5 +91,6 @@ function timestampForPostgres () {

module.exports = {
generateUUID,
periodsOverlap,
timestampForPostgres
}
125 changes: 125 additions & 0 deletions test/lib/general.lib.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,129 @@ describe('RequestLib', () => {
expect(result).to.equal('2015-10-21T20:31:57.000Z')
})
})

describe('#periodsOverlap', () => {
Beckyrose200 marked this conversation as resolved.
Show resolved Hide resolved
let referencePeriod
let checkPeriod

describe('when given periods that do not overlap', () => {
beforeEach(() => {
referencePeriod = [{
startDate: new Date('2023-02-01'),
endDate: new Date('2023-02-02')
}]

checkPeriod = [{
startDate: new Date('2023-01-01'),
endDate: new Date('2023-01-31')
}]
})

it('returns false', () => {
const result = GeneralLib.periodsOverlap(referencePeriod, checkPeriod)

expect(result).to.equal(false)
})
})

describe('when a check period overlaps the start of a reference period', () => {
beforeEach(() => {
referencePeriod = [{
startDate: new Date('2023-02-01'),
endDate: new Date('2023-02-28')
}]

checkPeriod = [{
startDate: new Date('2023-01-15'),
endDate: new Date('2023-02-15')
}]
})

it('returns true', () => {
const result = GeneralLib.periodsOverlap(referencePeriod, checkPeriod)

expect(result).to.equal(true)
})
})

describe('when a check period overlaps the end of a reference period', () => {
beforeEach(() => {
referencePeriod = [{
startDate: new Date('2023-01-01'),
endDate: new Date('2023-01-31')
}]

checkPeriod = [{
startDate: new Date('2023-01-15'),
endDate: new Date('2023-02-15')
}]
})

it('returns true', () => {
const result = GeneralLib.periodsOverlap(referencePeriod, checkPeriod)

expect(result).to.equal(true)
})
})

describe('when a reference period is completely inside a check period', () => {
beforeEach(() => {
referencePeriod = [{
startDate: new Date('2023-02-01'),
endDate: new Date('2023-02-15')
}]

checkPeriod = [{
startDate: new Date('2023-01-01'),
endDate: new Date('2023-02-28')
}]
})

it('returns true', () => {
const result = GeneralLib.periodsOverlap(referencePeriod, checkPeriod)

expect(result).to.equal(true)
})
})

describe('when a check period is completely inside a reference period', () => {
beforeEach(() => {
referencePeriod = [{
startDate: new Date('2023-01-01'),
endDate: new Date('2023-02-28')
}]

checkPeriod = [{
startDate: new Date('2023-02-01'),
endDate: new Date('2023-02-15')
}]
})

it('returns true', () => {
const result = GeneralLib.periodsOverlap(referencePeriod, checkPeriod)

expect(result).to.equal(true)
})
})

describe('when the periods are the same', () => {
beforeEach(() => {
referencePeriod = [{
startDate: new Date('2023-02-01'),
endDate: new Date('2023-02-28')
}]

checkPeriod = [{
startDate: new Date('2023-02-01'),
endDate: new Date('2023-02-28')
}]
})

it('returns true', () => {
const result = GeneralLib.periodsOverlap(referencePeriod, checkPeriod)

expect(result).to.equal(true)
})
})
})
})
Loading