From d50e7e6c838b0c03ebf3b150d84c8b808d86524d Mon Sep 17 00:00:00 2001 From: Paul McPhee Date: Fri, 2 Aug 2024 13:20:08 +0100 Subject: [PATCH 1/6] PI-2391: Added sort of court appearances and recal/withdrawn --- .../hmpps/data/generator/SentenceGenerator.kt | 10 ++- .../digital/hmpps/api/model/NsiDetails.kt | 4 +- .../delius/service/ConvictionService.kt | 7 +- .../delius/service/InterventionService.kt | 66 ++++++++++++++++- .../hmpps/service/InterventionServiceTest.kt | 73 +++++++++++++++++++ 5 files changed, 153 insertions(+), 7 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/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..c545ff9bc0 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,12 @@ class ConvictionService( } fun Event.toLatestOrSentencingCourtAppearanceOf(): CourtAppearanceBasic? { - return courtAppearances.filter { it.isSentenceing() }.maxByOrNull { it.appearanceDate } + return courtAppearances.filter { it.isSentenceing() } + .sortedByDescending(CourtAppearance::appearanceDate) + .maxByOrNull { it.appearanceDate } ?.let { return it.toCourtAppearanceBasic() } - ?: courtAppearances.maxByOrNull { it.appearanceDate }?.let { return it.toCourtAppearanceBasic() } + ?: courtAppearances.sortedByDescending(CourtAppearance::appearanceDate).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..fac61e3ffe 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 @@ -8,6 +8,7 @@ import uk.gov.justice.digital.hmpps.integrations.delius.event.nsi.NsiRepository import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonRepository import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.getPerson import uk.gov.justice.digital.hmpps.integrations.delius.provider.entity.Staff +import java.util.* import uk.gov.justice.digital.hmpps.integrations.delius.event.nsi.Nsi as NsiEntity import uk.gov.justice.digital.hmpps.integrations.delius.event.nsi.NsiManager as NsiManagerEntity @@ -50,7 +51,9 @@ class InterventionService( intendedProvider?.toProbationArea(false), active, softDeleted, - externalReference + externalReference, + this.toRecallRejectedOrWithdrawn(), + this.toOutcomeRecall() ) fun NsiManagerEntity.toNsiManager(): NsiManager = @@ -73,4 +76,65 @@ fun Staff.toStaffDetails(): StaffDetails = StaffDetails( grade?.keyValueOf() ) +fun NsiEntity.toOutcomeRecall(): Boolean? = + Optional.ofNullable(outcome).map { + try { + OutcomeType.valueOf(it.code) + } catch (ex: Exception) { + OutcomeType.UNKNOWN + } + .isOutcomeRecall + }.orElse( + null + ) + +fun NsiEntity.toRecallRejectedOrWithdrawn(): Boolean? { + val status = try { + Status.valueOf(nsiStatus.code) + } catch (ex: Exception) { + Status.UNKNOWN + } + + return if (status.isRejectedOrWithdrawn != null) { + if (status.isRejectedOrWithdrawn) { + true + } else { + Optional.ofNullable(outcome).map { + try { + OutcomeType.valueOf(it.code) + } catch (ex: Exception) { + OutcomeType.UNKNOWN + }.isOutcomeRejectedOrWithdrawn + }.orElse(false) + } + } else { + null + } +} + +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..00a3052fa9 --- /dev/null +++ b/projects/court-case-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/InterventionServiceTest.kt @@ -0,0 +1,73 @@ +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()) + } +} + From 0610127fa3ff3b1bfa4ee8bbddad1995e9228f33 Mon Sep 17 00:00:00 2001 From: Paul McPhee Date: Fri, 2 Aug 2024 13:38:35 +0100 Subject: [PATCH 2/6] PI-2391: Added sort of court appearances and recal/withdrawn --- .../digital/hmpps/NsisByCrnAndConvictionIdIntegrationTest.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 ) ) ) From 69eda3ad4bbd6af60d7feaba743dcf11a11bb87f Mon Sep 17 00:00:00 2001 From: Paul McPhee Date: Fri, 2 Aug 2024 14:46:56 +0100 Subject: [PATCH 3/6] PI-2391: Added sort of court appearances and recal/withdrawn --- .../delius/service/ConvictionService.kt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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 c545ff9bc0..c866c11ddd 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,12 +72,17 @@ class ConvictionService( } fun Event.toLatestOrSentencingCourtAppearanceOf(): CourtAppearanceBasic? { - return courtAppearances.filter { it.isSentenceing() } - .sortedByDescending(CourtAppearance::appearanceDate) - .maxByOrNull { it.appearanceDate } - ?.let { return it.toCourtAppearanceBasic() } - ?: courtAppearances.sortedByDescending(CourtAppearance::appearanceDate).maxByOrNull { it.appearanceDate } - ?.let { return it.toCourtAppearanceBasic() } + return courtAppearances + .stream() + .filter { it.isSentenceing() } + .max(Comparator.comparing(CourtAppearance::appearanceDate)) + .map { it.toCourtAppearanceBasic() }.orElseGet { + courtAppearances + .stream() + .max(Comparator.comparing(CourtAppearance::appearanceDate)) + .map { it.toCourtAppearanceBasic() } + .orElse(null) + } } fun CourtAppearance.toCourtAppearanceBasic(): CourtAppearanceBasic = From 0c477755697b3c8a311b1288642ecb5efda845b0 Mon Sep 17 00:00:00 2001 From: Paul McPhee Date: Fri, 2 Aug 2024 15:25:11 +0100 Subject: [PATCH 4/6] PI-2391: Added sort of court appearances and recal/withdrawn --- .../delius/service/ConvictionService.kt | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) 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 c866c11ddd..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,17 +72,13 @@ class ConvictionService( } fun Event.toLatestOrSentencingCourtAppearanceOf(): CourtAppearanceBasic? { - return courtAppearances - .stream() - .filter { it.isSentenceing() } - .max(Comparator.comparing(CourtAppearance::appearanceDate)) - .map { it.toCourtAppearanceBasic() }.orElseGet { - courtAppearances - .stream() - .max(Comparator.comparing(CourtAppearance::appearanceDate)) - .map { it.toCourtAppearanceBasic() } - .orElse(null) - } + return courtAppearances.filter { it.isSentenceing() } + .sortedBy(CourtAppearance::id) + .maxByOrNull { it.appearanceDate } + ?.let { return it.toCourtAppearanceBasic() } + ?: courtAppearances.sortedBy(CourtAppearance::id) + .maxByOrNull { it.appearanceDate } + ?.let { return it.toCourtAppearanceBasic() } } fun CourtAppearance.toCourtAppearanceBasic(): CourtAppearanceBasic = From 8d44bfd93f2f14f432064275996146341350ce10 Mon Sep 17 00:00:00 2001 From: Paul McPhee Date: Fri, 2 Aug 2024 16:16:14 +0100 Subject: [PATCH 5/6] PI-2391: Added sort of court appearances and recal/withdrawn --- .../hmpps/integrations/delius/service/InterventionService.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 fac61e3ffe..662f4322f6 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 @@ -82,8 +82,7 @@ fun NsiEntity.toOutcomeRecall(): Boolean? = OutcomeType.valueOf(it.code) } catch (ex: Exception) { OutcomeType.UNKNOWN - } - .isOutcomeRecall + }.isOutcomeRecall }.orElse( null ) From 40e453f9e8413c027bf8d6452434ffb2e93ba573 Mon Sep 17 00:00:00 2001 From: Paul McPhee Date: Fri, 2 Aug 2024 16:17:10 +0100 Subject: [PATCH 6/6] PI-2391: Added sort of court appearances and recal/withdrawn --- .../hmpps/integrations/delius/service/InterventionService.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 662f4322f6..1754ad2eb0 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 @@ -83,9 +83,7 @@ fun NsiEntity.toOutcomeRecall(): Boolean? = } catch (ex: Exception) { OutcomeType.UNKNOWN }.isOutcomeRecall - }.orElse( - null - ) + }.orElse(null) fun NsiEntity.toRecallRejectedOrWithdrawn(): Boolean? { val status = try {