Skip to content

Commit

Permalink
ADJUST1-272 Review remand (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
ldlharper authored Nov 24, 2023
1 parent 53f0594 commit d5d58d9
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 10 deletions.
78 changes: 78 additions & 0 deletions server/model/remandReviewModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import dayjs from 'dayjs'
import { Adjustment } from '../@types/adjustments/adjustmentsTypes'
import { PrisonApiOffenderSentenceAndOffences, PrisonApiPrisoner } from '../@types/prisonApi/prisonClientTypes'
import { daysBetween } from '../utils/utils'
import ReviewRemandForm from './reviewRemandForm'

export default class RemandReviewModel {
adjustmentIds: string[]

constructor(
public prisonerDetail: PrisonApiPrisoner,
public adjustments: { string?: Adjustment },
private sentencesAndOffences: PrisonApiOffenderSentenceAndOffences[],
public form: ReviewRemandForm,
) {
this.adjustmentIds = Object.keys(adjustments)
}

public totalDays(): number {
return Object.values(this.adjustments)
.map(it => daysBetween(new Date(it.fromDate), new Date(it.toDate)))
.reduce((sum, current) => sum + current, 0)
}

public adjustmentSummary(id: string) {
const adjustment = this.adjustments[id]
const offences = this.sentencesAndOffences.flatMap(it =>
it.offences.filter(off => adjustment.remand.chargeId.includes(off.offenderChargeId)),
)
return {
rows: [
{
key: {
text: 'Remand period',
},
value: {
text: `${dayjs(adjustment.fromDate).format('DD MMMM YYYY')} to ${dayjs(adjustment.toDate).format(
'DD MMMM YYYY',
)}`,
},
},
{
key: {
text: 'Offences',
},
value: {
html: `<ul class="govuk-list govuk-list--bullet">
${offences.map(it => `<li>${it.offenceDescription}</li>`).join('')}
</ul>`,
},
},
{
key: {
text: 'Days spend on remand',
},
value: {
text: daysBetween(new Date(adjustment.fromDate), new Date(adjustment.toDate)),
},
},
],
}
}

public totalDaysSummary() {
return {
rows: [
{
key: {
text: 'Total days',
},
value: {
text: this.totalDays(),
},
},
],
}
}
}
18 changes: 18 additions & 0 deletions server/model/reviewRemandForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import AbstractForm from './abstractForm'
import ValidationError from './validationError'

export default class ReviewRemandForm extends AbstractForm<ReviewRemandForm> {
another: 'yes' | 'no'

async validation(): Promise<ValidationError[]> {
if (!this.another) {
return [
{
fields: ['another'],
text: 'Select an answer',
},
]
}
return []
}
}
2 changes: 2 additions & 0 deletions server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export default function routes(service: Services): Router {
post('/:nomsId/remand/dates/:addOrEdit/:id', remandRoutes.submitDates)
get('/:nomsId/remand/offences/:addOrEdit/:id', remandRoutes.offences)
post('/:nomsId/remand/offences/:addOrEdit/:id', remandRoutes.submitOffences)
get('/:nomsId/remand/review', remandRoutes.review)
post('/:nomsId/remand/review', remandRoutes.submitReview)

get('/:nomsId/:adjustmentTypeUrl/view', adjustmentRoutes.view)
get('/:nomsId/:adjustmentTypeUrl/remove/:id', adjustmentRoutes.remove)
Expand Down
79 changes: 77 additions & 2 deletions server/routes/remandRoutes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,13 @@ const stubbedSentencesAndOffences = [
sentenceSequence: 1,
sentenceStatus: 'A',
offences: [
{ offenceDescription: 'Doing a crime', offenceStartDate: '2021-01-04', offenceEndDate: '2021-01-05' },
{ offenceDescription: 'Doing a different crime', offenceStartDate: '2021-03-06' },
{
offenderChargeId: 1,
offenceDescription: 'Doing a crime',
offenceStartDate: '2021-01-04',
offenceEndDate: '2021-01-05',
},
{ offenderChargeId: 2, offenceDescription: 'Doing a different crime', offenceStartDate: '2021-03-06' },
],
} as PrisonApiOffenderSentenceAndOffences,
]
Expand All @@ -71,6 +76,13 @@ const adjustmentWithDates = {
toDate: '2023-01-10',
} as Adjustment

const adjustmentWithDatesAndCharges = {
...adjustmentWithDates,
remand: {
chargeId: [1, 2],
},
} as Adjustment

let app: Express

beforeEach(() => {
Expand Down Expand Up @@ -232,4 +244,67 @@ describe('Adjustment routes tests', () => {
expect(res.text).toContain('Select an offence')
})
})
it('GET /{nomsId}/remand/review', () => {
const adjustments = {}
adjustments[SESSION_ID] = adjustmentWithDatesAndCharges
prisonerService.getPrisonerDetail.mockResolvedValue(stubbedPrisonerData)
prisonerService.getSentencesAndOffences.mockResolvedValue(stubbedSentencesAndOffences)
adjustmentsStoreService.getAll.mockReturnValue(adjustments)
return request(app)
.get(`/${NOMS_ID}/remand/review`)
.expect('Content-Type', /html/)
.expect(res => {
expect(res.text).toContain('Anon')
expect(res.text).toContain('Nobody')
expect(res.text).toContain('Review remand details')
expect(res.text).toContainInOrder([
'Remand period',
'01 January 2023 to 10 January 2023',
'Offences',
'Doing a crime',
'Doing a different crime',
'Days spend on remand',
'10',
'Total days',
'10',
])
})
})

it('POST /{nomsId}/remand/review yes more to add', () => {
return request(app)
.post(`/${NOMS_ID}/remand/review`)
.type('form')
.send({
another: 'yes',
})
.expect(302)
.expect('Location', `/${NOMS_ID}/remand/add`)
})

it('POST /{nomsId}/remand/review no more to add', () => {
return request(app)
.post(`/${NOMS_ID}/remand/review`)
.type('form')
.send({
another: 'no',
})
.expect(302)
.expect('Location', `/${NOMS_ID}/remand/save`)
})

it('POST /{nomsId}/remand/review nothing selected', () => {
const adjustments = {}
adjustments[SESSION_ID] = adjustmentWithDatesAndCharges
prisonerService.getPrisonerDetail.mockResolvedValue(stubbedPrisonerData)
prisonerService.getSentencesAndOffences.mockResolvedValue(stubbedSentencesAndOffences)
adjustmentsStoreService.getAll.mockReturnValue(adjustments)
return request(app)
.post(`/${NOMS_ID}/remand/review`)
.type('form')
.expect('Content-Type', /html/)
.expect(res => {
expect(res.text).toContain('Select an answer')
})
})
})
37 changes: 36 additions & 1 deletion server/routes/remandRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import FullPageError from '../model/FullPageError'
import RemandDatesForm from '../model/remandDatesForm'
import RemandOffencesForm from '../model/remandOffencesForm'
import RemandSelectOffencesModel from '../model/remandSelectOffencesModel'
import RemandReviewModel from '../model/remandReviewModel'
import ReviewRemandForm from '../model/reviewRemandForm'

export default class RemandRoutes {
constructor(
Expand Down Expand Up @@ -97,9 +99,9 @@ export default class RemandRoutes {

await adjustmentForm.validate()
const adjustment = this.adjustmentsStoreService.getById(req, nomsId, id)
const sentencesAndOffences = await this.prisonerService.getSentencesAndOffences(prisonerDetail.bookingId, token)

if (adjustmentForm.errors.length) {
const sentencesAndOffences = await this.prisonerService.getSentencesAndOffences(prisonerDetail.bookingId, token)
return res.render('pages/adjustments/remand/offences', {
model: new RemandSelectOffencesModel(id, prisonerDetail, adjustment, adjustmentForm, sentencesAndOffences),
})
Expand All @@ -109,4 +111,37 @@ export default class RemandRoutes {

return res.redirect(`/${nomsId}/remand/review`)
}

public review: RequestHandler = async (req, res): Promise<void> => {
const { caseloads, token } = res.locals.user
const { nomsId } = req.params

const adjustments = this.adjustmentsStoreService.getAll(req, nomsId)
const prisonerDetail = await this.prisonerService.getPrisonerDetail(nomsId, caseloads, token)
const sentencesAndOffences = await this.prisonerService.getSentencesAndOffences(prisonerDetail.bookingId, token)

return res.render('pages/adjustments/remand/review', {
model: new RemandReviewModel(prisonerDetail, adjustments, sentencesAndOffences, new ReviewRemandForm({})),
})
}

public submitReview: RequestHandler = async (req, res): Promise<void> => {
const { caseloads, token } = res.locals.user
const { nomsId } = req.params

const form = new ReviewRemandForm(req.body)
await form.validate()
if (form.errors.length) {
const adjustments = this.adjustmentsStoreService.getAll(req, nomsId)
const prisonerDetail = await this.prisonerService.getPrisonerDetail(nomsId, caseloads, token)
const sentencesAndOffences = await this.prisonerService.getSentencesAndOffences(prisonerDetail.bookingId, token)
return res.render('pages/adjustments/remand/review', {
model: new RemandReviewModel(prisonerDetail, adjustments, sentencesAndOffences, form),
})
}
if (form.another === 'yes') {
return res.redirect(`/${nomsId}/remand/add`)
}
return res.redirect(`/${nomsId}/remand/save`)
}
}
16 changes: 9 additions & 7 deletions server/routes/testutils/toContainInOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ expect.extend({
let match: jest.CustomMatcherResult = null
let text = received
all.forEach(expected => {
const result = text.indexOf(expected)
if (result === -1) {
match = {
message: () => `Remaining text didn't contain: '${expected}' within: \n ${text}`,
pass: false,
if (!match) {
const result = text.indexOf(expected)
if (result === -1) {
match = {
message: () => `Remaining text didn't contain: '${expected}' within: \n ${text}`,
pass: false,
}
return
}
return
text = text.substring(result)
}
text = text.substring(result)
})
if (match) {
return match
Expand Down
103 changes: 103 additions & 0 deletions server/views/pages/adjustments/remand/review.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
{% extends "../../../partials/layout.njk" %}
{% from "govuk/components/date-input/macro.njk" import govukDateInput %}
{% from "govuk/components/input/macro.njk" import govukInput %}
{% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %}
{% from "govuk/components/button/macro.njk" import govukButton %}
{% from "govuk/components/back-link/macro.njk" import govukBackLink %}
{% from "govuk/components/summary-list/macro.njk" import govukSummaryList %}
{% from "govuk/components/radios/macro.njk" import govukRadios %}

{% set pageTitle = applicationName + " - Review remand details" %}
{% set mainClasses = "app-container govuk-body" %}

{% block beforeContent %}
{{ super() }}
<nav>
{% if model.addOrEdit == 'edit' and model.id %}
{% set backlink = "/" + model.prisonerDetail.offenderNo + "/remand/view" %}
{% else %}
{% set backlink = "/" + model.prisonerDetail.offenderNo %}
{% endif %}
{{ govukBackLink({
text: "Back",
href: backlink
}) }}
</nav>
{% endblock %}

{% block content %}

{% if model.form.errors.length %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
{{ govukErrorSummary({
titleText: "There is a problem",
errorList: model.form.errorList()
}) }}
</div>
</div>
{% endif %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l">
<span class="govuk-caption-xl">Adjust release dates</span>
Review remand details
</h1>
</div>
</div>
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<p class="govuk-body">Check that the remand and offence information is correct. Once you've reviewed it, you can add more remand time if needed.</p>
</div>
</div>
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<h2 class="govuk-heading-m">Remand details</h2>

<form class="form" method="post">
<input type="hidden" name="_csrf" value="{{ csrfToken }}"/>

{% for adjustmentId in model.adjustmentIds %}
{{ govukSummaryList(model.adjustmentSummary(adjustmentId))}}
{% endfor %}
{{ govukSummaryList(model.totalDaysSummary())}}

{{ govukRadios({
classes: "govuk-radios--inline",
name: "another",
fieldset: {
legend: {
text: "Do you need to add another period of remand?",
classes: "govuk-fieldset__legend--m"
}
},
errorMessage: model.form.messageForField('another'),
items: [
{
value: "yes",
text: "Yes"
},
{
value: "no",
text: "No"
}
]
}) }}

<div class="govuk-button-group">
{{ govukButton({
text: "Continue",
type: submit,
preventDoubleClick: true,
attributes: { 'data-qa': 'submit-form' }
}) }}
{{ govukButton({
text: "Cancel",
classes: "govuk-button--secondary",
href: "/" + model.prisonerDetail.offenderNo
}) }}
</div>
</form>
</div>
</div>
{% endblock %}

0 comments on commit d5d58d9

Please sign in to comment.