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 View Licence Bills page #986

Merged
merged 18 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
16 changes: 13 additions & 3 deletions app/controllers/licences.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

const InitiateReturnRequirementSessionService = require('../services/return-requirements/initiate-return-requirement-session.service.js')
const ViewLicenceBillsService = require('../services/licences/view-licence-bills.service')
const ViewLicenceReturnsService = require('../services/licences/view-licence-returns.service')
const ViewLicenceSummaryService = require('../services/licences/view-licence-summary.service')

Expand All @@ -25,13 +26,22 @@ async function returnsRequired (request, h) {
return h.redirect(`/system/return-requirements/${session.id}/start-date`)
}

async function viewBills (request, h) {
const { params: { id }, auth, query: { page = 1 } } = request

const data = await ViewLicenceBillsService.go(id, auth, page)

return h.view('licences/view.njk', {
...data
})
}

async function viewSummary (request, h) {
const { params: { id }, auth } = request

const data = await ViewLicenceSummaryService.go(id, auth)

return h.view('licences/view.njk', {
activeNavBar: 'search',
...data
})
}
Expand All @@ -42,7 +52,6 @@ async function viewReturns (request, h) {
const data = await ViewLicenceReturnsService.go(id, auth, page)

return h.view('licences/view.njk', {
activeNavBar: 'search',
...data
})
}
Expand All @@ -51,5 +60,6 @@ module.exports = {
noReturnsRequired,
returnsRequired,
viewReturns,
viewSummary
viewSummary,
viewBills
}
45 changes: 45 additions & 0 deletions app/presenters/licences/view-licence-bills.presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict'

/**
* Formats data for the `/licences/{id}/bills` view licence bill page
* @module ViewLicenceBillsPresenter
*/

const { formatLongDate } = require('../base.presenter')

/**
* Formats data for the `/licences/{id}/bills` view licence bill page
*
* @returns {Object} The data formatted for the view template
*/
function go (bills) {
return {
activeTab: 'bills',
bills: _formatBillsToTableRow(bills)
}
}

function _formatCurrencyToGBP (amount) {
return amount.toLocaleString('en-gb', {
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved
style: 'currency',
currency: 'GBP'
})
}
function _formatBillsToTableRow (bills) {
return bills.map((bill) => {
return {
billNumber: bill.invoiceNumber,
dateCreated: formatLongDate(new Date(bill.createdAt)),
account: bill.accountNumber,
runType: bill.billRun.batchType,
financialYear: bill.financialYearEnding,
total: _formatCurrencyToGBP(bill.netAmount),
accountId: bill.billingAccountId,
id: bill.id
}
})
}

module.exports = {
go
}
3 changes: 2 additions & 1 deletion app/presenters/licences/view-licence.presenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ function go (licence, auth) {
pageTitle: `Licence ${licenceRef}`,
registeredTo,
roles: _authRoles(auth),
warning: _generateWarningMessage(ends)
warning: _generateWarningMessage(ends),
activeNavBar: 'search'
}
}

Expand Down
8 changes: 8 additions & 0 deletions app/routes/licence.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
const LicencesController = require('../controllers/licences.controller.js')

const routes = [
{
method: 'GET',
path: '/licences/{id}/bills',
handler: LicencesController.viewBills,
options: {
description: 'View a licence bills page'
}
},
{
method: 'GET',
path: '/licences/{id}/summary',
Expand Down
49 changes: 49 additions & 0 deletions app/services/licences/fetch-licence-bills.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict'

/**
* Fetches all return logs for a licence which is needed for the view '/licences/{id}/returns` page
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved
* @module FetchLicenceBillsService
*/

const BillLicenceModel = require('../../models/bill-licence.model')
const BillModel = require('../../models/bill.model')

const DatabaseConfig = require('../../../config/database.config')

/**
* Fetches all bills for a licence which is needed for the view '/licences/{id}/bills` page
*
* @param {string} licenceId - The UUID for the licence to fetch
*
* @returns {Promise<Object>} the data needed to populate the view licence page's returns tab
*/
async function go (licenceId, page) {
const { results, total } = await _fetch(licenceId, page)

return { bills: JSON.parse(JSON.stringify(results)), pagination: { total } }
}

async function _fetch (licenceId, page) {
const billLicences = await BillLicenceModel.query()
.select('*')
.where('billLicences.licence_id', licenceId)
.page(page - 1, DatabaseConfig.defaultPageSize)

const billIds = JSON.parse(JSON.stringify(billLicences)).results.map(b => b.billId)
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved

return BillModel.query()
.findByIds(billIds)
.select('*')
.withGraphFetched('billRun')
.modifyGraph('billRun', (builder) => {
builder.select(['batchType'])
})
.orderBy([
{ column: 'created_at', order: 'desc' }
])
.page(page - 1, DatabaseConfig.defaultPageSize)
}

module.exports = {
go
}
37 changes: 37 additions & 0 deletions app/services/licences/view-licence-bills.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict'

/**
* Orchestrates fetching and presenting the data needed for the licence summary page
* @module ViewLicenceBillsService
*/

const FetchLicenceBillsService = require('./fetch-licence-bills.service')
const ViewLicenceService = require('./view-licence.service')
const ViewLicenceBillsPresenter = require('../../presenters/licences/view-licence-bills.presenter')
const PaginatorPresenter = require('../../presenters/paginator.presenter')

/**
* Orchestrates fetching and presenting the data needed for the licence summary page
*
* @param {string} licenceId - The UUID of the licence
*
* @returns {Promise<Object>} an object representing the `pageData` needed by the licence summary template.
*/
async function go (licenceId, auth, page) {
const commonData = await ViewLicenceService.go(licenceId, auth)

const billsData = await FetchLicenceBillsService.go(licenceId, page)
const pageData = ViewLicenceBillsPresenter.go(billsData.bills)

const pagination = PaginatorPresenter.go(100, Number(page), `/system/licences/${licenceId}/bills`)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confirm this works with a bigger license

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have checked and this does work when the numbers are fudged. But it would be better to test with a licence with more data.


return {
...commonData,
...pageData,
pagination
}
}

module.exports = {
go
}
55 changes: 55 additions & 0 deletions app/views/licences/tabs/bills.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{% from "govuk/components/pagination/macro.njk" import govukPagination %}

<table class="govuk-table">
<h2 class="govuk-heading-l">Bills</h2>
{% if bills %}
<table class="govuk-table">
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th class="govuk-table__header" scope="col">Bill number</th>
<th class="govuk-table__header" scope="col">Date created</th>
<th class="govuk-table__header" scope="col">Billing account</th>
<th class="govuk-table__header" scope="col">Bill run type</th>
<th class="govuk-table__header govuk-table__header--numeric" scope="col">Financial year</th>
<th class="govuk-table__header govuk-table__header--numeric" scope="col">Bill total</th>
</tr>
</thead>
<tbody class="govuk-table__body">
{% for bill in bills %}
<tr class="govuk-table__row">
<td class="govuk-table__cell" scope="row">
<a href="/system/bills/{{ bill.id }}">
{{ bill.billNumber }}
</a>
</td>
<td class="govuk-table__cell">
{{ bill.dateCreated }}
</td>
<td class="govuk-table__cell">
<a href="/billing-accounts/{{ bill.accountId }}">
{{ bill.account }}
</a>
</td>
<td class="govuk-table__cell">
{{ bill.runType | title }}
</td>
<td class="govuk-table__cell govuk-table__cell--numeric">
{{ bill.financialYear }}
</td>
<td class="govuk-table__cell govuk-table__cell--numeric">
{{ bill.total }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No bills sent for this licence.</p>
{% endif %}
</table>

{% if bills and pagination.numberOfPages > 1 %}
{{ govukPagination(pagination.component) }}
{% endif %}


3 changes: 3 additions & 0 deletions app/views/licences/view.njk
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@
{% if activeTab === 'returns' %}
{% include "licences/tabs/returns.njk" %}
{% endif %}
{% if activeTab === 'bills' %}
{% include "licences/tabs/bills.njk" %}
{% endif %}
</div>
</div>
{% endblock %}
77 changes: 66 additions & 11 deletions test/controllers/licences.controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const Boom = require('@hapi/boom')

// Things we need to stub
const InitiateReturnRequirementSessionService = require('../../app/services/return-requirements/initiate-return-requirement-session.service.js')
const ViewLicenceBillsService = require('../../app/services/licences/view-licence-bills.service')
const ViewLicenceSummaryService = require('../../app/services/licences/view-licence-summary.service')
const ViewLicenceReturnsService = require('../../app/services/licences/view-licence-returns.service')

Expand Down Expand Up @@ -151,6 +152,53 @@ describe('Licences controller', () => {
})
})

describe('GET /licences/{id}/bills', () => {
beforeEach(async () => {
options = {
method: 'GET',
url: '/licences/7861814c-ca19-43f2-be11-3c612f0d744b/bills',
auth: {
strategy: 'session',
credentials: { scope: [] }
}
}
})

describe('when a request is valid and has bills', () => {
beforeEach(async () => {
Sinon.stub(ViewLicenceBillsService, 'go').resolves(_viewLicenceBills())
})

it('returns the page successfully', async () => {
const response = await server.inject(options)

expect(response.statusCode).to.equal(200)
expect(response.payload).to.contain('Bills')
// Check the table titles
expect(response.payload).to.contain('Bill number')
expect(response.payload).to.contain('Date created')
expect(response.payload).to.contain('Billing account')
expect(response.payload).to.contain('Bill run type')
expect(response.payload).to.contain('Bill total')
})
})
describe('when a request is valid and has no bills', () => {
beforeEach(async () => {
Sinon.stub(ViewLicenceBillsService, 'go').resolves({ activeTab: 'bills' })
})

it('returns the page successfully', async () => {
const response = await server.inject(options)

expect(response.statusCode).to.equal(200)
expect(response.payload).to.contain('Bills')
// Check the table titles
expect(response.payload).to.contain('Bills')
expect(response.payload).to.contain('No bills sent for this licence.')
})
})
})

describe('GET /licences/{id}/summary', () => {
beforeEach(async () => {
options = {
Expand All @@ -177,17 +225,6 @@ describe('Licences controller', () => {
expect(response.payload).to.contain('End date')
})
})

function _viewLicenceSummary () {
return {
id: '7861814c-ca19-43f2-be11-3c612f0d744b',
licenceRef: '01/130/R01',
region: 'Southern',
startDate: '1 November 2022',
endDate: '1 November 2032',
activeTab: 'summary'
}
}
})

describe('GET /licences/{id}/returns', () => {
Expand Down Expand Up @@ -222,9 +259,27 @@ describe('Licences controller', () => {
})
})

function _viewLicenceBills () {
return {
activeTab: 'bills',
bills: []
}
}

function _viewLicenceReturns () {
return {
activeTab: 'returns',
returns: [{}]
}
}

function _viewLicenceSummary () {
return {
id: '7861814c-ca19-43f2-be11-3c612f0d744b',
licenceRef: '01/130/R01',
region: 'Southern',
startDate: '1 November 2022',
endDate: '1 November 2032',
activeTab: 'summary'
}
}
Loading
Loading