From 17f6db602e8a3219b1709f7698c46b848001ec1b Mon Sep 17 00:00:00 2001 From: pmcphee77 <150798161+pmcphee77@users.noreply.github.com> Date: Tue, 9 Apr 2024 12:47:22 +0100 Subject: [PATCH] Pi 2054 compliance (#41) * PI-2054: Added compliance --- assets/scss/components/_summary-card.scss | 36 +++ integration_tests/e2e/compliance.cy.ts | 51 +++ integration_tests/e2e/overview.cy.ts | 4 +- integration_tests/pages/compliance.ts | 7 + server/data/masApiClient.ts | 5 + server/data/model/compliance.ts | 22 ++ server/data/model/overview.ts | 27 +- server/routes/compliance.ts | 32 ++ server/routes/index.ts | 2 + server/utils/nunjucksSetup.ts | 2 + server/utils/utils.test.ts | 18 ++ server/utils/utils.ts | 29 ++ server/views/pages/compliance.njk | 57 ++++ .../compliance/_breaches-on-current-order.njk | 19 ++ .../compliance/_compliance-current-order.njk | 299 ++++++++++++++++++ .../_compliance-previous-orders.njk | 85 +++++ server/views/pages/overview.njk | 39 ++- wiremock/mappings/X000001-compliance.json | 102 ++++++ wiremock/mappings/X000001-full.json | 21 +- 19 files changed, 833 insertions(+), 24 deletions(-) create mode 100644 integration_tests/e2e/compliance.cy.ts create mode 100644 integration_tests/pages/compliance.ts create mode 100644 server/data/model/compliance.ts create mode 100644 server/routes/compliance.ts create mode 100644 server/views/pages/compliance.njk create mode 100644 server/views/pages/compliance/_breaches-on-current-order.njk create mode 100644 server/views/pages/compliance/_compliance-current-order.njk create mode 100644 server/views/pages/compliance/_compliance-previous-orders.njk create mode 100644 wiremock/mappings/X000001-compliance.json diff --git a/assets/scss/components/_summary-card.scss b/assets/scss/components/_summary-card.scss index c0d31526..ed645f84 100644 --- a/assets/scss/components/_summary-card.scss +++ b/assets/scss/components/_summary-card.scss @@ -88,6 +88,42 @@ float: right; } + +.app-compliance-panel { + padding: govuk-spacing(3); + color: govuk-colour("white"); + background: govuk-colour("dark-grey"); + + p, ul, li, h2, h3, a { + color: inherit; + } +} + +.app-compliance-panel--red { + background: govuk-colour("red"); +} + +.app-compliance-panel--green { + background: govuk-colour("green"); +} + +.app-compliance-panel--blue { + background: govuk-colour("blue"); +} + +.app-compliance-panel--orange { + background: govuk-colour("orange"); +} + +.app-summary-card--compliance { + border-bottom-width: 0; +} + +.app-tag--dark-red { + background: govuk-colour("red"); +} + + .govuk-tag { display: inline-block; outline: 2px solid transparent; diff --git a/integration_tests/e2e/compliance.cy.ts b/integration_tests/e2e/compliance.cy.ts new file mode 100644 index 00000000..af2805db --- /dev/null +++ b/integration_tests/e2e/compliance.cy.ts @@ -0,0 +1,51 @@ +import Page from '../pages/page' +import CompliancePage from '../pages/compliance' + +context('Compliance', () => { + it('Compliance page is rendered', () => { + cy.visit('/case/X000001/compliance') + const page = Page.verifyOnPage(CompliancePage) + page.getCardHeader('sentence1').should('contain.text', 'Sentence (3)') + page + .getRowData('sentence1', 'mainOffenceDescription', 'Value') + .should('contain.text', 'Having possession a picklock') + page.getRowData('sentence1', 'orderDescription', 'Value').should('contain.text', 'ORA Community Order') + page.getRowData('sentence1', 'startDate', 'Value').should('contain.text', '2 March 2020') + page.getRowData('sentence1', 'breach', 'Value').should('contain.text', 'A breach is in progress') + + page.getCardHeader('breach1').should('contain.text', 'Breach details') + 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.getRowData('activity1', 'appointments', 'Value').should('contain.text', '1 national standard appointments') + page.getRowData('activity1', 'withoutOutcome', 'Value').should('contain.text', '3 without a recorded outcome') + + page.getRowData('activity1', 'waitingForEvidence', 'Value').should('contain.text', '1 absence waiting for evidence') + page.getRowData('activity1', 'complied', 'Value').should('contain.text', '2 complied') + page.getRowData('activity1', 'failureToComply', 'Value').should('contain.text', '2 failures to comply') + + page.getRowData('activity1', 'warningLetter', 'Value').should('contain.text', 'First warning letter sent') + page.getRowData('activity1', 'acceptableAbsences', 'Value').should('contain.text', '1 acceptable absences') + page.getRowData('activity1', 'rescheduled', 'Value').should('contain.text', '1 rescheduled') + + page.getCardHeader('sentence2').should('contain.text', 'Sentence (1)') + page + .getRowData('sentence2', 'mainOffenceDescription', 'Value') + .should('contain.text', 'Another main offence - 18502') + page.getRowData('sentence2', 'orderDescription', 'Value').should('contain.text', 'ORA Community Order') + page.getRowData('sentence2', 'startDate', 'Value').should('contain.text', '2 March 2020') + page.getRowData('sentence2', 'breach', 'Value').should('contain.text', 'No breaches on current order') + + page.getCardHeader('previousOrder1').should('contain.text', '12 month Community Order') + page + .getRowData('previousOrder1', 'mainOffenceDescription', 'Value') + .should('contain.text', 'Common Assault and Battery') + page + .getRowData('previousOrder1', 'status', 'Value') + .should('contain.text', 'Completed - Sentence/ PSS Expiry Reached') + page.getRowData('previousOrder1', 'startDate', 'Value').should('contain.text', '12 December 1990') + page.getRowData('previousOrder1', 'endDate', 'Value').should('contain.text', '12 December 1991') + page.getRowData('previousOrder1', 'breaches', 'Value').should('contain.text', '2') + }) +}) diff --git a/integration_tests/e2e/overview.cy.ts b/integration_tests/e2e/overview.cy.ts index c1ccc80b..0644a41b 100644 --- a/integration_tests/e2e/overview.cy.ts +++ b/integration_tests/e2e/overview.cy.ts @@ -46,10 +46,10 @@ context('Overview', () => { .should('contain.text', '1 previous orders (No breaches on previous orders)') page .getRowData('activityAndCompliance', 'compliance', 'Value') - .should('contain.text', '2 failure to comply within 12 months') + .should('contain.text', '2 without a recorded outcome') page .getRowData('activityAndCompliance', 'activityLog', 'Value') - .should('contain.text', '5 national standard appointments') + .should('contain.text', '2 national standard appointments') page.getRowData('risk', 'rosh', 'Value').should('contain.text', 'HIGH') page .getRowData('risk', 'harmToSelf', 'Value') diff --git a/integration_tests/pages/compliance.ts b/integration_tests/pages/compliance.ts new file mode 100644 index 00000000..f73b0a0a --- /dev/null +++ b/integration_tests/pages/compliance.ts @@ -0,0 +1,7 @@ +import Page from './page' + +export default class CompliancePage extends Page { + constructor() { + super('Compliance') + } +} diff --git a/server/data/masApiClient.ts b/server/data/masApiClient.ts index e7ff0343..3a023865 100644 --- a/server/data/masApiClient.ts +++ b/server/data/masApiClient.ts @@ -13,6 +13,7 @@ import { AddressOverview, PersonSummary } from './model/common' import { SentenceDetails } from './model/sentenceDetails' import { PersonActivity } from './model/activityLog' import { PersonRiskFlag, PersonRiskFlags } from './model/risk' +import { PersonCompliance } from './model/compliance' export default class MasApiClient extends RestClient { constructor(token: string) { @@ -78,4 +79,8 @@ export default class MasApiClient extends RestClient { async getPersonRiskFlag(crn: string, id: string): Promise { return this.get({ path: `/risk-flags/${crn}/${id}`, handle404: false }) } + + async getPersonCompliance(crn: string): Promise { + return this.get({ path: `/compliance/${crn}`, handle404: false }) + } } diff --git a/server/data/model/compliance.ts b/server/data/model/compliance.ts new file mode 100644 index 00000000..7035daea --- /dev/null +++ b/server/data/model/compliance.ts @@ -0,0 +1,22 @@ +import { PersonSummary } from './common' +import { ActivityCount, Compliance, Offence, Order, PreviousOrders, Rar } from './overview' + +export interface PersonCompliance { + personSummary: PersonSummary + previousOrders: PreviousOrders + currentSentences: SentenceCompliance[] +} + +export interface SentenceCompliance { + activity: ActivityCount + compliance: Compliance + mainOffence: Offence + order: Order + activeBreach?: Breach + rar?: Rar +} + +export interface Breach { + startDate: string + status: string +} diff --git a/server/data/model/overview.ts b/server/data/model/overview.ts index 3e6c0134..9992a8a3 100644 --- a/server/data/model/overview.ts +++ b/server/data/model/overview.ts @@ -3,7 +3,7 @@ import { Name, PersonalCircumstance } from './common' export interface Overview { appointmentsWithoutOutcome: number absencesWithoutEvidence: number - activity?: Activity + activity?: ActivityCount compliance?: Compliance personalDetails: PersonalDetails previousOrders: PreviousOrders @@ -31,14 +31,18 @@ export interface Rar { } export interface Order { + status?: string + mainOffence?: string description: string endDate?: string startDate: string + breaches?: number } export interface PreviousOrders { breaches: number count: number + orders?: Order[] } export interface Schedule { @@ -69,14 +73,25 @@ export interface Provision { description: string } -export interface Activity { - acceptableAbsences: number - complied: number - nationalStandardsAppointments: number - rescheduled: number +export interface ActivityCount { + unacceptableAbsenceCount: number + attendedButDidNotComplyCount: number + outcomeNotRecordedCount: number + waitingForEvidenceCount: number + rescheduledCount: number + absentCount: number + rescheduledByStaffCount: number + rescheduledByPersonOnProbationCount: number + lettersCount: number + nationalStandardAppointmentsCount: number + compliedAppointmentsCount: number } export interface Compliance { currentBreaches: number + priorBreachesOnCurrentOrderCount: number failureToComplyInLast12Months: number + breachStarted: boolean + breachesOnCurrentOrderCount: number + failureToComplyCount: number } diff --git a/server/routes/compliance.ts b/server/routes/compliance.ts new file mode 100644 index 00000000..0036b678 --- /dev/null +++ b/server/routes/compliance.ts @@ -0,0 +1,32 @@ +import { type RequestHandler, Router } from 'express' +import { auditService } from '@ministryofjustice/hmpps-audit-client' +import { v4 } from 'uuid' +import asyncMiddleware from '../middleware/asyncMiddleware' +import type { Services } from '../services' +import MasApiClient from '../data/masApiClient' + +export default function complianceRoutes(router: Router, { hmppsAuthClient }: Services) { + const get = (path: string | string[], handler: RequestHandler) => router.get(path, asyncMiddleware(handler)) + + get('/case/:crn/compliance', async (req, res, _next) => { + const { crn } = req.params + const token = await hmppsAuthClient.getSystemClientToken(res.locals.user.username) + + await auditService.sendAuditMessage({ + action: 'VIEW_MAS_COMPLIANCE', + who: res.locals.user.username, + subjectId: crn, + subjectType: 'CRN', + correlationId: v4(), + service: 'hmpps-manage-a-supervision-ui', + }) + + const masClient = new MasApiClient(token) + + const personCompliance = await masClient.getPersonCompliance(crn) + res.render('pages/compliance', { + personCompliance, + crn, + }) + }) +} diff --git a/server/routes/index.ts b/server/routes/index.ts index f732d5a4..002f812b 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -10,6 +10,7 @@ import sentenceRoutes from './sentence' import scheduleRoutes from './schedule' import activityLogRoutes from './activityLog' import risksRoutes from './risks' +import complianceRoutes from './compliance' export default function routes(services: Services): Router { const router = Router() @@ -21,5 +22,6 @@ export default function routes(services: Services): Router { scheduleRoutes(router, services) risksRoutes(router, services) activityLogRoutes(router, services) + complianceRoutes(router, services) return router } diff --git a/server/utils/nunjucksSetup.ts b/server/utils/nunjucksSetup.ts index 189f9179..c9ed1f96 100644 --- a/server/utils/nunjucksSetup.ts +++ b/server/utils/nunjucksSetup.ts @@ -17,6 +17,7 @@ import { deliusHomepageUrl, fullName, getAppointmentsToAction, + getComplianceStatus, getCurrentRisksToThemselves, getPreviousRisksToThemselves, getRisksToThemselves, @@ -91,6 +92,7 @@ export default function nunjucksSetup(app: express.Express, applicationInfo: App njkEnv.addFilter('compactActivityLogDate', compactActivityLogDate) njkEnv.addFilter('activityLogDate', activityLogDate) njkEnv.addFilter('removeEmpty', removeEmpty) + njkEnv.addGlobal('getComplianceStatus', getComplianceStatus) njkEnv.addGlobal('timeFromTo', timeFromTo) njkEnv.addGlobal('getRisksWithScore', getRisksWithScore) njkEnv.addGlobal('activityLog', activityLog) diff --git a/server/utils/utils.test.ts b/server/utils/utils.test.ts index 1525f974..27258564 100644 --- a/server/utils/utils.test.ts +++ b/server/utils/utils.test.ts @@ -14,6 +14,7 @@ import { deliusHomepageUrl, fullName, getAppointmentsToAction, + getComplianceStatus, getRisksToThemselves, getRisksWithScore, getTagClass, @@ -372,3 +373,20 @@ describe('renders time from and to', () => { expect(timeFromTo(a, b)).toEqual(expected) }) }) + +describe('Gets compliance status', () => { + it.each([ + ['Returns breach in progress', 2, true, { text: 'Breach in progress', panelClass: 'app-compliance-panel--red' }], + [ + 'Returns failure to comply', + 2, + false, + { + text: '2 failures to comply within 12 months. No breach in progress yet.', + panelClass: 'app-compliance-panel--red', + }, + ], + ])('%s timeFromTo(%s, %s)', (_: string, a: number, b: boolean, expected: { text: string; panelClass: string }) => { + expect(getComplianceStatus(a, b)).toEqual(expected) + }) +}) diff --git a/server/utils/utils.ts b/server/utils/utils.ts index ec7f53cb..784e852c 100644 --- a/server/utils/utils.ts +++ b/server/utils/utils.ts @@ -327,3 +327,32 @@ export const timeFromTo = (from: string, to: string) => { } return `${timeFrom} to ${timeTo}` } + +export const getComplianceStatus = (failureToComplyCount: number, breachStarted: boolean) => { + const status: { text: string; panelClass: string } = { + text: '', + panelClass: '', + } + + if (breachStarted) { + status.text = 'Breach in progress' + status.panelClass = 'app-compliance-panel--red' + } else { + switch (failureToComplyCount) { + case 0: + status.text = 'No failures to comply within 12 months' + status.panelClass = 'app-compliance-panel--green' + break + case 1: + status.text = '1 failure to comply within 12 months' + status.panelClass = '' + break + default: + status.text = `${failureToComplyCount} failures to comply within 12 months. No breach in progress yet.` + status.panelClass = 'app-compliance-panel--red' + break + } + } + + return status +} diff --git a/server/views/pages/compliance.njk b/server/views/pages/compliance.njk new file mode 100644 index 00000000..14a47706 --- /dev/null +++ b/server/views/pages/compliance.njk @@ -0,0 +1,57 @@ +{% extends "../partials/case.njk" %} +{% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner %} +{% set pageTitle = applicationName + " - Compliance" %} +{% set currentSectionName = 'Compliance' %} +{% set title = 'Compliance' %} +{% set currentNavSection = 'compliance' %} +{% set headerPersonName = personCompliance.personSummary.name | fullName %} +{% set headerCRN = personCompliance.personSummary.crn %} +{% block pageTitle %}{{ title }}{% endblock %} + +{% block beforeContent %} +{{ govukBreadcrumbs({ + items: [ + { + text: "Your cases", + href: "/search" + }, + { + text: headerPersonName, + href: "/case/" + crn + }, + { + text: currentSectionName + } + ] +}) }} +{% endblock %} + +{% block pageContent %} + +{% set showWarning = false %} +{% for sentence in personCompliance.currentSentences %} + {% if sentence.compliance.currentBreaches > 0 %} + {% set showWarning = true %} + {% endif %} +{% endfor %} + +{% if showWarning === true %} +
+
+ {{ govukWarningText({ + html: 'There are multiple breach NSIs in progress on Delius.
Use Delius to check and correct any problems.', + iconFallbackText: 'Warning' + }) }} +
+
+{% endif %} + +

+ Use Delius to start a breach +

+ +{% include './compliance/_compliance-current-order.njk' %} +{% include './compliance/_compliance-previous-orders.njk' %} + + +{% endblock %} diff --git a/server/views/pages/compliance/_breaches-on-current-order.njk b/server/views/pages/compliance/_breaches-on-current-order.njk new file mode 100644 index 00000000..13c46485 --- /dev/null +++ b/server/views/pages/compliance/_breaches-on-current-order.njk @@ -0,0 +1,19 @@ +{% if sentence.compliance.breachStarted %} + {%- switch sentence.compliance.priorBreachesOnCurrentOrderCount -%} + {%- case 0 -%} + No prior breaches on current order + {%- case 1 -%} + 1 prior breach on current order + {%- default -%} + {{ sentence.compliance.priorBreachesOnCurrentOrderCount }} prior breaches on current order + {%- endswitch -%} +{% else %} + {%- switch sentence.compliance.breachesOnCurrentOrderCount -%} + {%- case 0 -%} + No breaches on current order + {%- case 1 -%} + 1 breach on current order + {%- default -%} + {{ sentence.compliance.breachesOnCurrentOrderCount }} breaches on current order + {%- endswitch -%} +{% endif %} diff --git a/server/views/pages/compliance/_compliance-current-order.njk b/server/views/pages/compliance/_compliance-current-order.njk new file mode 100644 index 00000000..5977750f --- /dev/null +++ b/server/views/pages/compliance/_compliance-current-order.njk @@ -0,0 +1,299 @@ +{% for sentence in personCompliance.currentSentences %} + {% set failureToComplyCount = sentence.compliance.failureToComplyCount %} + {% set unacceptableAbsenceCount = sentence.activity.unacceptableAbsenceCount %} + {% set attendedButDidNotComplyCount = sentence.activity.attendedButDidNotComplyCount %} + {% set outcomeNotRecordedCount = sentence.activity.outcomeNotRecordedCount %} + {% set waitingForEvidenceCount = sentence.activity.waitingForEvidenceCount %} + {% set rescheduledCount = sentence.activity.rescheduledCount %} + {% set absentCount = sentence.activity.absentCount %} + {% set rescheduledByStaffCount = sentence.activity.rescheduledByStaffCount %} + {% set rescheduledByPersonOnProbationCount = sentence.activity.rescheduledByPersonOnProbationCount %} + {% set lettersCount = sentence.activity.lettersCount %} + {% set nationalStandardAppointmentsCount = sentence.activity.nationalStandardAppointmentsCount %} + {% set compliedAppointmentsCount = sentence.activity.compliedAppointmentsCount %} + + {% set complianceStatus = getComplianceStatus(failureToComplyCount, sentence.compliance.breachStarted) %} + + {% if sentence.rar %} + {% set requirements %} + {{ sentence.rar.totalDays }} days RAR, {{ sentence.rar.completed }} completed + {% endset %} + {% else %} + {% set requirements = false %} + {% endif %} + + + {% if not sentence.activeBreach and not sentence.rar %} + {% set lastSentence = "--last" %} + {% else %} + {% set lastSentence = "" %} + {% endif %} + + {% if sentence.activeBreach and not sentence.rar %} + {% set lastBreach = "--last" %} + {% else %} + {% set lastBreach = "" %} + {% endif %} + +
+

+ {% if failureToComplyCount >= 2 %} + {{ complianceStatus.text }} – Start a breach + {% else %} + {{ complianceStatus.text }} + {% endif %} +

+
+ + {% set breachesOnCurrentOrder %} + + {% endset %} + + {% set orderDetailsList %} + {{ govukSummaryList({ + rows: [ + { + key: { + text: 'Main offence' + }, + value: { + html: '' + sentence.mainOffence.description + '' + } + }, + { + key: { + text: 'Order' + }, + value: { + html: '' + sentence.order.description + ' (' + sentence.order.startDate | monthsOrDaysElapsed + ' elapsed)' + } + }, + { + key: { + text: 'Start date' + }, + value: { + html: '' + sentence.order.startDate | dateWithYear + '' + } + }, + { + key: { + text: 'Breaches' + }, + value: { + html: '' + breachesOnCurrentOrder + '' + } + } + ] + }) }} + {% endset %} + + {{ appSummaryCard({ + attributes: {'data-qa': 'sentence' + loop.index + 'Card'}, + classes: 'app-summary-card--compliance' + lastSentence + ' app-summary-card--large-title', + titleText: 'Sentence (' + sentence.eventNumber + ')', + html: orderDetailsList, + actions: { + items: [ + { + text: 'View sentence details', + href: '/case/'+ crn + '/sentence' + } + ] + } + }) }} + + {% if sentence.activeBreach %} + {% set breachList %} + {{ govukSummaryList({ + rows: [ + { + key: { + text: 'Breach started' + }, + value: { + html: '' + sentence.activeBreach.startDate | dateWithYear + '' + } + }, + { + key: { + text: 'Status' + }, + value: { + html: '' + sentence.activeBreach.status + '' + } + } + ] + }) }} + {% endset %} + + {{ appSummaryCard({ + attributes: {'data-qa': 'breach' + loop.index + 'Card'}, + classes: 'app-summary-card--compliance' + lastBreach + ' app-summary-card--large-title', + titleText: "Breach details", + html: breachList, + actions: { + items: [ + { + href: "", + text: "View in Delius", + visuallyHiddenText: "name" + } + ] + } + }) }} + {% endif %} + + {% set failureToComplyHtml %} + {% if failureToComplyCount == 0 %} + None + {% elseif attendedButDidNotComplyCount == 0 %} + + {% if unacceptableAbsenceCount == 1 %}1 unacceptable absence{% else %}{{ unacceptableAbsenceCount }} unacceptable absences{% endif %} + + {% elseif unacceptableAbsenceCount == 0 %} + + {% if attendedButDidNotComplyCount == 1 %}1 failure{% else %}{{ attendedButDidNotComplyCount }} failures{% endif %} to comply because of behaviour + + {% else %} +

{{ failureToComplyCount }} failures to comply:

+ + {% endif %} + {% endset %} + + {% set acceptableAbsencesHtml %} + + {% if absentCount > 0 %} + {{ absentCount }} acceptable absences + {% else %} + None + {% endif %} + {% endset %} + + {% set waitingForEvidenceHtml %} + {% if waitingForEvidenceCount > 0 %} + {{ waitingForEvidenceCount }} absence waiting for evidence + {% elseif waitingForEvidenceCount > 0 %} + {{ waitingForEvidenceCount }} absences waiting for evidence + {% else %} + None + {% endif %} + {% endset %} + + {% set rescheduledHtml %} + {% if rescheduledCount == 0 %} + None + {% elseif rescheduledByStaffCount == 0 %} + + {{ rescheduledByPersonOnProbationCount }} requested by {{ personCompliance.personSummary.name | fullName }} + + {% elseif rescheduledByPersonOnProbationCount == 0 %} + + {{ rescheduledByStaffCount }} requested by staff + + {% else %} +

{{ rescheduledCount }} rescheduled:

+ + {% endif %} + {% endset %} + + {% set warningLettersHtml %} + + {% if lettersCount === 1 %} + First warning letter sent + {% elif lettersCount > 1 %} + {{ lettersCount }} letters sent + {% else %} + None + {% endif %} + {% endset %} + + {% if requirements %} + {% set rarList %} + {{ govukSummaryList({ + rows: [ + { + key: { + text: 'Appointments' + }, + value: { + html: '' + nationalStandardAppointmentsCount + ' national standard appointments' + } + }, + { + key: { + text: 'Without an outcome' + }, + value: { + html: '' + outcomeNotRecordedCount + ' without a recorded outcome' + } + } if outcomeNotRecordedCount > 0, + { + key: { + text: 'Waiting for evidence' + }, + value: { + html: '' + waitingForEvidenceHtml + '' + } + } if waitingForEvidenceCount > 0, + { + key: { + text: 'Complied' + }, + value: { + html: '' + compliedAppointmentsCount + ' complied' + } + }, + { + key: { + html: 'Failures to comply
within 12 months' + }, + value: { + html: '' + failureToComplyHtml + '' + } + }, + { + key: { + html: 'Warning letters sent within 12 months' + }, + value: { + html: '' + warningLettersHtml + '' + } + }, + { + key: { + text: 'Acceptable absences' + }, + value: { + html: '' + acceptableAbsencesHtml + '' + } + }, + { + key: { + text: 'Rescheduled' + }, + value: { + html: '' + rescheduledHtml + '' + } + } + ] + }) }} + {% endset %} + + {{ appSummaryCard({ + attributes: {'data-qa': 'activity' + loop.index + 'Card'}, + classes: 'govuk-!-margin-bottom-6 app-summary-card--compliance-last app-summary-card--large-title', + titleHtml: 'Requirement
' + requirements, + html: rarList + }) }} + {% endif %} +{% endfor %} diff --git a/server/views/pages/compliance/_compliance-previous-orders.njk b/server/views/pages/compliance/_compliance-previous-orders.njk new file mode 100644 index 00000000..6990dd33 --- /dev/null +++ b/server/views/pages/compliance/_compliance-previous-orders.njk @@ -0,0 +1,85 @@ +

Previous orders

+{% if personCompliance.previousOrders.count > 0 %} +

{{ personCompliance.previousOrders.count }} previous orders ({{ personCompliance.previousOrders.breaches}} breaches on previous orders)
Last ended on {{ personCompliance.previousOrders.lastEndedDate | dateWithYear }}.

+ {% if personCompliance.previousOrders.orders.length > 0 %} +

Previous orders ending within the last 2 years

+ {% for order in personCompliance.previousOrders.orders %} + {% set breaches %} + {% if order.breaches > 0 %} + {{ order.breaches }} + {% else %} + None + {% endif %} + {% endset %} + {% set previousOrder %} + {{ govukSummaryList({ + rows: [ + { + key: { + text: 'Main offence' + }, + value: { + html: '' + order.mainOffence + '' + } + }, + { + key: { + text: 'Status' + }, + value: { + html: '' + order.status + '' + } + }, + { + key: { + text: 'Start date' + }, + value: { + html: '' + order.startDate | dateWithYear + '' + } + }, + { + key: { + text: 'End date' + }, + value: { + html: '' + order.endDate | dateWithYear + '' + } + }, + { + key: { + text: 'Breaches' + }, + value: { + html: '' + breaches + '' + } + } + ] + }) }} + {% endset %} + + {{ appSummaryCard({ + attributes: {'data-qa': 'previousOrder' + loop.index + 'Card'}, + classes: 'govuk-!-margin-bottom-6', + titleText: order.description + ' (Ended ' + order.endDate | dateWithYear + ')', + html: previousOrder, + actions: { + items: [ + { + text: 'View order', + href: '#' + } + ] + } + }) }} + {% endfor %} +

View all previous orders

+ {% else %} +

Previous orders ending within the last 2 years

+ {% endif %} +{% else %} + +

There are no previous orders.

+ +{% endif %} + diff --git a/server/views/pages/overview.njk b/server/views/pages/overview.njk index 2f8d14f1..e15cbd7f 100644 --- a/server/views/pages/overview.njk +++ b/server/views/pages/overview.njk @@ -351,22 +351,39 @@ {% endif %} {% endset %} {% set compliance %} - {% if overview.compliance %} - {{ overview.compliance.failureToComplyInLast12Months }} failure to comply within 12 months
- {{ overview.compliance.currentBreaches }} breaches on current order
- View all compliance details + {% if overview.compliance.breachStarted %} + {%- switch overview.compliance.priorBreachesOnCurrentOrderCount -%} + {%- case 0 -%} + No prior breaches on current order + {%- case 1 -%} + 1 prior breach on current order + {%- default -%} + {{ overview.compliance.priorBreachesOnCurrentOrderCount }} prior breaches on current order + {%- endswitch -%} {% else %} - None + {%- switch overview.compliance.breachesOnCurrentOrderCount -%} + {%- case 0 -%} + No breaches on current order + {%- case 1 -%} + 1 breach on current order + {%- default -%} + {{ overview.compliance.breachesOnCurrentOrderCount }} breaches on current order + {%- endswitch -%} {% endif %} +

+ View all compliance details +

{% endset %} {% set activity %} {% if overview.activity %} - {{ overview.activity.nationalStandardsAppointments }} national standard appointments
- ? without a recorded outcome
- {{ overview.activity.complied }} compiled
- {{ overview.activity.acceptableAbsences }} acceptable absences
- {{ overview.activity.rescheduled }} rescheduled
- View all compliance details + {{ overview.activity.nationalStandardAppointmentsCount }} national standard appointments
+ {{ overview.activity.outcomeNotRecordedCount }} without a recorded outcome
+ {{ overview.activity.compliedAppointmentsCount }} compiled
+ {{ overview.activity.acceptableAbsenceCount }} acceptable absences
+ {{ overview.activity.rescheduledCount }} rescheduled
+

+ View all activity details +

{% else %} None {% endif %} diff --git a/wiremock/mappings/X000001-compliance.json b/wiremock/mappings/X000001-compliance.json new file mode 100644 index 00000000..0f340c21 --- /dev/null +++ b/wiremock/mappings/X000001-compliance.json @@ -0,0 +1,102 @@ +{ + "mappings": [ + { + "request": { + "urlPattern": "/mas/compliance/X000001", + "method": "GET" + }, + "response": { + "status": 200, + "jsonBody": { + "personSummary": { + "name": { + "forename": "Eula", + "surname": "Schmeler" + }, + "crn": "X000001", + "dateOfBirth": "1979-08-18" + }, + "previousOrders": { + "count": 5, + "breaches": 2, + "lastEndedDate": "2020-12-12", + "orders": [ + { + "description": "12 month Community Order", + "mainOffence": "Common Assault and Battery", + "status": "Completed - Sentence/ PSS Expiry Reached", + "startDate": "1990-12-12", + "endDate": "1991-12-12", + "breaches": 2 + } + ] + }, + "currentSentences": [ + { + "eventNumber": "3", + "activity": { + "unacceptableAbsenceCount": 1, + "attendedButDidNotComplyCount": 1, + "outcomeNotRecordedCount": 3, + "waitingForEvidenceCount": 1, + "rescheduledCount": 1, + "absentCount": 1, + "rescheduledByStaffCount": 1, + "rescheduledByPersonOnProbationCount": 1, + "lettersCount": 1, + "nationalStandardAppointmentsCount": 1, + "compliedAppointmentsCount": 2 + }, + "compliance": { + "currentBreaches": 0, + "breachStarted": true, + "breachesOnCurrentOrderCount": 2, + "failureToComplyCount": 2 + }, + "mainOffence": { + "code": "18502", + "description": "(Having possession a picklock or other implement with intent to break into any premises - 18502)" + }, + "order": { + "description": "ORA Community Order", + "endDate": "2024-09-01", + "startDate": "2020-03-02" + }, + "activeBreach": { + "startDate": "2020-03-02", + "status": "An active breach status" + }, + "rar": { + "completed": 9, + "scheduled": 1, + "totalDays": 10 + } + }, + { + "eventNumber": "1", + "breachesOnCurrentOrderCount": 2, + "mainOffence": { + "code": "18502", + "description": "(Another main offence - 18502)" + }, + "compliance": { + "currentBreaches": 0, + "breachStarted": false, + "breachesOnCurrentOrderCount": 0, + "failureToComplyCount": 0 + }, + "order": { + "description": "ORA Community Order", + "endDate": "2024-09-01", + "startDate": "2020-03-02" + } + } + ] + }, + "headers": { + "Content-Type": "application/json" + } + } + } + ] +} diff --git a/wiremock/mappings/X000001-full.json b/wiremock/mappings/X000001-full.json index d72eedc2..9a59ccb1 100644 --- a/wiremock/mappings/X000001-full.json +++ b/wiremock/mappings/X000001-full.json @@ -102,14 +102,25 @@ } ], "activity": { - "acceptableAbsences": 1, - "complied": 1, - "nationalStandardsAppointments": 5, - "rescheduled": 6 + "acceptableAbsenceCount": 1, + "unacceptableAbsenceCount": 2, + "attendedButDidNotComplyCount": 2, + "outcomeNotRecordedCount": 2, + "waitingForEvidenceCount": 2, + "rescheduledCount": 2, + "absentCount": 2, + "rescheduledByStaffCount": 2, + "rescheduledByPersonOnProbationCount": 2, + "lettersCount": 2, + "nationalStandardAppointmentsCount": 2, + "compliedAppointmentsCount": 3 }, "compliance": { "currentBreaches": 2, - "failureToComplyInLast12Months": 2 + "breachStarted": true, + "breachesOnCurrentOrderCount": 1, + "priorBreachesOnCurrentOrderCount": 1, + "failureToComplyCount": 3 }, "registrations": ["Restraining Order", "Domestic Abuse Perpetrator", "Risk to Known Adult"] },