From 3155bd8951092e6c282b495cd32b5d77e885f5b3 Mon Sep 17 00:00:00 2001 From: Daniel Catchpole Date: Thu, 29 Aug 2024 14:45:35 +0100 Subject: [PATCH] DFPL-2478: Fix end date of previous hearing judge roles when cancelling hearings (#5521) * DFPL-2478: Refactor hearing judge logic + update previous hearing on cancellation * checkstyle * add tests * checkstyle * Merge DFPL-2483 into this branch as mutually incompatible * use the correct values! --------- Co-authored-by: Braimah101 <41795070+Braimah101@users.noreply.github.com> --- ...eHearingModificationRolesEventHandler.java | 21 ++- .../judicial/NewHearingJudgeEventHandler.java | 40 +--- .../gov/hmcts/reform/fpl/model/CaseData.java | 7 + .../reform/fpl/service/JudicialService.java | 41 ++++- ...ringModificationRolesEventHandlerTest.java | 107 +++++++++++ .../NewHearingJudgeEventHandlerTest.java | 94 +++++----- .../hmcts/reform/fpl/model/CaseDataTest.java | 26 +++ .../fpl/service/JudicialServiceTest.java | 171 ++++++++++++++++++ 8 files changed, 422 insertions(+), 85 deletions(-) diff --git a/service/src/main/java/uk/gov/hmcts/reform/fpl/handlers/judicial/HandleHearingModificationRolesEventHandler.java b/service/src/main/java/uk/gov/hmcts/reform/fpl/handlers/judicial/HandleHearingModificationRolesEventHandler.java index a88c2f63b1f..6214d3b4289 100644 --- a/service/src/main/java/uk/gov/hmcts/reform/fpl/handlers/judicial/HandleHearingModificationRolesEventHandler.java +++ b/service/src/main/java/uk/gov/hmcts/reform/fpl/handlers/judicial/HandleHearingModificationRolesEventHandler.java @@ -6,8 +6,11 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import uk.gov.hmcts.reform.fpl.events.judicial.HandleHearingModificationRolesEvent; +import uk.gov.hmcts.reform.fpl.model.HearingBooking; import uk.gov.hmcts.reform.fpl.service.JudicialService; +import java.util.Optional; + import static uk.gov.hmcts.reform.fpl.utils.ElementUtils.findElement; import static uk.gov.hmcts.reform.fpl.utils.ElementUtils.nullSafeList; @@ -23,6 +26,8 @@ public void handleCancelledHearingRoles(final HandleHearingModificationRolesEven // Not an async function as it has to take place before we grant more roles, in case the times overlap // when relisting. it has to be caught though to make sure nothing else afterward is impacted in case of // failure + final Long caseId = event.getCaseData().getId(); + final boolean isFirstHearing = event.getCaseData().getAllNonCancelledHearings().size() == 1; try { nullSafeList(event.getCaseData().getCancelledHearingDetails()) @@ -30,7 +35,21 @@ public void handleCancelledHearingRoles(final HandleHearingModificationRolesEven if (findElement(hearing.getId(), event.getCaseDataBefore().getCancelledHearingDetails()) .isEmpty()) { // new cancelled hearing - need to attempt deletion - judicialService.deleteSpecificHearingRole(event.getCaseData().getId(), hearing.getValue()); + judicialService.deleteSpecificHearingRole(caseId, hearing.getValue()); + + // find the latest active hearing before this one and fix its roles + Optional lastHearing = event.getCaseData() + .getLastHearingBefore(hearing.getValue().getStartDate()); + + if (lastHearing.isPresent()) { + judicialService.deleteSpecificHearingRole(caseId, lastHearing.get()); + + Optional possibleNextHearing = event.getCaseData() + .getNextHearingAfter(lastHearing.get().getStartDate()); + + judicialService + .assignHearingJudge(caseId, lastHearing.get(), possibleNextHearing, isFirstHearing); + } } }); } catch (Exception e) { diff --git a/service/src/main/java/uk/gov/hmcts/reform/fpl/handlers/judicial/NewHearingJudgeEventHandler.java b/service/src/main/java/uk/gov/hmcts/reform/fpl/handlers/judicial/NewHearingJudgeEventHandler.java index c38e95bcc58..54ec8e12311 100644 --- a/service/src/main/java/uk/gov/hmcts/reform/fpl/handlers/judicial/NewHearingJudgeEventHandler.java +++ b/service/src/main/java/uk/gov/hmcts/reform/fpl/handlers/judicial/NewHearingJudgeEventHandler.java @@ -6,16 +6,11 @@ import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import uk.gov.hmcts.reform.fpl.enums.JudgeOrMagistrateTitle; import uk.gov.hmcts.reform.fpl.events.judicial.NewHearingJudgeEvent; import uk.gov.hmcts.reform.fpl.model.HearingBooking; import uk.gov.hmcts.reform.fpl.model.common.Element; -import uk.gov.hmcts.reform.fpl.model.common.JudgeAndLegalAdvisor; import uk.gov.hmcts.reform.fpl.service.JudicialService; -import uk.gov.hmcts.reform.rd.model.JudicialUserProfile; -import java.time.ZoneId; -import java.time.ZonedDateTime; import java.util.Comparator; import java.util.Optional; @@ -40,45 +35,16 @@ public void handleNewHearingJudge(final NewHearingJudgeEvent event) { return; } - JudgeAndLegalAdvisor hearingJudge = event.getHearing().getJudgeAndLegalAdvisor(); - Optional nextHearing = event.getCaseData().getAllNonCancelledHearings() .stream() .map(Element::getValue) .filter(hearing -> hearing.getStartDate().isAfter(event.getHearing().getStartDate())) .min(Comparator.comparing(HearingBooking::getStartDate)); - ZonedDateTime possibleEnd = nextHearing.map(hearing -> hearing.getStartDate().atZone(ZoneId.systemDefault())) - .orElse(null); - - - if (!isEmpty(hearingJudge.getJudgeJudicialUser()) - && !isEmpty(hearingJudge.getJudgeJudicialUser().getIdamId())) { + final boolean isFirstHearing = event.getCaseData().getAllNonCancelledHearings().size() == 1; - // have an IDAM ID - use that to grant the role - judicialService.assignHearingJudge(event.getCaseData().getId(), - hearingJudge.getJudgeJudicialUser().getIdamId(), - event.getHearing().getStartDate().atZone(ZoneId.systemDefault()), - // if there's a hearing after the one added, we're going out of order, so set an end date - possibleEnd, - JudgeOrMagistrateTitle.LEGAL_ADVISOR.equals(hearingJudge.getJudgeTitle())); - } else if (!isEmpty(hearingJudge.getJudgeJudicialUser()) - && !isEmpty(hearingJudge.getJudgeJudicialUser().getPersonalCode())) { - - // no IDAM ID, but has personal code, lookup in JRD first - Optional judge = judicialService - .getJudge(hearingJudge.getJudgeJudicialUser().getPersonalCode()); - - judge.ifPresentOrElse(judicialUserProfile -> - judicialService.assignHearingJudge(event.getCaseData().getId(), judicialUserProfile.getSidamId(), - event.getHearing().getStartDate().atZone(ZoneId.systemDefault()), - possibleEnd, - JudgeOrMagistrateTitle.LEGAL_ADVISOR.equals(hearingJudge.getJudgeTitle())), - () -> log.info("Could not lookup in JRD, no auto allocation of hearing judge on case {}", - event.getCaseData().getId())); - } else { - log.info("No auto allocation of hearing judge on case {}", event.getCaseData().getId()); - } + judicialService + .assignHearingJudge(event.getCaseData().getId(), event.getHearing(), nextHearing, isFirstHearing); } private void handleEditedHearing(final NewHearingJudgeEvent event) { diff --git a/service/src/main/java/uk/gov/hmcts/reform/fpl/model/CaseData.java b/service/src/main/java/uk/gov/hmcts/reform/fpl/model/CaseData.java index e0ebb237d5a..a6161205178 100644 --- a/service/src/main/java/uk/gov/hmcts/reform/fpl/model/CaseData.java +++ b/service/src/main/java/uk/gov/hmcts/reform/fpl/model/CaseData.java @@ -767,6 +767,13 @@ public Optional getNextHearingAfter(LocalDateTime time) { .min(comparing(HearingBooking::getStartDate)); } + @JsonIgnore + public Optional getLastHearingBefore(LocalDateTime time) { + return unwrapElements(hearingDetails).stream() + .filter(hearingBooking -> hearingBooking.getStartDate().isBefore(time)) + .max(comparing(HearingBooking::getStartDate)); + } + @JsonIgnore public HearingBooking getMostUrgentHearingBookingAfter(LocalDateTime time) { return getNextHearingAfter(time).orElseThrow(NoHearingBookingException::new); diff --git a/service/src/main/java/uk/gov/hmcts/reform/fpl/service/JudicialService.java b/service/src/main/java/uk/gov/hmcts/reform/fpl/service/JudicialService.java index 6a87feab107..bb1479f5878 100644 --- a/service/src/main/java/uk/gov/hmcts/reform/fpl/service/JudicialService.java +++ b/service/src/main/java/uk/gov/hmcts/reform/fpl/service/JudicialService.java @@ -13,6 +13,7 @@ import uk.gov.hmcts.reform.ccd.client.model.CaseDetails; import uk.gov.hmcts.reform.fpl.config.rd.JudicialUsersConfiguration; import uk.gov.hmcts.reform.fpl.config.rd.LegalAdviserUsersConfiguration; +import uk.gov.hmcts.reform.fpl.enums.JudgeOrMagistrateTitle; import uk.gov.hmcts.reform.fpl.enums.YesNo; import uk.gov.hmcts.reform.fpl.model.CaseData; import uk.gov.hmcts.reform.fpl.model.HearingBooking; @@ -143,8 +144,8 @@ public void assignAllocatedJudge(Long caseId, String userId, boolean isLegalAdvi * @param userId the user to assign hearing-judge to * @param starting the time to start the new role at, and the old roles to END at (- HEARING_EXPIRY_OFFSET_MINS) */ - public void assignHearingJudge(Long caseId, String userId, ZonedDateTime starting, ZonedDateTime ending, - boolean isLegalAdviser) { + private void assignHearingJudgeRole(Long caseId, String userId, ZonedDateTime starting, ZonedDateTime ending, + boolean isLegalAdviser) { setExistingHearingJudgesAndLegalAdvisersToExpire(caseId, starting.minusMinutes(HEARING_EXPIRY_OFFSET_MINS)); if (isLegalAdviser) { @@ -155,6 +156,40 @@ public void assignHearingJudge(Long caseId, String userId, ZonedDateTime startin } } + public void assignHearingJudge(Long caseId, HearingBooking hearing, Optional nextHearing, + boolean startNow) { + Optional judgeId = getJudgeIdFromHearing(hearing); + ZonedDateTime possibleEndDate = nextHearing.map(HearingBooking::getStartDate) + .map(ld -> ld.atZone(ZoneId.systemDefault())) + .orElse(null); + + judgeId.ifPresentOrElse(s -> assignHearingJudgeRole(caseId, + s, + startNow ? ZonedDateTime.now() : hearing.getStartDate().atZone(ZoneId.systemDefault()), + possibleEndDate, + JudgeOrMagistrateTitle.LEGAL_ADVISOR.equals(hearing.getJudgeAndLegalAdvisor().getJudgeTitle())), + () -> log.error("No judge details on hearing starting at {} on case {} to assign roles to", + hearing.getStartDate(), caseId)); + } + + public Optional getJudgeIdFromHearing(HearingBooking booking) { + if (isEmpty(booking) + || isEmpty(booking.getJudgeAndLegalAdvisor()) + || isEmpty(booking.getJudgeAndLegalAdvisor().getJudgeJudicialUser())) { + return Optional.empty(); + } + + if (!isEmpty(booking.getJudgeAndLegalAdvisor().getJudgeJudicialUser().getIdamId())) { + return Optional.of(booking.getJudgeAndLegalAdvisor().getJudgeJudicialUser().getIdamId()); + } + + if (!isEmpty(booking.getJudgeAndLegalAdvisor().getJudgeJudicialUser().getPersonalCode())) { + return this.getJudge(booking.getJudgeAndLegalAdvisor().getJudgeJudicialUser().getPersonalCode()) + .map(JudicialUserProfile::getSidamId); + } + return Optional.empty(); + } + /** * Calls to Judicial Reference Data to check if a judge exists. * @@ -420,7 +455,7 @@ public List validateHearingJudgeEmail(CaseDetails caseDetails, CaseData .build(); } else { return List.of("No Judge could be found, please retry your search or enter their" - + " details manually."); + + " details manually."); } } else { // entered the judge manually - lookup in our mappings and add UUID manually diff --git a/service/src/test/java/uk/gov/hmcts/reform/fpl/handlers/judicial/HandleHearingModificationRolesEventHandlerTest.java b/service/src/test/java/uk/gov/hmcts/reform/fpl/handlers/judicial/HandleHearingModificationRolesEventHandlerTest.java index 69955c9aa99..8a3f61f50d4 100644 --- a/service/src/test/java/uk/gov/hmcts/reform/fpl/handlers/judicial/HandleHearingModificationRolesEventHandlerTest.java +++ b/service/src/test/java/uk/gov/hmcts/reform/fpl/handlers/judicial/HandleHearingModificationRolesEventHandlerTest.java @@ -5,6 +5,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.reform.fpl.enums.JudgeOrMagistrateTitle; import uk.gov.hmcts.reform.fpl.events.judicial.HandleHearingModificationRolesEvent; import uk.gov.hmcts.reform.fpl.model.CaseData; import uk.gov.hmcts.reform.fpl.model.HearingBooking; @@ -15,11 +16,14 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; +import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static uk.gov.hmcts.reform.fpl.utils.ElementUtils.element; +import static uk.gov.hmcts.reform.fpl.utils.ElementUtils.wrapElements; @ExtendWith(MockitoExtension.class) class HandleHearingModificationRolesEventHandlerTest { @@ -55,6 +59,109 @@ void shouldHandleCancelledHearingRoles() { verifyNoMoreInteractions(judicialService); } + @Test + void shouldReconfigurePreviousHearingRoleWhenNoFurtherHearings() { + LocalDateTime now = LocalDateTime.now(); + UUID cancelledBookingId = UUID.randomUUID(); + + HearingBooking prevBooking = HearingBooking.builder() + .startDate(now) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) + .judgeLastName("Test") + .judgeJudicialUser(JudicialUser.builder() + .idamId("1234") + .build()) + .build()) + .build(); + + HearingBooking cancelledBooking = HearingBooking.builder() + .startDate(now.plusDays(2)) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) + .judgeLastName("Test") + .judgeJudicialUser(JudicialUser.builder() + .idamId("1234") + .build()) + .build()) + .build(); + + + CaseData caseDataBefore = CaseData.builder() + .id(12345L) + .hearingDetails(List.of(element(prevBooking), element(cancelledBookingId, cancelledBooking))) + .build(); + + CaseData caseDataAfter = caseDataBefore.toBuilder() + .hearingDetails(List.of(element(prevBooking))) + .cancelledHearingDetails(List.of(element(cancelledBookingId, cancelledBooking))) + .build(); + + underTest.handleCancelledHearingRoles(new HandleHearingModificationRolesEvent(caseDataAfter, caseDataBefore)); + + verify(judicialService).deleteSpecificHearingRole(12345L, cancelledBooking); + verify(judicialService).deleteSpecificHearingRole(12345L, prevBooking); + verify(judicialService).assignHearingJudge(12345L, prevBooking, Optional.empty(), true); + } + + @Test + void shouldReconfigurePreviousHearingRoleWhenFurtherHearings() { + LocalDateTime now = LocalDateTime.now(); + UUID cancelledBookingId = UUID.randomUUID(); + + HearingBooking prevBooking = HearingBooking.builder() + .startDate(now) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) + .judgeLastName("Test") + .judgeJudicialUser(JudicialUser.builder() + .idamId("1234") + .build()) + .build()) + .build(); + + HearingBooking cancelledBooking = HearingBooking.builder() + .startDate(now.plusDays(2)) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) + .judgeLastName("Test") + .judgeJudicialUser(JudicialUser.builder() + .idamId("1234") + .build()) + .build()) + .build(); + + HearingBooking futureHearing = HearingBooking.builder() + .startDate(now.plusDays(4)) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) + .judgeLastName("Test") + .judgeJudicialUser(JudicialUser.builder() + .idamId("1234") + .build()) + .build()) + .build(); + + CaseData caseDataBefore = CaseData.builder() + .id(12345L) + .hearingDetails(List.of( + element(prevBooking), + element(cancelledBookingId, cancelledBooking), + element(futureHearing))) + .build(); + + CaseData caseDataAfter = caseDataBefore.toBuilder() + .hearingDetails(wrapElements(prevBooking, futureHearing)) + .cancelledHearingDetails(List.of(element(cancelledBookingId, cancelledBooking))) + .build(); + + underTest.handleCancelledHearingRoles(new HandleHearingModificationRolesEvent(caseDataAfter, caseDataBefore)); + + verify(judicialService).deleteSpecificHearingRole(12345L, cancelledBooking); + verify(judicialService).deleteSpecificHearingRole(12345L, prevBooking); + verify(judicialService).assignHearingJudge(12345L, prevBooking, Optional.of(futureHearing), false); + } + @Test void shouldNotThrowExceptionIfNullList() { assertDoesNotThrow(() -> underTest.handleCancelledHearingRoles(new HandleHearingModificationRolesEvent( diff --git a/service/src/test/java/uk/gov/hmcts/reform/fpl/handlers/judicial/NewHearingJudgeEventHandlerTest.java b/service/src/test/java/uk/gov/hmcts/reform/fpl/handlers/judicial/NewHearingJudgeEventHandlerTest.java index 522f161528e..1e9ab98fc66 100644 --- a/service/src/test/java/uk/gov/hmcts/reform/fpl/handlers/judicial/NewHearingJudgeEventHandlerTest.java +++ b/service/src/test/java/uk/gov/hmcts/reform/fpl/handlers/judicial/NewHearingJudgeEventHandlerTest.java @@ -12,21 +12,17 @@ import uk.gov.hmcts.reform.fpl.model.JudicialUser; import uk.gov.hmcts.reform.fpl.model.common.JudgeAndLegalAdvisor; import uk.gov.hmcts.reform.fpl.service.JudicialService; -import uk.gov.hmcts.reform.rd.model.JudicialUserProfile; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import java.util.UUID; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; import static uk.gov.hmcts.reform.fpl.utils.ElementUtils.element; +import static uk.gov.hmcts.reform.fpl.utils.ElementUtils.wrapElements; @ExtendWith(MockitoExtension.class) class NewHearingJudgeEventHandlerTest { @@ -60,71 +56,81 @@ void shouldNotDoAnythingIfNoHearingJudge() { } @Test - void shouldNotDoAnythingIfNoHearingJudgeJudicialUser() { - NewHearingJudgeEvent event = NewHearingJudgeEvent.builder() - .caseData(CaseData.builder().id(12345L).build()) - .hearing(HearingBooking.builder() - .startDate(LocalDateTime.now()) - .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() - .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) - .judgeLastName("Test") + void shouldAttemptAssignIfHearingJudgeJudicialUserWithIdamId() { + HearingBooking booking = HearingBooking.builder() + .startDate(LocalDateTime.now()) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) + .judgeLastName("Test") + .judgeJudicialUser(JudicialUser.builder() + .idamId("1234") .build()) .build()) + .build(); + + NewHearingJudgeEvent event = NewHearingJudgeEvent.builder() + .caseData(CaseData.builder().id(12345L).hearingDetails(wrapElements(booking)).build()) + .hearing(booking) .oldHearing(Optional.empty()) .build(); underTest.handleNewHearingJudge(event); - verifyNoInteractions(judicialService); + verify(judicialService).assignHearingJudge(12345L, booking, Optional.empty(), true); } @Test - void shouldAttemptAssignIfHearingJudgeJudicialUserWithIdamId() { - NewHearingJudgeEvent event = NewHearingJudgeEvent.builder() - .caseData(CaseData.builder().id(12345L).build()) - .hearing(HearingBooking.builder() - .startDate(LocalDateTime.now()) - .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() - .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) - .judgeLastName("Test") - .judgeJudicialUser(JudicialUser.builder() - .idamId("1234") - .build()) + void shouldAttemptToAssignRoleWithCorrectFollowUpHearing() { + HearingBooking booking = HearingBooking.builder() + .startDate(LocalDateTime.now()) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) + .judgeLastName("Test") + .judgeJudicialUser(JudicialUser.builder() + .idamId("1234") .build()) .build()) + .build(); + + HearingBooking existingBookingAfter = booking.toBuilder() + .startDate(LocalDateTime.now().plusDays(2)) + .build(); + + NewHearingJudgeEvent event = NewHearingJudgeEvent.builder() + .caseData(CaseData.builder().id(12345L) + .hearingDetails(wrapElements(booking, existingBookingAfter)).build()) + .hearing(booking) .oldHearing(Optional.empty()) .build(); underTest.handleNewHearingJudge(event); - verify(judicialService).assignHearingJudge(any(), eq("1234"), any(), any(), anyBoolean()); + verify(judicialService).assignHearingJudge(12345L, booking, Optional.of(existingBookingAfter), false); } + @Test void shouldAttemptAssignIfHearingJudgeJudicialUserWithPersonalCodeOnly() { - when(judicialService.getJudge("personal")) - .thenReturn(Optional.of(JudicialUserProfile.builder() - .sidamId("sidam") - .build())); - NewHearingJudgeEvent event = NewHearingJudgeEvent.builder() - .caseData(CaseData.builder().id(12345L).build()) - .hearing(HearingBooking.builder() - .startDate(LocalDateTime.now()) - .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() - .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) - .judgeLastName("Test") - .judgeJudicialUser(JudicialUser.builder() - .personalCode("personal") - .build()) + HearingBooking booking = HearingBooking.builder() + .startDate(LocalDateTime.now()) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeTitle(JudgeOrMagistrateTitle.HIS_HONOUR_JUDGE) + .judgeLastName("Test") + .judgeJudicialUser(JudicialUser.builder() + .personalCode("personal") .build()) .build()) + .build(); + + NewHearingJudgeEvent event = NewHearingJudgeEvent.builder() + .caseData(CaseData.builder().id(12345L).hearingDetails(wrapElements(booking)).build()) + .hearing(booking) .oldHearing(Optional.empty()) .build(); underTest.handleNewHearingJudge(event); - verify(judicialService).getJudge("personal"); - verify(judicialService).assignHearingJudge(any(), eq("sidam"), any(), any(), anyBoolean()); + verify(judicialService).assignHearingJudge(12345L, event.getHearing(), Optional.empty(), true); } @Test @@ -152,7 +158,7 @@ void shouldHandleEditedHearingRolesWhenTimeChanges() { )); verify(judicialService).deleteSpecificHearingRole(12345L, oldHearing); - verify(judicialService).assignHearingJudge(any(), eq("idamId"), any(), any(), anyBoolean()); + verify(judicialService).assignHearingJudge(12345L, newHearing, Optional.empty(), true); verifyNoMoreInteractions(judicialService); } @@ -185,7 +191,7 @@ void shouldHandleEditedHearingRolesWhenJudgeChanges() { )); verify(judicialService).deleteSpecificHearingRole(12345L, oldHearing); - verify(judicialService).assignHearingJudge(any(), eq("idamId2"), any(), any(), anyBoolean()); + verify(judicialService).assignHearingJudge(12345L, newHearing, Optional.empty(), true); verifyNoMoreInteractions(judicialService); } diff --git a/service/src/test/java/uk/gov/hmcts/reform/fpl/model/CaseDataTest.java b/service/src/test/java/uk/gov/hmcts/reform/fpl/model/CaseDataTest.java index 23d5bd52d28..028dacfcf68 100644 --- a/service/src/test/java/uk/gov/hmcts/reform/fpl/model/CaseDataTest.java +++ b/service/src/test/java/uk/gov/hmcts/reform/fpl/model/CaseDataTest.java @@ -2103,6 +2103,32 @@ void shouldReturnAllOrderCollection() { assertThat(caseData.getAllOrderCollections()).isEqualTo(expected); } + @Test + void shouldGetCorrectLastHearingBeforeDate() { + LocalDateTime now = LocalDateTime.now(); + HearingBooking hearing = HearingBooking.builder().startDate(now).build(); + HearingBooking hearingPrior = HearingBooking.builder().startDate(now.minusDays(2)).build(); + HearingBooking hearingPrior2 = HearingBooking.builder().startDate(now.minusDays(4)).build(); + + CaseData caseData = CaseData.builder() + .hearingDetails(wrapElements(hearing, hearingPrior, hearingPrior2)) + .build(); + + assertThat(caseData.getLastHearingBefore(now)).isEqualTo(Optional.of(hearingPrior)); + } + + @Test + void shouldReturnEmptyIfNoPriorHearingBeforeDate() { + LocalDateTime now = LocalDateTime.now(); + HearingBooking futureHearing = HearingBooking.builder().startDate(now.plusDays(2)).build(); + + CaseData caseData = CaseData.builder() + .hearingDetails(wrapElements(futureHearing)) + .build(); + + assertThat(caseData.getLastHearingBefore(now)).isEqualTo(Optional.empty()); + } + private HearingOrder buildHearingOrder(HearingOrderType type) { return HearingOrder.builder().type(type).build(); } diff --git a/service/src/test/java/uk/gov/hmcts/reform/fpl/service/JudicialServiceTest.java b/service/src/test/java/uk/gov/hmcts/reform/fpl/service/JudicialServiceTest.java index 96e8ddc44a7..8dd91003a16 100644 --- a/service/src/test/java/uk/gov/hmcts/reform/fpl/service/JudicialServiceTest.java +++ b/service/src/test/java/uk/gov/hmcts/reform/fpl/service/JudicialServiceTest.java @@ -8,6 +8,7 @@ import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import uk.gov.hmcts.reform.am.model.RoleAssignment; @@ -15,6 +16,7 @@ import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.fpl.config.rd.JudicialUsersConfiguration; import uk.gov.hmcts.reform.fpl.config.rd.LegalAdviserUsersConfiguration; +import uk.gov.hmcts.reform.fpl.enums.JudgeCaseRole; import uk.gov.hmcts.reform.fpl.enums.JudgeOrMagistrateTitle; import uk.gov.hmcts.reform.fpl.enums.YesNo; import uk.gov.hmcts.reform.fpl.model.CaseData; @@ -24,6 +26,7 @@ import uk.gov.hmcts.reform.fpl.model.common.JudgeAndLegalAdvisor; import uk.gov.hmcts.reform.rd.client.JudicialApi; import uk.gov.hmcts.reform.rd.model.JudicialUserProfile; +import uk.gov.hmcts.reform.rd.model.JudicialUserRequest; import java.time.LocalDateTime; import java.time.ZoneId; @@ -37,8 +40,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.quality.Strictness.LENIENT; @@ -354,4 +359,170 @@ void shouldDeleteSpecificHearingRole() { eq(List.of("hearing-judge", "hearing-legal-adviser"))); } + @Test + void shouldCreateTimeUnboundHearingJudgeRoleWhenNoFutureHearing() { + LocalDateTime startDate = LocalDateTime.now(); + + HearingBooking hearing = HearingBooking.builder() + .startDate(startDate) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeEnterManually(YesNo.NO) + .judgeTitle(JudgeOrMagistrateTitle.OTHER) + .judgeJudicialUser(JudicialUser.builder() + .idamId("idam") + .build()) + .build()) + .build(); + + underTest.assignHearingJudge(12345L, hearing, Optional.empty(), false); + + verify(roleAssignmentService).assignJudgesRole( + 12345L, + List.of("idam"), + JudgeCaseRole.HEARING_JUDGE, + startDate.atZone(ZoneId.systemDefault()), + null); + } + + @Test + void shouldCreateRoleStartingNowNotStartDateIfOnlyHearing() { + LocalDateTime now = LocalDateTime.now(); + final ZonedDateTime nowZoned = now.atZone(ZoneId.systemDefault()); + + HearingBooking hearing = HearingBooking.builder() + .startDate(now.plusDays(2)) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeEnterManually(YesNo.NO) + .judgeTitle(JudgeOrMagistrateTitle.OTHER) + .judgeJudicialUser(JudicialUser.builder() + .idamId("idam") + .build()) + .build()) + .build(); + + try (MockedStatic zonedStatic = mockStatic(ZonedDateTime.class)) { + zonedStatic.when(ZonedDateTime::now).thenReturn(nowZoned); + + underTest.assignHearingJudge(12345L, hearing, Optional.empty(), true); + + verify(roleAssignmentService).assignJudgesRole( + 12345L, + List.of("idam"), + JudgeCaseRole.HEARING_JUDGE, + nowZoned, + null); + } + } + + @Test + void shouldCreateTimeBoundHearingJudgeRoleWithFutureHearing() { + LocalDateTime now = LocalDateTime.now(); + + HearingBooking hearing = HearingBooking.builder() + .startDate(now) + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeEnterManually(YesNo.NO) + .judgeTitle(JudgeOrMagistrateTitle.OTHER) + .judgeJudicialUser(JudicialUser.builder() + .idamId("idam") + .build()) + .build()) + .build(); + + HearingBooking futureHearing = hearing.toBuilder() + .startDate(now.plusDays(2)) + .build(); + + underTest.assignHearingJudge(12345L, hearing, Optional.of(futureHearing), false); + + verify(roleAssignmentService).assignJudgesRole( + 12345L, + List.of("idam"), + JudgeCaseRole.HEARING_JUDGE, + now.atZone(ZoneId.systemDefault()), + now.plusDays(2).atZone(ZoneId.systemDefault())); + } + + @Nested + class GetJudgeId { + @Test + void shouldLookupJudgeIdWhenPersonalCodeOnly() { + HearingBooking booking = HearingBooking.builder() + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeEnterManually(YesNo.NO) + .judgeTitle(JudgeOrMagistrateTitle.OTHER) + .judgeJudicialUser(JudicialUser.builder() + .personalCode("code") + .build()) + .build()) + .build(); + + underTest.getJudgeIdFromHearing(booking); + + verify(judicialApi).findUsers(any(), any(), anyInt(), + any(), eq(JudicialUserRequest.fromPersonalCode("code"))); + } + + @Test + void shouldReturnJudgeIdWhenIdamOnly() { + HearingBooking booking = HearingBooking.builder() + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeEnterManually(YesNo.NO) + .judgeTitle(JudgeOrMagistrateTitle.OTHER) + .judgeJudicialUser(JudicialUser.builder() + .idamId("idam") + .build()) + .build()) + .build(); + + Optional idamId = underTest.getJudgeIdFromHearing(booking); + + assertThat(idamId.get()).isEqualTo("idam"); + verifyNoInteractions(judicialApi); + } + + @Test + void shouldReturnEmptyOptionalWhenNoIdamOrPersonalCode() { + HearingBooking booking = HearingBooking.builder() + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeEnterManually(YesNo.NO) + .judgeTitle(JudgeOrMagistrateTitle.OTHER) + .judgeJudicialUser(JudicialUser.builder() + .build()) + .build()) + .build(); + + Optional idamId = underTest.getJudgeIdFromHearing(booking); + + assertThat(idamId).isEqualTo(Optional.empty()); + verifyNoInteractions(judicialApi); + } + + @Test + void shouldReturnEmptyOptionalWhenNoJudicialUser() { + HearingBooking booking = HearingBooking.builder() + .judgeAndLegalAdvisor(JudgeAndLegalAdvisor.builder() + .judgeEnterManually(YesNo.NO) + .judgeTitle(JudgeOrMagistrateTitle.OTHER) + .build()) + .build(); + + Optional idamId = underTest.getJudgeIdFromHearing(booking); + + assertThat(idamId).isEqualTo(Optional.empty()); + verifyNoInteractions(judicialApi); + } + + @Test + void shouldReturnEmptyOptionalWhenNoHearingJudge() { + HearingBooking booking = HearingBooking.builder() + .build(); + + Optional idamId = underTest.getJudgeIdFromHearing(booking); + + assertThat(idamId).isEqualTo(Optional.empty()); + verifyNoInteractions(judicialApi); + } + } + }