diff --git a/integration_tests/e2e/compliance.cy.ts b/integration_tests/e2e/compliance.cy.ts index af2805db..54a02ca2 100644 --- a/integration_tests/e2e/compliance.cy.ts +++ b/integration_tests/e2e/compliance.cy.ts @@ -17,7 +17,7 @@ context('Compliance', () => { page.getRowData('breach1', 'startDate', 'Value').should('contain.text', '2 March 2020') page.getRowData('breach1', 'status', 'Value').should('contain.text', 'An active breach status') - page.getCardHeader('activity1').should('contain.text', '10 days RAR, 9 completed') + page.getCardHeader('activity1').should('contain.text', '9 of 10 RAR days completed') page.getRowData('activity1', 'appointments', 'Value').should('contain.text', '1 national standard appointments') page.getRowData('activity1', 'withoutOutcome', 'Value').should('contain.text', '3 without a recorded outcome') diff --git a/integration_tests/e2e/overview.cy.ts b/integration_tests/e2e/overview.cy.ts index 72d6a73e..72c80a22 100644 --- a/integration_tests/e2e/overview.cy.ts +++ b/integration_tests/e2e/overview.cy.ts @@ -28,6 +28,7 @@ context('Overview', () => { .should('contain.text', 'Committed/ Transferred to Crown: Life imprisonment (Adult)') page.getRowData('personalDetails', 'disabilities', 'Value').should('contain.text', 'Dyslexia, Arthritis') page.getRowData('personalDetails', 'adjustments', 'Value').should('contain.text', 'Hand Rails, Special Furniture') + page.getCardHeader('sentence2').should('contain.text', 'ORA Community Order') page .getRowData('sentence2', 'mainOffence', 'Value') .should( @@ -35,12 +36,13 @@ context('Overview', () => { '(Having possession a picklock or other implement with intent to break into any premises - 18502)', ) page.getRowData('sentence2', 'order', 'Value').should('contain.text', 'ORA Community Order') - page.getRowData('sentence2', 'requirements', 'Value').should('contain.text', '10 days RAR, 9 completed') + page.getRowData('sentence2', 'requirements', 'Value').should('contain.text', '9 of 10 RAR days completed') + page.getCardHeader('sentence3').should('contain.text', '12 month Community order') page .getRowData('sentence3', 'mainOffence', 'Value') .should('contain.text', 'Breach of Restraining Order (Protection from Harassment Act 1997) - 00831') page.getRowData('sentence3', 'order', 'Value').should('contain.text', '12 month Community order') - page.getRowData('sentence3', 'requirements', 'Value').should('contain.text', '16 days RAR, 14 completed') + page.getRowData('sentence3', 'requirements', 'Value').should('contain.text', '14 of 16 RAR days completed') page .getRowData('activityAndCompliance', 'previousOrders', 'Value') .should('contain.text', '1 previous orders (No breaches on previous orders)') diff --git a/integration_tests/e2e/requirement-note.cy.ts b/integration_tests/e2e/requirement-note.cy.ts new file mode 100644 index 00000000..d9e51eaf --- /dev/null +++ b/integration_tests/e2e/requirement-note.cy.ts @@ -0,0 +1,38 @@ +import Page from '../pages/page' +import SentencePage from '../pages/sentence' + +context('Sentence', () => { + it('Requirement note page is rendered', () => { + cy.visit('/case/X000001/sentence/requirement/1/note/0') + const page = Page.verifyOnPage(SentencePage) + page.headerCrn().should('contain.text', 'X000001') + page.headerName().should('contain.text', 'Caroline Wolff') + cy.get('[data-qa=pageHeading]').eq(0).should('contain.text', 'Sentence') + + cy.get(`[class=app-summary-card__header]`).within(() => + cy.get('h2').should('contain.text', '1 of 12 RAR days completed'), + ) + + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dt').should('have.length', 6)) + + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dd').should('have.length', 6)) + + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dt').eq(0).should('contain.text', 'Length of RAR')) + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dd').eq(0).should('contain.text', '12 days')) + + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dt').eq(1).should('contain.text', 'Completed RAR')) + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dd').eq(1).should('contain.text', '1 day')) + + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dt').eq(2).should('contain.text', 'Start date')) + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dd').eq(2).should('contain.text', '12 April 2024')) + + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dt').eq(3).should('contain.text', 'Note added by')) + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dd').eq(3).should('contain.text', 'Jon Jones')) + + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dt').eq(4).should('contain.text', 'Date added')) + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dd').eq(4).should('contain.text', '21 August 2024')) + + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dt').eq(5).should('contain.text', 'Note')) + cy.get(`[class=app-summary-card__body]`).within(() => cy.get('dd').eq(5).should('contain.text', '123456')) + }) +}) diff --git a/integration_tests/e2e/sentence.cy.ts b/integration_tests/e2e/sentence.cy.ts index 6f285ad6..164c7e29 100644 --- a/integration_tests/e2e/sentence.cy.ts +++ b/integration_tests/e2e/sentence.cy.ts @@ -7,7 +7,6 @@ context('Sentence', () => { const page = Page.verifyOnPage(SentencePage) page.headerCrn().should('contain.text', 'X000001') page.headerName().should('contain.text', 'Caroline Wolff') - cy.get('[data-qa=pageHeading]').eq(0).should('contain.text', 'Sentence') page.getTab('overview').should('contain.text', 'Overview') page.getTab('personalDetails').should('contain.text', 'Personal details') @@ -193,4 +192,55 @@ context('Sentence', () => { cy.get('[data-qa="convictionCard"]').should('not.exist') cy.get('[data-qa="offenceCard"]').should('not.exist') }) + + it('Sentence page is rendered with requirements', () => { + cy.visit('/case/X000001/sentence?number=1') + const page = Page.verifyOnPage(SentencePage) + + cy.get(`[data-qa="sentenceCard"]`).within(() => cy.get('dt').eq(5).should('contain.text', 'Requirements')) + cy.get(`[data-qa="requirementsValue"]`).within(() => + cy.get('details').eq(0).should('contain.text', '1 of 12 RAR days completed'), + ) + cy.get(`[data-qa="requirementsValue"]`).within(() => + cy.get('details').eq(1).should('contain.text', 'Curfew (Electronic Monitored)'), + ) + cy.get(`[data-qa="requirementsValue"]`).within(() => + cy.get('details').eq(2).should('contain.text', 'Unpaid Work - Regular'), + ) + cy.get(`[data-qa="requirementsValue"] `).within(() => cy.get('details').eq(1).click()) + page.getRequirementLabel(2, 1).should('contain.text', 'Length') + page.getRequirementValue(2, 1).should('contain.text', '10 hours') + page.getRequirementLabel(2, 2).should('contain.text', 'Start date') + page.getRequirementValue(2, 2).should('contain.text', '12 January 2024') + page.getRequirementLabel(2, 3).should('contain.text', 'End date') + page.getRequirementValue(2, 3).should('contain.text', '9 January 2024') + page.getRequirementLabel(2, 4).should('contain.text', 'Result') + page.getRequirementValue(2, 4).should('contain.text', 'Expired (Normal)') + page.getRequirementLabel(2, 5).should('contain.text', 'Notes') + page.getRequirementValue(2, 5).should('contain.text', 'curfew notes') + page + .getRequirementValue(2, 5) + .find('p:nth-of-type(2)') + .should('contain.text', 'Comment added by Jon Jones on 21 August 2024') + page.getRequirementValue(2, 5).find('a').should('not.exist') + + cy.get(`[data-qa="requirementsValue"] `).within(() => cy.get('details').eq(0).click()) + page.getRequirementLabel(1, 1).should('contain.text', 'Length of RAR') + page.getRequirementValue(1, 1).should('contain.text', '12 days') + page.getRequirementLabel(1, 2).should('contain.text', 'Completed RAR') + page.getRequirementValue(1, 2).should('contain.text', '1 day') + page.getRequirementLabel(1, 3).should('contain.text', 'Start date') + page.getRequirementValue(1, 3).should('contain.text', '12 April 2024') + page.getRequirementLabel(1, 4).should('contain.text', 'Notes') + page.getRequirementValue(1, 4).should('contain.text', 'Requirement created automatically') + page.getRequirementValue(1, 4).should('not.contain.text', '123456') + page.getRequirementValue(1, 4).find('a').should('contain.text', 'View full note') + page + .getRequirementValue(1, 4) + .find('p:nth-of-type(2)') + .should('contain.text', 'Comment added by Jon Jones on 21 August 2024') + page.getRequirementValue(1, 4).find('a').click() + cy.get(`[data-qa="name"]`).should('contain.text', 'Caroline Wolff') + cy.get('.app-summary-card__header').should('contain.text', '1 of 12 RAR days completed') + }) }) diff --git a/integration_tests/pages/page.ts b/integration_tests/pages/page.ts index e225849a..5cb17e13 100644 --- a/integration_tests/pages/page.ts +++ b/integration_tests/pages/page.ts @@ -10,7 +10,7 @@ export default abstract class Page { } checkOnPage(): void { - cy.get('h1').contains(this.title) + cy.get('[data-qa=pageHeading]').contains(this.title) } signOut = (): PageElement => cy.get('[data-qa=signOut]') diff --git a/integration_tests/pages/sentence.ts b/integration_tests/pages/sentence.ts index 81f3b062..de3cd09c 100644 --- a/integration_tests/pages/sentence.ts +++ b/integration_tests/pages/sentence.ts @@ -8,4 +8,14 @@ export default class SentencePage extends Page { activeSideNavItem = (): PageElement => cy.get('.moj-side-navigation__item--active a') noActiveSentence = (): PageElement => cy.get('[data-qa="no-active-sentence"]') + + getRequirementLabel = (requirementIndex: number, index: number): PageElement => + cy.get( + `[data-qa="requirementsValue"] details:nth-of-type(${requirementIndex}) .govuk-summary-list__row:nth-of-type(${index}) dt`, + ) + + getRequirementValue = (requirementIndex: number, index: number): PageElement => + cy.get( + `[data-qa="requirementsValue"] details:nth-of-type(${requirementIndex}) .govuk-summary-list__row:nth-of-type(${index}) dd`, + ) } diff --git a/server/data/masApiClient.ts b/server/data/masApiClient.ts index 23c87b82..f4af8840 100644 --- a/server/data/masApiClient.ts +++ b/server/data/masApiClient.ts @@ -21,6 +21,7 @@ import { ProfessionalContact } from './model/professionalContact' import { CaseAccess, UserAccess } from './model/caseAccess' import { LicenceConditionNoteDetails } from './model/licenceConditionNoteDetails' import { AppointmentRequestBody } from '../@types' +import { RequirementNoteDetails } from './model/requirementNoteDetails' export default class MasApiClient extends RestClient { constructor(token: string) { @@ -64,6 +65,17 @@ export default class MasApiClient extends RestClient { }) } + async getSentenceRequirementNote( + crn: string, + requirementId: string, + noteId: string, + ): Promise { + return this.get({ + path: `/sentence/${crn}/requirement/${requirementId}/note/${noteId}`, + handle404: false, + }) + } + async getContacts(crn: string): Promise { return this.get({ path: `/sentence/${crn}/contacts`, handle404: false }) } diff --git a/server/data/model/requirementNoteDetails.ts b/server/data/model/requirementNoteDetails.ts new file mode 100644 index 00000000..1585e8af --- /dev/null +++ b/server/data/model/requirementNoteDetails.ts @@ -0,0 +1,32 @@ +import { PersonSummary } from './common' + +export interface RequirementNoteDetails { + personSummary: PersonSummary + requirement: Requirement +} + +export interface Requirement { + code: string + expectedStartDate?: string + actualStartDate: string + expectedEndDate?: string + actualEndDate?: string + terminationReason?: string + description: string + length: number + lengthUnitValue: string + requirementNote: RequirementNote + rar?: { + completed: number + scheduled: number + totalDays: number + } +} + +export interface RequirementNote { + id: string + createdBy: string + createdByDate: string + note: string + hasNotesBeenTruncated: boolean +} diff --git a/server/data/model/sentenceDetails.ts b/server/data/model/sentenceDetails.ts index 1e8fd820..e227ab7e 100644 --- a/server/data/model/sentenceDetails.ts +++ b/server/data/model/sentenceDetails.ts @@ -53,8 +53,16 @@ export interface Order { startDate: string } +export interface RequirementNote { + id: number + createdBy: string + createdByDate: string + note: string + hasNoteBeenTruncated: boolean +} + export interface Requirement { - id?: number + id: number code: string expectedStartDate: string actualStartDate: string @@ -64,6 +72,7 @@ export interface Requirement { description: string codeDescription: string length: string + requirementNotes: RequirementNote[] notes: string rar: Rar } diff --git a/server/routes/sentence.ts b/server/routes/sentence.ts index 553d1065..4d47cf0c 100644 --- a/server/routes/sentence.ts +++ b/server/routes/sentence.ts @@ -174,4 +174,31 @@ export default function sentenceRoutes(router: Router, { hmppsAuthClient }: Serv crn, }) }) + + get('/case/:crn/sentence/requirement/:requirementId/note/:noteId', async (req, res, _next) => { + const { crn, requirementId, noteId } = req.params + const token = await hmppsAuthClient.getSystemClientToken(res.locals.user.username) + + await auditService.sendAuditMessage({ + action: 'VIEW_MAS_SENTENCE_REQUIREMENT_NOTE', + who: res.locals.user.username, + subjectId: crn, + subjectType: 'CRN', + correlationId: v4(), + service: 'hmpps-manage-a-supervision-ui', + }) + + const masClient = new MasApiClient(token) + const tierClient = new TierApiClient(token) + + const [requirementNoteDetails, tierCalculation] = await Promise.all([ + masClient.getSentenceRequirementNote(crn, requirementId, noteId), + tierClient.getCalculationDetails(crn), + ]) + res.render('pages/requirement-note', { + requirementNoteDetails, + tierCalculation, + crn, + }) + }) } diff --git a/server/views/pages/address-book-professional.njk b/server/views/pages/address-book-professional.njk index 1e829dd7..cab7c6f9 100755 --- a/server/views/pages/address-book-professional.njk +++ b/server/views/pages/address-book-professional.njk @@ -27,7 +27,7 @@ {% endblock %} {% block content %} -

{{ title }}

+

{{ title }}

diff --git a/server/views/pages/appointments/appointment.njk b/server/views/pages/appointments/appointment.njk index 48f6e28f..00e01d1a 100644 --- a/server/views/pages/appointments/appointment.njk +++ b/server/views/pages/appointments/appointment.njk @@ -33,7 +33,7 @@ {% block content %}
-

+

{% include './_appointment-prefix.njk' %} {{ title }} diff --git a/server/views/pages/caseload/caseload.njk b/server/views/pages/caseload/caseload.njk index 56bc3432..ef5d6738 100644 --- a/server/views/pages/caseload/caseload.njk +++ b/server/views/pages/caseload/caseload.njk @@ -10,7 +10,7 @@ {% endif %}
-

+

{{ title }}

{% include "./caseload-nav.njk" %} diff --git a/server/views/pages/compliance/_compliance-current-order.njk b/server/views/pages/compliance/_compliance-current-order.njk index 44fb0e79..b1641b67 100644 --- a/server/views/pages/compliance/_compliance-current-order.njk +++ b/server/views/pages/compliance/_compliance-current-order.njk @@ -17,7 +17,7 @@ {% if sentence.rar and sentence.rarCategory %} {% set queryParams = '?requirement=' + sentence.rarCategory | toSlug %} {% set requirements %} - {{ sentence.rar.totalDays }} days RAR, {{ sentence.rar.completed }} completed + {{ sentence.rar.completed }} of {{ sentence.rar.totalDays }} RAR days completed {% endset %} {% else %} {% set requirements = false %} diff --git a/server/views/pages/handoff/delius.njk b/server/views/pages/handoff/delius.njk index 5d0f0fe1..f01b63a6 100644 --- a/server/views/pages/handoff/delius.njk +++ b/server/views/pages/handoff/delius.njk @@ -24,7 +24,7 @@
-

{{title}}

+

{{title}}

You’ll need to use National Delius to:

    diff --git a/server/views/pages/handoff/oasys.njk b/server/views/pages/handoff/oasys.njk index e9322941..52cd4561 100644 --- a/server/views/pages/handoff/oasys.njk +++ b/server/views/pages/handoff/oasys.njk @@ -24,7 +24,7 @@
    -

    {{title}}

    +

    {{title}}

    You’ll need to use OASys to:

      diff --git a/server/views/pages/index.njk b/server/views/pages/index.njk index e9ca10af..52161dc0 100644 --- a/server/views/pages/index.njk +++ b/server/views/pages/index.njk @@ -4,7 +4,7 @@ {% set mainClasses = "app-container govuk-body" %} {% block content %} -

      Manage a Supervision

      +

      Manage a Supervision

      Not all cases are suitable for the Manage supervisions pilot. This service is only suitable for cases that have:

        diff --git a/server/views/pages/overview.njk b/server/views/pages/overview.njk index cff24256..a2c0659c 100644 --- a/server/views/pages/overview.njk +++ b/server/views/pages/overview.njk @@ -286,7 +286,7 @@ {% endset %} {% set requirements %} {% if sentence.rar %} - {{ sentence.rar.totalDays }} days RAR, {{ sentence.rar.completed }} completed + {{ sentence.rar.completed }} of {{ sentence.rar.totalDays }} RAR days completed {% endif %} {% endset %} {% set mainOffence %} @@ -322,7 +322,7 @@ {% endset %} {{ appSummaryCard({ - titleText: 'Sentence (' + sentence.eventNumber + ')', + titleText: sentence.order.description, classes: 'govuk-!-margin-bottom-6 app-summary-card--large-title', attributes: {'data-qa': 'sentence' + sentence.eventNumber + 'Card'}, html: sentenceHtml, diff --git a/server/views/pages/personal-details/addresses.njk b/server/views/pages/personal-details/addresses.njk index 61b716d6..904eaaf0 100644 --- a/server/views/pages/personal-details/addresses.njk +++ b/server/views/pages/personal-details/addresses.njk @@ -25,7 +25,7 @@ {% endblock %} {% block content %} -

        {{ title }}

        +

        {{ title }}