From 19f476ec9af6428a15fc30b7b7f3a6d36586b435 Mon Sep 17 00:00:00 2001 From: Robert Parkinson Date: Fri, 9 Feb 2024 14:59:33 +0000 Subject: [PATCH] View licence summary abstraction periods (#721) https://eaflood.atlassian.net/browse/WATER-4329 Migrating the licence summary page from the water-abstraction-ui to the water-abstraction-system. Show periods of abstraction --- app/models/licence-version.model.js | 8 + .../licences/view-licence.presenter.js | 44 +- .../licences/fetch-licence.service.js | 21 +- app/views/licences/tabs/summary.njk | 24 + .../licences/view-licence.presenter.test.js | 459 ++++++++++++------ .../licences/view-licence.service.test.js | 2 + 6 files changed, 402 insertions(+), 156 deletions(-) diff --git a/app/models/licence-version.model.js b/app/models/licence-version.model.js index 66498e9148..9522be5a17 100644 --- a/app/models/licence-version.model.js +++ b/app/models/licence-version.model.js @@ -24,6 +24,14 @@ class LicenceVersionModel extends BaseModel { to: 'licences.id' } }, + licenceVersionPurposes: { + relation: Model.HasManyRelation, + modelClass: 'licence-version-purpose.model', + join: { + from: 'licenceVersions.id', + to: 'licenceVersionPurposes.licenceVersionId' + } + }, purposes: { relation: Model.ManyToManyRelation, modelClass: 'purpose.model', diff --git a/app/presenters/licences/view-licence.presenter.js b/app/presenters/licences/view-licence.presenter.js index ee99c4a59a..0d12d0ac04 100644 --- a/app/presenters/licences/view-licence.presenter.js +++ b/app/presenters/licences/view-licence.presenter.js @@ -5,6 +5,7 @@ * @module ViewLicencePresenter */ +const { formatAbstractionDate } = require('../base.presenter.js') const { formatLongDate } = require('../base.presenter.js') /** @@ -29,15 +30,27 @@ function go (licence) { startDate } = licence + const abstractionPeriods = _generateAbstractionPeriods(licenceVersions) + const purposes = _generatePurposes(licenceVersions) + let abstractionPeriodsAndPurposesLinkText = null + + if (abstractionPeriods && purposes) { + const abstractionPeriodsLabel = abstractionPeriods.uniqueAbstractionPeriods.length > 1 ? 'periods' : 'period' + const purposesLabel = purposes.data.length > 1 ? 'purposes' : 'purpose' + abstractionPeriodsAndPurposesLinkText = `View details of your ${purposesLabel}, ${abstractionPeriodsLabel} and amounts` + } + return { id, + abstractionPeriods, + abstractionPeriodsAndPurposesLinkText, documentId: licenceDocumentHeader.id, endDate: _endDate(expiredDate), licenceHolder: _generateLicenceHolder(licenceHolder), licenceName, licenceRef, pageTitle: `Licence ${licenceRef}`, - purposes: _generatePurposes(licenceVersions), + purposes, region: region.displayName, registeredTo, startDate: formatLongDate(startDate), @@ -53,6 +66,29 @@ function _endDate (expiredDate) { return formatLongDate(expiredDate) } +function _generateAbstractionPeriods (licenceVersions) { + if (!licenceVersions || + licenceVersions.length === 0 || + licenceVersions[0]?.licenceVersionPurposes === undefined || + licenceVersions[0]?.licenceVersionPurposes?.length === 0 + ) { + return null + } + + const formattedAbstactionPeriods = licenceVersions[0].licenceVersionPurposes.map((purpose) => { + const startDate = formatAbstractionDate(purpose.abstractionPeriodStartDay, purpose.abstractionPeriodStartMonth) + const endDate = formatAbstractionDate(purpose.abstractionPeriodEndDay, purpose.abstractionPeriodEndMonth) + return `${startDate} to ${endDate}` + }) + + const uniqueAbstractionPeriods = [...new Set(formattedAbstactionPeriods)] + + return { + caption: uniqueAbstractionPeriods.length > 1 ? 'Periods of abstraction' : 'Period of abstraction', + uniqueAbstractionPeriods + } +} + function _generateLicenceHolder (licenceHolder) { if (!licenceHolder) { return 'Unregistered licence' @@ -62,7 +98,11 @@ function _generateLicenceHolder (licenceHolder) { } function _generatePurposes (licenceVersions) { - if (!licenceVersions || licenceVersions.length === 0 || licenceVersions[0]?.purposes.length === 0) { + if (!licenceVersions || + licenceVersions.length === 0 || + licenceVersions[0]?.purposes === undefined || + licenceVersions[0]?.purposes?.length === 0 + ) { return null } const allPurposeDescriptions = licenceVersions[0].purposes.map((item) => { diff --git a/app/services/licences/fetch-licence.service.js b/app/services/licences/fetch-licence.service.js index 966f497783..eab33c4ef6 100644 --- a/app/services/licences/fetch-licence.service.js +++ b/app/services/licences/fetch-licence.service.js @@ -51,7 +51,26 @@ async function _fetchLicence (id) { 'displayName' ]) }) - .withGraphFetched('licenceVersions.[purposes]') + .withGraphFetched('licenceDocumentHeader') + .modifyGraph('licenceDocumentHeader', (builder) => { + builder.select([ + 'licenceDocumentHeaders.id' + ]) + }) + .withGraphFetched('licenceVersions.[licenceVersionPurposes, purposes]') + .modifyGraph('[licenceVersionPurposes]', (builder) => { + builder.select([ + 'licenceVersionPurposes.abstractionPeriodStartDay', + 'licenceVersionPurposes.abstractionPeriodStartMonth', + 'licenceVersionPurposes.abstractionPeriodEndDay', + 'licenceVersionPurposes.abstractionPeriodEndMonth' + ]) + }) + .modifyGraph('[purposes]', (builder) => { + builder.select([ + 'purposes.description' + ]) + }) .modify('licenceHolder') .modify('registeredToAndLicenceName') diff --git a/app/views/licences/tabs/summary.njk b/app/views/licences/tabs/summary.njk index cf809faeb8..e823bcaa2b 100644 --- a/app/views/licences/tabs/summary.njk +++ b/app/views/licences/tabs/summary.njk @@ -45,6 +45,30 @@ {% endif %} + + {% if abstractionPeriods %} +
+
{{ abstractionPeriods.caption }}
+
+ {% if abstractionPeriods.uniqueAbstractionPeriods.length > 5 %} + + There are {{ abstractionPeriods.uniqueAbstractionPeriods.length }} purposes + + {% elseif abstractionPeriods.uniqueAbstractionPeriods.length > 1 %} +
    + {% for abstractionPeriod in abstractionPeriods.uniqueAbstractionPeriods %} +
  • {{ abstractionPeriod }}
  • + {% endfor %} +
+ {% else %} + + {{ abstractionPeriods.uniqueAbstractionPeriods[0] }} + + {% endif %} + {{ abstractionPeriodsAndPurposesLinkText }} +
+
+ {% endif %}
diff --git a/test/presenters/licences/view-licence.presenter.test.js b/test/presenters/licences/view-licence.presenter.test.js index 9b8f98d8f2..864328373c 100644 --- a/test/presenters/licences/view-licence.presenter.test.js +++ b/test/presenters/licences/view-licence.presenter.test.js @@ -33,6 +33,8 @@ describe('View Licence presenter', () => { const result = ViewLicencePresenter.go(licence) expect(result).to.equal({ + abstractionPeriods: null, + abstractionPeriodsAndPurposesLinkText: null, id: 'f1288f6c-8503-4dc1-b114-75c408a14bd0', documentId: '28665d16-eba3-4c9a-aa55-7ab671b0c4fb', endDate: null, @@ -47,238 +49,389 @@ describe('View Licence presenter', () => { warning: null }) }) + }) - describe("the 'endDate' property", () => { - describe('when the licence expired date is null', () => { - it('returns NULL', () => { - const result = ViewLicencePresenter.go(licence) + describe("the 'endDate' property", () => { + describe('when the licence expired date is null', () => { + it('returns NULL', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.endDate).to.be.null() - }) + expect(result.endDate).to.be.null() }) + }) - describe('when the licence expired date is set to a date less than or equal to today', () => { - beforeEach(() => { - // NOTE: The date we get back from the DB is without time. If we just assigned new Date() to expiredDate - // there is a chance the test could fail depending on how quickly this is compared to the logic in the - // presenter - const today = new Date() - today.setHours(0, 0, 0, 0) + describe('when the licence expired date is set to a date less than or equal to today', () => { + beforeEach(() => { + // NOTE: The date we get back from the DB is without time. If we just assigned new Date() to expiredDate + // there is a chance the test could fail depending on how quickly this is compared to the logic in the + // presenter + const today = new Date() + today.setHours(0, 0, 0, 0) - licence.expiredDate = today - }) + licence.expiredDate = today + }) - it('returns NULL', () => { - const result = ViewLicencePresenter.go(licence) + it('returns NULL', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.endDate).to.be.null() - }) + expect(result.endDate).to.be.null() }) + }) - describe('when the licence expired date is set to a date greater than today (2099-04-01)', () => { - beforeEach(() => { - licence.expiredDate = new Date('2099-04-01') - }) + describe('when the licence expired date is set to a date greater than today (2099-04-01)', () => { + beforeEach(() => { + licence.expiredDate = new Date('2099-04-01') + }) - it("returns '1 April 2099'", () => { - const result = ViewLicencePresenter.go(licence) + it("returns '1 April 2099'", () => { + const result = ViewLicencePresenter.go(licence) - expect(result.endDate).to.equal('1 April 2099') - }) + expect(result.endDate).to.equal('1 April 2099') }) }) + }) - describe("the 'licenceHolder' property", () => { - describe('when the licence holder is not set', () => { - it("returns 'Unregistered licence'", () => { - const result = ViewLicencePresenter.go(licence) + describe("the 'licenceHolder' property", () => { + describe('when the licence holder is not set', () => { + it("returns 'Unregistered licence'", () => { + const result = ViewLicencePresenter.go(licence) - expect(result.licenceHolder).to.equal('Unregistered licence') - }) + expect(result.licenceHolder).to.equal('Unregistered licence') }) + }) - describe('when the licence holder is set', () => { - beforeEach(() => { - licence.licenceHolder = 'Barbara Liskov' - }) + describe('when the licence holder is set', () => { + beforeEach(() => { + licence.licenceHolder = 'Barbara Liskov' + }) - it("returns 'Barbara Liskov'", () => { - const result = ViewLicencePresenter.go(licence) + it("returns 'Barbara Liskov'", () => { + const result = ViewLicencePresenter.go(licence) - expect(result.licenceHolder).to.equal('Barbara Liskov') - }) + expect(result.licenceHolder).to.equal('Barbara Liskov') }) }) + }) - describe("the 'warning' property", () => { - describe('when the licence does not have an end date (expired, lapsed or revoked)', () => { - it('returns NULL', () => { - const result = ViewLicencePresenter.go(licence) + describe("the 'warning' property", () => { + describe('when the licence does not have an end date (expired, lapsed or revoked)', () => { + it('returns NULL', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.warning).to.be.null() - }) + expect(result.warning).to.be.null() }) + }) - describe('when the licence does have an end date but it is in the future (expired, lapsed or revoked)', () => { - beforeEach(() => { - licence.expiredDate = new Date('2099-04-01') - }) + describe('when the licence does have an end date but it is in the future (expired, lapsed or revoked)', () => { + beforeEach(() => { + licence.expiredDate = new Date('2099-04-01') + }) - it('returns NULL', () => { - const result = ViewLicencePresenter.go(licence) + it('returns NULL', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.warning).to.be.null() - }) + expect(result.warning).to.be.null() }) + }) - describe('when the licence ends today or in the past (2019-04-01) because it is expired', () => { - beforeEach(() => { - licence.ends = { date: new Date('2019-04-01'), reason: 'expired' } - }) + describe('when the licence ends today or in the past (2019-04-01) because it is expired', () => { + beforeEach(() => { + licence.ends = { date: new Date('2019-04-01'), reason: 'expired' } + }) - it("returns 'This licence expired on 1 April 2019'", () => { - const result = ViewLicencePresenter.go(licence) + it("returns 'This licence expired on 1 April 2019'", () => { + const result = ViewLicencePresenter.go(licence) - expect(result.warning).to.equal('This licence expired on 1 April 2019') - }) + expect(result.warning).to.equal('This licence expired on 1 April 2019') }) + }) - describe('when the licence ends today or in the past (2019-04-01) because it is lapsed', () => { - beforeEach(() => { - licence.ends = { date: new Date('2019-04-01'), reason: 'lapsed' } - }) + describe('when the licence ends today or in the past (2019-04-01) because it is lapsed', () => { + beforeEach(() => { + licence.ends = { date: new Date('2019-04-01'), reason: 'lapsed' } + }) - it("returns 'This licence lapsed on 1 April 2019'", () => { - const result = ViewLicencePresenter.go(licence) + it("returns 'This licence lapsed on 1 April 2019'", () => { + const result = ViewLicencePresenter.go(licence) - expect(result.warning).to.equal('This licence lapsed on 1 April 2019') - }) + expect(result.warning).to.equal('This licence lapsed on 1 April 2019') }) + }) - describe('when the licence was ends today or in the past (2019-04-01) because it is revoked', () => { - beforeEach(() => { - licence.ends = { date: new Date('2019-04-01'), reason: 'revoked' } - }) + describe('when the licence was ends today or in the past (2019-04-01) because it is revoked', () => { + beforeEach(() => { + licence.ends = { date: new Date('2019-04-01'), reason: 'revoked' } + }) - it("returns 'This licence was revoked on 1 April 2019'", () => { - const result = ViewLicencePresenter.go(licence) + it("returns 'This licence was revoked on 1 April 2019'", () => { + const result = ViewLicencePresenter.go(licence) - expect(result.warning).to.equal('This licence was revoked on 1 April 2019') - }) + expect(result.warning).to.equal('This licence was revoked on 1 April 2019') }) }) + }) - describe("the 'purposes' property", () => { - describe('when there are no licenceVersions', () => { - it('returns null', () => { - const result = ViewLicencePresenter.go(licence) + describe("the 'purposes' property", () => { + describe('when there are no licenceVersions', () => { + it('returns null', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.purposes).to.equal(null) - }) + expect(result.purposes).to.equal(null) + }) + }) + + describe('when there is an empty licenceVersions array', () => { + beforeEach(() => { + licence.licenceVersions = [] }) - describe('when the licenceVersions has one entry', () => { - beforeEach(() => { - licence.licenceVersions = [{ - purposes: [{ - description: 'Spray Irrigation - Storage' - }] + it('returns null', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.purposes).to.equal(null) + }) + }) + + describe('when there is an empty licenceVersions purposes array', () => { + beforeEach(() => { + licence.licenceVersions = [{ + purposes: [] + }] + }) + + it('returns null', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.purposes).to.equal(null) + }) + }) + + describe('when the licenceVersions has one purpose', () => { + beforeEach(() => { + licence.licenceVersions = [{ + purposes: [{ + description: 'Spray Irrigation - Storage' }] - }) + }] + }) - it('returns an object with a caption and an array with one entry', () => { - const result = ViewLicencePresenter.go(licence) + it('returns an object with a caption and an array with one purpose', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.purposes).to.equal({ - caption: 'Purpose', - data: ['Spray Irrigation - Storage'] - }) + expect(result.purposes).to.equal({ + caption: 'Purpose', + data: ['Spray Irrigation - Storage'] }) }) + }) - describe('when the licenceVersions has more than one entry of the same type', () => { - beforeEach(() => { - licence.licenceVersions = [{ - purposes: [{ - description: 'Spray Irrigation - Storage' - }, { - description: 'Spray Irrigation - Storage' - }] + describe('when the licenceVersions has more than one purpose of the same type', () => { + beforeEach(() => { + licence.licenceVersions = [{ + purposes: [{ + description: 'Spray Irrigation - Storage' + }, { + description: 'Spray Irrigation - Storage' }] - }) + }] + }) - it('returns an object with a caption and an array with one entry', () => { - const result = ViewLicencePresenter.go(licence) + it('returns an object with a caption and an array with one entry', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.purposes).to.equal({ - caption: 'Purpose', - data: ['Spray Irrigation - Storage'] - }) + expect(result.purposes).to.equal({ + caption: 'Purpose', + data: ['Spray Irrigation - Storage'] }) }) + }) - describe('when the licenceVersions has more than one entry of different types', () => { - beforeEach(() => { - licence.licenceVersions = [{ - purposes: [{ - description: 'Spray Irrigation - Storage' - }, { - description: 'Make-Up Or Top Up Water' - }] + describe('when the licenceVersions has more than one purpose of different types', () => { + beforeEach(() => { + licence.licenceVersions = [{ + purposes: [{ + description: 'Spray Irrigation - Storage' + }, { + description: 'Make-Up Or Top Up Water' }] - }) + }] + }) - it('returns an object with a caption and an array with two entries', () => { - const result = ViewLicencePresenter.go(licence) + it('returns an object with a caption and an array with two entries', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.purposes).to.equal({ - caption: 'Purposes', - data: ['Spray Irrigation - Storage', 'Make-Up Or Top Up Water'] - }) + expect(result.purposes).to.equal({ + caption: 'Purposes', + data: ['Spray Irrigation - Storage', 'Make-Up Or Top Up Water'] }) }) }) + }) - describe("the 'registeredTo' property", () => { - describe('when there is no registeredTo property', () => { - it('returns null', () => { - const result = ViewLicencePresenter.go(licence) + describe("the 'registeredTo' property", () => { + describe('when there is no registeredTo property', () => { + it('returns null', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.registeredTo).to.equal(null) - }) + expect(result.registeredTo).to.equal(null) }) + }) - describe('when there is a registeredTo property', () => { - beforeEach(() => { - licence.registeredTo = 'Company' - }) + describe('when there is a registeredTo property', () => { + beforeEach(() => { + licence.registeredTo = 'Company' + }) - it('returns a string with the registered to name', () => { - const result = ViewLicencePresenter.go(licence) + it('returns a string with the registered to name', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.registeredTo).to.equal('Company') - }) + expect(result.registeredTo).to.equal('Company') + }) + }) + }) + + describe("the 'licenceName' property", () => { + describe('when there is no licenceName property', () => { + it('returns Unregistered licence', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.licenceName).to.equal('Unregistered licence') + }) + }) + + describe('when there is a licenceName property', () => { + beforeEach(() => { + licence.licenceName = 'example@example.com' + }) + + it('returns a string with the licence name valus', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.licenceName).to.equal('example@example.com') + }) + }) + }) + + describe("the 'licenceVersionPurposes' property", () => { + describe('when there are no licenceVersions', () => { + it('returns null', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.abstractionPeriods).to.equal(null) + expect(result.abstractionPeriodsAndPurposesLinkText).to.equal(null) + }) + }) + + describe('when there are no licenceVersions so an empty array is returned', () => { + beforeEach(() => { + licence.licenceVersions = [] + }) + + it('returns null', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.abstractionPeriods).to.equal(null) + expect(result.abstractionPeriodsAndPurposesLinkText).to.equal(null) + }) + }) + + describe('when the licenceVersionPurposes has no abstraction period and one purpose', () => { + beforeEach(() => { + licence.licenceVersions = [{ + purposes: [{ + description: 'Spray Irrigation - Storage' + }], + licenceVersionPurposes: [] + }] + }) + + it('returns null', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.abstractionPeriods).to.equal(null) }) }) - describe("the 'licenceName' property", () => { - describe('when there is no licenceName property', () => { - it('returns Unregistered licence', () => { - const result = ViewLicencePresenter.go(licence) + describe('when the licenceVersionPurposes has one abstraction period and one purpose', () => { + beforeEach(() => { + licence.licenceVersions = [{ + purposes: [{ + description: 'Spray Irrigation - Storage' + }], + licenceVersionPurposes: [{ + abstractionPeriodStartDay: 1, + abstractionPeriodStartMonth: 1, + abstractionPeriodEndDay: 28, + abstractionPeriodEndMonth: 2 + }] + }] + }) - expect(result.licenceName).to.equal('Unregistered licence') + it('returns an object with a caption and an array with one abstraction period', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.abstractionPeriods).to.equal({ + caption: 'Period of abstraction', + uniqueAbstractionPeriods: ['1 January to 28 February'] }) }) + }) + + describe('when the licenceVersions has more than one abstraction period of the same range', () => { + beforeEach(() => { + licence.licenceVersions = [{ + licenceVersionPurposes: [{ + abstractionPeriodStartDay: 1, + abstractionPeriodStartMonth: 1, + abstractionPeriodEndDay: 28, + abstractionPeriodEndMonth: 2 + }, { + abstractionPeriodStartDay: 1, + abstractionPeriodStartMonth: 1, + abstractionPeriodEndDay: 28, + abstractionPeriodEndMonth: 2 + }] + }] + }) - describe('when there is a licenceName property', () => { - beforeEach(() => { - licence.licenceName = 'example@example.com' + it('returns an object with a caption and an array with one abstraction period', () => { + const result = ViewLicencePresenter.go(licence) + + expect(result.abstractionPeriods).to.equal({ + caption: 'Period of abstraction', + uniqueAbstractionPeriods: ['1 January to 28 February'] }) + }) + }) + + describe('when the licenceVersions has more than one abstraction period and purposes of different types', () => { + beforeEach(() => { + licence.licenceVersions = [{ + purposes: [{ + description: 'Spray Irrigation - Storage' + }, { + description: 'Make-Up Or Top Up Water' + }], + licenceVersionPurposes: [{ + abstractionPeriodStartDay: 1, + abstractionPeriodStartMonth: 1, + abstractionPeriodEndDay: 28, + abstractionPeriodEndMonth: 2 + }, { + abstractionPeriodStartDay: 1, + abstractionPeriodStartMonth: 3, + abstractionPeriodEndDay: 28, + abstractionPeriodEndMonth: 4 + }] + }] + }) - it('returns a string with the licence name valus', () => { - const result = ViewLicencePresenter.go(licence) + it('returns an object with a caption and an array with two purposes', () => { + const result = ViewLicencePresenter.go(licence) - expect(result.licenceName).to.equal('example@example.com') + expect(result.abstractionPeriods).to.equal({ + caption: 'Periods of abstraction', + uniqueAbstractionPeriods: ['1 January to 28 February', '1 March to 28 April'] }) }) }) diff --git a/test/services/licences/view-licence.service.test.js b/test/services/licences/view-licence.service.test.js index 5b193440b0..80afdf6459 100644 --- a/test/services/licences/view-licence.service.test.js +++ b/test/services/licences/view-licence.service.test.js @@ -36,6 +36,8 @@ describe('View Licence service', () => { const result = await ViewLicenceService.go(testId) expect(result).to.equal({ + abstractionPeriods: null, + abstractionPeriodsAndPurposesLinkText: null, id: '2c80bd22-a005-4cf4-a2a2-73812a9861de', documentId: '40306a46-d4ce-4874-9c9e-30ab6469b3fe', endDate: null,