diff --git a/pom.xml b/pom.xml
index 5c8cb5a6a8..cf8c3d9f27 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,12 +42,12 @@
4.1.4-ATCM
17.0.57
17.0.1
- 17.103.133
- 17.0.38
+ 17.104.137
+ 17.104.52
17.0.11
0.1.49
17.104.48
- 17.0.262
+ 17.0.286
17.0.66
4.1.4-ATCM
true
@@ -65,7 +65,7 @@
2.0.11
-Djava.locale.providers=CLDR
6.13
- 9.2.2
+ 7.16.2
17.104.81
17.104.4-DD-41592-SNAPSHOT
1.5.0
diff --git a/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/JudicialResultHelper.java b/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/JudicialResultHelper.java
index 6adcdc2925..f72d98a4c5 100644
--- a/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/JudicialResultHelper.java
+++ b/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/JudicialResultHelper.java
@@ -4,6 +4,7 @@
import static java.lang.System.lineSeparator;
import static java.util.UUID.fromString;
import static java.util.stream.Collectors.joining;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static uk.gov.justice.core.courts.JudicialResult.judicialResult;
import uk.gov.justice.core.courts.JudicialResult;
@@ -65,6 +66,7 @@ public class JudicialResultHelper {
"I", "INTERMEDIARY");
public static final String Y = "Y";
+ public static final String DURATION = "duration";
private JudicialResultHelper() {
@@ -93,13 +95,21 @@ public static JudicialResultPrompt.Builder populatePromptDefinitionAttributesBas
resultDefinition.getJsonArray("prompts")
.getValuesAs(JsonObject.class)
.stream()
- .filter(prompt -> prompt.getString("id").equals(jsPrompt.getId().toString())
- && duration.equals(prompt.getString("duration")))
+ .filter(prompt -> prompt.getString("id").equals(jsPrompt.getId().toString()))
+ .filter(prompt -> prompt.containsKey(DURATION))
+ .filter(prompt -> isNotBlank(prompt.getString(DURATION)) && isNotBlank(duration))
+ .filter(prompt -> isDurationStringMatchesPromptValue(duration, prompt))
.findFirst();
return populatePrompt(jsPrompt, promptJson);
}
+ private static boolean isDurationStringMatchesPromptValue(final String duration, final JsonObject prompt) {
+ final String alphaOnlyPattern = "[^a-zA-Z]";
+ final String promptValue = prompt.getString(DURATION).replaceAll(alphaOnlyPattern, "");
+ return duration.equals(promptValue) || duration.toUpperCase().contains(promptValue.toUpperCase()) || promptValue.toUpperCase().contains(duration.toUpperCase());
+ }
+
@SuppressWarnings("squid:S00112")
private static JudicialResultPrompt.Builder populatePrompt(final JPrompt jsPrompt,
final Optional promptJson) {
diff --git a/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/DismissDecisionResultAggregator.java b/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/DismissDecisionResultAggregator.java
index e8ace4c81f..22b7286f51 100644
--- a/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/DismissDecisionResultAggregator.java
+++ b/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/DismissDecisionResultAggregator.java
@@ -8,6 +8,7 @@
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.moj.cpp.sjp.domain.decision.Dismiss;
import uk.gov.moj.cpp.sjp.domain.decision.OffenceDecision;
+import uk.gov.moj.cpp.sjp.event.processor.results.converter.ConvictionInfo;
import uk.gov.moj.cpp.sjp.event.processor.results.converter.judicialresult.JCachedReferenceData;
import uk.gov.moj.cpp.sjp.event.processor.results.converter.judicialresult.DecisionAggregate;
@@ -49,6 +50,13 @@ public void aggregate(final OffenceDecision offenceDecision,
setFinalOffence(decisionAggregate, offenceId, judicialResults);
+ // conviction information - a dismissal carries the FOUND_NOT_GUILTY verdict dated on the
+ // resulting date but has no convicting court
+ decisionAggregate.putConvictionInfo(offenceId,
+ new ConvictionInfo(offenceId,
+ dismissOffenceDecision.getOffenceDecisionInformation().getVerdict(),
+ resultedOn.toLocalDate(),
+ null));
}
}
diff --git a/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/ReferForCourtHearingDecisionResultAggregator.java b/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/ReferForCourtHearingDecisionResultAggregator.java
index 3981aa7eab..ebd7186c7c 100644
--- a/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/ReferForCourtHearingDecisionResultAggregator.java
+++ b/sjp-event/sjp-event-processor/src/main/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/ReferForCourtHearingDecisionResultAggregator.java
@@ -34,6 +34,7 @@
import uk.gov.moj.cpp.sjp.event.processor.results.converter.judicialresult.DecisionAggregate;
import uk.gov.moj.cpp.sjp.event.processor.results.converter.judicialresult.JCachedReferenceData;
+import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
@@ -160,7 +161,8 @@ private void populateCourtTimingDetails(final JsonObject resultDefinition,
.withJudicialResultPromptTypeId(NHMC_DURATION_IN_MINUTES.getId())
.withValue(caseOffenceListedInCriminalCourts.getHearingDays().get(0).getListedDurationMinutes().toString()).build());
- final ZonedDateTime sittingDateTime = caseOffenceListedInCriminalCourts.getHearingDays().get(0).getSittingDay();
+ final ZonedDateTime sittingDateTime = caseOffenceListedInCriminalCourts.getHearingDays().get(0).getSittingDay()
+ .withZoneSameInstant(ZoneId.of("Europe/London"));
judicialResultPrompts.add(getPrompt(NHMC_HOUSE_HEARING_TIME, resultDefinition)
.withJudicialResultPromptTypeId(NHMC_HOUSE_HEARING_TIME.getId())
diff --git a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/JudicialResultHelperTest.java b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/JudicialResultHelperTest.java
new file mode 100644
index 0000000000..2b484c286a
--- /dev/null
+++ b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/JudicialResultHelperTest.java
@@ -0,0 +1,162 @@
+package uk.gov.moj.cpp.sjp.event.processor.results.converter.judicialresult;
+
+import static java.util.UUID.fromString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static uk.gov.moj.cpp.sjp.event.processor.results.converter.judicialresult.JPrompt.DDP_DISQUALIFICATION_PERIOD;
+import static uk.gov.moj.cpp.sjp.event.processor.utils.FileUtil.getFileContentAsJson;
+
+import uk.gov.justice.core.courts.JudicialResultPrompt;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.UUID;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class JudicialResultHelperTest {
+
+ private static final String RESULT_DEFINITION_FILE = "ddq-result-definition.json";
+ private static final UUID DISQUALIFICATION_PERIOD_PROMPT_ID = fromString("2bf54447-328c-4c1b-a123-341adbd52172");
+
+ private JsonObject resultDefinition;
+
+ @BeforeEach
+ public void setUp() {
+ resultDefinition = getFileContentAsJson(RESULT_DEFINITION_FILE, new HashMap<>());
+ }
+
+ @Test
+ public void shouldPopulatePromptDefinitionAttributesForYearsDuration() {
+ final JudicialResultPrompt.Builder builder = JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, "Years");
+
+ final JudicialResultPrompt prompt = builder.build();
+
+ assertThat(prompt.getJudicialResultPromptTypeId(), is(DISQUALIFICATION_PERIOD_PROMPT_ID));
+ assertThat(prompt.getLabel(), is("Disqualification period"));
+ assertThat(prompt.getWelshLabel(), is("Cyfnod gwahardd"));
+ assertThat(prompt.getPromptReference(), is("disqualificationPeriod"));
+ assertThat(prompt.getType(), is("INTM"));
+ assertThat(prompt.getCourtExtract(), is("Y"));
+ assertThat(prompt.getDurationSequence(), is(1));
+ assertThat(prompt.getPromptSequence(), is(new BigDecimal(100)));
+ assertThat(prompt.getIsFinancialImposition(), is(false));
+ }
+
+ @Test
+ public void shouldPopulatePromptDefinitionAttributesForMonthsDuration() {
+ final JudicialResultPrompt.Builder builder = JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, "Months");
+
+ final JudicialResultPrompt prompt = builder.build();
+
+ assertThat(prompt.getJudicialResultPromptTypeId(), is(DISQUALIFICATION_PERIOD_PROMPT_ID));
+ assertThat(prompt.getLabel(), is("Disqualification period"));
+ assertThat(prompt.getWelshLabel(), is("Cyfnod gwahardd"));
+ assertThat(prompt.getPromptReference(), is("disqualificationPeriod"));
+ assertThat(prompt.getType(), is("INTM"));
+ assertThat(prompt.getCourtExtract(), is("Y"));
+ assertThat(prompt.getDurationSequence(), is(1));
+ assertThat(prompt.getPromptSequence(), is(new BigDecimal(100)));
+ assertThat(prompt.getIsFinancialImposition(), is(false));
+ }
+
+ @Test
+ public void shouldPopulatePromptDefinitionAttributesForDaysDuration() {
+ final JudicialResultPrompt.Builder builder = JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, "Days");
+
+ final JudicialResultPrompt prompt = builder.build();
+
+ assertThat(prompt.getJudicialResultPromptTypeId(), is(DISQUALIFICATION_PERIOD_PROMPT_ID));
+ assertThat(prompt.getLabel(), is("Disqualification period"));
+ assertThat(prompt.getWelshLabel(), is("Cyfnod gwahardd"));
+ assertThat(prompt.getPromptReference(), is("disqualificationPeriod"));
+ assertThat(prompt.getType(), is("INTM"));
+ assertThat(prompt.getCourtExtract(), is("Y"));
+ assertThat(prompt.getDurationSequence(), is(1));
+ assertThat(prompt.getPromptSequence(), is(new BigDecimal(100)));
+ assertThat(prompt.getIsFinancialImposition(), is(false));
+ }
+
+ @Test
+ public void shouldPopulatePromptDefinitionAttributesWhenDurationDiffersOnlyByCase() {
+ final JudicialResultPrompt.Builder builder = JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, "months");
+
+ final JudicialResultPrompt prompt = builder.build();
+
+ assertThat(prompt.getJudicialResultPromptTypeId(), is(DISQUALIFICATION_PERIOD_PROMPT_ID));
+ assertThat(prompt.getPromptReference(), is("disqualificationPeriod"));
+ }
+
+ @Test
+ public void shouldPopulatePromptDefinitionAttributesWhenDurationIsSingularFormOfPromptValue() {
+ final JudicialResultPrompt.Builder builder = JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, "Year");
+
+ final JudicialResultPrompt prompt = builder.build();
+
+ assertThat(prompt.getJudicialResultPromptTypeId(), is(DISQUALIFICATION_PERIOD_PROMPT_ID));
+ assertThat(prompt.getPromptReference(), is("disqualificationPeriod"));
+ }
+
+ @Test
+ public void shouldPopulatePromptDefinitionAttributesWhenPromptValueIsSubstringOfDuration() {
+ final JudicialResultPrompt.Builder builder = JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, "CalendarDays");
+
+ final JudicialResultPrompt prompt = builder.build();
+
+ assertThat(prompt.getJudicialResultPromptTypeId(), is(DISQUALIFICATION_PERIOD_PROMPT_ID));
+ assertThat(prompt.getPromptReference(), is("disqualificationPeriod"));
+ }
+
+ @Test
+ public void shouldThrowRuntimeExceptionWhenPromptNotFoundForDuration() {
+ assertThrows(RuntimeException.class, () ->
+ JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ JPrompt.AMOUNT_OF_FINE, resultDefinition, "Years"));
+ }
+
+ @Test
+ public void shouldThrowRuntimeExceptionWhenDurationDoesNotMatchAnyPromptValue() {
+ assertThrows(RuntimeException.class, () ->
+ JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, "Weeks"));
+ }
+
+ @Test
+ public void shouldThrowRuntimeExceptionWhenDurationIsBlank() {
+ assertThrows(RuntimeException.class, () ->
+ JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, ""));
+ }
+
+ @Test
+ public void shouldThrowRuntimeExceptionWhenDurationIsNull() {
+ assertThrows(RuntimeException.class, () ->
+ JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinition, null));
+ }
+
+ @Test
+ public void shouldThrowRuntimeExceptionWhenMatchingPromptHasBlankDurationValue() {
+ final JsonObject resultDefinitionWithBlankDuration = Json.createObjectBuilder()
+ .add("prompts", Json.createArrayBuilder()
+ .add(Json.createObjectBuilder()
+ .add("id", DISQUALIFICATION_PERIOD_PROMPT_ID.toString())
+ .add("duration", "")))
+ .build();
+
+ assertThrows(RuntimeException.class, () ->
+ JudicialResultHelper.populatePromptDefinitionAttributesBasedOnDuration(
+ DDP_DISQUALIFICATION_PERIOD, resultDefinitionWithBlankDuration, "Years"));
+ }
+}
\ No newline at end of file
diff --git a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/DismissDecisionResultAggregatorTest.java b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/DismissDecisionResultAggregatorTest.java
index 5758a2ff8f..b028eed2aa 100644
--- a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/DismissDecisionResultAggregatorTest.java
+++ b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/DismissDecisionResultAggregatorTest.java
@@ -8,10 +8,11 @@
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.MatcherAssert.assertThat;
import static uk.gov.moj.cpp.sjp.domain.decision.OffenceDecisionInformation.createOffenceDecisionInformation;
-import static uk.gov.moj.cpp.sjp.domain.verdict.VerdictType.FOUND_GUILTY;
+import static uk.gov.moj.cpp.sjp.domain.verdict.VerdictType.FOUND_NOT_GUILTY;
import static uk.gov.moj.cpp.sjp.event.processor.results.converter.judicialresult.JCaseResultsConstants.DATE_FORMAT;
import uk.gov.moj.cpp.sjp.domain.decision.Dismiss;
+import uk.gov.moj.cpp.sjp.event.processor.results.converter.ConvictionInfo;
import uk.gov.moj.cpp.sjp.event.processor.results.converter.judicialresult.DecisionAggregate;
import org.hamcrest.Matchers;
@@ -37,7 +38,7 @@ public void setUp() {
@Test
public void shouldPopulateResultWithRightPrompts() {
final Dismiss offenceDecision
- = new Dismiss(null, createOffenceDecisionInformation(offence1Id, FOUND_GUILTY));
+ = new Dismiss(null, createOffenceDecisionInformation(offence1Id, FOUND_NOT_GUILTY));
aggregator.aggregate(offenceDecision, sjpSessionEnvelope, resultsAggregate, resultedOn);
@@ -49,4 +50,19 @@ public void shouldPopulateResultWithRightPrompts() {
hasProperty("judicialResultPrompts", is(nullValue()))))));
assertThat(resultsAggregate.getFinalOffence(offenceDecision.getOffenceIds().get(0)),Matchers.is(true));
}
+
+ @Test
+ public void shouldPopulateConvictionInfoWithFoundNotGuiltyVerdict() {
+ final Dismiss offenceDecision
+ = new Dismiss(null, createOffenceDecisionInformation(offence1Id, FOUND_NOT_GUILTY));
+
+ aggregator.aggregate(offenceDecision, sjpSessionEnvelope, resultsAggregate, resultedOn);
+
+ final ConvictionInfo convictionInfo = resultsAggregate.getConvictionInfo(offence1Id);
+ assertThat(convictionInfo, is(Matchers.notNullValue()));
+ assertThat(convictionInfo.getOffenceId(), Matchers.is(offence1Id));
+ assertThat(convictionInfo.getVerdictType(), Matchers.is(FOUND_NOT_GUILTY));
+ assertThat(convictionInfo.getConvictionDate(), is(resultedOn.toLocalDate()));
+ assertThat(convictionInfo.getConvictingCourt(), is(nullValue()));
+ }
}
diff --git a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/ReferForCourtHearingDecisionResultAggregatorTest.java b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/ReferForCourtHearingDecisionResultAggregatorTest.java
index 260061783b..b703fa0d34 100644
--- a/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/ReferForCourtHearingDecisionResultAggregatorTest.java
+++ b/sjp-event/sjp-event-processor/src/test/java/uk/gov/moj/cpp/sjp/event/processor/results/converter/judicialresult/aggregator/ReferForCourtHearingDecisionResultAggregatorTest.java
@@ -32,6 +32,7 @@
import uk.gov.moj.cpp.sjp.event.CaseOffenceListedInCriminalCourts;
import uk.gov.moj.cpp.sjp.event.processor.results.converter.CourtCentreConverter;
+import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Collections;
@@ -94,7 +95,8 @@ public void shouldPopulateResultWithRightPrompts() {
"Note",
30,
courtOptions, null);
- final ZonedDateTime zonedDateTime = ZonedDateTime.now().plusDays(1);
+ final ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("UTC")).plusDays(1);
+ final ZonedDateTime zonedDateTimeLocal = zonedDateTime.withZoneSameInstant(ZoneId.of("Europe/London"));
final UUID caseId = randomUUID();
final UUID defendantId = randomUUID();
final UUID hearingId = randomUUID();
@@ -116,7 +118,7 @@ public void shouldPopulateResultWithRightPrompts() {
assertThat(judicialResult.getJudicialResultId().toString(), is("600edfc3-a584-4f9f-a52e-5bb8a99646c1"));
assertThat(judicialResult.getOrderedDate(), is(resultedOn.format(DATE_FORMAT)));
- assertThat(judicialResult.getResultText(), is("Refer for a full court hearing\n" +"Reasons for referring to court referral reason (referral sub reason)\nCourthouse organisation name Court Name\nCourtroom Court Room Name\nEstimated duration 123456\nTime of hearing "+zonedDateTime.toLocalTime()+"\nDate of hearing "+zonedDateTime.toLocalDate()+"\nCourthouse address line 1 Address 1\nCourthouse address line 2 Address 2\nCourthouse address line 3 Address 3\nCourthouse address line 4 Address 4\nCourthouse address line 5 Address 5\nCourthouse post code DD4 4DD"));
+ assertThat(judicialResult.getResultText(), is("Refer for a full court hearing\n" +"Reasons for referring to court referral reason (referral sub reason)\nCourthouse organisation name Court Name\nCourtroom Court Room Name\nEstimated duration 123456\nTime of hearing "+zonedDateTimeLocal.toLocalTime()+"\nDate of hearing "+zonedDateTimeLocal.toLocalDate()+"\nCourthouse address line 1 Address 1\nCourthouse address line 2 Address 2\nCourthouse address line 3 Address 3\nCourthouse address line 4 Address 4\nCourthouse address line 5 Address 5\nCourthouse post code DD4 4DD"));
assertThat(judicialResult.getJudicialResultPrompts().size(), is(12));
@@ -147,7 +149,8 @@ public void shouldNotPopulateResultWithEmptyValuePrompts() {
"Note",
30,
courtOptions, null);
- final ZonedDateTime zonedDateTime = ZonedDateTime.now().plusDays(1);
+ final ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("UTC")).plusDays(1);
+ final ZonedDateTime zonedDateTimeLocal = zonedDateTime.withZoneSameInstant(ZoneId.of("Europe/London"));
final UUID caseId = randomUUID();
final UUID defendantId = randomUUID();
final UUID hearingId = randomUUID();
@@ -170,7 +173,7 @@ public void shouldNotPopulateResultWithEmptyValuePrompts() {
assertThat(judicialResult.getJudicialResultId().toString(), is("600edfc3-a584-4f9f-a52e-5bb8a99646c1"));
assertThat(judicialResult.getOrderedDate(), is(resultedOn.format(DATE_FORMAT)));
- assertThat(judicialResult.getResultText(), is("Refer for a full court hearing\n" +"Reasons for referring to court referral reason (referral sub reason)\nCourthouse organisation name Court Name\nCourtroom Court Room Name\nEstimated duration 123456\nTime of hearing "+zonedDateTime.toLocalTime()+"\nDate of hearing "+zonedDateTime.toLocalDate()+"\nCourthouse address line 1 Address 1"));
+ assertThat(judicialResult.getResultText(), is("Refer for a full court hearing\n" +"Reasons for referring to court referral reason (referral sub reason)\nCourthouse organisation name Court Name\nCourtroom Court Room Name\nEstimated duration 123456\nTime of hearing "+zonedDateTimeLocal.toLocalTime()+"\nDate of hearing "+zonedDateTimeLocal.toLocalDate()+"\nCourthouse address line 1 Address 1"));
assertThat(resultsAggregate.getFinalOffence(offence1Id),is(nullValue()));
assertThat(judicialResult.getJudicialResultPrompts().size(), is(7));
assertThat(judicialResult.getJudicialResultPrompts(),
@@ -192,6 +195,58 @@ public void shouldNotPopulateResultWithEmptyValuePrompts() {
}
+ @Test
+ public void shouldConvertUtcSittingTimeToBstInResultTextDuringSummer() {
+ // 10:00 UTC on a BST date (May) should appear as 11:00 in the result text
+ final ZonedDateTime utcSummerDateTime = ZonedDateTime.parse("2026-05-28T10:00:00.000Z");
+
+ final ReferForCourtHearing offenceDecision = new ReferForCourtHearing(null,
+ Collections.singletonList(createOffenceDecisionInformation(offence1Id, PROVED_SJP)),
+ REFERRAL_REASON_ID, "Note", 30, courtOptions, null);
+
+ final List hearingDays = Arrays.asList(HearingDay.hearingDay()
+ .withSittingDay(utcSummerDateTime)
+ .withListedDurationMinutes(60)
+ .build());
+ final CaseOffenceListedInCriminalCourts caseOffenceListedInCriminalCourts =
+ new CaseOffenceListedInCriminalCourts(randomUUID(), randomUUID(), Arrays.asList(offence1Id),
+ randomUUID(), createCourtCenter(), hearingDays, hearingType);
+ final CaseListedInCriminalCourtsV2 caseListedInCcForReferToCourt =
+ new CaseListedInCriminalCourtsV2(Arrays.asList(caseOffenceListedInCriminalCourts), null, randomUUID());
+
+ aggregator.aggregate(offenceDecision, sjpSessionEnvelope, resultsAggregate, caseListedInCcForReferToCourt, resultedOn);
+
+ final JudicialResult judicialResult = resultsAggregate.getResults(offence1Id).get(0);
+ assertThat(judicialResult.getResultText(), Matchers.containsString("Time of hearing 11:00"));
+ assertThat(judicialResult.getResultText(), Matchers.containsString("Date of hearing 2026-05-28"));
+ }
+
+ @Test
+ public void shouldNotOffsetUtcSittingTimeInResultTextDuringWinter() {
+ // 10:00 UTC on a GMT date (December) should appear as 10:00 in the result text (no offset)
+ final ZonedDateTime utcWinterDateTime = ZonedDateTime.parse("2026-12-28T10:00:00.000Z");
+
+ final ReferForCourtHearing offenceDecision = new ReferForCourtHearing(null,
+ Collections.singletonList(createOffenceDecisionInformation(offence1Id, PROVED_SJP)),
+ REFERRAL_REASON_ID, "Note", 30, courtOptions, null);
+
+ final List hearingDays = Arrays.asList(HearingDay.hearingDay()
+ .withSittingDay(utcWinterDateTime)
+ .withListedDurationMinutes(60)
+ .build());
+ final CaseOffenceListedInCriminalCourts caseOffenceListedInCriminalCourts =
+ new CaseOffenceListedInCriminalCourts(randomUUID(), randomUUID(), Arrays.asList(offence1Id),
+ randomUUID(), createCourtCenter(), hearingDays, hearingType);
+ final CaseListedInCriminalCourtsV2 caseListedInCcForReferToCourt =
+ new CaseListedInCriminalCourtsV2(Arrays.asList(caseOffenceListedInCriminalCourts), null, randomUUID());
+
+ aggregator.aggregate(offenceDecision, sjpSessionEnvelope, resultsAggregate, caseListedInCcForReferToCourt, resultedOn);
+
+ final JudicialResult judicialResult = resultsAggregate.getResults(offence1Id).get(0);
+ assertThat(judicialResult.getResultText(), Matchers.containsString("Time of hearing 10:00"));
+ assertThat(judicialResult.getResultText(), Matchers.containsString("Date of hearing 2026-12-28"));
+ }
+
private CourtCentre createCourtCenterWithEmptyAddresses() {
return CourtCentre.courtCentre()
.withId(randomUUID())
diff --git a/sjp-event/sjp-event-processor/src/test/resources/ddq-result-definition.json b/sjp-event/sjp-event-processor/src/test/resources/ddq-result-definition.json
new file mode 100644
index 0000000000..ca234bfcd8
--- /dev/null
+++ b/sjp-event/sjp-event-processor/src/test/resources/ddq-result-definition.json
@@ -0,0 +1,232 @@
+{
+ "id": "73fe22ca-76bd-4aba-bdea-6dfef8ee03a2",
+ "version": "2026-07-06T15:43:41.279Z",
+ "startDate": "2026-07-07",
+ "label": "Points disqualification",
+ "shortCode": "DDP",
+ "level": "O",
+ "rank": 94000,
+ "wordGroups": [
+ {
+ "wordGroup": [
+ "DDP"
+ ]
+ },
+ {
+ "wordGroup": [
+ "disqualification",
+ "points"
+ ]
+ }
+ ],
+ "prompts": [
+ {
+ "id": "2bf54447-328c-4c1b-a123-341adbd52172",
+ "label": "Disqualification period",
+ "welshLabel": "Cyfnod gwahardd",
+ "resultPromptRule": "mandatory",
+ "type": "INTM",
+ "sequence": 100,
+ "duration": "Year(s)",
+ "financial": "N",
+ "reference": "disqualificationPeriod",
+ "userGroups": [],
+ "min": "1",
+ "max": "999",
+ "durationSequence": 1,
+ "welshDuration": "Flynedd",
+ "courtExtract": "Y",
+ "jurisdiction": "B",
+ "hidden": false,
+ "nameEmail": false,
+ "associatedWithReferenceData": false
+ },
+ {
+ "id": "2bf54447-328c-4c1b-a123-341adbd52172",
+ "label": "Disqualification period",
+ "welshLabel": "Cyfnod gwahardd",
+ "resultPromptRule": "mandatory",
+ "type": "INTM",
+ "sequence": 100,
+ "duration": "Month(s)",
+ "financial": "N",
+ "reference": "disqualificationPeriod",
+ "userGroups": [],
+ "min": "1",
+ "max": "999",
+ "durationSequence": 1,
+ "welshDuration": "Mis",
+ "courtExtract": "Y",
+ "jurisdiction": "B",
+ "hidden": false,
+ "nameEmail": false,
+ "associatedWithReferenceData": false
+ },
+ {
+ "id": "2bf54447-328c-4c1b-a123-341adbd52172",
+ "label": "Disqualification period",
+ "welshLabel": "Cyfnod gwahardd",
+ "resultPromptRule": "mandatory",
+ "type": "INTM",
+ "sequence": 100,
+ "duration": "Day(s)",
+ "financial": "N",
+ "reference": "disqualificationPeriod",
+ "userGroups": [],
+ "min": "1",
+ "max": "999",
+ "durationSequence": 1,
+ "welshDuration": "Niwrnod",
+ "courtExtract": "Y",
+ "jurisdiction": "B",
+ "hidden": false,
+ "nameEmail": false,
+ "associatedWithReferenceData": false
+ },
+ {
+ "id": "2bf54447-328c-4c1b-a123-341adbd52172",
+ "label": "Disqualification period",
+ "welshLabel": "Cyfnod gwahardd",
+ "resultPromptRule": "mandatory",
+ "type": "INTM",
+ "sequence": 100,
+ "financial": "N",
+ "reference": "disqualificationPeriod",
+ "wordGroup": [
+ "months",
+ "Years"
+ ],
+ "userGroups": [],
+ "min": "1",
+ "max": "999",
+ "durationSequence": 1,
+ "courtExtract": "Y",
+ "jurisdiction": "B",
+ "hidden": false,
+ "nameEmail": false,
+ "associatedWithReferenceData": false
+ },
+ {
+ "id": "0143f821-7cab-4e53-a00b-2fd1c3dc8204",
+ "label": "Extension period section 35B (effect of custodial sentence noted)",
+ "welshLabel": "Cyfnod estyniad adran 35B (effaith dedfryd o garchar wedi’i nodi)",
+ "resultPromptRule": "optional",
+ "type": "TXT",
+ "sequence": 400,
+ "financial": "N",
+ "reference": "extensionPeriodSection35BEffectOfCustodialSentenceNoted",
+ "userGroups": [],
+ "min": "1",
+ "max": "100",
+ "courtExtract": "Y",
+ "jurisdiction": "B",
+ "hidden": false,
+ "nameEmail": false,
+ "associatedWithReferenceData": false
+ },
+ {
+ "id": "462d5ba8-7a1c-44a0-a732-6c75601bd6af",
+ "label": "Notional penalty points",
+ "welshLabel": "Pwyntiau cosb tybiedig",
+ "resultPromptRule": "mandatory",
+ "type": "INT",
+ "sequence": 500,
+ "financial": "N",
+ "reference": "notionalPenaltyPoints",
+ "userGroups": [],
+ "min": "1",
+ "max": "999",
+ "courtExtract": "Y",
+ "jurisdiction": "B",
+ "hidden": false,
+ "nameEmail": false,
+ "associatedWithReferenceData": false
+ },
+ {
+ "id": "a593ae4a-9d69-45b9-9585-c11aeed28404",
+ "label": "Defendant driving licence number",
+ "welshLabel": "Rhif trwydded yrru'r Diffynnydd",
+ "resultPromptRule": "optional",
+ "type": "TXT",
+ "sequence": 600,
+ "financial": "N",
+ "reference": "defendantDrivingLicenceNumber",
+ "userGroups": [
+ "CPS",
+ "Defence",
+ "Judiciary",
+ "NCES",
+ "Prosecuting Authorities",
+ "Probation",
+ "YOTs"
+ ],
+ "min": "1",
+ "max": "16",
+ "courtExtract": "N",
+ "jurisdiction": "B",
+ "hidden": false,
+ "nameEmail": false,
+ "cacheable": 2,
+ "cacheDataPath": "personDefendant.driverNumber",
+ "associatedWithReferenceData": false
+ },
+ {
+ "id": "81638260-3cd1-41e7-9725-c8e91e23bda1",
+ "label": "Extension period section 35A (immediate custodial sentence)",
+ "welshLabel": "Cyfnod estyniad adran 35A (dedfryd o garchar ar unwaith)",
+ "resultPromptRule": "optional",
+ "type": "TXT",
+ "sequence": 300,
+ "financial": "N",
+ "reference": "extensionPeriodSection35AImmediateCustodialSentence",
+ "userGroups": [],
+ "min": "1",
+ "max": "100",
+ "courtExtract": "Y",
+ "jurisdiction": "B",
+ "hidden": false,
+ "nameEmail": false,
+ "associatedWithReferenceData": false
+ }
+ ],
+ "financial": "N",
+ "unscheduled": false,
+ "adjournment": "N",
+ "category": "A",
+ "urgent": true,
+ "convicted": "Y",
+ "d20": true,
+ "cjsCode": "3072",
+ "postHearingCustodyStatus": "A",
+ "terminatesOffenceProceedings": false,
+ "lifeDuration": false,
+ "rollUpPrompts": true,
+ "publishedForNows": false,
+ "publishedAsAPrompt": false,
+ "excludedFromResults": false,
+ "alwaysPublished": false,
+ "jurisdiction": "B",
+ "libraCode": "DDP",
+ "resultWording": "Disqualified for holding or obtaining a driving licence. Disqualification obligatory due to repeat offending. Driving record endorsed. Section 35(1) Road Traffic Offenders Act 1988",
+ "welshResultWording": "Gwahardd rhag dal neu gael trwydded yrru. Gwaharddiad yn orfodol oherwydd aildroseddu. Cofnod gyrru wedi’i ardystio. Adran 35 (1) Deddf Troseddwyr Traffig Ffyrdd 1988",
+ "resultDefinitionRules": [
+ {
+ "childResultDefinitionId": "429fb553-4fb3-45eb-a700-7e5e69d1ca29",
+ "ruleType": "mandatory"
+ },
+ {
+ "childResultDefinitionId": "ea1ee5a4-be13-48dc-8411-78f22e01c236",
+ "ruleType": "optional"
+ }
+ ],
+ "canBeSubjectOfBreach": false,
+ "canBeSubjectOfVariation": true,
+ "pointsDisqualificationCode": "TT99",
+ "resultTextTemplate": "Disqualified for holding or obtaining a driving licence for {disqualificationPeriod}.[ %INTD%] Disqualification obligatory due to repeat offending. Driving record endorsed.[ ({notionalPenaltyPoints} penalty points determined).] Section 35(1) Road Traffic Offenders Act 1988.[ This includes an extension period of {extensionPeriodSection35AImmediateCustodialSentence} in accordance with section 35A (immediate custodial sentence).][ This includes an extension period of {extensionPeriodSection35BEffectOfCustodialSentenceNoted} in accordance with section 35B (effect of custodial sentence noted).]",
+ "sentToCC": false,
+ "committedToCC": false,
+ "isDeemedServed": false,
+ "unpaidWorkExtensionCommunityOrdYro": false,
+ "isAvailableForCourtExtract": true,
+ "isBooleanResult": false
+}
\ No newline at end of file