Skip to content

Commit

Permalink
Add new data formatters to base presenter (#350)
Browse files Browse the repository at this point in the history
https://eaflood.atlassian.net/browse/WATER-4070
https://eaflood.atlassian.net/browse/WATER-4073

Whilst working on [Create endpoint to generate fake data](#347) we added a number of formatters. These take DB data and re-format it to match how the UI displays the values.

We will need these formatters in future changes we'll be making, the first of which is a re-designed bill run page.

So, to help prepare for that we are adding those formatters to our `BasePresenter` so they are available to any future presenters we add.
  • Loading branch information
Cruikshanks authored Aug 10, 2023
1 parent 79ac131 commit 94b7a2f
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 11 deletions.
85 changes: 81 additions & 4 deletions app/presenters/base.presenter.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,60 @@
'use strict'

/**
* Converts a date into the format required by output files, eg 25/03/2021 becomes 25-MAR-2021
* Converts a number which represents pence into pounds by dividing it by 100
*
* @param {Date} date The date value to format to a string
* This is such a simply calculation it could be done in place. But by having it as a named method we make it clear
* what we are doing rather than those that follow having to derive the intent.
*
* @param {Number} value
*
* @returns {Number} the value divided by 100
*/
function convertPenceToPounds (value) {
return value / 100
}

/**
* Formats an abstraction day and month into its string variant, for example, 1 and 4 becomes '1 April'
*
* @param {Number} abstractionDay
* @param {Number} abstractionMonth Note: the index starts at 1, for example, 4 would be April
*
* @returns {string} The abstraction date formatted as a 'DD MMMM' string
*/
function formatAbstractionDate (abstractionDay, abstractionMonth) {
// NOTE: 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.
const abstractionDate = new Date(1970, abstractionMonth - 1, abstractionDay)

return abstractionDate.toLocaleDateString('en-GB', { day: 'numeric', month: 'long' })
}

/**
* Formats an abstraction period into its string variant, for example, '1 April to 31 October'
*
* @param {Number} startDay
* @param {Number} startMonth
* @param {Number} endDay
* @param {Number} endMonth
*
* @returns {string} The abstraction period formatted as a 'DD MMMM to DD MMMM' string
*/
function formatAbstractionPeriod (startDay, startMonth, endDay, endMonth) {
const startDate = formatAbstractionDate(startDay, startMonth)
const endDate = formatAbstractionDate(endDay, endMonth)

return `${startDate} to ${endDate}`
}

/**
* Converts a date into the format required by the Charging Module, eg 25/03/2021 becomes 25-MAR-2021
*
* @param {Date} date The date to be formatted
*
* @returns {string} The date formatted as a 'DD-MMM-YYYY' string
*/
function formatDate (date) {
function formatChargingModuleDate (date) {
// The output date format of methods such as toLocaleString() are based on the Unicode CLDR which is subject to
// change and cannot be relied on to be consistent: https://github.com/nodejs/node/issues/42030. We therefore
// generate the formatted date ourselves.
Expand All @@ -22,6 +69,31 @@ function formatDate (date) {
return `${day}-${month}-${year}`
}

/**
* Formats a date into a human readable day, month and year string, for example, '12 September 2021'
*
* @param {Date} date The date to be formatted
*
* @returns {string} The date formatted as a 'DD MMMM YYYY' string
*/
function formatLongDate (date) {
return date.toLocaleDateString('en-GB', { year: 'numeric', month: 'long', day: 'numeric' })
}

/**
* Formats a number which represents a value in pounds as a money string, for example, 1149 as '1149.00'
*
* @param {Number} value The value to display as currency. Assumed to be in pounds
* @param {Boolean} includeSymbol Whether to add the £ symbol to the start of the returned string
*
* @returns {string} The value formatted as a money string with optional currency symbol
*/
function formatNumberAsMoney (value, includeSymbol = false) {
const symbol = includeSymbol ? '£' : ''

return `${symbol}${value.toFixed(2)}`
}

/**
* Pads a number to a given length with leading zeroes and returns the result as a string
*
Expand All @@ -37,6 +109,11 @@ function leftPadZeroes (number, length) {
}

module.exports = {
formatDate,
convertPenceToPounds,
formatAbstractionDate,
formatAbstractionPeriod,
formatChargingModuleDate,
formatLongDate,
formatNumberAsMoney,
leftPadZeroes
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { formatDate } = require('../base.presenter.js')
const { formatChargingModuleDate } = require('../base.presenter.js')

/**
* Formats a transaction object, generated by `FormatSrocTransactionLineService`, as a Charging Module API transaction
Expand All @@ -15,8 +15,8 @@ const { formatDate } = require('../base.presenter.js')
* @returns {Object} an object that can be directly used as the body in a charging module POST transaction request
*/
function go (transaction, billingPeriod, invoiceAccountNumber, licence) {
const periodStart = formatDate(billingPeriod.startDate)
const periodEnd = formatDate(billingPeriod.endDate)
const periodStart = formatChargingModuleDate(billingPeriod.startDate)
const periodEnd = formatChargingModuleDate(billingPeriod.endDate)

return {
clientId: transaction.billingTransactionId,
Expand Down
88 changes: 84 additions & 4 deletions test/presenters/base.presenter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,67 @@
const Lab = require('@hapi/lab')
const Code = require('@hapi/code')

const { describe, it } = exports.lab = Lab.script()
const { describe, it, beforeEach } = exports.lab = Lab.script()
const { expect } = Code

// Thing under test
const BasePresenter = require('../../app/presenters/base.presenter.js')

describe('Base presenter', () => {
describe('#formatDate()', () => {
it('correctly formats dates', async () => {
describe('#convertPenceToPounds()', () => {
let valueInPence

describe('when the value divides evenly', () => {
beforeEach(() => {
valueInPence = 114900
})

it('correctly returns the value in pounds, for example, 1149', async () => {
const result = BasePresenter.convertPenceToPounds(valueInPence)

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

describe('when the value does not divide evenly', () => {
beforeEach(() => {
valueInPence = 114901
})

it('correctly returns the value in pounds, for example, 1149.01', async () => {
const result = BasePresenter.convertPenceToPounds(valueInPence)

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

describe('#formatAbstractionDate()', () => {
const day = 12
const month = 9

it('correctly formats the given date, for example, 12 September', async () => {
const result = BasePresenter.formatAbstractionDate(day, month)

expect(result).to.equal('12 September')
})
})

describe('#formatAbstractionPeriod()', () => {
const startDay = 1
const startMonth = 4
const endDay = 12
const endMonth = 9

it('correctly formats the given period, for example, 1 April to 12 September', async () => {
const result = BasePresenter.formatAbstractionPeriod(startDay, startMonth, endDay, endMonth)

expect(result).to.equal('1 April to 12 September')
})
})

describe('#formatChargingModuleDate()', () => {
it('correctly formats the given date, for example, 12-SEP-2021', async () => {
// We check an array of dates, one for each month, to ensure that every month is formatted correctly
const results = [
new Date('2021-01-01T14:41:10.511Z'),
Expand All @@ -27,7 +79,7 @@ describe('Base presenter', () => {
new Date('2021-10-12T14:41:10.511Z'),
new Date('2021-11-12T14:41:10.511Z'),
new Date('2021-12-12T14:41:10.511Z')
].map(date => BasePresenter.formatDate(date))
].map(date => BasePresenter.formatChargingModuleDate(date))

expect(results).to.equal([
'01-JAN-2021',
Expand All @@ -46,6 +98,34 @@ describe('Base presenter', () => {
})
})

describe('#formatLongDate()', () => {
it('correctly formats the given date, for example, 12 September 2021', async () => {
const result = BasePresenter.formatLongDate(new Date('2021-09-12T14:41:10.511Z'))

expect(result).to.equal('12 September 2021')
})
})

describe('#formatNumberAsMoney()', () => {
const valueInPence = 1149.5

describe('when no £ symbol is requested', () => {
it('correctly returns the value as a money string with no symbol, for example, 1149.50', async () => {
const result = BasePresenter.formatNumberAsMoney(valueInPence)

expect(result).to.equal('1149.50')
})
})

describe('when the £ symbol is requested', () => {
it('correctly returns the value as a money string with a symbol, for example, £1149.50', async () => {
const result = BasePresenter.formatNumberAsMoney(valueInPence, true)

expect(result).to.equal('£1149.50')
})
})
})

describe('#leftPadZeroes()', () => {
it('correctly pads numbers', async () => {
const number = 123
Expand Down

0 comments on commit 94b7a2f

Please sign in to comment.