diff --git a/progression-command/progression-command-api/src/raml/json/schema/progression.initiate-court-proceedings-for-application.json b/progression-command/progression-command-api/src/raml/json/schema/progression.initiate-court-proceedings-for-application.json index 5814e83714..41c75a9391 100644 --- a/progression-command/progression-command-api/src/raml/json/schema/progression.initiate-court-proceedings-for-application.json +++ b/progression-command/progression-command-api/src/raml/json/schema/progression.initiate-court-proceedings-for-application.json @@ -19,6 +19,9 @@ "summonsApprovalRequired": { "description": "Indicates that the summons approval process is required to be initiated", "type": "boolean" + }, + "applicationSource": { + "$ref": "http://moj.gov.uk/cpp/progression/enum-def.json#/definitions/applicationSource" } }, "required": [ diff --git a/progression-command/progression-command-handler/src/main/java/uk/gov/moj/cpp/progression/handler/CourtApplicationHandler.java b/progression-command/progression-command-handler/src/main/java/uk/gov/moj/cpp/progression/handler/CourtApplicationHandler.java index 5268987362..7dea15aef7 100644 --- a/progression-command/progression-command-handler/src/main/java/uk/gov/moj/cpp/progression/handler/CourtApplicationHandler.java +++ b/progression-command/progression-command-handler/src/main/java/uk/gov/moj/cpp/progression/handler/CourtApplicationHandler.java @@ -15,6 +15,10 @@ import static uk.gov.justice.core.courts.ProsecutingAuthority.prosecutingAuthority; import static uk.gov.justice.services.core.annotation.Component.COMMAND_HANDLER; import static uk.gov.justice.services.messaging.JsonEnvelope.envelopeFrom; +import static uk.gov.moj.cpp.progression.domain.constant.CaseStatusEnum.CLOSED; +import static uk.gov.moj.cpp.progression.domain.constant.CaseStatusEnum.INACTIVE; +import static uk.gov.moj.cpp.progression.enums.ApplicationSource.MH; +import static uk.gov.moj.cpp.progression.enums.ApplicationSource.UNKNOWN; import uk.gov.justice.core.courts.AddBreachApplication; import uk.gov.justice.core.courts.AddCourtApplicationToCase; @@ -63,6 +67,7 @@ import uk.gov.justice.services.messaging.Metadata; import uk.gov.moj.cpp.progression.aggregate.ApplicationAggregate; import uk.gov.moj.cpp.progression.aggregate.HearingAggregate; +import uk.gov.moj.cpp.progression.enums.ApplicationSource; import uk.gov.moj.cpp.progression.service.ApplicationDetailsEnrichmentService; import uk.gov.moj.cpp.progression.service.ProsecutionCaseQueryService; import uk.gov.moj.cpp.progression.service.RefDataService; @@ -349,7 +354,7 @@ private InitiateCourtApplicationProceedings rebuildInitiateCourtApplicationProce final boolean applicationReferredToNewHearing, final Envelope envelope) { final boolean summonsApprovalRequired = isSummonsApprovalRequired(initiateCourtProceedingsForApplication.getBoxHearing(), initiateCourtProceedingsForApplication.getCourtApplication()); final InitiateCourtApplicationProceedings.Builder initiateCourtProceedingsForApplicationBuilder = InitiateCourtApplicationProceedings.initiateCourtApplicationProceedings() - .withCourtApplication(rebuildCourtApplication(initiateCourtProceedingsForApplication.getCourtApplication(), envelope)) + .withCourtApplication(rebuildCourtApplication(initiateCourtProceedingsForApplication.getCourtApplication(), initiateCourtProceedingsForApplication.getApplicationSource(), envelope)) .withSummonsApprovalRequired(summonsApprovalRequired) .withIsAmended(initiateCourtProceedingsForApplication.getIsAmended()) .withOldApplicationId(initiateCourtProceedingsForApplication.getOldApplicationId()) @@ -384,13 +389,13 @@ private EditCourtApplicationProceedings rebuildEditCourtApplicationProceedings(f final boolean summonsApprovalRequired = isSummonsApprovalRequired(editCourtApplicationProceedings.getBoxHearing(), editCourtApplicationProceedings.getCourtApplication()); final EditCourtApplicationProceedings.Builder editCourtProceedingsForApplicationBuilder = EditCourtApplicationProceedings.editCourtApplicationProceedings() .withValuesFrom(editCourtApplicationProceedings) - .withCourtApplication(rebuildCourtApplication(editCourtApplicationProceedings.getCourtApplication(), envelope)) + .withCourtApplication(rebuildCourtApplication(editCourtApplicationProceedings.getCourtApplication(), UNKNOWN, envelope)) .withSummonsApprovalRequired(summonsApprovalRequired); return editCourtProceedingsForApplicationBuilder.build(); } - private CourtApplication rebuildCourtApplication(final CourtApplication courtApplication, final Envelope envelope) { + private CourtApplication rebuildCourtApplication(final CourtApplication courtApplication, final ApplicationSource applicationSource, final Envelope envelope) { final LaaReference laaReference = getLaaApplnReference(courtApplication, envelope); @@ -404,7 +409,7 @@ private CourtApplication rebuildCourtApplication(final CourtApplication courtApp final JsonEnvelope jsonEnvelope = envelopeFrom(envelope.metadata(), JsonValue.NULL); updateClonedCourtApplicationParties(courtApplicationBuilder, courtApplication, jsonEnvelope); - updateClonedOffenceApplicationCases(courtApplicationBuilder, courtApplication); + updateClonedOffenceApplicationCases(courtApplicationBuilder, courtApplication, applicationSource); updateClonedOffenceCourtOrder(courtApplicationBuilder, courtApplication); @@ -446,7 +451,7 @@ private boolean hasActivationCode(CourtApplicationType type) { return StringUtils.isNotEmpty(type.getResentencingActivationCode()); } - private void updateClonedOffenceApplicationCases(final CourtApplication.Builder courtApplicationBuilder, final CourtApplication courtApplication) { + private void updateClonedOffenceApplicationCases(final CourtApplication.Builder courtApplicationBuilder, final CourtApplication courtApplication, final ApplicationSource applicationSource) { if (isNull(courtApplication.getCourtApplicationCases())) { return; } @@ -461,17 +466,31 @@ private void updateClonedOffenceApplicationCases(final CourtApplication.Builder final String resentencingActivationCode = courtApplication.getType().getResentencingActivationCode(); final List courtApplicationCases = courtApplication.getCourtApplicationCases().stream() - .map(courtApplicationCase -> courtApplicationCase() - .withValuesFrom(courtApplicationCase) - .withOffences(ofNullable(courtApplicationCase.getOffences()).map(Collection::stream).orElseGet(Stream::empty) - .map(courtApplicationOffence -> updateOffence(courtApplication.getType(), courtApplicationOffence, wordingPattern, resentencingActivationCode)) - .collect(collectingAndThen(toList(), getListOrNull()))) - .build()) - .collect(toList()); + .map(courtApplicationCase -> { + if (ignoreCaseOffences(courtApplicationCase.getCaseStatus(), applicationSource)) { + return courtApplicationCase() + .withValuesFrom(courtApplicationCase) + .withOffences(null) + .build(); + } else { + return courtApplicationCase() + .withValuesFrom(courtApplicationCase) + .withOffences(ofNullable(courtApplicationCase.getOffences()).stream().flatMap(Collection::stream) + .map(courtApplicationOffence -> updateOffence(courtApplication.getType(), courtApplicationOffence, wordingPattern, resentencingActivationCode)) + .collect(collectingAndThen(toList(), getListOrNull()))) + .build(); + } + }).collect(toList()); courtApplicationBuilder.withCourtApplicationCases(courtApplicationCases); } + private boolean ignoreCaseOffences(final String caseStatus, final ApplicationSource applicationSource) { + final boolean activeCase = nonNull(caseStatus) && !(INACTIVE.name().equalsIgnoreCase(caseStatus) || CLOSED.name().equalsIgnoreCase(caseStatus)); + + return activeCase && MH == applicationSource; + } + private UnaryOperator> getListOrNull() { return list -> list.isEmpty() ? null : list; } diff --git a/progression-command/progression-command-handler/src/raml/json/schema/progression.command.initiate-court-proceedings-for-application.json b/progression-command/progression-command-handler/src/raml/json/schema/progression.command.initiate-court-proceedings-for-application.json index b5f025db7a..72e73a1f07 100644 --- a/progression-command/progression-command-handler/src/raml/json/schema/progression.command.initiate-court-proceedings-for-application.json +++ b/progression-command/progression-command-handler/src/raml/json/schema/progression.command.initiate-court-proceedings-for-application.json @@ -20,6 +20,9 @@ "description": "Indicates that the summons approval process is required to be initiated", "type": "boolean" }, + "applicationSource": { + "$ref": "http://moj.gov.uk/cpp/progression/enum-def.json#/definitions/applicationSource" + }, "isAmended": { "type": "boolean" }, diff --git a/progression-command/progression-command-handler/src/test/java/uk/gov/moj/cpp/progression/handler/CourtApplicationHandlerMHSourceTest.java b/progression-command/progression-command-handler/src/test/java/uk/gov/moj/cpp/progression/handler/CourtApplicationHandlerMHSourceTest.java new file mode 100644 index 0000000000..65771c3641 --- /dev/null +++ b/progression-command/progression-command-handler/src/test/java/uk/gov/moj/cpp/progression/handler/CourtApplicationHandlerMHSourceTest.java @@ -0,0 +1,692 @@ +package uk.gov.moj.cpp.progression.handler; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath; +import static com.jayway.jsonpath.matchers.JsonPathMatchers.withJsonPath; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static java.util.UUID.randomUUID; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static uk.gov.justice.core.courts.CourtApplication.courtApplication; +import static uk.gov.justice.core.courts.CourtApplicationCase.courtApplicationCase; +import static uk.gov.justice.core.courts.CourtApplicationType.courtApplicationType; +import static uk.gov.justice.core.courts.InitiateCourtApplicationProceedings.initiateCourtApplicationProceedings; +import static uk.gov.justice.core.courts.Offence.offence; +import static uk.gov.justice.core.courts.ProsecutionCaseIdentifier.prosecutionCaseIdentifier; +import static uk.gov.justice.core.courts.SummonsTemplateType.NOT_APPLICABLE; +import static uk.gov.justice.services.messaging.Envelope.envelopeFrom; +import static uk.gov.justice.services.test.utils.core.helper.EventStreamMockHelper.verifyAppendAndGetArgumentFrom; +import static uk.gov.justice.services.test.utils.core.matchers.JsonEnvelopeMatcher.jsonEnvelope; +import static uk.gov.justice.services.test.utils.core.matchers.JsonEnvelopeMetadataMatcher.metadata; +import static uk.gov.justice.services.test.utils.core.matchers.JsonEnvelopePayloadMatcher.payload; +import static uk.gov.justice.services.test.utils.core.matchers.JsonEnvelopeStreamMatcher.streamContaining; +import static uk.gov.justice.services.test.utils.core.random.RandomGenerator.STRING; +import static uk.gov.moj.cpp.progression.enums.ApplicationSource.CAAG; +import static uk.gov.moj.cpp.progression.enums.ApplicationSource.HOME; +import static uk.gov.moj.cpp.progression.enums.ApplicationSource.MH; +import static uk.gov.moj.cpp.progression.enums.ApplicationSource.UNKNOWN; + +import uk.gov.justice.core.courts.Address; +import uk.gov.justice.core.courts.ApplicationReferredToBoxwork; +import uk.gov.justice.core.courts.ApplicationReferredToCourtHearing; +import uk.gov.justice.core.courts.ApplicationReferredToExistingHearing; +import uk.gov.justice.core.courts.BoxHearingRequest; +import uk.gov.justice.core.courts.CourtApplicationAddedToCase; +import uk.gov.justice.core.courts.CourtApplicationCreated; +import uk.gov.justice.core.courts.CourtApplicationParty; +import uk.gov.justice.core.courts.CourtApplicationProceedingsEdited; +import uk.gov.justice.core.courts.CourtApplicationProceedingsInitiateIgnored; +import uk.gov.justice.core.courts.CourtApplicationProceedingsInitiated; +import uk.gov.justice.core.courts.CourtApplicationSummonsApproved; +import uk.gov.justice.core.courts.CourtApplicationSummonsRejected; +import uk.gov.justice.core.courts.CourtApplicationUpdated; +import uk.gov.justice.core.courts.CourtHearingRequest; +import uk.gov.justice.core.courts.HearingResultedApplicationUpdated; +import uk.gov.justice.core.courts.HearingUpdatedWithCourtApplication; +import uk.gov.justice.core.courts.InitiateCourtApplicationProceedings; +import uk.gov.justice.core.courts.InitiateCourtHearingAfterSummonsApproved; +import uk.gov.justice.core.courts.MasterDefendant; +import uk.gov.justice.core.courts.Offence; +import uk.gov.justice.core.courts.Person; +import uk.gov.justice.core.courts.PersonDefendant; +import uk.gov.justice.core.courts.ProsecutingAuthority; +import uk.gov.justice.core.courts.SendNotificationForApplicationIgnored; +import uk.gov.justice.core.courts.SendNotificationForApplicationInitiated; +import uk.gov.justice.core.courts.SendNotificationForAutoApplicationInitiated; +import uk.gov.justice.progression.courts.HearingPopulatedToProbationCaseworker; +import uk.gov.justice.progression.courts.VejHearingPopulatedToProbationCaseworker; +import uk.gov.justice.progression.event.ApplicationHearingDefendantUpdated; +import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; +import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter; +import uk.gov.justice.services.common.converter.jackson.ObjectMapperProducer; +import uk.gov.justice.services.core.aggregate.AggregateService; +import uk.gov.justice.services.core.enveloper.Enveloper; +import uk.gov.justice.services.eventsourcing.source.core.EventSource; +import uk.gov.justice.services.eventsourcing.source.core.EventStream; +import uk.gov.justice.services.messaging.Envelope; +import uk.gov.justice.services.messaging.JsonEnvelope; +import uk.gov.justice.services.messaging.Metadata; +import uk.gov.justice.services.test.utils.core.enveloper.EnveloperFactory; +import uk.gov.justice.services.test.utils.framework.api.JsonObjectConvertersFactory; +import uk.gov.moj.cpp.progression.aggregate.ApplicationAggregate; +import uk.gov.moj.cpp.progression.service.ApplicationDetailsEnrichmentService; +import uk.gov.moj.cpp.progression.service.ProsecutionCaseQueryService; +import uk.gov.moj.cpp.progression.service.RefDataService; +import uk.gov.moj.cpp.progression.service.service.ProgressionService; + +import java.util.UUID; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +/** + * Tests for the MH (Manage Hearing) applicationSource fix — AC1 and AC2. + *

+ * AC1: MH source + ACTIVE case → offences must be nulled (no CCT-2487 offence selection). + * AC2: MH source + INACTIVE/CLOSED case → offences must be preserved (as-is). + */ +@ExtendWith(MockitoExtension.class) +class CourtApplicationHandlerMHSourceTest { + + @Spy + private final Enveloper enveloper = EnveloperFactory.createEnveloperWithEvents( + CourtApplicationCreated.class, + CourtApplicationAddedToCase.class, + CourtApplicationProceedingsInitiated.class, + ApplicationReferredToBoxwork.class, + ApplicationReferredToCourtHearing.class, + ApplicationReferredToExistingHearing.class, + CourtApplicationProceedingsInitiateIgnored.class, + CourtApplicationProceedingsEdited.class, + CourtApplicationSummonsRejected.class, + CourtApplicationSummonsApproved.class, + InitiateCourtHearingAfterSummonsApproved.class, + HearingResultedApplicationUpdated.class, + HearingUpdatedWithCourtApplication.class, + HearingPopulatedToProbationCaseworker.class, + VejHearingPopulatedToProbationCaseworker.class, + SendNotificationForApplicationInitiated.class, + SendNotificationForApplicationIgnored.class, + SendNotificationForAutoApplicationInitiated.class, + CourtApplicationUpdated.class, + ApplicationHearingDefendantUpdated.class + ); + + @Mock + private EventSource eventSource; + + @Mock + private EventStream eventStream; + + @Mock + private AggregateService aggregateService; + + @Mock + private RefDataService referenceDataService; + + @Mock + private ProgressionService progressionService; + + @Mock + private ProsecutionCaseQueryService prosecutionCaseQueryService; + + @Mock + private ApplicationDetailsEnrichmentService applicationDetailsEnrichmentService; + + @Spy + @InjectMocks + private final ObjectToJsonObjectConverter objectToJsonObjectConverter = + new ObjectToJsonObjectConverter(new ObjectMapperProducer().objectMapper()); + + @Spy + private JsonObjectToObjectConverter jsonObjectToObjectConverter = + new JsonObjectConvertersFactory().jsonObjectToObjectConverter(); + + @Mock + private ApplicationAggregate applicationAggregateMock; + + @InjectMocks + private CourtApplicationHandler courtApplicationHandler; + + private UUID masterDefendantId; + + @BeforeEach + void setUp() { + masterDefendantId = randomUUID(); + } + + // ----------------------------------------------------------------------- + // AC1: Case hearing — MH source + ACTIVE case → offences must be nulled + // ----------------------------------------------------------------------- + + @Test + void shouldNullOffencesForActiveCaseWhenApplicationSourceIsMH() throws Exception { + final Offence activeOffence = offence() + .withId(randomUUID()) + .withOffenceCode("OF001") + .withWording("Some wording") + .build(); + + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + .withApplicationSource(MH) + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(singletonList(courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("ACTIVE") + .withOffences(singletonList(activeOffence)) + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson(allOf( + withJsonPath("$.courtApplication.courtApplicationCases[0].caseStatus", is("ACTIVE")), + hasNoJsonPath("$.courtApplication.courtApplicationCases[0].offences") + )) + ))); + } + + @Test + void shouldNullOffencesForMultipleCasesWhenAllAreActiveAndSourceIsMH() throws Exception { + final Offence offence = offence().withId(randomUUID()).withOffenceCode("OF002").build(); + + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + .withApplicationSource(MH) + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(asList( + courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("ACTIVE") + .withOffences(singletonList(offence)) + .build(), + courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("ACTIVE") + .withOffences(singletonList(offence)) + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson(allOf( + hasNoJsonPath("$.courtApplication.courtApplicationCases[0].offences"), + hasNoJsonPath("$.courtApplication.courtApplicationCases[1].offences") + )) + ))); + } + + // ----------------------------------------------------------------------- + // AC2: Application hearing — MH source + INACTIVE case → offences kept + // ----------------------------------------------------------------------- + + @Test + void shouldPreserveOffencesForInactiveCaseWhenApplicationSourceIsMH() throws Exception { + final UUID offenceId = randomUUID(); + final Offence inactiveOffence = offence() + .withId(offenceId) + .withOffenceCode("OF003") + .withWording("Breach wording") + .build(); + + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + .withApplicationSource(MH) + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(singletonList(courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("INACTIVE") + .withOffences(singletonList(inactiveOffence)) + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson(allOf( + withJsonPath("$.courtApplication.courtApplicationCases[0].caseStatus", is("INACTIVE")), + withJsonPath("$.courtApplication.courtApplicationCases[0].offences[0].id", is(offenceId.toString())) + )) + ))); + } + + @Test + void shouldPreserveOffencesForClosedCaseWhenApplicationSourceIsMH() throws Exception { + final UUID offenceId = randomUUID(); + final Offence closedOffence = offence().withId(offenceId).withOffenceCode("OF004").build(); + + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + .withApplicationSource(MH) + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(singletonList(courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("CLOSED") + .withOffences(singletonList(closedOffence)) + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson(allOf( + withJsonPath("$.courtApplication.courtApplicationCases[0].offences[0].id", is(offenceId.toString())) + )) + ))); + } + + // ----------------------------------------------------------------------- + // Mixed cases: one ACTIVE (offences nulled), one INACTIVE (offences kept) + // ----------------------------------------------------------------------- + + @Test + void shouldNullOffencesOnlyForActiveCasesInMixedScenarioWithMHSource() throws Exception { + final UUID activeOffenceId = randomUUID(); + final UUID inactiveOffenceId = randomUUID(); + + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + .withApplicationSource(MH) + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(asList( + courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("ACTIVE") + .withOffences(singletonList(offence().withId(activeOffenceId).withOffenceCode("OF_A").build())) + .build(), + courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("INACTIVE") + .withOffences(singletonList(offence().withId(inactiveOffenceId).withOffenceCode("OF_I").build())) + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson(allOf( + hasNoJsonPath("$.courtApplication.courtApplicationCases[0].offences"), + withJsonPath("$.courtApplication.courtApplicationCases[1].offences[0].id", is(inactiveOffenceId.toString())) + )) + ))); + } + + // ----------------------------------------------------------------------- + // Non-MH sources must NOT trigger the suppression (as-is behaviour) + // ----------------------------------------------------------------------- + + @Test + void shouldPreserveOffencesForActiveCaseWhenApplicationSourceIsHome() throws Exception { + final UUID offenceId = randomUUID(); + shouldPreserveOffencesForActiveCaseWithSource(HOME, offenceId); + } + + @Test + void shouldPreserveOffencesForActiveCaseWhenApplicationSourceIsCaag() throws Exception { + final UUID offenceId = randomUUID(); + shouldPreserveOffencesForActiveCaseWithSource(CAAG, offenceId); + } + + @Test + void shouldPreserveOffencesForActiveCaseWhenApplicationSourceIsUnknown() throws Exception { + final UUID offenceId = randomUUID(); + shouldPreserveOffencesForActiveCaseWithSource(UNKNOWN, offenceId); + } + + @Test + void shouldPreserveOffencesForActiveCaseWhenApplicationSourceIsNull() throws Exception { + final UUID offenceId = randomUUID(); + + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + // no .withApplicationSource() — field absent, getApplicationSource() returns null + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(singletonList(courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("ACTIVE") + .withOffences(singletonList(offence().withId(offenceId).withOffenceCode("OF_NULL").build())) + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson( + withJsonPath("$.courtApplication.courtApplicationCases[0].offences[0].id", is(offenceId.toString())) + ) + ))); + } + + // ----------------------------------------------------------------------- + // Edge case: null caseStatus with MH source — treat as non-active (keep offences) + // ----------------------------------------------------------------------- + + @Test + void shouldPreserveOffencesWhenCaseStatusIsNullAndSourceIsMH() throws Exception { + final UUID offenceId = randomUUID(); + + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + .withApplicationSource(MH) + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(singletonList(courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + // no .withCaseStatus() — null + .withOffences(singletonList(offence().withId(offenceId).withOffenceCode("OF_NULL_STATUS").build())) + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson( + withJsonPath("$.courtApplication.courtApplicationCases[0].offences[0].id", is(offenceId.toString())) + ) + ))); + } + + // ----------------------------------------------------------------------- + // MH source — case with no offences at all should not fail + // ----------------------------------------------------------------------- + + @Test + void shouldHandleActiveCaseWithNoOffencesWhenSourceIsMH() throws Exception { + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + .withApplicationSource(MH) + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(singletonList(courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("ACTIVE") + // deliberately no offences + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson( + hasNoJsonPath("$.courtApplication.courtApplicationCases[0].offences") + ) + ))); + } + + // ----------------------------------------------------------------------- + // Helpers + // ----------------------------------------------------------------------- + + private void shouldPreserveOffencesForActiveCaseWithSource( + final uk.gov.moj.cpp.progression.enums.ApplicationSource source, + final UUID offenceId) throws Exception { + + final InitiateCourtApplicationProceedings command = initiateCourtApplicationProceedings() + .withApplicationSource(source) + .withCourtApplication(courtApplication() + .withId(randomUUID()) + .withType(courtApplicationType() + .withProsecutorThirdPartyFlag(false) + .withSummonsTemplateType(NOT_APPLICABLE) + .build()) + .withApplicant(buildParty()) + .withSubject(buildParty()) + .withCourtApplicationCases(singletonList(courtApplicationCase() + .withIsSJP(false) + .withProsecutionCaseIdentifier(prosecutionCaseIdentifier().withCaseURN(STRING.next()).build()) + .withCaseStatus("ACTIVE") + .withOffences(singletonList(offence().withId(offenceId).withOffenceCode("OF_NON_MH").build())) + .build())) + .build()) + .withCourtHearing(CourtHearingRequest.courtHearingRequest().build()) + .build(); + + final Envelope envelope = buildEnvelope(command); + + final ApplicationAggregate aggregate = new ApplicationAggregate(); + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(aggregateService.get(eventStream, ApplicationAggregate.class)).thenReturn(aggregate); + aggregate.apply(new CourtApplicationProceedingsInitiated.Builder() + .withCourtHearing(new CourtHearingRequest.Builder().build()) + .withBoxHearing(new BoxHearingRequest.Builder().build()) + .build()); + + courtApplicationHandler.initiateCourtApplicationProceedings(envelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("progression.event.court-application-proceedings-initiated"), + payload().isJson( + withJsonPath("$.courtApplication.courtApplicationCases[0].offences[0].id", is(offenceId.toString())) + ) + ))); + } + + private Envelope buildEnvelope(final InitiateCourtApplicationProceedings command) { + final Metadata metadata = Envelope + .metadataBuilder() + .withName("progression.command.initiate-court-proceedings-for-application") + .withId(randomUUID()) + .build(); + return envelopeFrom(metadata, command); + } + + private CourtApplicationParty buildParty() { + return CourtApplicationParty.courtApplicationParty() + .withId(randomUUID()) + .withProsecutingAuthority(ProsecutingAuthority.prosecutingAuthority() + .withProsecutionAuthorityId(randomUUID()) + .withProsecutionAuthorityCode("TEST_CODE") + .build()) + .withMasterDefendant(MasterDefendant.masterDefendant() + .withMasterDefendantId(masterDefendantId) + .withPersonDefendant(PersonDefendant.personDefendant() + .withPersonDetails(Person.person() + .withAddress(Address.address() + .withAddress1("1 Test Street") + .withPostcode("TK1 1AA") + .build()) + .build()) + .build()) + .build()) + .build(); + } +} diff --git a/progression-domain/progression-datatypes-common/src/main/resources/json/schema/enum-def.json b/progression-domain/progression-datatypes-common/src/main/resources/json/schema/enum-def.json index 5a0695ee99..ed8113ec59 100644 --- a/progression-domain/progression-datatypes-common/src/main/resources/json/schema/enum-def.json +++ b/progression-domain/progression-datatypes-common/src/main/resources/json/schema/enum-def.json @@ -66,6 +66,16 @@ "CONFIRMED", "RESULTED" ] + }, + "applicationSource": { + "id": "http://moj.gov.uk/cpp/progression/enums/application-source.json", + "type": "string", + "enum": [ + "HOME", + "CAAG", + "MH", + "UNKNOWN" + ] } } } \ No newline at end of file diff --git a/progression-domain/progression-domain-message/src/raml/json/schema/progression.command.initiate-court-proceedings-for-application.json b/progression-domain/progression-domain-message/src/raml/json/schema/progression.command.initiate-court-proceedings-for-application.json index b5f025db7a..72e73a1f07 100644 --- a/progression-domain/progression-domain-message/src/raml/json/schema/progression.command.initiate-court-proceedings-for-application.json +++ b/progression-domain/progression-domain-message/src/raml/json/schema/progression.command.initiate-court-proceedings-for-application.json @@ -20,6 +20,9 @@ "description": "Indicates that the summons approval process is required to be initiated", "type": "boolean" }, + "applicationSource": { + "$ref": "http://moj.gov.uk/cpp/progression/enum-def.json#/definitions/applicationSource" + }, "isAmended": { "type": "boolean" }, diff --git a/progression-integration-test/src/test/java/uk/gov/moj/cpp/progression/InitiateCourtProceedingsMHSourceIT.java b/progression-integration-test/src/test/java/uk/gov/moj/cpp/progression/InitiateCourtProceedingsMHSourceIT.java new file mode 100644 index 0000000000..7790754d34 --- /dev/null +++ b/progression-integration-test/src/test/java/uk/gov/moj/cpp/progression/InitiateCourtProceedingsMHSourceIT.java @@ -0,0 +1,123 @@ +package uk.gov.moj.cpp.progression; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath; +import static com.jayway.jsonpath.matchers.JsonPathMatchers.withJsonPath; +import static java.util.UUID.randomUUID; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static uk.gov.justice.services.integrationtest.utils.jms.JmsMessageConsumerClientProvider.newPublicJmsMessageConsumerClientProvider; +import static uk.gov.moj.cpp.progression.applications.applicationHelper.ApplicationHelper.initiateCourtProceedingsForCourtApplication; +import static uk.gov.moj.cpp.progression.helper.PreAndPostConditionHelper.addProsecutionCaseToCrownCourt; +import static uk.gov.moj.cpp.progression.helper.PreAndPostConditionHelper.pollForApplication; +import static uk.gov.moj.cpp.progression.helper.PreAndPostConditionHelper.pollProsecutionCasesProgressionFor; +import static uk.gov.moj.cpp.progression.helper.QueueUtil.retrieveMessageBody; +import static uk.gov.moj.cpp.progression.util.ReferProsecutionCaseToCrownCourtHelper.getProsecutionCaseMatchers; + +import uk.gov.justice.services.integrationtest.utils.jms.JmsMessageConsumerClient; + +import java.util.Optional; + +import javax.json.JsonObject; + +import org.hamcrest.Matcher; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Integration tests for the CCT-2487 fix — applicationSource=MH suppresses offence selection. + * + * AC1: Case hearing (MH + ACTIVE case) → offences must NOT be stored on the court application. + * AC2: Application hearing (MH + INACTIVE case, e.g. breach) → offences MUST be preserved. + */ +@SuppressWarnings("squid:S1607") +public class InitiateCourtProceedingsMHSourceIT extends AbstractIT { + + private static final String COURT_APPLICATION_CREATED = "public.progression.court-application-created"; + + private static final String MH_ACTIVE_CASE_FIXTURE = + "applications/progression.initiate-court-proceedings-mh-source-active-case.json"; + + private static final String MH_INACTIVE_CASE_FIXTURE = + "applications/progression.initiate-court-proceedings-mh-source-inactive-case.json"; + + private final JmsMessageConsumerClient consumerForCourtApplicationCreated = + newPublicJmsMessageConsumerClientProvider() + .withEventNames(COURT_APPLICATION_CREATED) + .getMessageConsumerClient(); + + private String caseId; + private String offenceId; + + @BeforeEach + public void setUp() { + caseId = randomUUID().toString(); + offenceId = randomUUID().toString(); + } + + /** + * AC1: MH source + ACTIVE case hearing. + * + * The offences sent in the command must NOT be stored on the resulting court application. + * The CCT-2487 offence-selection logic must not run. + */ + @Test + public void shouldNotStoreOffencesWhenApplicationSourceIsMHAndCaseIsActive() throws Exception { + addProsecutionCaseToCrownCourt(caseId, randomUUID().toString()); + pollProsecutionCasesProgressionFor(caseId, getProsecutionCaseMatchers(caseId, randomUUID().toString())); + + final String applicationId = randomUUID().toString(); + + initiateCourtProceedingsForCourtApplication(applicationId, caseId, MH_ACTIVE_CASE_FIXTURE); + + verifyCourtApplicationCreatedEventPublished(applicationId); + + final Matcher[] matchers = { + withJsonPath("$.courtApplication.id", is(applicationId)), + withJsonPath("$.courtApplication.applicationStatus", notNullValue()), + // offences must be absent — suppressed because source=MH and caseStatus=ACTIVE + hasNoJsonPath("$.courtApplication.courtApplicationCases[0].offences") + }; + + pollForApplication(applicationId, matchers); + } + + /** + * AC2: MH source + INACTIVE case (application hearing, e.g. breach hearing). + * + * The offences sent in the command must be preserved on the resulting court application. + * As-is behaviour applies for inactive cases regardless of source. + */ + @Test + public void shouldPreserveOffencesWhenApplicationSourceIsMHAndCaseIsInactive() throws Exception { + addProsecutionCaseToCrownCourt(caseId, randomUUID().toString()); + pollProsecutionCasesProgressionFor(caseId, getProsecutionCaseMatchers(caseId, randomUUID().toString())); + + final String applicationId = randomUUID().toString(); + + initiateCourtProceedingsForCourtApplication(applicationId, caseId, MH_INACTIVE_CASE_FIXTURE); + + verifyCourtApplicationCreatedEventPublished(applicationId); + + // The offence id in the fixture is the literal placeholder OFFENCE_ID which is NOT + // replaced in this flow (it is a fixed test UUID in the fixture). We assert the + // offences array is present and non-empty instead. + final Matcher[] matchers = { + withJsonPath("$.courtApplication.id", is(applicationId)), + withJsonPath("$.courtApplication.courtApplicationCases[0].caseStatus", is("INACTIVE")), + withJsonPath("$.courtApplication.courtApplicationCases[0].offences[0]", notNullValue()), + withJsonPath("$.courtApplication.courtApplicationCases[0].offences[0].offenceCode", is("CA03012")) + }; + + pollForApplication(applicationId, matchers); + } + + private void verifyCourtApplicationCreatedEventPublished(final String applicationId) { + final Optional message = retrieveMessageBody(consumerForCourtApplicationCreated); + assertTrue(message.isPresent(), "Expected court-application-created event on JMS topic"); + final String idFromEvent = message.get().getJsonObject("courtApplication").getString("id"); + assertThat(idFromEvent, equalTo(applicationId)); + } +} diff --git a/progression-integration-test/src/test/resources/applications/progression.initiate-court-proceedings-mh-source-active-case.json b/progression-integration-test/src/test/resources/applications/progression.initiate-court-proceedings-mh-source-active-case.json new file mode 100644 index 0000000000..b94025293a --- /dev/null +++ b/progression-integration-test/src/test/resources/applications/progression.initiate-court-proceedings-mh-source-active-case.json @@ -0,0 +1,100 @@ +{ + "courtApplication": { + "id": "APPLICATION_ID", + "type": { + "id": "e857c8ea-cd95-47d1-842f-2d618e77a9b5", + "code": "AS14518", + "type": "Application for an order of reimbursement in relation to a closure order", + "legislation": "In accordance with section 16E of the Magistrates' Courts Act 1980.", + "categoryCode": "VP", + "linkType": "LINKED", + "jurisdiction": "MAGISTRATES", + "summonsTemplateType": "GENERIC_APPLICATION", + "breachType": "NOT_APPLICABLE", + "appealFlag": false, + "applicantAppellantFlag": false, + "pleaApplicableFlag": false, + "commrOfOathFlag": false, + "courtOfAppealFlag": false, + "courtExtractAvlFlag": false, + "prosecutorThirdPartyFlag": false, + "spiOutApplicableFlag": false, + "offenceActiveOrder": "NOT_APPLICABLE" + }, + "applicationReceivedDate": "2020-01-01", + "applicationReference": "AS145197659", + "applicant": { + "id": "9ce6e439-d5da-4202-a446-ef4ca7072540", + "summonsRequired": false, + "notificationRequired": true + }, + "subject": { + "id": "cd3b251d-20e8-44ad-b95e-2f81afde56a4", + "summonsRequired": false, + "notificationRequired": true + }, + "applicationStatus": "DRAFT", + "outOfTimeReasons": "MH source active case — offences must not be stored", + "courtApplicationCases": [ + { + "prosecutionCaseId": "CASE_ID", + "prosecutionCaseIdentifier": { + "prosecutionAuthorityId": "cf73207f-3ced-488a-82a0-3fba79c2ce85", + "prosecutionAuthorityCode": "TFL", + "caseURN": "TFL4359536" + }, + "isSJP": false, + "caseStatus": "ACTIVE", + "offences": [ + { + "id": "OFFENCE_ID", + "chargeDate": "2020-03-14", + "count": 1, + "isDisposed": false, + "offenceCode": "CA03012", + "offenceDefinitionId": "3ebe41c9-666d-4964-aa80-a005d89927a3", + "offenceLegislation": "Contrary to section 363(3)(b) and (4) of the Communications Act 2003.", + "offenceLegislationWelsh": "", + "offenceTitle": "Active offence that should be suppressed when source is MH", + "offenceTitleWelsh": "", + "orderIndex": 1, + "startDate": "2020-03-10", + "wording": "MH source active offence wording", + "wordingWelsh": "MH source active offence wording welsh" + } + ] + } + ] + }, + "courtHearing": { + "hearingType": { + "id": "8cdfd3da-8900-42ca-9835-9f29d1e03cd6", + "description": "Sentence" + }, + "jurisdictionType": "MAGISTRATES", + "listedStartDateTime": "2020-12-21T05:27:17.210Z", + "estimatedMinutes": 20, + "earliestStartDateTime": "2020-12-21T05:27:17.210Z", + "courtCentre": { + "address": { + "address1": "176A Lavender Hill", + "address2": "London", + "address3": "", + "address4": "", + "address5": "", + "postcode": "SW11 1JU" + }, + "code": "B01LY00", + "id": "f8254db1-1683-483e-afb3-b87fde5a0a26", + "lja": { + "ljaCode": "2577", + "ljaName": "South West London Magistrates' Court" + }, + "name": "Lavender Hill Magistrates' Court", + "roomId": "9e4932f7-97b2-3010-b942-ddd2624e4dd8", + "roomName": "Courtroom 01" + } + }, + "applicationSource": "MH", + "summonsApprovalRequired": false +} diff --git a/progression-integration-test/src/test/resources/applications/progression.initiate-court-proceedings-mh-source-inactive-case.json b/progression-integration-test/src/test/resources/applications/progression.initiate-court-proceedings-mh-source-inactive-case.json new file mode 100644 index 0000000000..9e6ead5ffd --- /dev/null +++ b/progression-integration-test/src/test/resources/applications/progression.initiate-court-proceedings-mh-source-inactive-case.json @@ -0,0 +1,100 @@ +{ + "courtApplication": { + "id": "APPLICATION_ID", + "type": { + "id": "e857c8ea-cd95-47d1-842f-2d618e77a9b5", + "code": "AS14518", + "type": "Application for an order of reimbursement in relation to a closure order", + "legislation": "In accordance with section 16E of the Magistrates' Courts Act 1980.", + "categoryCode": "VP", + "linkType": "LINKED", + "jurisdiction": "MAGISTRATES", + "summonsTemplateType": "GENERIC_APPLICATION", + "breachType": "GENERIC_BREACH", + "appealFlag": false, + "applicantAppellantFlag": false, + "pleaApplicableFlag": false, + "commrOfOathFlag": false, + "courtOfAppealFlag": false, + "courtExtractAvlFlag": false, + "prosecutorThirdPartyFlag": false, + "spiOutApplicableFlag": false, + "offenceActiveOrder": "NOT_APPLICABLE" + }, + "applicationReceivedDate": "2020-01-01", + "applicationReference": "AS145197659", + "applicant": { + "id": "9ce6e439-d5da-4202-a446-ef4ca7072540", + "summonsRequired": false, + "notificationRequired": true + }, + "subject": { + "id": "cd3b251d-20e8-44ad-b95e-2f81afde56a4", + "summonsRequired": false, + "notificationRequired": true + }, + "applicationStatus": "DRAFT", + "outOfTimeReasons": "MH source inactive breach case — offences must be preserved", + "courtApplicationCases": [ + { + "prosecutionCaseId": "CASE_ID", + "prosecutionCaseIdentifier": { + "prosecutionAuthorityId": "cf73207f-3ced-488a-82a0-3fba79c2ce85", + "prosecutionAuthorityCode": "TFL", + "caseURN": "TFL4359536" + }, + "isSJP": false, + "caseStatus": "INACTIVE", + "offences": [ + { + "id": "OFFENCE_ID", + "chargeDate": "2020-03-14", + "count": 1, + "isDisposed": true, + "offenceCode": "CA03012", + "offenceDefinitionId": "3ebe41c9-666d-4964-aa80-a005d89927a3", + "offenceLegislation": "Contrary to section 363(3)(b) and (4) of the Communications Act 2003.", + "offenceLegislationWelsh": "", + "offenceTitle": "Inactive offence for breach hearing that should be preserved", + "offenceTitleWelsh": "", + "orderIndex": 1, + "startDate": "2020-03-10", + "wording": "MH source inactive offence wording", + "wordingWelsh": "MH source inactive offence wording welsh" + } + ] + } + ] + }, + "courtHearing": { + "hearingType": { + "id": "8cdfd3da-8900-42ca-9835-9f29d1e03cd6", + "description": "Breach Hearing" + }, + "jurisdictionType": "MAGISTRATES", + "listedStartDateTime": "2020-12-21T05:27:17.210Z", + "estimatedMinutes": 20, + "earliestStartDateTime": "2020-12-21T05:27:17.210Z", + "courtCentre": { + "address": { + "address1": "176A Lavender Hill", + "address2": "London", + "address3": "", + "address4": "", + "address5": "", + "postcode": "SW11 1JU" + }, + "code": "B01LY00", + "id": "f8254db1-1683-483e-afb3-b87fde5a0a26", + "lja": { + "ljaCode": "2577", + "ljaName": "South West London Magistrates' Court" + }, + "name": "Lavender Hill Magistrates' Court", + "roomId": "9e4932f7-97b2-3010-b942-ddd2624e4dd8", + "roomName": "Courtroom 01" + } + }, + "applicationSource": "MH", + "summonsApprovalRequired": false +}