From 645fc25058dc5a8b2408de62bc41dc201042a7f2 Mon Sep 17 00:00:00 2001 From: Anthony Britton <105213050+anthony-britton-moj@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:41:07 +0100 Subject: [PATCH] PI-2111 update EPF detail endpoint (#3646) * PI-2111 update EPF detail endpoint * Formatting changes * PI-2111 update EPF detail endpoint --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../justice/digital/hmpps/data/DataLoader.kt | 5 ++- .../hmpps/data/generator/ManagerGenerator.kt | 2 +- .../hmpps/data/generator/PersonGenerator.kt | 2 +- .../hmpps/data/generator/SentenceGenerator.kt | 21 ++++++--- .../justice/digital/hmpps/IntegrationTest.kt | 19 ++++---- .../justice/digital/hmpps/epf/CaseDetails.kt | 12 +++-- .../digital/hmpps/epf/CaseDetailsService.kt | 18 +++----- .../hmpps/epf/entity/CourtAppearance.kt | 23 +++------- .../justice/digital/hmpps/epf/entity/Event.kt | 44 ++++++++++++------- .../hmpps/epf/CaseDetailsServiceTest.kt | 6 ++- .../digital/hmpps/epf/CaseDetailsTest.kt | 6 +-- 11 files changed, 81 insertions(+), 77 deletions(-) diff --git a/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt b/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt index 23a0fb82ec..2458ec270e 100644 --- a/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt +++ b/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt @@ -44,13 +44,14 @@ class DataLoader( SentenceGenerator.generateOgrsAssessment(LocalDate.now().minusDays(1), 3), SentenceGenerator.generateOgrsAssessment(LocalDate.now().minusDays(5), 1), SentenceGenerator.generateOgrsAssessment(LocalDate.now(), 5, softDeleted = true), - PersonGenerator.RELEASED, + PersonGenerator.WITH_RELEASE_DATE, ManagerGenerator.RELEASED_PERSON_MANAGER, + SentenceGenerator.RELEASE_DATE_TYPE, SentenceGenerator.RELEASED_EVENT, SentenceGenerator.RELEASED_COURT_APPEARANCE, SentenceGenerator.RELEASED_SENTENCE, SentenceGenerator.RELEASED_CUSTODY, - SentenceGenerator.RELEASE + SentenceGenerator.RELEASE_DATE ) } diff --git a/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ManagerGenerator.kt b/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ManagerGenerator.kt index 11c94360c4..5b518b26c9 100644 --- a/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ManagerGenerator.kt +++ b/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ManagerGenerator.kt @@ -8,7 +8,7 @@ object ManagerGenerator { val DEFAULT_PERSON_MANAGER = personManagerGenerator(PersonGenerator.DEFAULT) val DEFAULT_RESPONSIBLE_OFFICER = responsibleOfficerGenerator(communityManager = DEFAULT_PERSON_MANAGER, prisonManager = null, endDate = null) - val RELEASED_PERSON_MANAGER = personManagerGenerator(PersonGenerator.RELEASED) + val RELEASED_PERSON_MANAGER = personManagerGenerator(PersonGenerator.WITH_RELEASE_DATE) fun responsibleOfficerGenerator( id: Long = IdGenerator.getAndIncrement(), diff --git a/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt b/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt index a3a7e4fa2e..f4371a3431 100644 --- a/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt +++ b/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt @@ -9,7 +9,7 @@ object PersonGenerator { val DEFAULT = generate("N123456", "A1234YZ") val EXCLUDED = generate("E123456", currentExclusion = true) val RESTRICTED = generate("R123456", currentRestriction = true) - val RELEASED = generate("F123456") + val WITH_RELEASE_DATE = generate("F123456") fun generate( crn: String, diff --git a/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt b/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt index 03862249da..2a9f1445de 100644 --- a/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt +++ b/projects/effective-proposal-framework-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt @@ -2,20 +2,20 @@ package uk.gov.justice.digital.hmpps.data.generator import uk.gov.justice.digital.hmpps.epf.entity.* import java.time.LocalDate -import java.time.ZonedDateTime object SentenceGenerator { val DEFAULT_COURT = generateCourt() + val RELEASE_DATE_TYPE = generateKeyDateType(KeyDate.Type.EXPECTED_RELEASE_DATE.code) val DEFAULT_EVENT = generateEvent() val DEFAULT_COURT_APPEARANCE = generateCourtAppearance(DEFAULT_EVENT) val DEFAULT_SENTENCE = generateSentence(DEFAULT_EVENT) - val RELEASED_EVENT = generateEvent(person = PersonGenerator.RELEASED) + val RELEASED_EVENT = generateEvent(person = PersonGenerator.WITH_RELEASE_DATE) val RELEASED_COURT_APPEARANCE = generateCourtAppearance(RELEASED_EVENT) val RELEASED_SENTENCE = generateSentence(RELEASED_EVENT) val RELEASED_CUSTODY = generateCustody(RELEASED_SENTENCE) - val RELEASE = generateRelease(RELEASED_CUSTODY) + val RELEASE_DATE = generateExpectedReleaseDate(RELEASED_CUSTODY, LocalDate.now().plusWeeks(6)) fun generateSentence( event: Event, @@ -54,12 +54,19 @@ object SentenceGenerator { disposal: Disposal, softDeleted: Boolean = false, id: Long = IdGenerator.getAndIncrement() - ) = Custody(disposal, listOf(), softDeleted, id) + ) = Custody(disposal, softDeleted, id) - fun generateRelease( + fun generateKeyDateType( + code: String, + description: String = "Key Date Type of $code", + id: Long = IdGenerator.getAndIncrement() + ) = ReferenceData(id, code, description) + + fun generateExpectedReleaseDate( custody: Custody, date: LocalDate = LocalDate.now(), - createdDateTime: ZonedDateTime = ZonedDateTime.now(), + type: ReferenceData = RELEASE_DATE_TYPE, + softDeleted: Boolean = false, id: Long = IdGenerator.getAndIncrement() - ) = Release(custody, date, createdDateTime, id) + ) = KeyDate(custody, type, date, softDeleted, id) } diff --git a/projects/effective-proposal-framework-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt b/projects/effective-proposal-framework-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt index 67ffed6fc2..8549fa4853 100644 --- a/projects/effective-proposal-framework-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt +++ b/projects/effective-proposal-framework-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt @@ -51,7 +51,7 @@ internal class IntegrationTest { @Test fun `release details are correctly displayed`() { - val person = PersonGenerator.RELEASED + val person = PersonGenerator.WITH_RELEASE_DATE val crn = person.crn val eventNumber = 1 val detailResponse = mockMvc @@ -66,14 +66,12 @@ internal class IntegrationTest { Name(person.forename, person.secondName, person.surname), person.dateOfBirth, person.gender.description, - Conviction( - SentenceGenerator.RELEASED_EVENT.convictionDate!!, + Appearance( + SentenceGenerator.DEFAULT_COURT_APPEARANCE.appearanceDate, Court(SentenceGenerator.DEFAULT_COURT.name) ), Sentence( - SentenceGenerator.RELEASED_SENTENCE.date, - Court(SentenceGenerator.DEFAULT_COURT.name), - SentenceGenerator.RELEASE.date + SentenceGenerator.RELEASE_DATE.date ), Provider(ProviderGenerator.DEFAULT.code, ProviderGenerator.DEFAULT.description), null @@ -92,12 +90,11 @@ internal class IntegrationTest { ), PersonGenerator.DEFAULT.dateOfBirth, PersonGenerator.DEFAULT.gender.description, - Conviction(SentenceGenerator.DEFAULT_EVENT.convictionDate!!, Court(SentenceGenerator.DEFAULT_COURT.name)), - Sentence( - SentenceGenerator.DEFAULT_SENTENCE.date, - Court(SentenceGenerator.DEFAULT_COURT.name), - null + Appearance( + SentenceGenerator.DEFAULT_COURT_APPEARANCE.appearanceDate, + Court(SentenceGenerator.DEFAULT_COURT.name) ), + null, Provider(ProviderGenerator.DEFAULT.code, ProviderGenerator.DEFAULT.description), 3 ) diff --git a/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetails.kt b/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetails.kt index 535c2d4605..8f72ea45f2 100644 --- a/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetails.kt +++ b/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetails.kt @@ -9,7 +9,7 @@ data class CaseDetails( val name: Name, val dateOfBirth: LocalDate, val gender: String, - val conviction: Conviction?, + val courtAppearance: Appearance?, val sentence: Sentence?, val responsibleProvider: Provider?, val ogrsScore: Long? @@ -18,19 +18,17 @@ data class CaseDetails( get() = YEARS.between(dateOfBirth, LocalDate.now()) val ageAtRelease - get() = sentence?.releaseDate?.let { YEARS.between(dateOfBirth, it) } + get() = sentence?.expectedReleaseDate?.let { YEARS.between(dateOfBirth, it) } } data class Name(val forename: String, val middleName: String?, val surname: String) -data class Conviction( +data class Appearance( val date: LocalDate, - val court: Court, + val court: Court ) data class Sentence( - val date: LocalDate, - val sentencingCourt: Court, - val releaseDate: LocalDate? + val expectedReleaseDate: LocalDate? ) data class Court(val name: String) diff --git a/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsService.kt b/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsService.kt index 8258cea766..daa1e1390b 100644 --- a/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsService.kt +++ b/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsService.kt @@ -1,6 +1,7 @@ package uk.gov.justice.digital.hmpps.epf import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import uk.gov.justice.digital.hmpps.epf.entity.* @Service @@ -10,30 +11,25 @@ class CaseDetailsService( private val personManagerRepository: PersonManagerRepository, private val courtAppearanceRepository: CourtAppearanceRepository, private val eventRepository: EventRepository, + private val keyDateRepository: KeyDateRepository, private val ogrsAssessmentRepository: OgrsAssessmentRepository ) { + @Transactional fun caseDetails(crn: String, eventNumber: Int): CaseDetails { val person = personRepository.getPerson(crn) val provider = responsibleOfficerRepository.findByPersonIdAndEndDateIsNull(person.id)?.provider() ?: personManagerRepository.findByPersonId(person.id)?.provider() val event = eventRepository.getEvent(person.crn, eventNumber.toString()) - val courtName = courtAppearanceRepository.findMostRecentCourtNameByEventId(event.id) + val appearance = courtAppearanceRepository.findByEventIdOrderByAppearanceDateDesc(event.id) + val erd = event.disposal?.custody?.let { keyDateRepository.getExpectedReleaseDate(it.id) } val ogrsScore = ogrsAssessmentRepository.findFirstByEventIdOrderByAssessmentDateDesc(event.id)?.score return CaseDetails( person.nomsId, person.name(), person.dateOfBirth, person.gender.description, - event.convictionDate?.let { - Conviction(it, Court(courtName)) - }, - event.disposal?.date?.let { - Sentence( - it, - Court(courtName), - event.disposal.custody?.mostRecentRelease()?.date - ) - }, + appearance?.let { Appearance(it.appearanceDate, Court(it.court.name)) }, + erd?.let { Sentence(it.date) }, provider, ogrsScore ) diff --git a/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/entity/CourtAppearance.kt b/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/entity/CourtAppearance.kt index 6f04566a56..72e65373b7 100644 --- a/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/entity/CourtAppearance.kt +++ b/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/entity/CourtAppearance.kt @@ -1,16 +1,11 @@ package uk.gov.justice.digital.hmpps.epf.entity -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.Id -import jakarta.persistence.JoinColumn -import jakarta.persistence.ManyToOne -import jakarta.persistence.Table +import jakarta.persistence.* import org.hibernate.annotations.Immutable import org.hibernate.annotations.SQLRestriction import org.springframework.data.domain.PageRequest +import org.springframework.data.jpa.repository.EntityGraph import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.Query import java.time.LocalDate @Entity @@ -51,15 +46,11 @@ class CourtAppearance( val softDeleted: Boolean = false ) -interface CourtRepository : JpaRepository interface CourtAppearanceRepository : JpaRepository { - @Query( - """ - select ca.court.name as name from CourtAppearance ca - where ca.event.id = :eventId - order by ca.appearanceDate desc - """ - ) - fun findMostRecentCourtNameByEventId(eventId: Long, page: PageRequest = PageRequest.of(0, 1)): String + @EntityGraph(attributePaths = ["court"]) + fun findByEventIdOrderByAppearanceDateDesc( + eventId: Long, + page: PageRequest = PageRequest.of(0, 1) + ): CourtAppearance? } diff --git a/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/entity/Event.kt b/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/entity/Event.kt index a677e57087..82193f0504 100644 --- a/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/entity/Event.kt +++ b/projects/effective-proposal-framework-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/epf/entity/Event.kt @@ -7,7 +7,6 @@ import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import uk.gov.justice.digital.hmpps.exception.NotFoundException import java.time.LocalDate -import java.time.ZonedDateTime @Immutable @Entity @@ -70,36 +69,41 @@ class Custody( @JoinColumn(name = "disposal_id", updatable = false) val disposal: Disposal, - @OneToMany(mappedBy = "custody") - val releases: List, - @Column(columnDefinition = "number", nullable = false) val softDeleted: Boolean, @Id @Column(name = "custody_id") val id: Long -) { - fun mostRecentRelease() = releases.maxWithOrNull(compareBy({ it.date }, { it.createdDateTime })) -} +) -@Immutable @Entity -class Release( +@Immutable +@SQLRestriction("soft_deleted = 0") +class KeyDate( + @ManyToOne - @JoinColumn(name = "custody_id", nullable = false) + @JoinColumn(name = "custody_id") val custody: Custody, - @Column(name = "actual_release_date") - val date: LocalDate, + @ManyToOne + @JoinColumn(name = "key_date_type_id") + val type: ReferenceData, + + @Column(name = "key_date") + var date: LocalDate, - @Column(name = "created_datetime") - val createdDateTime: ZonedDateTime, + @Column(columnDefinition = "number") + val softDeleted: Boolean, @Id - @Column(name = "release_id", nullable = false) + @Column(name = "key_date_id") val id: Long -) +) { + enum class Type(val code: String) { + EXPECTED_RELEASE_DATE("EXP") + } +} interface EventRepository : JpaRepository { @@ -109,7 +113,6 @@ interface EventRepository : JpaRepository { join fetch e.person p left join fetch e.disposal d left join fetch d.custody c - left join fetch c.releases where e.number = :number and p.crn = :crn """ @@ -119,3 +122,10 @@ interface EventRepository : JpaRepository { fun EventRepository.getEvent(crn: String, number: String) = findEventByCrnAndEventNumber(crn, number) ?: throw NotFoundException("Event", "crn", crn) + +interface KeyDateRepository : JpaRepository { + fun findByCustodyIdAndTypeCode(custodyId: Long, typeCode: String): KeyDate? +} + +fun KeyDateRepository.getExpectedReleaseDate(custodyId: Long) = + findByCustodyIdAndTypeCode(custodyId, KeyDate.Type.EXPECTED_RELEASE_DATE.code) diff --git a/projects/effective-proposal-framework-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsServiceTest.kt b/projects/effective-proposal-framework-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsServiceTest.kt index 9ffa29ba09..651f3c5593 100644 --- a/projects/effective-proposal-framework-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsServiceTest.kt +++ b/projects/effective-proposal-framework-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsServiceTest.kt @@ -32,6 +32,9 @@ internal class CaseDetailsServiceTest { @Mock internal lateinit var eventRepository: EventRepository + @Mock + internal lateinit var keyDateRepository: KeyDateRepository + @Mock internal lateinit var ogrsAssessmentRepository: OgrsAssessmentRepository @@ -46,7 +49,8 @@ internal class CaseDetailsServiceTest { whenever(responsibleOfficerRepository.findByPersonIdAndEndDateIsNull(person.id)).thenReturn(null) whenever(personManagerRepository.findByPersonId(person.id)).thenReturn(ManagerGenerator.DEFAULT_PERSON_MANAGER) whenever(eventRepository.findEventByCrnAndEventNumber(person.crn, event.number)).thenReturn(event) - whenever(courtAppearanceRepository.findMostRecentCourtNameByEventId(event.id)).thenReturn(SentenceGenerator.DEFAULT_COURT.name) + whenever(courtAppearanceRepository.findByEventIdOrderByAppearanceDateDesc(event.id)) + .thenReturn(SentenceGenerator.generateCourtAppearance(event)) val res = service.caseDetails(person.crn, event.number.toInt()) assertNotNull(res.responsibleProvider) diff --git a/projects/effective-proposal-framework-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsTest.kt b/projects/effective-proposal-framework-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsTest.kt index a372de9344..b930cffb80 100644 --- a/projects/effective-proposal-framework-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsTest.kt +++ b/projects/effective-proposal-framework-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/epf/CaseDetailsTest.kt @@ -20,14 +20,14 @@ internal class CaseDetailsTest { Name("John", "", "Smith"), LocalDate.of(1982, 8, 3), "Male", - Conviction(LocalDate.now(), Court("NA Court")), - Sentence(LocalDate.of(2013, 9, 23), Court("NA Court"), LocalDate.of(2022, 8, 2)), + Appearance(LocalDate.now(), Court("NA Court")), + Sentence(LocalDate.of(2022, 8, 2)), Provider("N00", "London"), null ) private fun CaseDetails.withReleaseDate(releaseDate: LocalDate) = - copy(sentence = this.sentence?.copy(releaseDate = releaseDate)) + copy(sentence = this.sentence?.copy(expectedReleaseDate = releaseDate)) @JvmStatic fun ageAtRelease() = listOf(