Skip to content

Commit

Permalink
DFPL-2478: Fix end date of previous hearing judge roles when cancelli…
Browse files Browse the repository at this point in the history
…ng 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 <[email protected]>
  • Loading branch information
DanCatchpole and Braimah101 authored Aug 29, 2024
1 parent fbcfb7a commit 3155bd8
Show file tree
Hide file tree
Showing 8 changed files with 422 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -23,14 +26,30 @@ 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())
.forEach(hearing -> {
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<HearingBooking> lastHearing = event.getCaseData()
.getLastHearingBefore(hearing.getValue().getStartDate());

if (lastHearing.isPresent()) {
judicialService.deleteSpecificHearingRole(caseId, lastHearing.get());

Optional<HearingBooking> possibleNextHearing = event.getCaseData()
.getNextHearingAfter(lastHearing.get().getStartDate());

judicialService
.assignHearingJudge(caseId, lastHearing.get(), possibleNextHearing, isFirstHearing);
}
}
});
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -40,45 +35,16 @@ public void handleNewHearingJudge(final NewHearingJudgeEvent event) {
return;
}

JudgeAndLegalAdvisor hearingJudge = event.getHearing().getJudgeAndLegalAdvisor();

Optional<HearingBooking> 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<JudicialUserProfile> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,13 @@ public Optional<HearingBooking> getNextHearingAfter(LocalDateTime time) {
.min(comparing(HearingBooking::getStartDate));
}

@JsonIgnore
public Optional<HearingBooking> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -155,6 +156,40 @@ public void assignHearingJudge(Long caseId, String userId, ZonedDateTime startin
}
}

public void assignHearingJudge(Long caseId, HearingBooking hearing, Optional<HearingBooking> nextHearing,
boolean startNow) {
Optional<String> 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<String> 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.
*
Expand Down Expand Up @@ -420,7 +455,7 @@ public List<String> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down
Loading

0 comments on commit 3155bd8

Please sign in to comment.