Skip to content

Commit 93fdd97

Browse files
Man 36 sentence requirements (#4536)
* MAN-36 - create common note format function * MAN-36 - update requirement notes * MAN-36 - add new api * Formatting changes --------- Co-authored-by: probation-integration-bot[bot] <177347787+probation-integration-bot[bot]@users.noreply.github.com>
1 parent 4f6f596 commit 93fdd97

File tree

12 files changed

+296
-101
lines changed

12 files changed

+296
-101
lines changed

projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/LicenceConditionIntegrationTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import org.springframework.test.web.servlet.MockMvc
99
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
1010
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
1111
import uk.gov.justice.digital.hmpps.api.model.sentence.LicenceCondition
12-
import uk.gov.justice.digital.hmpps.api.model.sentence.LicenceConditionNote
1312
import uk.gov.justice.digital.hmpps.api.model.sentence.LicenceConditionNoteDetail
13+
import uk.gov.justice.digital.hmpps.api.model.sentence.NoteDetail
1414
import uk.gov.justice.digital.hmpps.data.generator.LicenceConditionGenerator
1515
import uk.gov.justice.digital.hmpps.data.generator.LicenceConditionGenerator.LC_WITH_NOTES
1616
import uk.gov.justice.digital.hmpps.data.generator.LicenceConditionGenerator.LIC_COND_MAIN_CAT
@@ -91,7 +91,7 @@ class LicenceConditionIntegrationTest {
9191
LIC_COND_SUB_CAT.description,
9292
LocalDate.now().minusDays(7),
9393
LocalDate.now(),
94-
licenceConditionNote = LicenceConditionNote(
94+
licenceConditionNote = NoteDetail(
9595
1,
9696
"CVL Service",
9797
LocalDate.of(2024, 4, 22),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package uk.gov.justice.digital.hmpps
2+
3+
import org.junit.jupiter.api.Assertions.assertEquals
4+
import org.junit.jupiter.api.Test
5+
import org.springframework.beans.factory.annotation.Autowired
6+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
7+
import org.springframework.boot.test.context.SpringBootTest
8+
import org.springframework.test.web.servlet.MockMvc
9+
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
10+
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
11+
import uk.gov.justice.digital.hmpps.api.model.overview.Rar
12+
import uk.gov.justice.digital.hmpps.api.model.sentence.NoteDetail
13+
import uk.gov.justice.digital.hmpps.api.model.sentence.Requirement
14+
import uk.gov.justice.digital.hmpps.api.model.sentence.RequirementNoteDetail
15+
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator
16+
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator.REQUIREMENT
17+
import uk.gov.justice.digital.hmpps.service.toSummary
18+
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.contentAsJson
19+
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.withToken
20+
import java.time.LocalDate
21+
22+
@AutoConfigureMockMvc
23+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
24+
class RequirementIntegrationTest {
25+
@Autowired
26+
lateinit var mockMvc: MockMvc
27+
28+
@Test
29+
fun `unauthorized status returned`() {
30+
mockMvc
31+
.perform(MockMvcRequestBuilders.get("/sentence/${PersonGenerator.OVERVIEW.crn}/requirement/1/note/1"))
32+
.andExpect(MockMvcResultMatchers.status().isUnauthorized)
33+
}
34+
35+
@Test
36+
fun `requirement not found`() {
37+
val response = mockMvc
38+
.perform(
39+
MockMvcRequestBuilders.get("/sentence/${PersonGenerator.OVERVIEW.crn}/requirement/0/note/6")
40+
.withToken()
41+
)
42+
.andExpect(MockMvcResultMatchers.status().isOk)
43+
.andReturn().response.contentAsJson<RequirementNoteDetail>()
44+
45+
val expected = RequirementNoteDetail(PersonGenerator.OVERVIEW.toSummary())
46+
47+
assertEquals(expected, response)
48+
}
49+
50+
@Test
51+
fun `get requirement`() {
52+
val response = mockMvc
53+
.perform(
54+
MockMvcRequestBuilders.get("/sentence/${PersonGenerator.OVERVIEW.crn}/requirement/${REQUIREMENT.id}/note/0")
55+
.withToken()
56+
)
57+
.andExpect(MockMvcResultMatchers.status().isOk)
58+
.andReturn().response.contentAsJson<RequirementNoteDetail>()
59+
60+
val expected = RequirementNoteDetail(
61+
PersonGenerator.OVERVIEW.toSummary(),
62+
Requirement(
63+
REQUIREMENT.id,
64+
"F",
65+
LocalDate.now().minusDays(1),
66+
LocalDate.now(),
67+
LocalDate.now().minusDays(2),
68+
LocalDate.now().minusDays(3),
69+
null,
70+
"1 days RAR, 1 completed",
71+
12,
72+
null,
73+
requirementNote = NoteDetail(0, note = "my notes"),
74+
rar = Rar(completed = 1, scheduled = 0, totalDays = 1)
75+
)
76+
)
77+
78+
assertEquals(expected, response)
79+
}
80+
}

projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt

+12-8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import uk.gov.justice.digital.hmpps.data.generator.LicenceConditionGenerator.LC_
2222
import uk.gov.justice.digital.hmpps.data.generator.LicenceConditionGenerator.LIC_COND_MAIN_CAT
2323
import uk.gov.justice.digital.hmpps.data.generator.LicenceConditionGenerator.LIC_COND_SUB_CAT
2424
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator
25+
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator.REQUIREMENT
26+
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator.REQUIREMENT_UNPAID_WORK
2527
import uk.gov.justice.digital.hmpps.data.generator.personalDetails.PersonDetailsGenerator
2628
import uk.gov.justice.digital.hmpps.service.toSummary
2729
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.contentAsJson
@@ -120,6 +122,7 @@ class SentenceIntegrationTest {
120122
),
121123
listOf(
122124
Requirement(
125+
REQUIREMENT.id,
123126
"F",
124127
LocalDate.now().minusDays(1),
125128
LocalDate.now(),
@@ -129,10 +132,11 @@ class SentenceIntegrationTest {
129132
"1 days RAR, 1 completed",
130133
12,
131134
null,
132-
"my notes",
133-
Rar(completed = 1, scheduled = 0, totalDays = 1)
135+
listOf(NoteDetail(0, note = "my notes", hasNoteBeenTruncated = false)),
136+
rar = Rar(completed = 1, scheduled = 0, totalDays = 1)
134137
),
135138
Requirement(
139+
REQUIREMENT_UNPAID_WORK.id,
136140
"W",
137141
LocalDate.now().minusDays(1),
138142
LocalDate.now(),
@@ -142,7 +146,7 @@ class SentenceIntegrationTest {
142146
"Unpaid Work - Intensive",
143147
12,
144148
null,
145-
"my notes",
149+
listOf(NoteDetail(0, note = "my notes", hasNoteBeenTruncated = false)),
146150
null
147151
)
148152
),
@@ -165,7 +169,7 @@ class SentenceIntegrationTest {
165169
LocalDate.now().minusDays(7),
166170
LocalDate.now(),
167171
listOf(
168-
LicenceConditionNote(
172+
NoteDetail(
169173
0,
170174
"Joe Root",
171175
LocalDate.of(2024, 4, 23),
@@ -176,7 +180,7 @@ class SentenceIntegrationTest {
176180
""".trimIndent(),
177181
false
178182
),
179-
LicenceConditionNote(
183+
NoteDetail(
180184
1,
181185
"CVL Service",
182186
LocalDate.of(2024, 4, 22),
@@ -194,7 +198,7 @@ class SentenceIntegrationTest {
194198
LocalDate.now().minusDays(7),
195199
LocalDate.now(),
196200
listOf(
197-
LicenceConditionNote(
201+
NoteDetail(
198202
0,
199203
note = "He shall not contact or associate with Peter Jones without the prior approval of the supervising officer;",
200204
hasNoteBeenTruncated = false
@@ -208,7 +212,7 @@ class SentenceIntegrationTest {
208212
LocalDate.now().minusDays(7),
209213
LocalDate.now(),
210214
listOf(
211-
LicenceConditionNote(
215+
NoteDetail(
212216
0,
213217
"Tom Brady",
214218
LocalDate.of(2024, 10, 29),
@@ -217,7 +221,7 @@ class SentenceIntegrationTest {
217221
""".trimIndent(),
218222
false
219223
),
220-
LicenceConditionNote(
224+
NoteDetail(
221225
1,
222226
"Harry Kane",
223227
LocalDate.of(2024, 10, 29),

projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/controller/SentenceController.kt

+10-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ class SentenceController(
1515
private val ordersService: OrdersService,
1616
private val offenceService: OffenceService,
1717
private val contactService: ContactService,
18-
private val licenceConditionService: LicenceConditionService
18+
private val licenceConditionService: LicenceConditionService,
19+
private val requirementService: RequirementService
1920
) {
2021

2122
@GetMapping
@@ -49,4 +50,12 @@ class SentenceController(
4950
@PathVariable licenceConditionId: Long,
5051
@PathVariable noteId: Int
5152
) = licenceConditionService.getLicenceConditionNote(crn, licenceConditionId, noteId)
53+
54+
@GetMapping("/requirement/{requirementId}/note/{noteId}")
55+
@Operation(summary = "Get licence condition note")
56+
fun getRequirementNote(
57+
@PathVariable crn: String,
58+
@PathVariable requirementId: Long,
59+
@PathVariable noteId: Int
60+
) = requirementService.getRequirementNote(crn, requirementId, noteId)
5261
}

projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/LicenceCondition.kt

+2-10
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,8 @@ data class LicenceCondition(
99
val subTypeDescription: String? = null,
1010
val imposedReleasedDate: LocalDate,
1111
val actualStartDate: LocalDate? = null,
12-
val licenceConditionNotes: List<LicenceConditionNote>? = null,
13-
val licenceConditionNote: LicenceConditionNote? = null
14-
)
15-
16-
data class LicenceConditionNote(
17-
val id: Int,
18-
val createdBy: String? = null,
19-
val createdByDate: LocalDate? = null,
20-
val note: String,
21-
val hasNoteBeenTruncated: Boolean? = null
12+
val licenceConditionNotes: List<NoteDetail>? = null,
13+
val licenceConditionNote: NoteDetail? = null
2214
)
2315

2416
data class LicenceConditionNoteDetail(

projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Requirement.kt

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package uk.gov.justice.digital.hmpps.api.model.sentence
22

3+
import uk.gov.justice.digital.hmpps.api.model.PersonSummary
34
import uk.gov.justice.digital.hmpps.api.model.overview.Rar
45
import java.time.LocalDate
56

67
data class Requirement(
8+
val id: Long,
79
val code: String,
810
val expectedStartDate: LocalDate?,
911
val actualStartDate: LocalDate,
@@ -13,11 +15,17 @@ data class Requirement(
1315
val description: String,
1416
val length: Long?,
1517
val lengthUnitValue: String?,
16-
val notes: String?,
18+
val requirementNotes: List<NoteDetail>? = null,
19+
val requirementNote: NoteDetail? = null,
1720
val rar: Rar? = null
1821
)
1922

2023
data class MinimalRequirement(
2124
val id: Long,
2225
val description: String
2326
)
27+
28+
data class RequirementNoteDetail(
29+
val personSummary: PersonSummary,
30+
val requirement: Requirement? = null
31+
)

projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt

+8
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,11 @@ data class MinimalOrder(
2525
val startDate: LocalDate,
2626
val endDate: LocalDate? = null,
2727
)
28+
29+
data class NoteDetail(
30+
val id: Int,
31+
val createdBy: String? = null,
32+
val createdByDate: LocalDate? = null,
33+
val note: String,
34+
val hasNoteBeenTruncated: Boolean? = null
35+
)

projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/overview/entity/Requirement.kt

+36
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,42 @@ interface RequirementRepository : JpaRepository<Requirement, Long> {
161161
)
162162
fun getRequirements(id: Long, eventNumber: String): List<RequirementDetails>
163163

164+
@Query(
165+
"""
166+
SELECT r.rqmnt_id AS id,
167+
r.expected_start_date as expectedStartDate,
168+
r.start_date as startDate,
169+
r.commencement_date as commencementDate,
170+
r.expected_end_date as expectedEndDate,
171+
r.termination_date as terminationDate,
172+
rsrl3.code_description as terminationReason,
173+
r."LENGTH",
174+
rsrl2.code_description as lengthUnitValue,
175+
rrtmc.code,
176+
rrtmc.description,
177+
rsrl.code_description AS codeDescription,
178+
TO_CHAR(SUBSTR(r.rqmnt_notes, 1, 4000)) AS notes
179+
FROM rqmnt r
180+
JOIN r_rqmnt_type_main_category rrtmc
181+
ON rrtmc.rqmnt_type_main_category_id = r.rqmnt_type_main_category_id
182+
JOIN disposal d
183+
ON d.disposal_id = r.disposal_id
184+
JOIN event e
185+
ON e.event_id = d.event_id
186+
LEFT JOIN r_standard_reference_list rsrl
187+
ON rsrl.standard_reference_list_id = r.rqmnt_type_sub_category_id
188+
LEFT JOIN r_standard_reference_list rsrl2
189+
ON rsrl2.standard_reference_list_id = rrtmc.units_id
190+
LEFT JOIN r_standard_reference_list rsrl3
191+
ON rsrl3.standard_reference_list_id = r.rqmnt_termination_reason_id
192+
WHERE r.rqmnt_id = :id
193+
AND e.soft_deleted = 0
194+
AND e.active_flag = 1
195+
ORDER BY rrtmc.description
196+
""", nativeQuery = true
197+
)
198+
fun getRequirement(id: Long): RequirementDetails?
199+
164200
@Query(
165201
"""
166202
SELECT COALESCE(SUM(r.length), 0)
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
package uk.gov.justice.digital.hmpps.service
22

33
import org.springframework.stereotype.Service
4-
import uk.gov.justice.digital.hmpps.api.model.sentence.LicenceCondition
5-
import uk.gov.justice.digital.hmpps.api.model.sentence.LicenceConditionNote
6-
import uk.gov.justice.digital.hmpps.api.model.sentence.LicenceConditionNoteDetail
7-
import uk.gov.justice.digital.hmpps.api.model.sentence.MinimalLicenceCondition
8-
import uk.gov.justice.digital.hmpps.datetime.DeliusDateFormatter
4+
import uk.gov.justice.digital.hmpps.api.model.sentence.*
95
import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.PersonRepository
106
import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.getPerson
117
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionRepository
12-
import java.time.LocalDate
138
import kotlin.jvm.optionals.getOrNull
149
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceCondition as EntityLicenceCondition
1510

@@ -53,37 +48,7 @@ fun EntityLicenceCondition.toLicenceConditionSingleNote(noteId: Int, truncateNot
5348
licenceConditionNote = toLicenceConditionNote(truncateNote).elementAtOrNull(noteId)
5449
)
5550

56-
fun EntityLicenceCondition.toLicenceConditionNote(truncateNote: Boolean): List<LicenceConditionNote> {
51+
fun EntityLicenceCondition.toLicenceConditionNote(truncateNote: Boolean): List<NoteDetail> {
5752

58-
return notes?.let {
59-
val splitParam = "---------------------------------------------------------" + System.lineSeparator()
60-
notes.split(splitParam).asReversed().mapIndexed { index, note ->
61-
val matchResult = Regex(
62-
"^Comment added by (.+?) on (\\d{2}/\\d{2}/\\d{4}) at \\d{2}:\\d{2}"
63-
+ System.lineSeparator()
64-
).find(note)
65-
val commentLine = matchResult?.value
66-
val commentText =
67-
commentLine?.let { note.removePrefix(commentLine).removeSuffix(System.lineSeparator()) } ?: note
68-
69-
val userCreatedBy = matchResult?.groupValues?.get(1)
70-
val dateCreatedBy = matchResult?.groupValues?.get(2)
71-
?.let { LocalDate.parse(it, DeliusDateFormatter) }
72-
73-
74-
LicenceConditionNote(
75-
index,
76-
userCreatedBy,
77-
dateCreatedBy,
78-
when (truncateNote) {
79-
true -> commentText.removeSuffix(System.lineSeparator()).chunked(1500)[0]
80-
else -> commentText
81-
},
82-
when (truncateNote) {
83-
true -> commentText.length > 1500
84-
else -> null
85-
}
86-
)
87-
}
88-
} ?: listOf()
53+
return formatNote(notes, truncateNote)
8954
}

0 commit comments

Comments
 (0)