Skip to content

Commit

Permalink
MAN-26: Return metadata and filter by codes (#4261)
Browse files Browse the repository at this point in the history
* MAN-26: Return metadata and filter by codes

* MAN-26: Return metadata and filter by codes
  • Loading branch information
pmcphee77 authored Sep 24, 2024
1 parent 0fe400e commit 151ae09
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class ContactIntegrationTest {
val contact2 =
Contact("Bruce Wayne", null, null, "Description of N01", "Leicestershire All", "OMU B", LocalDate.now())

val expected = ProfessionalContact(name, listOf(contact1, contact2))
val expected = ProfessionalContact(name, listOf(contact2, contact1))

val response = mockMvc
.perform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()
Expand All @@ -143,7 +143,7 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search").withToken()
.withJson(UserSearchFilter(nameOrCrn = "Blog", nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = "Blog", nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()
Expand All @@ -159,7 +159,13 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search").withToken()
.withJson(UserSearchFilter(nameOrCrn = "Caroline Blog", nextContact = null, sentence = null))
.withJson(
UserSearchFilter(
nameOrCrn = "Caroline Blog",
nextContactCode = null,
sentenceCode = null
)
)
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()
Expand All @@ -175,7 +181,7 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = "Murder"))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = "MAIN"))
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()
Expand All @@ -192,7 +198,7 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = "doorstep", sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = "CODI", sentenceCode = null))
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()
Expand All @@ -203,37 +209,37 @@ internal class UserIntegrationTest {
}

@Test
fun `caseload search returns null sentence type first case when sentence type is in the sort criteria as descending`() {
fun `caseload search returns null sentence type as last case when sentence type is in the sort criteria as descending`() {

val user = USER
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search?sortBy=sentence.desc").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()

assertThat(res.caseload.size, equalTo(2))
assertThat(res.caseload[0].crn, equalTo("X000005"))
assertThat(res.caseload[0].latestSentence, equalTo(null))
assertThat(res.caseload[1].crn, equalTo("X000005"))
assertThat(res.caseload[1].latestSentence, equalTo(null))
}

@Test
fun `caseload search returns null next appointment first case when next contact is in the sort criteria as descending`() {
fun `caseload search returns null next appointment as the last case when next contact is in the sort criteria as descending`() {

val user = USER
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search?sortBy=nextContact.desc").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()

assertThat(res.caseload.size, equalTo(2))
assertThat(res.caseload[0].crn, equalTo("X000005"))
assertThat(res.caseload[0].nextAppointment, equalTo(null))
assertThat(res.caseload[1].crn, equalTo("X000005"))
assertThat(res.caseload[1].nextAppointment, equalTo(null))
}

@Test
Expand All @@ -243,7 +249,7 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search?sortBy=surname.asc").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()
Expand All @@ -260,14 +266,16 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search?sortBy=surname.desc").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<StaffCaseload>()

assertThat(res.caseload.size, equalTo(2))
assertThat(res.caseload[0].crn, equalTo("X000004"))
assertThat(res.caseload[0].caseName.surname, equalTo("Surname"))
assertThat(res.metaData?.contactTypes?.size, equalTo(3))
assertThat(res.metaData?.sentenceTypes?.size, equalTo(2))
}

@Test
Expand All @@ -277,7 +285,7 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search?sortBy=surname.desc.ssss").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isBadRequest)
.andReturn().response.contentAsJson<ErrorResponse>()
Expand All @@ -291,7 +299,7 @@ internal class UserIntegrationTest {
val res = mockMvc
.perform(
post("/caseload/user/${user.username}/search?sortBy=sausages.desc").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isBadRequest)
.andReturn().response.contentAsJson<ErrorResponse>()
Expand All @@ -303,7 +311,7 @@ internal class UserIntegrationTest {
mockMvc
.perform(
post("/caseload/user/NOT_KNOWN/search?sortBy=sentence.desc").withToken()
.withJson(UserSearchFilter(nameOrCrn = null, nextContact = null, sentence = null))
.withJson(UserSearchFilter(nameOrCrn = null, nextContactCode = null, sentenceCode = null))
)
.andExpect(status().isNotFound)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class CaseloadController(private val userService: UserService) {
@RequestParam(required = false, defaultValue = "100") size: Int,
@RequestParam(required = false, defaultValue = "nextContact.desc") sortBy: String,
@RequestBody body: UserSearchFilter
) = userService.searchUserCaseload(username, body, PageRequest.of(page, size, sort(sortBy)))
) = userService.searchUserCaseload(username, body, PageRequest.of(page, size, sort(sortBy)), sortBy)

@GetMapping("/user/{username}/teams")
@Operation(summary = "Gets the users teams")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@ import uk.gov.justice.digital.hmpps.api.model.Name
data class StaffCaseload(
val totalPages: Int,
val totalElements: Int,
val sortedBy: String? = null,
val provider: String?,
val staff: Name,
val caseload: List<StaffCase>
val caseload: List<StaffCase>,
val metaData: MetaData? = null
)

data class MetaData(
val sentenceTypes: List<KeyPair>,
val contactTypes: List<KeyPair>
)

data class KeyPair(
val code: String,
val description: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package uk.gov.justice.digital.hmpps.api.model.user

data class UserSearchFilter(
val nameOrCrn: String? = null,
val sentence: String? = null,
val nextContact: String? = null
val sentenceCode: String? = null,
val nextContactCode: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.springframework.data.jpa.repository.Query
import uk.gov.justice.digital.hmpps.exception.NotFoundException
import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.ContactType
import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.MainOffence
import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.Offence
import java.time.LocalDate
import java.time.ZonedDateTime

Expand Down Expand Up @@ -403,15 +404,15 @@ interface CaseloadRepository : JpaRepository<Caseload, Long> {
or upper(p.forename || ' ' || p.surname) like '%' || upper(:nameOrCrn) || '%'
or upper(p.surname || ' ' || p.forename) like '%' || upper(:nameOrCrn) || '%'
or upper(p.surname || ', ' || p.forename) like '%' || upper(:nameOrCrn) || '%')
and (:nextContact is null or (upper(naType.description) like '%' || upper(:nextContact) || '%'))
and (:sentence is null or (upper(moo.description) like '%' || upper(:sentence) || '%'))
and (:nextContactCode is null or (upper(trim(naType.code)) = upper(trim(:nextContactCode))))
and (:sentenceCode is null or (upper(trim(moo.code)) = upper(trim(:sentenceCode))))
"""
)
fun searchByStaffCode(
staffCode: String,
nameOrCrn: String?,
nextContact: String?,
sentence: String?,
nextContactCode: String?,
sentenceCode: String?,
pageable: Pageable
): Page<Caseload>

Expand All @@ -424,6 +425,25 @@ interface CaseloadRepository : JpaRepository<Caseload, Long> {
"""
)
fun findByTeamCode(teamCode: String, pageable: Pageable): Page<Caseload>

@Query(
"""
select distinct cont.type from Caseload c
join Contact cont on cont.personId = c.person.id
where c.staff.code = :staffCode and cont.type.attendanceContact = true
"""
)
fun findContactTypesForStaff(staffCode: String): List<ContactType>

@Query(
"""
select distinct e.mainOffence.offence from Caseload c
join Event e on e.personId = c.person.id and e.active = true and e.softDeleted = false
where e.mainOffence.offence is not null
and c.staff.code = :staffCode
"""
)
fun findOffenceTypesForStaff(staffCode: String): List<Offence>
}

enum class CaseloadOrderType(val sortColumn: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,33 @@ class UserService(
}

@Transactional
fun searchUserCaseload(username: String, searchFilter: UserSearchFilter, pageable: Pageable): StaffCaseload {
fun searchUserCaseload(
username: String,
searchFilter: UserSearchFilter,
pageable: Pageable,
sortedBy: String
): StaffCaseload {
val user = userRepository.getUser(username)
val caseload = caseloadRepository.searchByStaffCode(
user.staff!!.code,
searchFilter.nameOrCrn,
searchFilter.nextContact,
searchFilter.sentence,
searchFilter.nextContactCode,
searchFilter.sentenceCode,
pageable
)
val sentenceTypes =
caseloadRepository.findOffenceTypesForStaff(user.staff.code).map { KeyPair(it.code.trim(), it.description) }
val contactTypes =
caseloadRepository.findContactTypesForStaff(user.staff.code).map { KeyPair(it.code.trim(), it.description) }

return StaffCaseload(
totalElements = caseload.totalElements.toInt(),
totalPages = caseload.totalPages,
provider = user.staff.provider.description,
caseload = caseload.content.map { it.toStaffCase() },
staff = Name(forename = user.staff.forename, surname = user.staff.surname),
metaData = MetaData(sentenceTypes = sentenceTypes, contactTypes = contactTypes),
sortedBy = sortedBy
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Default config
server.shutdown: graceful

spring:
jackson:
default-property-inclusion: non_null
Expand All @@ -8,6 +9,8 @@ spring:
database-platform: org.hibernate.dialect.OracleDialect
properties:
hibernate:
order_by:
default_null_ordering: last
timezone.default_storage: NORMALIZE
query.mutation_strategy: org.hibernate.query.sqm.mutation.internal.inline.InlineMutationStrategy
query.mutation_strategy.persistent:
Expand Down

0 comments on commit 151ae09

Please sign in to comment.