diff --git a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt index 735071c8be..b40008235e 100644 --- a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt +++ b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt @@ -95,6 +95,11 @@ class DataLoader( OffenderManagerGenerator.OFFENDER_MANAGER_INACTIVE, PersonGenerator.DEFAULT_DISPOSAL_TYPE, PersonGenerator.ACTIVE_ORDER, + LicenceConditionGenerator.LIC_COND_MAIN_CAT, + LicenceConditionGenerator.LIC_COND_SUB_CAT, + LicenceConditionGenerator.LC_WITH_NOTES, + LicenceConditionGenerator.LC_WITHOUT_NOTES, + LicenceConditionGenerator.LC_WITH_NOTES_WITHOUT_ADDED_BY, PersonGenerator.TERMINATION_REASON, PersonGenerator.REF_DATA_YEARS, PersonGenerator.INACTIVE_ORDER_1, diff --git a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LicenceConditionGenerator.kt b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LicenceConditionGenerator.kt new file mode 100644 index 0000000000..d387d786b6 --- /dev/null +++ b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LicenceConditionGenerator.kt @@ -0,0 +1,62 @@ +package uk.gov.justice.digital.hmpps.data.generator + +import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator.ACTIVE_ORDER +import uk.gov.justice.digital.hmpps.integrations.delius.referencedata.entity.ReferenceData +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceCondition +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionMainCategory +import java.time.LocalDate + +object LicenceConditionGenerator { + + val LIC_COND_MAIN_CAT = LicenceConditionMainCategory( + IdGenerator.getAndIncrement(), + "LicMain", + "lic cond main" + ) + + val LIC_COND_SUB_CAT = ReferenceData( + IdGenerator.getAndIncrement(), + "LicSub", + "Lic Sub cat" + ) + + val LC_WITHOUT_NOTES = LicenceCondition( + IdGenerator.getAndIncrement(), + LIC_COND_MAIN_CAT, + null, + ACTIVE_ORDER.id, + LocalDate.now().minusDays(14), + null, + null + ) + + val LC_WITH_NOTES = LicenceCondition( + IdGenerator.getAndIncrement(), + LIC_COND_MAIN_CAT, + LIC_COND_SUB_CAT, + ACTIVE_ORDER.id, + LocalDate.now().minusDays(7), + LocalDate.now(), + """ + Comment added by CVL Service on 22/04/2024 at 10:00 + Licence Condition created automatically from the Create and Vary a licence system of\nAllow person(s) as designated by your supervising officer to install an electronic monitoring tag on you and access to install any associated equipment in your property, and for the purpose of ensuring that equipment is functioning correctly. You must not damage or tamper with these devices and ensure that the tag is charged, and report to your supervising officer and the EM provider immediately if the tag or the associated equipment are not working correctly. This will be for the purpose of monitoring your alcohol abstinence licence condition(s) unless otherwise authorised by your supervising officer. Licence Condition created automatically from the Create and Vary a licence system of\nAllow person(s) as designated by your supervising officer to install an electronic monitoring tag on you and access to install any associated equipment in your property, and for the purpose of ensuring that equipment is functioning correctly. You must not damage or tamper with these devices and ensure that the tag is charged, and report to your supervising officer and the EM provider immediately if the tag or the associated equipment are not working correctly. This will be for the purpose of monitoring your alcohol abstinence licence condition(s) unless otherwise authorised by your supervising officer.Licence Condition created automatically from the Create and Vary a licence system of\nAllow person(s) as desi123456 + --------------------------------------------------------- + Comment added by Joe Root on 23/04/2024 at 13:45 + You must not drink any alcohol until Wednesday 7th August 2024 unless your + probation officer says you can. You will need to wear an electronic tag all the time so + we can check this. + """.trimIndent() + ) + + val LC_WITH_NOTES_WITHOUT_ADDED_BY = LicenceCondition( + IdGenerator.getAndIncrement(), + LIC_COND_MAIN_CAT, + LIC_COND_SUB_CAT, + ACTIVE_ORDER.id, + LocalDate.now().minusDays(7), + LocalDate.now(), + """ + He shall not contact or associate with Peter Jones without the prior approval of the supervising officer; + """.trimIndent() + ) +} \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt b/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt index be40682c7e..c12beab5aa 100644 --- a/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt +++ b/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt @@ -19,6 +19,8 @@ import uk.gov.justice.digital.hmpps.service.toSummary import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.contentAsJson import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.withToken import java.time.LocalDate +import uk.gov.justice.digital.hmpps.data.generator.LicenceConditionGenerator.LIC_COND_MAIN_CAT +import uk.gov.justice.digital.hmpps.data.generator.LicenceConditionGenerator.LIC_COND_SUB_CAT @AutoConfigureMockMvc @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -62,7 +64,8 @@ class SentenceIntegrationTest { null, listOf(), listOf(), - null + null, + listOf() ), Sentence( OffenceDetails( @@ -114,7 +117,51 @@ class SentenceIntegrationTest { CourtDocument(COURT_DOCUMENT.alfrescoId, LocalDate.now().minusDays(1), "court report"), CourtDocument(EVENT_DOCUMENT.alfrescoId, LocalDate.now().minusDays(3), "event report") ), - "3 minutes completed (of 12 hours)" + "3 minutes completed (of 12 hours)", + listOf( + LicenceCondition( + LIC_COND_MAIN_CAT.description, + LIC_COND_SUB_CAT.description, + LocalDate.now().minusDays(7), + LocalDate.now(), + listOf( + LicenceConditionNote( + "CVL Service", + LocalDate.of(2024, 4, 22), + """ + Licence Condition created automatically from the Create and Vary a licence system of\nAllow person(s) as designated by your supervising officer to install an electronic monitoring tag on you and access to install any associated equipment in your property, and for the purpose of ensuring that equipment is functioning correctly. You must not damage or tamper with these devices and ensure that the tag is charged, and report to your supervising officer and the EM provider immediately if the tag or the associated equipment are not working correctly. This will be for the purpose of monitoring your alcohol abstinence licence condition(s) unless otherwise authorised by your supervising officer. Licence Condition created automatically from the Create and Vary a licence system of\nAllow person(s) as designated by your supervising officer to install an electronic monitoring tag on you and access to install any associated equipment in your property, and for the purpose of ensuring that equipment is functioning correctly. You must not damage or tamper with these devices and ensure that the tag is charged, and report to your supervising officer and the EM provider immediately if the tag or the associated equipment are not working correctly. This will be for the purpose of monitoring your alcohol abstinence licence condition(s) unless otherwise authorised by your supervising officer.Licence Condition created automatically from the Create and Vary a licence system of\nAllow person(s) as desi + """.trimIndent(), + true + ), + LicenceConditionNote( + "Joe Root", + LocalDate.of(2024, 4, 23), + """ + You must not drink any alcohol until Wednesday 7th August 2024 unless your + probation officer says you can. You will need to wear an electronic tag all the time so + we can check this. + """.trimIndent(), + false + ), + ) + ), + LicenceCondition( + LIC_COND_MAIN_CAT.description, + imposedReleasedDate = LocalDate.now().minusDays(14), + ), + LicenceCondition( + LIC_COND_MAIN_CAT.description, + LIC_COND_SUB_CAT.description, + LocalDate.now().minusDays(7), + LocalDate.now(), + listOf( + LicenceConditionNote( + note = "He shall not contact or associate with Peter Jones without the prior approval of the supervising officer;", + hasNotesBeenTruncated = false + ) + ) + ) + ) ) ), ProbationHistory(2, LocalDate.now().minusDays(7), 2, 2) diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/LicenceCondition.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/LicenceCondition.kt new file mode 100644 index 0000000000..9b4606662d --- /dev/null +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/LicenceCondition.kt @@ -0,0 +1,18 @@ +package uk.gov.justice.digital.hmpps.api.model.sentence + +import java.time.LocalDate + +data class LicenceCondition( + val mainDescription: String, + val subTypeDescription: String? = null, + val imposedReleasedDate: LocalDate, + val actualStartDate: LocalDate? = null, + val notes: List = listOf() +) + +data class LicenceConditionNote( + val createdBy: String? = null, + val createdByDate: LocalDate? = null, + val note: String, + val hasNotesBeenTruncated: Boolean? = null +) diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt index ace75da39f..c4cae7c0b9 100644 --- a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt @@ -8,5 +8,6 @@ data class Sentence( val order: Order? = null, val requirements: List = listOf(), val courtDocuments: List = listOf(), - val unpaidWorkProgress: String? + val unpaidWorkProgress: String?, + val licenceConditions: List = listOf() ) \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/LicenceCondition.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/LicenceCondition.kt new file mode 100644 index 0000000000..08874594a0 --- /dev/null +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/LicenceCondition.kt @@ -0,0 +1,63 @@ +package uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity + +import jakarta.persistence.* +import org.hibernate.annotations.Immutable +import org.hibernate.annotations.SQLRestriction +import org.springframework.data.jpa.repository.JpaRepository +import uk.gov.justice.digital.hmpps.integrations.delius.referencedata.entity.ReferenceData +import java.time.LocalDate + +@Immutable +@Entity +@Table(name = "lic_condition") +@SQLRestriction("soft_deleted = 0 and active_flag = 1") +class LicenceCondition( + @Id + @Column(name = "lic_condition_id", nullable = false) + val id: Long, + + @ManyToOne + @JoinColumn(name = "lic_cond_type_main_cat_id") + val mainCategory: LicenceConditionMainCategory, + + @ManyToOne + @JoinColumn(name = "lic_cond_type_sub_cat_id") + val subCategory: ReferenceData?, + + @Column + val disposalId: Long, + + @Column(name = "start_date", nullable = false) + val imposedReleasedDate: LocalDate, + + @Column(name = "commencement_date") + val actualStartDate: LocalDate?, + + @Column(name = "lic_condition_notes", columnDefinition = "clob") + val notes: String?, + + @Column(name = "active_flag", columnDefinition = "number", nullable = false) + val active: Boolean = true, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false +) + +interface LicenceConditionRepository : JpaRepository { + fun findAllByDisposalId(disposalId: Long): List +} + +@Immutable +@Table(name = "r_lic_cond_type_main_cat") +@Entity +class LicenceConditionMainCategory( + @Id + @Column(name = "lic_cond_type_main_cat_id") + val id: Long, + + @Column + val code: String, + + @Column + val description: String +) \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/SentenceService.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/SentenceService.kt index b08df7ea98..4955ec9058 100644 --- a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/SentenceService.kt +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/SentenceService.kt @@ -6,6 +6,7 @@ import uk.gov.justice.digital.hmpps.api.model.overview.Rar import uk.gov.justice.digital.hmpps.api.model.sentence.* import uk.gov.justice.digital.hmpps.api.model.sentence.Offence import uk.gov.justice.digital.hmpps.api.model.sentence.Requirement +import uk.gov.justice.digital.hmpps.datetime.DeliusDateFormatter import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.* import uk.gov.justice.digital.hmpps.integrations.delius.personalDetails.entity.CourtDocumentDetails import uk.gov.justice.digital.hmpps.integrations.delius.personalDetails.entity.DocumentRepository @@ -14,6 +15,7 @@ import java.time.Duration import java.time.LocalDate import kotlin.time.toKotlinDuration import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.AdditionalSentence as ExtraSentence +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceCondition as EntityLicenceCondition @Service class SentenceService( @@ -24,7 +26,8 @@ class SentenceService( private val requirementRepository: RequirementRepository, private val documentRepository: DocumentRepository, private val offenderManagerRepository: OffenderManagerRepository, - private val upwAppointmentRepository: UpwAppointmentRepository + private val upwAppointmentRepository: UpwAppointmentRepository, + private val licenceConditionRepository: LicenceConditionRepository ) { fun getEvents(crn: String): SentenceOverview { val person = personRepository.getPerson(crn) @@ -67,9 +70,53 @@ class SentenceService( requirements = requirementRepository.getRequirements(id, eventNumber) .map { it.toRequirement() }, courtDocuments = documentRepository.getCourtDocuments(id, eventNumber).map { it.toCourtDocument() }, - disposal?.id?.let { getUnpaidWorkTime(it) } + disposal?.id?.let { getUnpaidWorkTime(it) }, + licenceConditions = disposal?.let { + licenceConditionRepository.findAllByDisposalId(disposal.id).map { + it.toLicenceCondition() + } + } ?: emptyList(), + ) + + fun EntityLicenceCondition.toLicenceCondition() = + LicenceCondition( + mainCategory.description, + subCategory?.description, + imposedReleasedDate, + actualStartDate, + populateLicenceConditionNotes(notes) ) + fun populateLicenceConditionNotes(notes: String?): List { + val noteLength = 1500 + + notes?.let { + val splitParam = "---------------------------------------------------------" + System.lineSeparator() + return notes.split(splitParam).map { note -> + val matchResult = Regex( + "^Comment added by (.+?) on (\\d{2}/\\d{2}/\\d{4}) at \\d{2}:\\d{2}" + + System.lineSeparator() + ).find(note) + val commentLine = matchResult?.value + val commentText = commentLine?.let { note.removePrefix(commentLine) } ?: note + + val userCreatedBy = matchResult?.groupValues?.get(1) + val dateCreatedBy = matchResult?.groupValues?.get(2) + ?.let { LocalDate.parse(it, DeliusDateFormatter) } + + + LicenceConditionNote( + userCreatedBy, + dateCreatedBy, + commentText.removeSuffix(System.lineSeparator()).chunked(1500)[0], + note.length > noteLength + ) + } + + } + return listOf() + } + fun ExtraSentence.toAdditionalSentence(): AdditionalSentence = AdditionalSentence(length, amount, notes, type.description) diff --git a/projects/manage-supervision-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/SentenceServiceTest.kt b/projects/manage-supervision-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/SentenceServiceTest.kt index ba1f43b813..386a43b05d 100644 --- a/projects/manage-supervision-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/SentenceServiceTest.kt +++ b/projects/manage-supervision-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/SentenceServiceTest.kt @@ -49,6 +49,9 @@ class SentenceServiceTest { @Mock lateinit var upwAppointmentRepository: UpwAppointmentRepository + @Mock + lateinit var licenceConditionRepository: LicenceConditionRepository + @InjectMocks lateinit var service: SentenceService @@ -263,7 +266,8 @@ class SentenceServiceTest { ) ), listOf(CourtDocument("A001", LocalDate.now(), "Pre Sentence Event")), - "65 hours 36 minutes completed (of 70 hours)" + "65 hours 36 minutes completed (of 70 hours)", + listOf() ) ), ProbationHistory(0, null, 0, 0)