Skip to content

Commit

Permalink
PI-2391: Added sort of court appearances and recall/withdrawn (#4100)
Browse files Browse the repository at this point in the history
* PI-2391: Added sort of court appearances and recall/withdrawn
  • Loading branch information
pmcphee77 authored Aug 2, 2024
1 parent a961d75 commit aef8ef2
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class InterventionService(
private val eventRepository: EventRepository,
private val nsiRepository: NsiRepository,
) {

fun getNsiByCodes(crn: String, convictionId: Long, nsiCodes: List<String>): NsiDetails {
val person = personRepository.getPerson(crn)
val event = eventRepository.getByPersonAndEventNumber(person, convictionId)
Expand Down Expand Up @@ -50,7 +49,9 @@ class InterventionService(
intendedProvider?.toProbationArea(false),
active,
softDeleted,
externalReference
externalReference,
this.toRecallRejectedOrWithdrawn(),
this.toOutcomeRecall()
)

fun NsiManagerEntity.toNsiManager(): NsiManager =
Expand All @@ -73,4 +74,38 @@ fun Staff.toStaffDetails(): StaffDetails = StaffDetails(
grade?.keyValueOf()
)

private inline fun <reified T : Enum<T>> 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)
}




Original file line number Diff line number Diff line change
@@ -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())
}
}

0 comments on commit aef8ef2

Please sign in to comment.