From aef8ef235193dddac074779f7f56b34e89742e6d Mon Sep 17 00:00:00 2001 From: pmcphee77 <150798161+pmcphee77@users.noreply.github.com> Date: Fri, 2 Aug 2024 17:28:37 +0100 Subject: [PATCH] PI-2391: Added sort of court appearances and recall/withdrawn (#4100) * PI-2391: Added sort of court appearances and recall/withdrawn --- .../hmpps/data/generator/SentenceGenerator.kt | 10 ++- ...NsisByCrnAndConvictionIdIntegrationTest.kt | 4 +- .../digital/hmpps/api/model/NsiDetails.kt | 4 +- .../delius/service/ConvictionService.kt | 8 +- .../delius/service/InterventionService.kt | 39 +++++++- .../hmpps/service/InterventionServiceTest.kt | 88 +++++++++++++++++++ 6 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 projects/court-case-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/InterventionServiceTest.kt diff --git a/projects/court-case-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt b/projects/court-case-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt index 5b259b11d9..ea8e664a76 100644 --- a/projects/court-case-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt +++ b/projects/court-case-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt @@ -337,15 +337,19 @@ object SentenceGenerator { id ) - fun generateBreachNsi(disposal: Disposal) = Nsi( + fun generateBreachNsi( + disposal: Disposal, + outcome: ReferenceData? = ReferenceDataGenerator.NSI_BREACH_OUTCOME, + status: NsiStatus = ACTIVE_NSI_STATUS + ) = Nsi( disposal.event.person.id, disposal.event.id, ReferenceDataGenerator.NSI_TYPE, - ACTIVE_NSI_STATUS, + status, referralDate = LocalDate.now().plusDays(1), statusDate = LocalDateTime.of(2024, 7, 1, 12, 0, 0, 0), null, - ReferenceDataGenerator.NSI_BREACH_OUTCOME, + outcome, actualStartDate = LocalDate.now(), expectedStartDate = LocalDate.of(2024, 1, 1), actualEndDate = LocalDate.now(), diff --git a/projects/court-case-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/NsisByCrnAndConvictionIdIntegrationTest.kt b/projects/court-case-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/NsisByCrnAndConvictionIdIntegrationTest.kt index b8bbd21789..a679a5bb9f 100644 --- a/projects/court-case-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/NsisByCrnAndConvictionIdIntegrationTest.kt +++ b/projects/court-case-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/NsisByCrnAndConvictionIdIntegrationTest.kt @@ -117,7 +117,9 @@ internal class NsisByCrnAndConvictionIdIntegrationTest { BREACH_NSIS.intendedProvider?.toProbationArea(false), BREACH_NSIS.active, BREACH_NSIS.softDeleted, - BREACH_NSIS.externalReference + BREACH_NSIS.externalReference, + null, + null ) ) ) diff --git a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/NsiDetails.kt b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/NsiDetails.kt index 7a787e7abc..d66dc6ffcb 100644 --- a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/NsiDetails.kt +++ b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/NsiDetails.kt @@ -29,7 +29,9 @@ data class Nsi( val intendedProvider: ProbationArea?, val active: Boolean, val softDeleted: Boolean, - val externalReference: String? + val externalReference: String?, + val recallRejectedOrWithdrawn: Boolean?, + val outcomeRecall: Boolean? ) data class NsiManager( diff --git a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/ConvictionService.kt b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/ConvictionService.kt index 9d7eb86025..8ccd686d12 100644 --- a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/ConvictionService.kt +++ b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/ConvictionService.kt @@ -72,9 +72,13 @@ class ConvictionService( } fun Event.toLatestOrSentencingCourtAppearanceOf(): CourtAppearanceBasic? { - return courtAppearances.filter { it.isSentenceing() }.maxByOrNull { it.appearanceDate } + return courtAppearances.filter { it.isSentenceing() } + .sortedBy(CourtAppearance::id) + .maxByOrNull { it.appearanceDate } ?.let { return it.toCourtAppearanceBasic() } - ?: courtAppearances.maxByOrNull { it.appearanceDate }?.let { return it.toCourtAppearanceBasic() } + ?: courtAppearances.sortedBy(CourtAppearance::id) + .maxByOrNull { it.appearanceDate } + ?.let { return it.toCourtAppearanceBasic() } } fun CourtAppearance.toCourtAppearanceBasic(): CourtAppearanceBasic = diff --git a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/InterventionService.kt b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/InterventionService.kt index fef18ad915..2d0e9fb3bd 100644 --- a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/InterventionService.kt +++ b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/InterventionService.kt @@ -17,7 +17,6 @@ class InterventionService( private val eventRepository: EventRepository, private val nsiRepository: NsiRepository, ) { - fun getNsiByCodes(crn: String, convictionId: Long, nsiCodes: List): NsiDetails { val person = personRepository.getPerson(crn) val event = eventRepository.getByPersonAndEventNumber(person, convictionId) @@ -50,7 +49,9 @@ class InterventionService( intendedProvider?.toProbationArea(false), active, softDeleted, - externalReference + externalReference, + this.toRecallRejectedOrWithdrawn(), + this.toOutcomeRecall() ) fun NsiManagerEntity.toNsiManager(): NsiManager = @@ -73,4 +74,38 @@ fun Staff.toStaffDetails(): StaffDetails = StaffDetails( grade?.keyValueOf() ) +private inline fun > String.toEnumOrElse(default: T) = + T::class.java.enumConstants.firstOrNull { it.name == this } ?: default + +fun NsiEntity.toOutcomeRecall() = outcome?.code?.toEnumOrElse(OutcomeType.UNKNOWN)?.isOutcomeRecall + +fun NsiEntity.toRecallRejectedOrWithdrawn() = nsiStatus.code.toEnumOrElse(Status.UNKNOWN).isRejectedOrWithdrawn?.let { + it || (outcome?.code?.toEnumOrElse(OutcomeType.UNKNOWN)?.isOutcomeRejectedOrWithdrawn ?: false) +} + +enum class OutcomeType(val code: String, val isOutcomeRejectedOrWithdrawn: Boolean?, val isOutcomeRecall: Boolean?) { + REC01("Fixed Term Recall", true, false), + REC02("Standard Term Recall", true, false), + REC03("Recall Rejected by NPS", false, true), + REC04("Recall Rejected by PPCS", false, true), + REC05("Request Withdrawn by OM", false, true), + UNKNOWN("No recall matching code", null, null) +} + +enum class Status(val code: String, val isRejectedOrWithdrawn: Boolean?) { + REC01("Recall Initiated", false), + REC02("Part A Completed by NPS/CRC OM", false), + REC03("Part A Countersigned by NPS/CRC Manager", false), + REC04("NPS Recall Endorsed by Senior Manager", false), + REC05("NPS Recall Rejected by Senior Manager", true), + REC06("Recall Referred to NPS", false), + REC07("PPCS Recall Decision Received", false), + REC08("Recall Submitted to PPCS", false), + REC09("Out-of-hours Recall Instigated", false), + REC10("Request Withdrawn by OM", true), + UNKNOWN("No recall matching code", null) +} + + + diff --git a/projects/court-case-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/InterventionServiceTest.kt b/projects/court-case-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/InterventionServiceTest.kt new file mode 100644 index 0000000000..ee5bcdf082 --- /dev/null +++ b/projects/court-case-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/InterventionServiceTest.kt @@ -0,0 +1,88 @@ +package uk.gov.justice.digital.hmpps.service + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import uk.gov.justice.digital.hmpps.data.generator.IdGenerator +import uk.gov.justice.digital.hmpps.data.generator.SentenceGenerator +import uk.gov.justice.digital.hmpps.integrations.delius.entity.ReferenceData +import uk.gov.justice.digital.hmpps.integrations.delius.event.nsi.NsiStatus +import uk.gov.justice.digital.hmpps.integrations.delius.service.toOutcomeRecall +import uk.gov.justice.digital.hmpps.integrations.delius.service.toRecallRejectedOrWithdrawn + +class InterventionServiceTest { + + @Test + fun `toRecallRejectedOrWithdrawn returns true `() { + + val nsiStatus = NsiStatus(IdGenerator.getAndIncrement(), "REC05", "REC05") + val nsi = SentenceGenerator.generateBreachNsi(SentenceGenerator.CURRENT_SENTENCE, status = nsiStatus) + assertEquals(true, nsi.toRecallRejectedOrWithdrawn()) + } + + @Test + fun `toRecallRejectedOrWithdrawn returns false due to no outcome`() { + + val nsiStatus = NsiStatus(IdGenerator.getAndIncrement(), "REC03", "REC03") + val nsi = + SentenceGenerator.generateBreachNsi(SentenceGenerator.CURRENT_SENTENCE, status = nsiStatus, outcome = null) + assertEquals(false, nsi.toRecallRejectedOrWithdrawn()) + } + + @Test + fun `toRecallRejectedOrWithdrawn returns null due to other status enum`() { + + val nsiStatus = NsiStatus(IdGenerator.getAndIncrement(), "OTHER", "OTHER") + val nsi = + SentenceGenerator.generateBreachNsi(SentenceGenerator.CURRENT_SENTENCE, status = nsiStatus, outcome = null) + assertEquals(null, nsi.toRecallRejectedOrWithdrawn()) + } + + @Test + fun `toRecallRejectedOrWithdrawn returns true due to outcome type enum`() { + + val nsiStatus = NsiStatus(IdGenerator.getAndIncrement(), "REC03", "REC03") + val outcome = ReferenceData("REC02", "REC02", IdGenerator.getAndIncrement()) + val nsi = SentenceGenerator.generateBreachNsi( + SentenceGenerator.CURRENT_SENTENCE, + status = nsiStatus, + outcome = outcome + ) + assertEquals(true, nsi.toRecallRejectedOrWithdrawn()) + } + + @Test + fun `toOutcome returns null due to null outcome`() { + + val nsiStatus = NsiStatus(IdGenerator.getAndIncrement(), "REC03", "REC03") + val nsi = + SentenceGenerator.generateBreachNsi(SentenceGenerator.CURRENT_SENTENCE, status = nsiStatus, outcome = null) + assertEquals(null, nsi.toOutcomeRecall()) + } + + @Test + fun `toOutcome returns false due to outcome type`() { + + val nsiStatus = NsiStatus(IdGenerator.getAndIncrement(), "REC03", "REC03") + val outcome = ReferenceData("REC02", "REC02", IdGenerator.getAndIncrement()) + val nsi = SentenceGenerator.generateBreachNsi( + SentenceGenerator.CURRENT_SENTENCE, + status = nsiStatus, + outcome = outcome + ) + assertEquals(false, nsi.toOutcomeRecall()) + } + + @Test + fun `toOutcome returns true due to outcome type`() { + + val nsiStatus = NsiStatus(IdGenerator.getAndIncrement(), "REC03", "REC03") + val outcome = ReferenceData("REC03", "REC03", IdGenerator.getAndIncrement()) + val nsi = SentenceGenerator.generateBreachNsi( + SentenceGenerator.CURRENT_SENTENCE, + status = nsiStatus, + outcome = outcome + ) + assertEquals(true, nsi.toOutcomeRecall()) + } +} +