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

ADJUST1-193 Temporary approve saving ADA adjustments. #60

Merged
merged 2 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
68 changes: 20 additions & 48 deletions server/@types/adjustments/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export interface components {
schemas: {
DlqMessage: {
body: {
[key: string]: Record<string, never>
[key: string]: Record<string, never> | undefined
}
messageId: string
}
Expand Down Expand Up @@ -145,13 +145,8 @@ export interface components {
}
/** @description The details of an additional days awarded (ADA) adjustment */
AdditionalDaysAwardedDto: {
/**
* Format: int32
* @description The id of the adjudication that resulted in the ADA
*/
adjudicationId: number
/** @description Is the ADA consecutive or concurrent */
consecutive: boolean
/** @description The id of the adjudication that resulted in the ADA */
adjudicationId: number[]
}
/** @description The adjustment and its identifier */
AdjustmentDto: {
Expand Down Expand Up @@ -213,8 +208,11 @@ export interface components {
prisonName?: string
/** @description The person last updating this adjustment */
lastUpdatedBy?: string
/** @description The status of this adjustment */
status?: string
/**
* @description The status of this adjustment
* @enum {string}
*/
status?: 'ACTIVE' | 'INACTIVE' | 'DELETED'
/**
* Format: date-time
* @description The date and time this adjustment was last updated
Expand Down Expand Up @@ -277,8 +275,6 @@ export interface components {
pathItems: never
}

export type $defs = Record<string, never>

export type external = Record<string, never>

export interface operations {
Expand Down Expand Up @@ -372,17 +368,11 @@ export interface operations {
}
responses: {
/** @description Adjustment update */
200: {
content: never
}
200: never
/** @description Unauthorised, requires a valid Oauth2 token */
401: {
content: never
}
401: never
/** @description Adjustment not found */
404: {
content: never
}
404: never
}
}
/**
Expand All @@ -398,17 +388,11 @@ export interface operations {
}
responses: {
/** @description Adjustment deleted */
200: {
content: never
}
200: never
/** @description Unauthorised, requires a valid Oauth2 token */
401: {
content: never
}
401: never
/** @description Adjustment not found */
404: {
content: never
}
404: never
}
}
/**
Expand Down Expand Up @@ -461,17 +445,11 @@ export interface operations {
}
responses: {
/** @description Adjustment update */
200: {
content: never
}
200: never
/** @description Unauthorised, requires a valid Oauth2 token */
401: {
content: never
}
401: never
/** @description Adjustment not found */
404: {
content: never
}
404: never
}
}
/**
Expand All @@ -487,17 +465,11 @@ export interface operations {
}
responses: {
/** @description Adjustment deleted */
200: {
content: never
}
200: never
/** @description Unauthorised, requires a valid Oauth2 token */
401: {
content: never
}
401: never
/** @description Adjustment not found */
404: {
content: never
}
404: never
}
}
/**
Expand Down
18 changes: 18 additions & 0 deletions server/routes/additionalDaysAwardedRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,22 @@ export default class AdditionalDaysAwardedRoutes {
adasToReview,
})
}

public approve: RequestHandler = async (req, res): Promise<void> => {
const { caseloads, token, username } = res.locals.user
const { nomsId } = req.params
const prisonerDetail = await this.prisonerService.getPrisonerDetail(nomsId, caseloads, token)
const startOfSentenceEnvelope = await this.prisonerService.getStartOfSentenceEnvelope(
prisonerDetail.bookingId,
token,
)
await this.additionalDaysAwardedService.approveAdjudications(
prisonerDetail,
startOfSentenceEnvelope,
username,
token,
)

return res.redirect(`/${nomsId}`)
}
}
2 changes: 1 addition & 1 deletion server/routes/adjustmentRoutes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ describe('Adjustment routes tests', () => {
it('GET /{nomsId}/{adjustmentType}/view', () => {
prisonerService.getPrisonerDetail.mockResolvedValue(stubbedPrisonerData)
adjustmentsService.findByPerson.mockResolvedValue([
{ ...radaAdjustment, id: 'this-is-an-id', lastUpdatedBy: 'Doris McNealy', status: 'Active', prisonName: 'Leeds' },
{ ...radaAdjustment, id: 'this-is-an-id', lastUpdatedBy: 'Doris McNealy', status: 'ACTIVE', prisonName: 'Leeds' },
])

return request(app)
Expand Down
1 change: 1 addition & 0 deletions server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default function routes(service: Services): Router {
post('/:nomsId/review', adjustmentRoutes.submitReview)

get('/:nomsId/ada/review', additionalDaysAwardedRoutes.review)
post('/:nomsId/ada/review', additionalDaysAwardedRoutes.approve)

get('/:nomsId/:adjustmentTypeUrl/view', adjustmentRoutes.view)
get('/:nomsId/:adjustmentTypeUrl/remove/:id', adjustmentRoutes.remove)
Expand Down
6 changes: 3 additions & 3 deletions server/services/additionalDaysAwardedService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ const adjudicationThreeNonAda = JSON.parse(

const adjudicationOneAdjustment = {
adjustmentType: 'ADDITIONAL_DAYS_AWARDED',
additionalDaysAwarded: { adjudicationId: 1525916 },
additionalDaysAwarded: { adjudicationId: [1525916] },
} as Adjustment
const adjudicationTwoAdjustment = {
adjustmentType: 'ADDITIONAL_DAYS_AWARDED',
additionalDaysAwarded: { adjudicationId: 1525917 },
additionalDaysAwarded: { adjudicationId: [1525917] },
} as Adjustment
const adjudicationThreeAdjustment = {
adjustmentType: 'ADDITIONAL_DAYS_AWARDED',
additionalDaysAwarded: { adjudicationId: 1525918 },
additionalDaysAwarded: { adjudicationId: [1525918] },
} as Adjustment

const adjustmentResponsesWithChargeNumber = [
Expand Down
64 changes: 61 additions & 3 deletions server/services/additionalDaysAwardedService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { AdjudicationSearchResponse, IndividualAdjudication, Sanction } from '..
import { HmppsAuthClient } from '../data'
import { Ada, AdasByDateCharged, AdasToReview } from '../@types/AdaTypes'
import AdjustmentsClient from '../api/adjustmentsClient'
import { PrisonApiPrisoner } from '../@types/prisonApi/prisonClientTypes'
import { Adjustment } from '../@types/adjustments/adjustmentsTypes'

/* The adjudications status from NOMIS DB mapped to the adjudications API status are listed here temporarily to make it easier to implement the stories which use the NOMIS status
* 'AS_AWARDED' = 'Activated as Awarded'
Expand Down Expand Up @@ -61,7 +63,7 @@ export default class AdditionalDaysAwardedService {
): Promise<AdasToReview> {
const existingAdaChargeIds = (await new AdjustmentsClient(token).findByPerson(nomsId))
.filter(it => it.adjustmentType === 'ADDITIONAL_DAYS_AWARDED' && it.additionalDaysAwarded)
.map(ada => ada.additionalDaysAwarded.adjudicationId)
.flatMap(ada => ada.additionalDaysAwarded.adjudicationId)
const systemToken = await this.hmppsAuthClient.getSystemClientToken(username)
const adjudicationClient = new AdjudicationClient(systemToken)
const adjudications: AdjudicationSearchResponse = await adjudicationClient.getAdjudications(nomsId)
Expand Down Expand Up @@ -132,11 +134,13 @@ export default class AdditionalDaysAwardedService {
const calculatedDays = chains
.filter(it => it.length > 0)
.map(chain => chain.reduce((acc, cur) => acc + cur.days, 0))

if (!calculatedDays.length) {
return 0
}
return Math.max(...calculatedDays)
}

createChain(ada: Ada, chain: Ada[], consecCharges: Ada[]) {
private createChain(ada: Ada, chain: Ada[], consecCharges: Ada[]) {
const consecFrom = consecCharges.find(it => it.consecutiveToSequence === ada.sequence)
if (consecFrom) {
chain.push(consecFrom)
Expand Down Expand Up @@ -242,4 +246,58 @@ export default class AdditionalDaysAwardedService {
)
.map(consecutiveAda => allAdas.find(sourceAda => sourceAda.sequence === consecutiveAda.consecutiveToSequence))
}

public async approveAdjudications(
prisonerDetail: PrisonApiPrisoner,
startOfSentenceEnvelope: Date,
username: string,
token: string,
) {
const allAdaAdjustments = (await new AdjustmentsClient(token).findByPerson(prisonerDetail.offenderNo)).filter(
it => it.adjustmentType === 'ADDITIONAL_DAYS_AWARDED',
)
const existingAdaChargeIds = allAdaAdjustments
.filter(it => it.additionalDaysAwarded)
.flatMap(ada => ada.additionalDaysAwarded.adjudicationId)
const systemToken = await this.hmppsAuthClient.getSystemClientToken(username)
const adjudicationClient = new AdjudicationClient(systemToken)
const adjudications: AdjudicationSearchResponse = await adjudicationClient.getAdjudications(
prisonerDetail.offenderNo,
)
const individualAdjudications = await Promise.all(
adjudications.results.content.map(async it => {
return adjudicationClient.getAdjudication(prisonerDetail.offenderNo, it.adjudicationNumber)
}),
)
const allAdas: Ada[] = this.getAdas(individualAdjudications, startOfSentenceEnvelope, existingAdaChargeIds)

const adas: AdasByDateCharged[] = this.getAdasByDateCharged(allAdas, AWARDED)

const adjustments = adas.map(it => {
return {
person: prisonerDetail.offenderNo,
bookingId: prisonerDetail.bookingId,
adjustmentType: 'ADDITIONAL_DAYS_AWARDED',
fromDate: it.dateChargeProved.toISOString().substring(0, 10),
days: it.total,
prisonId: prisonerDetail.agencyId,
additionalDaysAwarded: { adjudicationId: it.charges.map(charge => charge.chargeNumber) },
} as Adjustment
})

// Delete all unlinked ADAs.
await Promise.all(
allAdaAdjustments
.filter(it => !it.additionalDaysAwarded)
.map(it => {
return new AdjustmentsClient(token).delete(it.id)
}),
)
// Create adjustments
await Promise.all(
adjustments.map(it => {
return new AdjustmentsClient(token).create(it)
}),
)
}
}
17 changes: 12 additions & 5 deletions server/views/pages/adjustments/ada/review.njk
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{% extends "../../../partials/layout.njk" %}
{% from "../../../macros/adaTable.njk" import adaTable %}
{% from "govuk/components/button/macro.njk" import govukButton %}

{% set pageTitle = applicationName + " - Review additional days awarded" %}
{% set mainClasses = "app-container govuk-body" %}
Expand All @@ -24,7 +25,7 @@
<h2 class="govuk-heading-m">Awarded ADAs</h2>
{{ adaTable('awarded-adas', adasToReview.adas, adasToReview.totalAdas, 'Total ADAs taken into calculation', 'Awarded ADAs will be included in the calculation.', 'No active ADA adjudications exist for this offender') }}

{% if adasToReview.suspended | length %}
{% if adasToReview.suspended | length %}
<h2 class="govuk-heading-m">Suspended ADAs</h2>
{{ adaTable('suspended-adas', adasToReview.suspended, adasToReview.totalSuspended, 'Total suspended ADAs', 'Suspended ADAs will not be included in the calculation.') }}
{% endif %}
Expand All @@ -38,13 +39,19 @@
If you think some information is wrong, contact the team responsible for adjudications.
</div>
<div class="govuk-!-margin-top-8 govuk-button-group">
<a href="#" class="govuk-button" data-module="govuk-button">
Approve
</a>

<form class="form" method="post">
<input type="hidden" name="_csrf" value="{{ csrfToken }}"/>
{{ govukButton({
text: "Approve",
type: submit,
preventDoubleClick: true
}) }}
</form>
</div>
<a href="../">Cancel and return to dashboard</a>
</div>
</div>
</main>
</div>
{% endblock %}
{% endblock %}