From 6d15c22d980397c726a028c50215e1943c346141 Mon Sep 17 00:00:00 2001 From: aschalew Date: Wed, 18 Mar 2026 11:22:43 +0000 Subject: [PATCH 01/18] Added logic for creation, mangemnet and display of case links --- build.gradle | 5 +- .../pcs-api/values.ccd.preview.template.yaml | 2 +- .../uk/gov/hmcts/reform/pcs/ccd/CaseType.java | 5 + .../gov/hmcts/reform/pcs/ccd/PCSCaseView.java | 41 ++++++- .../ccd/accesscontrol/CaseLinkingAccess.java | 21 ++++ .../hmcts/reform/pcs/ccd/domain/PCSCase.java | 22 +++- .../reform/pcs/ccd/entity/CaseLinkEntity.java | 66 +++++++++++ .../pcs/ccd/entity/CaseLinkReasonEntity.java | 44 +++++++ .../reform/pcs/ccd/entity/PcsCaseEntity.java | 60 ++++++++++ .../reform/pcs/ccd/event/CreateCaseLink.java | 51 +++++++++ .../hmcts/reform/pcs/ccd/event/EventId.java | 4 +- .../pcs/ccd/event/MaintainLinkCase.java | 53 +++++++++ .../pcs/ccd/service/PcsCaseMergeService.java | 67 +++++++++++ .../pcs/ccd/service/PcsCaseService.java | 24 ++++ ...reate_case_link_and_link_reason_tables.sql | 57 ++++++++++ .../testdata/V002.1__postcode_test_data.sql | 2 +- .../hmcts/reform/pcs/ccd/PCSCaseViewTest.java | 99 +++++++++++++++- .../accesscontrol/CaseLinkingAccessTest.java | 27 +++++ .../pcs/ccd/entity/CaseLinkEntityTest.java | 37 ++++++ .../ccd/entity/CaseLinkReasonEntityTest.java | 31 +++++ .../pcs/ccd/event/CreateCaseLinkTest.java | 57 ++++++++++ .../pcs/ccd/event/MaintainLinkCaseTest.java | 55 +++++++++ .../ccd/service/DraftCaseJsonMergerTest.java | 1 + .../ccd/service/PcsCaseMergeServiceTest.java | 107 ++++++++++++++++++ .../pcs/ccd/service/PcsCaseServiceTest.java | 21 +++- 25 files changed, 950 insertions(+), 9 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccess.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntity.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCase.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeService.java create mode 100644 src/main/resources/db/migration/V063__create_case_link_and_link_reason_tables.sql create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntityTest.java create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLinkTest.java create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCaseTest.java create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java diff --git a/build.gradle b/build.gradle index 60343fff68..0d363e233c 100644 --- a/build.gradle +++ b/build.gradle @@ -320,8 +320,11 @@ sonarqube { "**/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/**/*.java," + "**/uk/gov/hmcts/reform/pcs/ccd/config/HighLevelDataSetupApp.java," + "**/uk/gov/hmcts/reform/pcs/ccd/domain/**/*.java," + + "**/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java," + "**/uk/gov/hmcts/reform/pcs/ccd/page/**/*.java," + "**/uk/gov/hmcts/reform/pcs/ccd/entity/**/*.java," + + "**/uk/gov/hmcts/reform/pcs/ccd/event/**/*.java," + + "**/uk/gov/hmcts/reform/pcs/ccd/service/**/*.java," + "**/entities/**," + "**/model/**," + "**/uk/gov/hmcts/reform/pcs/testingsupport/**/*.java" property "sonar.cpd.exclusions", "**/uk/gov/hmcts/reform/pcs/ccd/domain/enforcetheorder/warrant/StatementOfTruthDetails.java" } @@ -512,7 +515,7 @@ tasks.named('integration') { tasks.withType(CftlibExec).configureEach { group = 'ccd tasks' - authMode = AuthMode.Local + authMode = AuthMode.AAT //Attach mockito agent for inline mocking to remove warning in logs classpath += configurations.mockitoAgentConfig diff --git a/charts/pcs-api/values.ccd.preview.template.yaml b/charts/pcs-api/values.ccd.preview.template.yaml index 46eb62b2fb..bddb8ee76b 100644 --- a/charts/pcs-api/values.ccd.preview.template.yaml +++ b/charts/pcs-api/values.ccd.preview.template.yaml @@ -193,7 +193,7 @@ xui-webapp: SERVICES_CCD_DATA_STORE_API: http://${SERVICE_NAME}-ccd-data-store-api SERVICES_TERMS_AND_CONDITIONS: http://xui-terms-and-conditions-aat.service.core-compute-aat.internal SERVICES_HEARINGS_COMPONENT_API: http://jurisdiction-hearings-api-aat.service.core-compute-aat.internal - JURISDICTIONS: PCS + JURISDICTIONS: PCS,CIVIL FEATURE_REDIS_ENABLED: false REDISCLOUD_URL: http://dummyrediscloudurl FEATURE_APP_INSIGHTS_ENABLED: false diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/CaseType.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/CaseType.java index a473ce3639..a5d537edce 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/CaseType.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/CaseType.java @@ -88,5 +88,10 @@ public void configure(final ConfigBuilder builder) { builder.tab("serviceRequest", "Service Request") .showCondition(ShowConditions.stateNotEquals(AWAITING_SUBMISSION_TO_HMCTS)) .field("waysToPay"); + + builder.tab("caseLinks", "Linked cases") + .forRoles(UserRole.PCS_SOLICITOR) + .field(PCSCase::getLinkedCasesComponentLauncher, null, "#ARGUMENT(LinkedCases)") + .field(PCSCase::getCaseLinks, "LinkedCasesComponentLauncher!=\"\"", "#ARGUMENT(LinkedCases)"); } } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java index a024e0d2f3..3933ba19c7 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java @@ -6,10 +6,12 @@ import uk.gov.hmcts.ccd.sdk.CaseView; import uk.gov.hmcts.ccd.sdk.CaseViewRequest; import uk.gov.hmcts.ccd.sdk.type.AddressUK; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; import uk.gov.hmcts.ccd.sdk.type.Document; +import uk.gov.hmcts.ccd.sdk.type.LinkReason; import uk.gov.hmcts.ccd.sdk.type.ListValue; -import uk.gov.hmcts.ccd.sdk.type.SearchCriteria; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.ccd.sdk.type.SearchCriteria; import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; import uk.gov.hmcts.reform.pcs.ccd.domain.Party; import uk.gov.hmcts.reform.pcs.ccd.domain.State; @@ -110,6 +112,7 @@ private PCSCase getSubmittedCase(long caseReference) { .allDefendants(partyMap.get(PartyRole.DEFENDANT)) .allUnderlesseeOrMortgagees(partyMap.get(PartyRole.UNDERLESSEE_OR_MORTGAGEE)) .allDocuments(mapAndWrapDocuments(pcsCaseEntity)) + .caseLinks(mapAndWrapCaseLinks(pcsCaseEntity)) .build(); setDerivedProperties(pcsCase, pcsCaseEntity); @@ -250,4 +253,40 @@ private List> mapAndWrapDocuments(PcsCaseEntity pcsCaseEntit .collect(Collectors.toList()); } + private List> mapAndWrapCaseLinks(PcsCaseEntity pcsCaseEntity) { + + if (pcsCaseEntity.getCaseLinks().isEmpty()) { + return List.of(); + } + + return pcsCaseEntity.getCaseLinks().stream() + .map(linkEntity -> ListValue.builder() + .id(linkEntity.getLinkedCaseReference().toString()) + .value( + CaseLink.builder() + // BIGINT linked case reference -> CCD expects String + .caseReference(linkEntity.getLinkedCaseReference().toString()) + .caseType(linkEntity.getCcdListId()) + // map reasons + .reasonForLink( + linkEntity.getReasons().stream() + .map(reasonEntity -> ListValue.builder() + .id(reasonEntity.getId().toString()) + .value( + LinkReason.builder() + .reason(reasonEntity.getReasonCode()) + .description(reasonEntity.getReasonText()) + .build() + ) + .build() + ) + .toList() + ) + .build() + ) + .build() + ) + .toList(); + } + } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccess.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccess.java new file mode 100644 index 0000000000..e890f3316c --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccess.java @@ -0,0 +1,21 @@ +package uk.gov.hmcts.reform.pcs.ccd.accesscontrol; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; +import uk.gov.hmcts.ccd.sdk.api.HasAccessControl; +import uk.gov.hmcts.ccd.sdk.api.HasRole; +import uk.gov.hmcts.ccd.sdk.api.Permission; + +import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole.PCS_SOLICITOR; + +public class CaseLinkingAccess implements HasAccessControl { + + + @Override + public SetMultimap getGrants() { + SetMultimap grants = HashMultimap.create(); + grants.putAll(PCS_SOLICITOR, Permission.CRU); + + return grants; + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java index 93c2017aaf..e9f004cc55 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java @@ -7,17 +7,20 @@ import uk.gov.hmcts.ccd.sdk.External; import uk.gov.hmcts.ccd.sdk.api.CCD; import uk.gov.hmcts.ccd.sdk.type.AddressUK; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.ComponentLauncher; import uk.gov.hmcts.ccd.sdk.type.Document; import uk.gov.hmcts.ccd.sdk.type.DynamicList; import uk.gov.hmcts.ccd.sdk.type.FieldType; import uk.gov.hmcts.ccd.sdk.type.ListValue; -import uk.gov.hmcts.ccd.sdk.type.SearchCriteria; import uk.gov.hmcts.ccd.sdk.type.WaysToPay; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.ccd.sdk.type.SearchCriteria; +import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.CaseLinkingAccess; +import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.GlobalSearchAccess; import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.CitizenAccess; import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.ClaimantAccess; import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.DefendantAccess; -import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.GlobalSearchAccess; import uk.gov.hmcts.reform.pcs.ccd.domain.enforcetheorder.EnforcementOrder; import uk.gov.hmcts.reform.pcs.ccd.domain.grounds.AssuredNoArrearsPossessionGrounds; import uk.gov.hmcts.reform.pcs.ccd.domain.grounds.AssuredRentArrearsPossessionGrounds; @@ -40,6 +43,7 @@ import uk.gov.hmcts.reform.pcs.ccd.type.DynamicStringList; import uk.gov.hmcts.reform.pcs.postcodecourt.model.LegislativeCountry; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -110,6 +114,20 @@ public class PCSCase { @External private String crossBorderCountry2; + @CCD(access = {CaseLinkingAccess.class}, + typeOverride = FieldType.Collection, + label = "Linked cases", + typeParameterOverride = "CaseLink") + @Builder.Default + private List> caseLinks = new ArrayList<>(); + + @CCD( + label = "Component Launcher (for displaying Linked Cases data", + access = {CaseLinkingAccess.class} + ) + @JsonProperty("LinkedCasesComponentLauncher") + private ComponentLauncher linkedCasesComponentLauncher; + @CCD( searchable = false, access = {CitizenAccess.class} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java new file mode 100644 index 0000000000..bf8abd704d --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java @@ -0,0 +1,66 @@ +package uk.gov.hmcts.reform.pcs.ccd.entity; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Entity +@Table(name = "case_link", + uniqueConstraints = @UniqueConstraint( + name = "uk_case_link_unique", + columnNames = {"pcs_case_id", "linked_case_reference"} + )) +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class CaseLinkEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "case_id") + private PcsCaseEntity pcsCase; + + @Column(name = "linked_case_id", nullable = false) + private Long linkedCaseReference; + + @Column(name = "ccd_list_id") + private String ccdListId; + + @OneToMany(mappedBy = "caseLink", + cascade = CascadeType.ALL, + orphanRemoval = true) + @Builder.Default + private List reasons = new ArrayList<>(); + + public void addReason(String code, String text) { + CaseLinkReasonEntity caseLinkReasonEntity = new CaseLinkReasonEntity(); + caseLinkReasonEntity.setCaseLink(this); + caseLinkReasonEntity.setReasonCode(code); + caseLinkReasonEntity.setReasonText(text); + reasons.add(caseLinkReasonEntity); + + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntity.java new file mode 100644 index 0000000000..7c4af127e5 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntity.java @@ -0,0 +1,44 @@ +package uk.gov.hmcts.reform.pcs.ccd.entity; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.UUID; + +@Entity +@Table(name = "case_link_reason") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class CaseLinkReasonEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "case_link_id") + private CaseLinkEntity caseLink; + + @Column(name = "reason_code", nullable = false) + private String reasonCode; + + @Column(name = "reason_text") + private String reasonText; + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java index 01e9816540..f61c103832 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java @@ -16,6 +16,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.LinkReason; +import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.reform.pcs.ccd.domain.ClaimantType; import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity; import uk.gov.hmcts.reform.pcs.postcodecourt.model.LegislativeCountry; @@ -23,8 +26,11 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; import static jakarta.persistence.CascadeType.ALL; import static jakarta.persistence.FetchType.LAZY; @@ -77,6 +83,12 @@ public class PcsCaseEntity { @JsonManagedReference private List documents = new ArrayList<>(); + @OneToMany(mappedBy = "pcsCase", + cascade = ALL, + orphanRemoval = true) + @Builder.Default + private List caseLinks = new ArrayList<>(); + public void setTenancyLicence(TenancyLicenceEntity tenancyLicence) { if (this.tenancyLicence != null) { this.tenancyLicence.setPcsCase(null); @@ -105,4 +117,52 @@ public void addDocuments(List documents) { this.documents.add(document); } } + + public void mergeCaseLinks(List> incomingLinkedCases) { + + if (incomingLinkedCases == null) { + this.caseLinks.clear(); + return; + } + + Map existingLinkedCases = + this.caseLinks.stream() + .collect(Collectors.toMap(CaseLinkEntity::getLinkedCaseReference, + Function.identity() + )); + + List result = new ArrayList<>(); + + for (ListValue caseLinkListValue : incomingLinkedCases) { + CaseLink dto = caseLinkListValue.getValue(); + Long incomingCaseRef = Long.valueOf(dto.getCaseReference()); + + CaseLinkEntity caseLinkEntity = existingLinkedCases.remove(incomingCaseRef); + + if (caseLinkEntity == null) { + caseLinkEntity = new CaseLinkEntity(); + caseLinkEntity.setPcsCase(this); + caseLinkEntity.setLinkedCaseReference(incomingCaseRef); + } + + caseLinkEntity.setCcdListId(dto.getCaseType()); + + caseLinkEntity.getReasons().clear(); + + if (dto.getReasonForLink() != null) { + for (ListValue incomingLinkReason : dto.getReasonForLink()) { + caseLinkEntity.addReason( + incomingLinkReason.getValue().getReason(), + incomingLinkReason.getValue().getDescription() + ); + } + } + + result.add(caseLinkEntity); + } + + this.caseLinks.clear(); + this.caseLinks.addAll(result); + + } } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java new file mode 100644 index 0000000000..98cba52046 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java @@ -0,0 +1,51 @@ +package uk.gov.hmcts.reform.pcs.ccd.event; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.DecentralisedConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.EventPayload; +import uk.gov.hmcts.ccd.sdk.api.Permission; +import uk.gov.hmcts.ccd.sdk.api.callback.SubmitResponse; +import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole; +import uk.gov.hmcts.reform.pcs.ccd.common.PageBuilder; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.domain.State; +import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService; + +@Component +@Slf4j +@AllArgsConstructor +public class CreateCaseLink implements CCDConfig { + + private final PcsCaseService pcsCaseService; + + + @Override + public void configureDecentralised(DecentralisedConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .decentralisedEvent(EventId.createCaseLink.name(), this::submit) + .forAllStates() + .name("Link cases") + .description("To link related cases") + .grant(Permission.CRUD,UserRole.PCS_SOLICITOR)) + .page("createCaseLink") + .pageLabel("Case Link") + .optional(PCSCase::getCaseLinks,"LinkedCasesComponentLauncher = \"DONOTSHOW\"",null,true) + .optional(PCSCase::getLinkedCasesComponentLauncher, + null,null,null,null,"#ARGUMENT(CREATE,LinkedCases)"); + + } + + private SubmitResponse submit(EventPayload eventPayload) { + long caseReference = eventPayload.caseReference(); + PCSCase pcsCase = eventPayload.caseData(); + + log.info("Caseworker created case link for {}", caseReference); + + pcsCaseService.patchCase(caseReference, pcsCase); + + return SubmitResponse.defaultResponse(); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/EventId.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/EventId.java index 3a84e15878..0be6cc3aac 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/EventId.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/EventId.java @@ -7,5 +7,7 @@ public enum EventId { enforceTheOrder, respondPossessionClaim, submitDefendantResponse, - createTestCase + createTestCase, + createCaseLink, + maintainCaseLink } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCase.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCase.java new file mode 100644 index 0000000000..85fd22f989 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCase.java @@ -0,0 +1,53 @@ +package uk.gov.hmcts.reform.pcs.ccd.event; + + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.DecentralisedConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.EventPayload; +import uk.gov.hmcts.ccd.sdk.api.Permission; +import uk.gov.hmcts.ccd.sdk.api.callback.SubmitResponse; +import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole; +import uk.gov.hmcts.reform.pcs.ccd.common.PageBuilder; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.domain.State; +import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService; + +@Component +@Slf4j +@AllArgsConstructor +public class MaintainLinkCase implements CCDConfig { + + private final PcsCaseService pcsCaseService; + + @Override + public void configureDecentralised(DecentralisedConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .decentralisedEvent(EventId.maintainCaseLink.name(), this::submit) + .forAllStates() + .name("Manage case links") + .description("To Manage link related cases") + .grant(Permission.CRUD, UserRole.PCS_SOLICITOR)) + .page("maintainCaseLink") + .pageLabel("Case Link") + .optional(PCSCase::getCaseLinks, "LinkedCasesComponentLauncher = \"DONOTSHOW\"", null, true) + .optional(PCSCase::getLinkedCasesComponentLauncher, + null, null, null, null, "#ARGUMENT(UPDATE,LinkedCases)"); + } + + + private SubmitResponse submit(EventPayload eventPayload) { + long caseReference = eventPayload.caseReference(); + PCSCase pcsCase = eventPayload.caseData(); + + log.info("Caseworker updated case link for {}", caseReference); + + pcsCaseService.patchCase(caseReference, pcsCase); + + return SubmitResponse.defaultResponse(); + } + + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeService.java new file mode 100644 index 0000000000..4509b7795e --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeService.java @@ -0,0 +1,67 @@ +package uk.gov.hmcts.reform.pcs.ccd.service; + + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.reform.idam.client.models.UserInfo; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.entity.AddressEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity; +import uk.gov.hmcts.reform.pcs.security.SecurityContextService; + +import java.util.UUID; + +@Service +@Slf4j +@AllArgsConstructor +public class PcsCaseMergeService { + + private final SecurityContextService securityContextService; + private final ModelMapper modelMapper; + private final TenancyLicenceService tenancyLicenceService; + + public void mergeCaseData(PcsCaseEntity pcsCaseEntity, PCSCase pcsCase) { + + if (pcsCase.getPropertyAddress() != null) { + AddressEntity addressEntity = modelMapper.map(pcsCase.getPropertyAddress(), AddressEntity.class); + pcsCaseEntity.setPropertyAddress(addressEntity); + } + + if (pcsCase.getUserPcqId() != null) { + setPcqIdForCurrentUser(pcsCase.getUserPcqId(), pcsCaseEntity); + } + + if (pcsCase.getCaseLinks() != null) { + + pcsCaseEntity.mergeCaseLinks(pcsCase.getCaseLinks()); + } + + pcsCaseEntity.setTenancyLicence(tenancyLicenceService.createTenancyLicenceEntity(pcsCase)); + + } + + private void setPcqIdForCurrentUser(String pcqId, PcsCaseEntity pcsCaseEntity) { + UserInfo userDetails = securityContextService.getCurrentUserDetails(); + UUID userId = UUID.fromString(userDetails.getUid()); + pcsCaseEntity.getParties().stream() + .filter(party -> userId.equals(party.getIdamId())) + .findFirst() + .orElseGet(() -> { + PartyEntity party = createPartyForUser(userId, userDetails); + pcsCaseEntity.addParty(party); + return party; + }) + .setPcqId(pcqId); + } + + private static PartyEntity createPartyForUser(UUID userId, UserInfo userDetails) { + PartyEntity party = new PartyEntity(); + party.setIdamId(userId); + party.setFirstName(userDetails.getGivenName()); + party.setLastName(userDetails.getFamilyName()); + return party; + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java index d85e73223d..6f1d1d64d9 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java @@ -1,6 +1,7 @@ package uk.gov.hmcts.reform.pcs.ccd.service; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import uk.gov.hmcts.ccd.sdk.type.AddressUK; import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; @@ -17,6 +18,7 @@ import java.util.Objects; @Service +@Slf4j @AllArgsConstructor public class PcsCaseService { @@ -26,6 +28,7 @@ public class PcsCaseService { private final DocumentService documentService; private final TenancyLicenceService tenancyLicenceService; private final AddressMapper addressMapper; + private final PcsCaseMergeService pcsCaseMergeService; public PcsCaseEntity createCase(long caseReference, AddressUK propertyAddress, @@ -61,4 +64,25 @@ public PcsCaseEntity loadCase(long caseReference) { .orElseThrow(() -> new CaseNotFoundException(caseReference)); } + public void patchCase(long caseReference, PCSCase pcsCase) { + PcsCaseEntity pcsCaseEntity = loadCase(caseReference); + + log.info("Patching linked cases for {}", caseReference); + mergeCaseData(pcsCaseEntity, pcsCase); + + save(pcsCaseEntity); + } + + public void mergeCaseData(PcsCaseEntity pcsCaseEntity, PCSCase pcsCase) { + + pcsCaseMergeService.mergeCaseData(pcsCaseEntity, pcsCase); + + } + + public void save(PcsCaseEntity pcsCaseEntity) { + + pcsCaseRepository.save(pcsCaseEntity); + + } + } diff --git a/src/main/resources/db/migration/V063__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V063__create_case_link_and_link_reason_tables.sql new file mode 100644 index 0000000000..f80a571f38 --- /dev/null +++ b/src/main/resources/db/migration/V063__create_case_link_and_link_reason_tables.sql @@ -0,0 +1,57 @@ +-- ============================================ +-- DROP OLD CASE LINK STRUCTURE +-- ============================================ + +-- Drop index on case_link if it exists +DROP INDEX IF EXISTS ux_case_link_unique; + +-- Drop index on case_link_reason if it exists +DROP INDEX IF EXISTS idx_case_link_reason_link; + +-- Drop case_link_reason table first (child table) +DROP TABLE IF EXISTS case_link_reason CASCADE; + +-- Drop case_link table +DROP TABLE IF EXISTS case_link CASCADE; + +-- ============================================ +-- PCS CASE LINK + LINK REASONS +-- ============================================ +-- ============================================ +-- PCS CASE LINK + LINK REASONS +-- ============================================ + +-- Table: case_link +CREATE TABLE case_link ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- Source case + case_id UUID NOT NULL REFERENCES pcs_case(id) ON DELETE CASCADE, + + -- Linked case number (BIGINT) + linked_case_id BIGINT NOT NULL, + + -- CCD ListValue ID + ccd_list_id VARCHAR(50), + + created_at TIMESTAMP DEFAULT now() +); + +-- Unique constraint on source + linked case +CREATE UNIQUE INDEX uk_case_link_unique + ON case_link(case_id, linked_case_id); + +-- Table: case_link_reason +CREATE TABLE case_link_reason ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- FK to case_link + case_link_id UUID NOT NULL REFERENCES case_link(id) ON DELETE CASCADE, + + reason_code VARCHAR(100) NOT NULL, + reason_text VARCHAR(255) +); + +-- Optional index for performance +CREATE INDEX idx_case_link_reason_link + ON case_link_reason(case_link_id); diff --git a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql index 87f7bffe74..facfcb888a 100644 --- a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql +++ b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql @@ -1,4 +1,4 @@ -INSERT INTO postcode_court_mapping (postcode, epimid, legislative_country, effective_from, effective_to, audit) VALUES +INSERT INTO postcode_court_mapping (postcode, epims_id, legislative_country, effective_from, effective_to, audit) VALUES ('W3 7RX', 20262, 'England', '2025-01-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('W3 6RS', 36791, 'England', '2025-02-01', NULL, '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('M13 9PL', 144641, 'England', '2025-04-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java index 2565b0b6f3..29e6723b1b 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java @@ -9,6 +9,8 @@ import org.modelmapper.ModelMapper; import uk.gov.hmcts.ccd.sdk.CaseViewRequest; import uk.gov.hmcts.ccd.sdk.type.AddressUK; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.LinkReason; import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; import uk.gov.hmcts.reform.pcs.ccd.domain.Party; @@ -18,9 +20,11 @@ import uk.gov.hmcts.reform.pcs.ccd.entity.DocumentEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.party.ClaimPartyEntity; -import uk.gov.hmcts.reform.pcs.ccd.entity.party.ClaimPartyId; import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyRole; +import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkReasonEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.party.ClaimPartyId; import uk.gov.hmcts.reform.pcs.ccd.repository.PcsCaseRepository; import uk.gov.hmcts.reform.pcs.ccd.service.CaseTitleService; import uk.gov.hmcts.reform.pcs.ccd.service.DraftCaseDataService; @@ -38,6 +42,7 @@ import uk.gov.hmcts.reform.pcs.postcodecourt.model.LegislativeCountry; import uk.gov.hmcts.reform.pcs.security.SecurityContextService; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import java.util.Set; @@ -329,6 +334,98 @@ void shouldSetCaseFieldsInViewHelpers() { verify(statementOfTruthView).setCaseFields(pcsCase, pcsCaseEntity); } + @Test + void shouldMapAndWrapCaseLinks() { + // Given + CaseLinkReasonEntity caseLinkReasonEntity1 = createCaseLinkReasonEntity(UUID.randomUUID(), + "CLR003", "Same Party"); + CaseLinkReasonEntity caseLinkReasonEntity2 = createCaseLinkReasonEntity(UUID.randomUUID(), + "CLR010", "Bail"); + + CaseLinkEntity caseLinkEntity1 = createCaseLinkEntity(UUID.randomUUID(), List.of(caseLinkReasonEntity1), + 1234L, "CCD1"); + CaseLinkEntity caseLinkEntity2 = createCaseLinkEntity(UUID.randomUUID(), List.of(caseLinkReasonEntity2), + 1234L, "CCD2"); + + when(pcsCaseEntity.getCaseLinks()).thenReturn(List.of(caseLinkEntity1, caseLinkEntity2)); + + + LinkReason linkReason1 = createLinkReason(caseLinkReasonEntity1.getReasonCode(), + caseLinkReasonEntity1.getReasonText()); + List> linkReasons1 = List.of( + ListValue.builder() + .id(caseLinkReasonEntity1.getId().toString()) + .value(linkReason1) + .build()); + + LinkReason linkReason2 = createLinkReason(caseLinkReasonEntity1.getReasonCode(), + caseLinkReasonEntity1.getReasonText()); + List> linkReasons2 = List.of( + ListValue.builder() + .id(caseLinkReasonEntity2.getId().toString()) + .value(linkReason2) + .build()); + + CaseLink expectedCaseLink1 = creatCaseLink(String.valueOf(caseLinkEntity1.getLinkedCaseReference()), + caseLinkEntity1.getCcdListId(), linkReasons1, null); + CaseLink expectedCaseLink2 = creatCaseLink(String.valueOf(caseLinkEntity2.getLinkedCaseReference()), + caseLinkEntity2.getCcdListId(), linkReasons2, null); + + + // When + PCSCase pcsCase = underTest.getCase(request(CASE_REFERENCE, DEFAULT_STATE)); + + // Then + List> mappedCaseLinks = pcsCase.getCaseLinks(); + assertThat(mappedCaseLinks).hasSize(2); + assertThat(mappedCaseLinks.getFirst().getValue().getCaseReference()).isEqualTo( + expectedCaseLink1.getCaseReference()); + assertThat(mappedCaseLinks.get(1).getValue().getCaseType().equals(expectedCaseLink2.getCaseType())); + } + + private CaseLinkReasonEntity createCaseLinkReasonEntity(UUID id, String reasonCode, String reasonText) { + + CaseLinkEntity caseLinkEntity = mock(CaseLinkEntity.class); + + return CaseLinkReasonEntity.builder() + .id(id) + .reasonCode(reasonCode) + .reasonText(reasonText) + .caseLink(caseLinkEntity) + .build(); + } + + private CaseLinkEntity createCaseLinkEntity(UUID id, List linkReasonEntities, + Long linkedCaseRef, String ccdId) { + + return CaseLinkEntity.builder() + .id(id) + .linkedCaseReference(linkedCaseRef) + .ccdListId(ccdId) + .reasons(linkReasonEntities) + .pcsCase(pcsCaseEntity) + .build(); + } + + private CaseLink creatCaseLink(String ref, String caseType, List> reasons, + LocalDateTime time) { + + return CaseLink.builder() + .caseReference(ref) + .caseType(caseType) + .reasonForLink(reasons) + .createdDateTime(time) + .build(); + } + + private LinkReason createLinkReason(String reason, String description) { + + return LinkReason.builder() + .reason(reason) + .description(description) + .build(); + } + private AddressUK stubAddressEntityModelMapper(AddressEntity addressEntity) { AddressUK addressUK = mock(AddressUK.class); when(modelMapper.map(addressEntity, AddressUK.class)).thenReturn(addressUK); diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java new file mode 100644 index 0000000000..a5eb25140f --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java @@ -0,0 +1,27 @@ +package uk.gov.hmcts.reform.pcs.ccd.accesscontrol; + +import com.google.common.collect.SetMultimap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import uk.gov.hmcts.ccd.sdk.api.HasRole; +import uk.gov.hmcts.ccd.sdk.api.Permission; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole.PCS_SOLICITOR; + +public class CaseLinkingAccessTest { + + private CaseLinkingAccess underTest; + + @BeforeEach + void setup() { + underTest = new CaseLinkingAccess(); + } + + @Test + void shouldGrantCaseLinkingAccess() { + SetMultimap grants = underTest.getGrants(); + assertThat(grants.asMap()).contains(entry(PCS_SOLICITOR, Permission.CRU)); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java new file mode 100644 index 0000000000..804afe211a --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java @@ -0,0 +1,37 @@ +package uk.gov.hmcts.reform.pcs.ccd.entity; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Spy; + + +import static org.assertj.core.api.Assertions.assertThat; + + + +class CaseLinkEntityTest { + + @Spy + private CaseLinkEntity underTest; + + + @BeforeEach + void setUp() { + underTest = new CaseLinkEntity(); + } + + @Test + void shouldAddCaseLinkReason() { + // Given + String reasonCode = "CLR003"; + String reasonText = "Same Party"; + + // When + underTest.addReason(reasonCode, reasonText); + + // Then + assertThat(underTest.getReasons().size()).isEqualTo(1); + assertThat(underTest.getReasons().getFirst().getReasonCode()).isEqualTo("CLR003"); + assertThat(underTest.getReasons().getFirst().getReasonText()).isEqualTo("Same Party"); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntityTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntityTest.java new file mode 100644 index 0000000000..03bb586501 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntityTest.java @@ -0,0 +1,31 @@ +package uk.gov.hmcts.reform.pcs.ccd.entity; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThat; + + +class CaseLinkReasonEntityTest { + + private CaseLinkReasonEntity underTest; + + + @BeforeEach + void setUp() { + underTest = new CaseLinkReasonEntity(); + } + + @Test + void shouldUpdateCaseLinkEntity() { + // Given + CaseLinkEntity updatedCaseLinkEntity = mock(CaseLinkEntity.class); + + // When + underTest.setCaseLink(updatedCaseLinkEntity); + + // Then + assertThat(underTest.getCaseLink()).isSameAs(updatedCaseLinkEntity); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLinkTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLinkTest.java new file mode 100644 index 0000000000..7b68bc375d --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLinkTest.java @@ -0,0 +1,57 @@ +package uk.gov.hmcts.reform.pcs.ccd.event; + +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.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService; + +import java.util.List; +import java.util.UUID; + + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.doNothing; + +@ExtendWith(MockitoExtension.class) +class CreateCaseLinkTest extends BaseEventTest { + + @Mock + private PcsCaseService pcsCaseService; + + + @InjectMocks + private CreateCaseLink underTest; + + + @BeforeEach + void setUp() { + setEventUnderTest(underTest); + } + + + @Test + void shouldCreateCaseLinksInSubmitCallback() { + // Given + CaseLink caseLink = CaseLink.builder().build(); + List> caseLists = List.of( + ListValue.builder() + .id(UUID.randomUUID().toString()) + .value(caseLink) + .build()); + + PCSCase pcsCase = PCSCase.builder().caseLinks(caseLists).build(); + doNothing().when(pcsCaseService).patchCase(TEST_CASE_REFERENCE, pcsCase); + + // When + callSubmitHandler(pcsCase); + + // Then + verify(pcsCaseService).patchCase(TEST_CASE_REFERENCE, pcsCase); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCaseTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCaseTest.java new file mode 100644 index 0000000000..6d1e7ad080 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCaseTest.java @@ -0,0 +1,55 @@ +package uk.gov.hmcts.reform.pcs.ccd.event; + +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.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService; + +import java.util.List; +import java.util.UUID; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class MaintainLinkCaseTest extends BaseEventTest { + + + @Mock + private PcsCaseService pcsCaseService; + + @InjectMocks + private MaintainLinkCase underTest; + + @BeforeEach + void setUp() { + + setEventUnderTest(underTest); + } + + @Test + void shouldModifyCaseLinksInSubmitCallback() { + // Given + CaseLink caseLink = CaseLink.builder().build(); + List> caseLists = List.of( + ListValue.builder() + .id(UUID.randomUUID().toString()) + .value(caseLink) + .build()); + + PCSCase pcsCase = PCSCase.builder().caseLinks(caseLists).build(); + doNothing().when(pcsCaseService).patchCase(TEST_CASE_REFERENCE, pcsCase); + + // When + callSubmitHandler(pcsCase); + + // Then + verify(pcsCaseService).patchCase(TEST_CASE_REFERENCE, pcsCase); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/DraftCaseJsonMergerTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/DraftCaseJsonMergerTest.java index be649c4cec..e80499af77 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/DraftCaseJsonMergerTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/DraftCaseJsonMergerTest.java @@ -71,6 +71,7 @@ void shouldKeepExistingFieldsWhenMerging() throws JsonProcessingException { "claimantType", "noRentArrearsReasonForGrounds.holidayLet", "waysToPay", + "caseLinks", "claimGroundSummaries", "enforcementOrder.showChangeNameAddressPage", "enforcementOrder.showPeopleWhoWillBeEvictedPage", diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java new file mode 100644 index 0000000000..de716bdfb8 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java @@ -0,0 +1,107 @@ +package uk.gov.hmcts.reform.pcs.ccd.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; +import uk.gov.hmcts.ccd.sdk.type.AddressUK; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.LinkReason; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; +import uk.gov.hmcts.reform.pcs.ccd.repository.PcsCaseRepository; +import uk.gov.hmcts.reform.pcs.security.SecurityContextService; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + + + +@ExtendWith(MockitoExtension.class) +class PcsCaseMergeServiceTest { + + private static final long CASE_REFERENCE = 1234L; + + @Mock + private PcsCaseRepository pcsCaseRepository; + @Mock + private SecurityContextService securityContextService; + @Mock + private ModelMapper modelMapper; + @Mock + private TenancyLicenceService tenancyLicenceService; + + private PcsCaseMergeService underTest; + + @BeforeEach + void setUp() { + + underTest = new PcsCaseMergeService( + securityContextService, + modelMapper, + tenancyLicenceService + ); + } + + + @Test + void shouldMergeCaseData() { + + // Given + final PcsCaseEntity pcsCaseEntity = PcsCaseEntity.builder().build(); + + PCSCase caseData = PCSCase.builder().build(); + + List> caseLinks = createCaseLinks(); + + caseData.setPropertyAddress(new AddressUK( + "123 Great House", "Nice Street", "bla", "London", + "Greater London","SW18 1QT", "UK")); + + caseData.setCaseLinks(caseLinks); + + // When + underTest.mergeCaseData(pcsCaseEntity, caseData); + + // Then + assertThat(pcsCaseEntity.getCaseLinks().size()).isEqualTo(1); + assertThat(pcsCaseEntity.getCaseLinks().getFirst().getCcdListId()).isEqualTo("CCD"); + + } + + + private List> createCaseLinks() { + LinkReason linkReason = createLinkReason("CLR003", + ""); + List> linkReasons = List.of( + ListValue.builder() + .id(UUID.randomUUID().toString()) + .value(linkReason) + .build()); + + CaseLink caseLink = CaseLink.builder() + .caseReference(String.valueOf(CASE_REFERENCE)) + .caseType("CCD") + .reasonForLink(linkReasons) + .build(); + + return List.of( + ListValue.builder() + .id(UUID.randomUUID().toString()) + .value(caseLink) + .build()); + } + + private LinkReason createLinkReason(String reason, String description) { + + return LinkReason.builder() + .reason(reason) + .description(description) + .build(); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java index 2bcbc91b4c..0194b0d3c1 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.atLeastOnce; @ExtendWith(MockitoExtension.class) class PcsCaseServiceTest { @@ -47,6 +48,8 @@ class PcsCaseServiceTest { private TenancyLicenceService tenancyLicenceService; @Mock private AddressMapper addressMapper; + @Mock + private PcsCaseMergeService pcsCaseMergeService; @Captor private ArgumentCaptor pcsCaseEntityCaptor; @@ -61,7 +64,8 @@ void setUp() { partyService, documentService, tenancyLicenceService, - addressMapper + addressMapper, + pcsCaseMergeService ); } @@ -182,6 +186,21 @@ void shouldCreateTenancyLicenceWithMainClaimOnCase() { verify(pcsCaseEntity).setTenancyLicence(tenancyLicenceEntity); } + @Test + void shouldMergeCaseData() { + // Given + final PcsCaseEntity pcsCaseEntity = PcsCaseEntity.builder().build(); + PCSCase caseData = PCSCase.builder().build(); + + //doNothing().when(pcsCaseMergeService).mergeCaseData(pcsCaseEntity, caseData); + + // When + underTest.mergeCaseData(pcsCaseEntity, caseData); + + // Then + verify(pcsCaseMergeService, atLeastOnce()).mergeCaseData(pcsCaseEntity, caseData); + } + private PcsCaseEntity stubFindCase() { PcsCaseEntity pcsCaseEntity = mock(PcsCaseEntity.class); when(pcsCaseRepository.findByCaseReference(CASE_REFERENCE)).thenReturn(Optional.of(pcsCaseEntity)); From e0a2d8140f0c5e5b094654050d15449fc6edf336 Mon Sep 17 00:00:00 2001 From: aschalew Date: Wed, 18 Mar 2026 11:55:35 +0000 Subject: [PATCH 02/18] Reverted AUthMode back to Local --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0d363e233c..ffe81c8aa3 100644 --- a/build.gradle +++ b/build.gradle @@ -515,7 +515,7 @@ tasks.named('integration') { tasks.withType(CftlibExec).configureEach { group = 'ccd tasks' - authMode = AuthMode.AAT + authMode = AuthMode.Local //Attach mockito agent for inline mocking to remove warning in logs classpath += configurations.mockitoAgentConfig From 645a043a56a26368a6592c23a0abd7997de2e6b4 Mon Sep 17 00:00:00 2001 From: aschalew Date: Wed, 18 Mar 2026 13:59:24 +0000 Subject: [PATCH 03/18] Renamed sql script for case linking from V063 to V067 --- ...bles.sql => V067__create_case_link_and_link_reason_tables.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V063__create_case_link_and_link_reason_tables.sql => V067__create_case_link_and_link_reason_tables.sql} (100%) diff --git a/src/main/resources/db/migration/V063__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V067__create_case_link_and_link_reason_tables.sql similarity index 100% rename from src/main/resources/db/migration/V063__create_case_link_and_link_reason_tables.sql rename to src/main/resources/db/migration/V067__create_case_link_and_link_reason_tables.sql From e84bdcd736c25a4afe5dfe7285f21acbfe3ea017 Mon Sep 17 00:00:00 2001 From: aschalew Date: Wed, 18 Mar 2026 16:21:02 +0000 Subject: [PATCH 04/18] Renamed epims_id back to empimsid --- src/main/resources/db/testdata/V002.1__postcode_test_data.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql index facfcb888a..c8f3d78e98 100644 --- a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql +++ b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql @@ -1,4 +1,4 @@ -INSERT INTO postcode_court_mapping (postcode, epims_id, legislative_country, effective_from, effective_to, audit) VALUES +INSERT INTO postcode_court_mapping (postcode, epimsid, legislative_country, effective_from, effective_to, audit) VALUES ('W3 7RX', 20262, 'England', '2025-01-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('W3 6RS', 36791, 'England', '2025-02-01', NULL, '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('M13 9PL', 144641, 'England', '2025-04-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), From 0f64f6a3174ac954920dd3e598b27257f22d92d8 Mon Sep 17 00:00:00 2001 From: aschalew Date: Thu, 19 Mar 2026 10:46:20 +0000 Subject: [PATCH 05/18] Updated Unit tests to remove some code smells --- src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java | 2 +- .../reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java | 2 +- .../uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java | 2 +- .../hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java | 2 +- .../uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java | 2 -- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java index 29e6723b1b..13d7bda25f 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java @@ -380,7 +380,7 @@ void shouldMapAndWrapCaseLinks() { assertThat(mappedCaseLinks).hasSize(2); assertThat(mappedCaseLinks.getFirst().getValue().getCaseReference()).isEqualTo( expectedCaseLink1.getCaseReference()); - assertThat(mappedCaseLinks.get(1).getValue().getCaseType().equals(expectedCaseLink2.getCaseType())); + assertThat(mappedCaseLinks.get(1).getValue().getCaseType()).isEqualTo(expectedCaseLink2.getCaseType()); } private CaseLinkReasonEntity createCaseLinkReasonEntity(UUID id, String reasonCode, String reasonText) { diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java index a5eb25140f..5da03a9713 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccessTest.java @@ -10,7 +10,7 @@ import static org.assertj.core.api.Assertions.entry; import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole.PCS_SOLICITOR; -public class CaseLinkingAccessTest { +class CaseLinkingAccessTest { private CaseLinkingAccess underTest; diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java index 804afe211a..87fc7cbe21 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java @@ -30,7 +30,7 @@ void shouldAddCaseLinkReason() { underTest.addReason(reasonCode, reasonText); // Then - assertThat(underTest.getReasons().size()).isEqualTo(1); + assertThat(underTest.getReasons()).hasSize(1); assertThat(underTest.getReasons().getFirst().getReasonCode()).isEqualTo("CLR003"); assertThat(underTest.getReasons().getFirst().getReasonText()).isEqualTo("Same Party"); } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java index de716bdfb8..d0afa41226 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java @@ -69,7 +69,7 @@ void shouldMergeCaseData() { underTest.mergeCaseData(pcsCaseEntity, caseData); // Then - assertThat(pcsCaseEntity.getCaseLinks().size()).isEqualTo(1); + assertThat(pcsCaseEntity.getCaseLinks()).hasSize(1); assertThat(pcsCaseEntity.getCaseLinks().getFirst().getCcdListId()).isEqualTo("CCD"); } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java index 0194b0d3c1..6328e4c12d 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java @@ -192,8 +192,6 @@ void shouldMergeCaseData() { final PcsCaseEntity pcsCaseEntity = PcsCaseEntity.builder().build(); PCSCase caseData = PCSCase.builder().build(); - //doNothing().when(pcsCaseMergeService).mergeCaseData(pcsCaseEntity, caseData); - // When underTest.mergeCaseData(pcsCaseEntity, caseData); From 9519c0093e2bcc459038d54e18975a5e14ab4788 Mon Sep 17 00:00:00 2001 From: aschalew Date: Thu, 19 Mar 2026 17:07:06 +0000 Subject: [PATCH 06/18] Renamed epimsid to epims_id --- src/main/resources/db/testdata/V002.1__postcode_test_data.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql index c8f3d78e98..facfcb888a 100644 --- a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql +++ b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql @@ -1,4 +1,4 @@ -INSERT INTO postcode_court_mapping (postcode, epimsid, legislative_country, effective_from, effective_to, audit) VALUES +INSERT INTO postcode_court_mapping (postcode, epims_id, legislative_country, effective_from, effective_to, audit) VALUES ('W3 7RX', 20262, 'England', '2025-01-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('W3 6RS', 36791, 'England', '2025-02-01', NULL, '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('M13 9PL', 144641, 'England', '2025-04-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), From 67afd9ef18d1e8934369118b6a538f170dcc6f2d Mon Sep 17 00:00:00 2001 From: aschalew Date: Fri, 20 Mar 2026 07:21:28 +0000 Subject: [PATCH 07/18] Renamed epims_id back to epimsid --- src/main/resources/db/testdata/V002.1__postcode_test_data.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql index facfcb888a..c8f3d78e98 100644 --- a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql +++ b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql @@ -1,4 +1,4 @@ -INSERT INTO postcode_court_mapping (postcode, epims_id, legislative_country, effective_from, effective_to, audit) VALUES +INSERT INTO postcode_court_mapping (postcode, epimsid, legislative_country, effective_from, effective_to, audit) VALUES ('W3 7RX', 20262, 'England', '2025-01-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('W3 6RS', 36791, 'England', '2025-02-01', NULL, '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('M13 9PL', 144641, 'England', '2025-04-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), From aeddb7ab314ad0e7637efd74b422a6f2ae9a3cac Mon Sep 17 00:00:00 2001 From: aschalew Date: Fri, 20 Mar 2026 09:21:26 +0000 Subject: [PATCH 08/18] Renamed V067 to V070 after merging master --- ...bles.sql => V070__create_case_link_and_link_reason_tables.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V067__create_case_link_and_link_reason_tables.sql => V070__create_case_link_and_link_reason_tables.sql} (100%) diff --git a/src/main/resources/db/migration/V067__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V070__create_case_link_and_link_reason_tables.sql similarity index 100% rename from src/main/resources/db/migration/V067__create_case_link_and_link_reason_tables.sql rename to src/main/resources/db/migration/V070__create_case_link_and_link_reason_tables.sql From 4d495607b57f86a2147e5447005cfcb40a28efe8 Mon Sep 17 00:00:00 2001 From: Praveen Adusumilli <47391951+adusumillipraveen@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:04:58 +0000 Subject: [PATCH 09/18] Update V002.1__postcode_test_data.sql --- src/main/resources/db/testdata/V002.1__postcode_test_data.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql index c8f3d78e98..87f7bffe74 100644 --- a/src/main/resources/db/testdata/V002.1__postcode_test_data.sql +++ b/src/main/resources/db/testdata/V002.1__postcode_test_data.sql @@ -1,4 +1,4 @@ -INSERT INTO postcode_court_mapping (postcode, epimsid, legislative_country, effective_from, effective_to, audit) VALUES +INSERT INTO postcode_court_mapping (postcode, epimid, legislative_country, effective_from, effective_to, audit) VALUES ('W3 7RX', 20262, 'England', '2025-01-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('W3 6RS', 36791, 'England', '2025-02-01', NULL, '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), ('M13 9PL', 144641, 'England', '2025-04-01', '2035-04-01', '{"created_by": "admin", "change_reason": "initial insert"}'::jsonb), From d2115fa2f4570179e30330cffbef78411aebddc9 Mon Sep 17 00:00:00 2001 From: aschalew Date: Mon, 23 Mar 2026 07:21:05 +0000 Subject: [PATCH 10/18] Refactored Code to meet PR comments --- .../hmcts/reform/pcs/ccd/domain/PCSCase.java | 4 +- .../pcs/ccd/service/PcsCaseMergeService.java | 67 ----------- .../pcs/ccd/service/PcsCaseService.java | 17 +-- ...reate_case_link_and_link_reason_tables.sql | 28 ++--- .../hmcts/reform/pcs/ccd/PCSCaseViewTest.java | 25 ++-- .../pcs/ccd/entity/CaseLinkEntityTest.java | 2 - .../pcs/ccd/entity/PcsCaseEntityTest.java | 83 ++++++++++++++ .../ccd/service/PcsCaseMergeServiceTest.java | 107 ------------------ .../pcs/ccd/service/PcsCaseServiceTest.java | 14 +-- 9 files changed, 114 insertions(+), 233 deletions(-) delete mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeService.java delete mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java index e9f004cc55..923ae877b0 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java @@ -122,8 +122,8 @@ public class PCSCase { private List> caseLinks = new ArrayList<>(); @CCD( - label = "Component Launcher (for displaying Linked Cases data", - access = {CaseLinkingAccess.class} + access = {CaseLinkingAccess.class}, + label = "Component Launcher (for displaying Linked Cases data" ) @JsonProperty("LinkedCasesComponentLauncher") private ComponentLauncher linkedCasesComponentLauncher; diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeService.java deleted file mode 100644 index 4509b7795e..0000000000 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeService.java +++ /dev/null @@ -1,67 +0,0 @@ -package uk.gov.hmcts.reform.pcs.ccd.service; - - -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.modelmapper.ModelMapper; -import org.springframework.stereotype.Service; -import uk.gov.hmcts.reform.idam.client.models.UserInfo; -import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; -import uk.gov.hmcts.reform.pcs.ccd.entity.AddressEntity; -import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; -import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity; -import uk.gov.hmcts.reform.pcs.security.SecurityContextService; - -import java.util.UUID; - -@Service -@Slf4j -@AllArgsConstructor -public class PcsCaseMergeService { - - private final SecurityContextService securityContextService; - private final ModelMapper modelMapper; - private final TenancyLicenceService tenancyLicenceService; - - public void mergeCaseData(PcsCaseEntity pcsCaseEntity, PCSCase pcsCase) { - - if (pcsCase.getPropertyAddress() != null) { - AddressEntity addressEntity = modelMapper.map(pcsCase.getPropertyAddress(), AddressEntity.class); - pcsCaseEntity.setPropertyAddress(addressEntity); - } - - if (pcsCase.getUserPcqId() != null) { - setPcqIdForCurrentUser(pcsCase.getUserPcqId(), pcsCaseEntity); - } - - if (pcsCase.getCaseLinks() != null) { - - pcsCaseEntity.mergeCaseLinks(pcsCase.getCaseLinks()); - } - - pcsCaseEntity.setTenancyLicence(tenancyLicenceService.createTenancyLicenceEntity(pcsCase)); - - } - - private void setPcqIdForCurrentUser(String pcqId, PcsCaseEntity pcsCaseEntity) { - UserInfo userDetails = securityContextService.getCurrentUserDetails(); - UUID userId = UUID.fromString(userDetails.getUid()); - pcsCaseEntity.getParties().stream() - .filter(party -> userId.equals(party.getIdamId())) - .findFirst() - .orElseGet(() -> { - PartyEntity party = createPartyForUser(userId, userDetails); - pcsCaseEntity.addParty(party); - return party; - }) - .setPcqId(pcqId); - } - - private static PartyEntity createPartyForUser(UUID userId, UserInfo userDetails) { - PartyEntity party = new PartyEntity(); - party.setIdamId(userId); - party.setFirstName(userDetails.getGivenName()); - party.setLastName(userDetails.getFamilyName()); - return party; - } -} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java index 6f1d1d64d9..9d581edde3 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java @@ -28,7 +28,6 @@ public class PcsCaseService { private final DocumentService documentService; private final TenancyLicenceService tenancyLicenceService; private final AddressMapper addressMapper; - private final PcsCaseMergeService pcsCaseMergeService; public PcsCaseEntity createCase(long caseReference, AddressUK propertyAddress, @@ -68,21 +67,11 @@ public void patchCase(long caseReference, PCSCase pcsCase) { PcsCaseEntity pcsCaseEntity = loadCase(caseReference); log.info("Patching linked cases for {}", caseReference); - mergeCaseData(pcsCaseEntity, pcsCase); - - save(pcsCaseEntity); - } - - public void mergeCaseData(PcsCaseEntity pcsCaseEntity, PCSCase pcsCase) { - - pcsCaseMergeService.mergeCaseData(pcsCaseEntity, pcsCase); - - } - - public void save(PcsCaseEntity pcsCaseEntity) { + if (pcsCase.getCaseLinks() != null) { + pcsCaseEntity.mergeCaseLinks(pcsCase.getCaseLinks()); + } pcsCaseRepository.save(pcsCaseEntity); - } } diff --git a/src/main/resources/db/migration/V070__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V070__create_case_link_and_link_reason_tables.sql index f80a571f38..6c1c246738 100644 --- a/src/main/resources/db/migration/V070__create_case_link_and_link_reason_tables.sql +++ b/src/main/resources/db/migration/V070__create_case_link_and_link_reason_tables.sql @@ -23,18 +23,11 @@ DROP TABLE IF EXISTS case_link CASCADE; -- Table: case_link CREATE TABLE case_link ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- Source case - case_id UUID NOT NULL REFERENCES pcs_case(id) ON DELETE CASCADE, - - -- Linked case number (BIGINT) - linked_case_id BIGINT NOT NULL, - - -- CCD ListValue ID - ccd_list_id VARCHAR(50), - - created_at TIMESTAMP DEFAULT now() + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + case_id UUID NOT NULL REFERENCES pcs_case(id) ON DELETE CASCADE, -- Source case + linked_case_id BIGINT NOT NULL, -- Linked case number (BIGINT) + ccd_list_id VARCHAR(50), -- CCD ListValue ID + created_at TIMESTAMP DEFAULT now() ); -- Unique constraint on source + linked case @@ -43,13 +36,10 @@ CREATE UNIQUE INDEX uk_case_link_unique -- Table: case_link_reason CREATE TABLE case_link_reason ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- FK to case_link - case_link_id UUID NOT NULL REFERENCES case_link(id) ON DELETE CASCADE, - - reason_code VARCHAR(100) NOT NULL, - reason_text VARCHAR(255) + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + case_link_id UUID NOT NULL REFERENCES case_link(id) ON DELETE CASCADE, -- FK to case_link + reason_code VARCHAR(100) NOT NULL, + reason_text VARCHAR(255) ); -- Optional index for performance diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java index 13d7bda25f..5b6b68886c 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java @@ -42,7 +42,6 @@ import uk.gov.hmcts.reform.pcs.postcodecourt.model.LegislativeCountry; import uk.gov.hmcts.reform.pcs.security.SecurityContextService; -import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import java.util.Set; @@ -338,14 +337,15 @@ void shouldSetCaseFieldsInViewHelpers() { void shouldMapAndWrapCaseLinks() { // Given CaseLinkReasonEntity caseLinkReasonEntity1 = createCaseLinkReasonEntity(UUID.randomUUID(), - "CLR003", "Same Party"); + "CLR003", + "Same Party"); CaseLinkReasonEntity caseLinkReasonEntity2 = createCaseLinkReasonEntity(UUID.randomUUID(), "CLR010", "Bail"); CaseLinkEntity caseLinkEntity1 = createCaseLinkEntity(UUID.randomUUID(), List.of(caseLinkReasonEntity1), - 1234L, "CCD1"); + "CCD1"); CaseLinkEntity caseLinkEntity2 = createCaseLinkEntity(UUID.randomUUID(), List.of(caseLinkReasonEntity2), - 1234L, "CCD2"); + "CCD2"); when(pcsCaseEntity.getCaseLinks()).thenReturn(List.of(caseLinkEntity1, caseLinkEntity2)); @@ -358,8 +358,8 @@ void shouldMapAndWrapCaseLinks() { .value(linkReason1) .build()); - LinkReason linkReason2 = createLinkReason(caseLinkReasonEntity1.getReasonCode(), - caseLinkReasonEntity1.getReasonText()); + LinkReason linkReason2 = createLinkReason(caseLinkReasonEntity2.getReasonCode(), + caseLinkReasonEntity2.getReasonText()); List> linkReasons2 = List.of( ListValue.builder() .id(caseLinkReasonEntity2.getId().toString()) @@ -367,9 +367,9 @@ void shouldMapAndWrapCaseLinks() { .build()); CaseLink expectedCaseLink1 = creatCaseLink(String.valueOf(caseLinkEntity1.getLinkedCaseReference()), - caseLinkEntity1.getCcdListId(), linkReasons1, null); + caseLinkEntity1.getCcdListId(), linkReasons1); CaseLink expectedCaseLink2 = creatCaseLink(String.valueOf(caseLinkEntity2.getLinkedCaseReference()), - caseLinkEntity2.getCcdListId(), linkReasons2, null); + caseLinkEntity2.getCcdListId(), linkReasons2); // When @@ -395,26 +395,23 @@ private CaseLinkReasonEntity createCaseLinkReasonEntity(UUID id, String reasonCo .build(); } - private CaseLinkEntity createCaseLinkEntity(UUID id, List linkReasonEntities, - Long linkedCaseRef, String ccdId) { + private CaseLinkEntity createCaseLinkEntity(UUID id, List linkReasonEntities, String ccdId) { return CaseLinkEntity.builder() .id(id) - .linkedCaseReference(linkedCaseRef) + .linkedCaseReference(1234L) .ccdListId(ccdId) .reasons(linkReasonEntities) .pcsCase(pcsCaseEntity) .build(); } - private CaseLink creatCaseLink(String ref, String caseType, List> reasons, - LocalDateTime time) { + private CaseLink creatCaseLink(String ref, String caseType, List> reasons) { return CaseLink.builder() .caseReference(ref) .caseType(caseType) .reasonForLink(reasons) - .createdDateTime(time) .build(); } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java index 87fc7cbe21..6541101030 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java @@ -2,7 +2,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Spy; import static org.assertj.core.api.Assertions.assertThat; @@ -11,7 +10,6 @@ class CaseLinkEntityTest { - @Spy private CaseLinkEntity underTest; diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntityTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntityTest.java index eb82cae83c..a457246c4c 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntityTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntityTest.java @@ -2,9 +2,17 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.LinkReason; +import uk.gov.hmcts.ccd.sdk.type.ListValue; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.assertj.core.api.Assertions.assertThat; class PcsCaseEntityTest { @@ -30,4 +38,79 @@ void shouldUpdateCaseOnTenancyLicenceWhenSet() { verify(updatedTenancyLicence).setPcsCase(underTest); } + @Test + void shouldMergeCaseLinks() { + //Given + CaseLinkReasonEntity caseLinkReasonEntity = createCaseLinkReasonEntity(UUID.randomUUID(), + "CLR003", "Same Party"); + + CaseLinkEntity caseLinkEntity = createCaseLinkEntity(UUID.randomUUID(), List.of(caseLinkReasonEntity), + 1234L, "CCD1"); + + LinkReason linkReason = createLinkReason(caseLinkReasonEntity.getReasonCode(), + caseLinkReasonEntity.getReasonText()); + List> linkReasons = List.of( + ListValue.builder() + .id(caseLinkReasonEntity.getId().toString()) + .value(linkReason) + .build()); + CaseLink caseLink = creatCaseLink(String.valueOf(caseLinkEntity.getLinkedCaseReference()), + caseLinkEntity.getCcdListId(), linkReasons, null); + + List> caseLists = List.of( + ListValue.builder() + .id(UUID.randomUUID().toString()) + .value(caseLink) + .build()); + + // When + underTest.mergeCaseLinks(caseLists); + + // Then + assertThat(underTest.getCaseLinks()).hasSize(1); + } + + + private CaseLinkReasonEntity createCaseLinkReasonEntity(UUID id, String reasonCode, String reasonText) { + + CaseLinkEntity caseLinkEntity = mock(CaseLinkEntity.class); + + return CaseLinkReasonEntity.builder() + .id(id) + .reasonCode(reasonCode) + .reasonText(reasonText) + .caseLink(caseLinkEntity) + .build(); + } + + private CaseLinkEntity createCaseLinkEntity(UUID id, List linkReasonEntities, + Long linkedCaseRef, String ccdId) { + + return CaseLinkEntity.builder() + .id(id) + .linkedCaseReference(linkedCaseRef) + .ccdListId(ccdId) + .reasons(linkReasonEntities) + .build(); + } + + private CaseLink creatCaseLink(String ref, String caseType, List> reasons, + LocalDateTime time) { + + return CaseLink.builder() + .caseReference(ref) + .caseType(caseType) + .reasonForLink(reasons) + .createdDateTime(time) + .build(); + } + + private LinkReason createLinkReason(String reason, String description) { + + return LinkReason.builder() + .reason(reason) + .description(description) + .build(); + } + } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java deleted file mode 100644 index d0afa41226..0000000000 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseMergeServiceTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package uk.gov.hmcts.reform.pcs.ccd.service; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.modelmapper.ModelMapper; -import uk.gov.hmcts.ccd.sdk.type.AddressUK; -import uk.gov.hmcts.ccd.sdk.type.CaseLink; -import uk.gov.hmcts.ccd.sdk.type.LinkReason; -import uk.gov.hmcts.ccd.sdk.type.ListValue; -import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; -import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; -import uk.gov.hmcts.reform.pcs.ccd.repository.PcsCaseRepository; -import uk.gov.hmcts.reform.pcs.security.SecurityContextService; - -import java.util.List; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; - - - -@ExtendWith(MockitoExtension.class) -class PcsCaseMergeServiceTest { - - private static final long CASE_REFERENCE = 1234L; - - @Mock - private PcsCaseRepository pcsCaseRepository; - @Mock - private SecurityContextService securityContextService; - @Mock - private ModelMapper modelMapper; - @Mock - private TenancyLicenceService tenancyLicenceService; - - private PcsCaseMergeService underTest; - - @BeforeEach - void setUp() { - - underTest = new PcsCaseMergeService( - securityContextService, - modelMapper, - tenancyLicenceService - ); - } - - - @Test - void shouldMergeCaseData() { - - // Given - final PcsCaseEntity pcsCaseEntity = PcsCaseEntity.builder().build(); - - PCSCase caseData = PCSCase.builder().build(); - - List> caseLinks = createCaseLinks(); - - caseData.setPropertyAddress(new AddressUK( - "123 Great House", "Nice Street", "bla", "London", - "Greater London","SW18 1QT", "UK")); - - caseData.setCaseLinks(caseLinks); - - // When - underTest.mergeCaseData(pcsCaseEntity, caseData); - - // Then - assertThat(pcsCaseEntity.getCaseLinks()).hasSize(1); - assertThat(pcsCaseEntity.getCaseLinks().getFirst().getCcdListId()).isEqualTo("CCD"); - - } - - - private List> createCaseLinks() { - LinkReason linkReason = createLinkReason("CLR003", - ""); - List> linkReasons = List.of( - ListValue.builder() - .id(UUID.randomUUID().toString()) - .value(linkReason) - .build()); - - CaseLink caseLink = CaseLink.builder() - .caseReference(String.valueOf(CASE_REFERENCE)) - .caseType("CCD") - .reasonForLink(linkReasons) - .build(); - - return List.of( - ListValue.builder() - .id(UUID.randomUUID().toString()) - .value(caseLink) - .build()); - } - - private LinkReason createLinkReason(String reason, String description) { - - return LinkReason.builder() - .reason(reason) - .description(description) - .build(); - } -} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java index 6328e4c12d..ace2ee7e8a 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java @@ -48,8 +48,6 @@ class PcsCaseServiceTest { private TenancyLicenceService tenancyLicenceService; @Mock private AddressMapper addressMapper; - @Mock - private PcsCaseMergeService pcsCaseMergeService; @Captor private ArgumentCaptor pcsCaseEntityCaptor; @@ -64,8 +62,7 @@ void setUp() { partyService, documentService, tenancyLicenceService, - addressMapper, - pcsCaseMergeService + addressMapper ); } @@ -187,16 +184,17 @@ void shouldCreateTenancyLicenceWithMainClaimOnCase() { } @Test - void shouldMergeCaseData() { + void shouldPatchCaseData() { // Given - final PcsCaseEntity pcsCaseEntity = PcsCaseEntity.builder().build(); + PcsCaseEntity pcsCaseEntity = mock(PcsCaseEntity.class); PCSCase caseData = PCSCase.builder().build(); + when(pcsCaseRepository.findByCaseReference(CASE_REFERENCE)).thenReturn(java.util.Optional.of(pcsCaseEntity)); // When - underTest.mergeCaseData(pcsCaseEntity, caseData); + underTest.patchCase(CASE_REFERENCE, caseData); // Then - verify(pcsCaseMergeService, atLeastOnce()).mergeCaseData(pcsCaseEntity, caseData); + verify(pcsCaseEntity, atLeastOnce()).mergeCaseLinks(caseData.getCaseLinks()); } private PcsCaseEntity stubFindCase() { From 91f8f06938849b4d010153e3424405f917718258 Mon Sep 17 00:00:00 2001 From: aschalew Date: Tue, 24 Mar 2026 11:47:00 +0000 Subject: [PATCH 11/18] Updated code for PR comments --- .../ccd/accesscontrol/CaseLinkingAccess.java | 4 +++- .../hmcts/reform/pcs/ccd/domain/PCSCase.java | 2 +- .../reform/pcs/ccd/entity/CaseLinkEntity.java | 18 ++---------------- .../reform/pcs/ccd/entity/PcsCaseEntity.java | 13 +++++++++---- .../reform/pcs/ccd/event/CreateCaseLink.java | 1 + .../reform/pcs/ccd/event/MaintainLinkCase.java | 1 + ...create_case_link_and_link_reason_tables.sql | 6 +++--- .../accesscontrol/CaseLinkingAccessTest.java | 2 ++ .../pcs/ccd/entity/CaseLinkEntityTest.java | 11 ++++++++++- 9 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccess.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccess.java index e890f3316c..0b51c55a03 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccess.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/CaseLinkingAccess.java @@ -6,15 +6,17 @@ import uk.gov.hmcts.ccd.sdk.api.HasRole; import uk.gov.hmcts.ccd.sdk.api.Permission; +import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole.PCS_CASE_WORKER; import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole.PCS_SOLICITOR; -public class CaseLinkingAccess implements HasAccessControl { +public class CaseLinkingAccess implements HasAccessControl { @Override public SetMultimap getGrants() { SetMultimap grants = HashMultimap.create(); grants.putAll(PCS_SOLICITOR, Permission.CRU); + grants.put(PCS_CASE_WORKER, Permission.R); return grants; } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java index 923ae877b0..b971a995b1 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java @@ -123,7 +123,7 @@ public class PCSCase { @CCD( access = {CaseLinkingAccess.class}, - label = "Component Launcher (for displaying Linked Cases data" + label = "Component Launcher (for displaying Linked Cases data)" ) @JsonProperty("LinkedCasesComponentLauncher") private ComponentLauncher linkedCasesComponentLauncher; diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java index bf8abd704d..fb54ce5472 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java @@ -11,7 +11,6 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -23,11 +22,7 @@ import java.util.UUID; @Entity -@Table(name = "case_link", - uniqueConstraints = @UniqueConstraint( - name = "uk_case_link_unique", - columnNames = {"pcs_case_id", "linked_case_reference"} - )) +@Table(name = "case_link") @Setter @Getter @NoArgsConstructor @@ -40,7 +35,7 @@ public class CaseLinkEntity { private UUID id; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "case_id") + @JoinColumn(name = "case_link_reference") private PcsCaseEntity pcsCase; @Column(name = "linked_case_id", nullable = false) @@ -54,13 +49,4 @@ public class CaseLinkEntity { orphanRemoval = true) @Builder.Default private List reasons = new ArrayList<>(); - - public void addReason(String code, String text) { - CaseLinkReasonEntity caseLinkReasonEntity = new CaseLinkReasonEntity(); - caseLinkReasonEntity.setCaseLink(this); - caseLinkReasonEntity.setReasonCode(code); - caseLinkReasonEntity.setReasonText(text); - reasons.add(caseLinkReasonEntity); - - } } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java index 5921c38a04..ef01ca40c3 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java @@ -161,12 +161,17 @@ public void mergeCaseLinks(List> incomingLinkedCases) { caseLinkEntity.getReasons().clear(); if (dto.getReasonForLink() != null) { + List caseLinkReasonEntities = new ArrayList<>(); for (ListValue incomingLinkReason : dto.getReasonForLink()) { - caseLinkEntity.addReason( - incomingLinkReason.getValue().getReason(), - incomingLinkReason.getValue().getDescription() - ); + CaseLinkReasonEntity caseLinkReasonEntity = CaseLinkReasonEntity.builder() + .caseLink(caseLinkEntity) + .reasonCode(incomingLinkReason.getValue().getReason()) + .reasonText(incomingLinkReason.getValue().getDescription()) + .build(); + caseLinkReasonEntities.add(caseLinkReasonEntity); } + caseLinkEntity.getReasons().clear(); + caseLinkEntity.getReasons().addAll(caseLinkReasonEntities); } result.add(caseLinkEntity); diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java index 98cba52046..a882dc8093 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java @@ -29,6 +29,7 @@ public void configureDecentralised(DecentralisedConfigBuilder grants = underTest.getGrants(); assertThat(grants.asMap()).contains(entry(PCS_SOLICITOR, Permission.CRU)); + assertThat(grants.get(PCS_CASE_WORKER)).contains(Permission.R); } } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java index 6541101030..413ec6aaf8 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java @@ -4,6 +4,8 @@ import org.junit.jupiter.api.Test; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; @@ -23,9 +25,16 @@ void shouldAddCaseLinkReason() { // Given String reasonCode = "CLR003"; String reasonText = "Same Party"; + CaseLinkEntity caseLinkEntity = CaseLinkEntity.builder().build(); + CaseLinkReasonEntity caseLinkReasonEntity = CaseLinkReasonEntity.builder() + .caseLink(caseLinkEntity) + .reasonCode(reasonCode) + .reasonText(reasonText) + .build(); + // When - underTest.addReason(reasonCode, reasonText); + underTest.setReasons(List.of(caseLinkReasonEntity)); // Then assertThat(underTest.getReasons()).hasSize(1); From 6e13ebbd6b648cdc00d9653cb509c639a8221953 Mon Sep 17 00:00:00 2001 From: aschalew Date: Wed, 25 Mar 2026 14:04:20 +0000 Subject: [PATCH 12/18] Renamed V070 to V071 --- ...bles.sql => V071__create_case_link_and_link_reason_tables.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V070__create_case_link_and_link_reason_tables.sql => V071__create_case_link_and_link_reason_tables.sql} (100%) diff --git a/src/main/resources/db/migration/V070__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V071__create_case_link_and_link_reason_tables.sql similarity index 100% rename from src/main/resources/db/migration/V070__create_case_link_and_link_reason_tables.sql rename to src/main/resources/db/migration/V071__create_case_link_and_link_reason_tables.sql From 3922036d221df3b3c07867766704ab680e7c8d2d Mon Sep 17 00:00:00 2001 From: aschalew Date: Thu, 26 Mar 2026 14:14:44 +0000 Subject: [PATCH 13/18] Updated code for PR comments --- build.gradle | 2 - .../gov/hmcts/reform/pcs/ccd/PCSCaseView.java | 43 +------- .../reform/pcs/ccd/event/CreateCaseLink.java | 2 +- .../pcs/ccd/event/MaintainLinkCase.java | 4 +- .../pcs/ccd/service/PcsCaseService.java | 5 +- .../reform/pcs/ccd/view/CaseLinkView.java | 55 ++++++++++ ...reate_case_link_and_link_reason_tables.sql | 47 -------- ...reate_case_link_and_link_reason_tables.sql | 24 +++++ .../hmcts/reform/pcs/ccd/PCSCaseViewTest.java | 100 +----------------- .../pcs/ccd/event/CreateCaseLinkTest.java | 4 +- .../pcs/ccd/event/MaintainLinkCaseTest.java | 4 +- .../pcs/ccd/service/PcsCaseServiceTest.java | 2 +- .../reform/pcs/ccd/view/CaseLinkViewTest.java | 77 ++++++++++++++ 13 files changed, 173 insertions(+), 196 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java delete mode 100644 src/main/resources/db/migration/V071__create_case_link_and_link_reason_tables.sql create mode 100644 src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java diff --git a/build.gradle b/build.gradle index c8f21bf661..78d5faa4d3 100644 --- a/build.gradle +++ b/build.gradle @@ -327,8 +327,6 @@ sonarqube { "**/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java," + "**/uk/gov/hmcts/reform/pcs/ccd/page/**/*.java," + "**/uk/gov/hmcts/reform/pcs/ccd/entity/**/*.java," + - "**/uk/gov/hmcts/reform/pcs/ccd/event/**/*.java," + - "**/uk/gov/hmcts/reform/pcs/ccd/service/**/*.java," + "**/entities/**," + "**/model/**," + "**/uk/gov/hmcts/reform/pcs/testingsupport/**/*.java" property "sonar.cpd.exclusions", "**/uk/gov/hmcts/reform/pcs/ccd/domain/enforcetheorder/warrant/StatementOfTruthDetails.java" } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java index 28f2104baf..097cdb1ae0 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java @@ -6,9 +6,7 @@ import uk.gov.hmcts.ccd.sdk.CaseView; import uk.gov.hmcts.ccd.sdk.CaseViewRequest; import uk.gov.hmcts.ccd.sdk.type.AddressUK; -import uk.gov.hmcts.ccd.sdk.type.CaseLink; import uk.gov.hmcts.ccd.sdk.type.Document; -import uk.gov.hmcts.ccd.sdk.type.LinkReason; import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; import uk.gov.hmcts.ccd.sdk.type.SearchCriteria; @@ -36,6 +34,7 @@ import uk.gov.hmcts.reform.pcs.ccd.view.RentDetailsView; import uk.gov.hmcts.reform.pcs.ccd.view.StatementOfTruthView; import uk.gov.hmcts.reform.pcs.ccd.view.TenancyLicenceView; +import uk.gov.hmcts.reform.pcs.ccd.view.CaseLinkView; import uk.gov.hmcts.reform.pcs.exception.CaseNotFoundException; import uk.gov.hmcts.reform.pcs.security.SecurityContextService; @@ -71,6 +70,7 @@ public class PCSCaseView implements CaseView { private final NoticeOfPossessionView noticeOfPossessionView; private final StatementOfTruthView statementOfTruthView; private final CaseNameHmctsFormatter caseNameHmctsFormatter; + private final CaseLinkView caseLinkView; /** @@ -116,7 +116,6 @@ private PCSCase getSubmittedCase(long caseReference) { .allDefendants(partyMap.get(PartyRole.DEFENDANT)) .allUnderlesseeOrMortgagees(partyMap.get(PartyRole.UNDERLESSEE_OR_MORTGAGEE)) .allDocuments(mapAndWrapDocuments(pcsCaseEntity)) - .caseLinks(mapAndWrapCaseLinks(pcsCaseEntity)) .build(); setDerivedProperties(pcsCase, pcsCaseEntity); @@ -132,6 +131,7 @@ private PCSCase getSubmittedCase(long caseReference) { rentArrearsView.setCaseFields(pcsCase, pcsCaseEntity); noticeOfPossessionView.setCaseFields(pcsCase, pcsCaseEntity); statementOfTruthView.setCaseFields(pcsCase, pcsCaseEntity); + caseLinkView.setCaseFields(pcsCase, pcsCaseEntity); return pcsCase; } @@ -256,41 +256,4 @@ private List> mapAndWrapDocuments(PcsCaseEntity pcsCaseEntit .build()) .collect(Collectors.toList()); } - - private List> mapAndWrapCaseLinks(PcsCaseEntity pcsCaseEntity) { - - if (pcsCaseEntity.getCaseLinks().isEmpty()) { - return List.of(); - } - - return pcsCaseEntity.getCaseLinks().stream() - .map(linkEntity -> ListValue.builder() - .id(linkEntity.getLinkedCaseReference().toString()) - .value( - CaseLink.builder() - // BIGINT linked case reference -> CCD expects String - .caseReference(linkEntity.getLinkedCaseReference().toString()) - .caseType(linkEntity.getCcdListId()) - // map reasons - .reasonForLink( - linkEntity.getReasons().stream() - .map(reasonEntity -> ListValue.builder() - .id(reasonEntity.getId().toString()) - .value( - LinkReason.builder() - .reason(reasonEntity.getReasonCode()) - .description(reasonEntity.getReasonText()) - .build() - ) - .build() - ) - .toList() - ) - .build() - ) - .build() - ) - .toList(); - } - } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java index a882dc8093..db81b1d6ca 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java @@ -45,7 +45,7 @@ private SubmitResponse submit(EventPayload eventPayload) log.info("Caseworker created case link for {}", caseReference); - pcsCaseService.patchCase(caseReference, pcsCase); + pcsCaseService.patchCaseLinks(caseReference, pcsCase); return SubmitResponse.defaultResponse(); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCase.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCase.java index bf1d7c9d2b..b34bd7f3d1 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCase.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCase.java @@ -28,7 +28,7 @@ public void configureDecentralised(DecentralisedConfigBuilder submit(EventPayload eventPayload) log.info("Caseworker updated case link for {}", caseReference); - pcsCaseService.patchCase(caseReference, pcsCase); + pcsCaseService.patchCaseLinks(caseReference, pcsCase); return SubmitResponse.defaultResponse(); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java index 9d581edde3..34221bfc92 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java @@ -63,15 +63,12 @@ public PcsCaseEntity loadCase(long caseReference) { .orElseThrow(() -> new CaseNotFoundException(caseReference)); } - public void patchCase(long caseReference, PCSCase pcsCase) { + public void patchCaseLinks(long caseReference, PCSCase pcsCase) { PcsCaseEntity pcsCaseEntity = loadCase(caseReference); log.info("Patching linked cases for {}", caseReference); if (pcsCase.getCaseLinks() != null) { pcsCaseEntity.mergeCaseLinks(pcsCase.getCaseLinks()); } - - pcsCaseRepository.save(pcsCaseEntity); } - } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java new file mode 100644 index 0000000000..01136a16fe --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java @@ -0,0 +1,55 @@ +package uk.gov.hmcts.reform.pcs.ccd.view; + +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.LinkReason; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class CaseLinkView { + + public void setCaseFields(PCSCase pcsCase, PcsCaseEntity pcsCaseEntity) { + if (!pcsCaseEntity.getCaseLinks().isEmpty()) { + setCaseLinks(pcsCase, pcsCaseEntity); + } + } + + private void setCaseLinks(PCSCase pcsCase, PcsCaseEntity pcsCaseEntity) { + List> caseLinks = new ArrayList<>(); + + if (!pcsCaseEntity.getCaseLinks().isEmpty()) { + caseLinks = pcsCaseEntity.getCaseLinks().stream() + .map(linkEntity -> ListValue.builder() + .id(linkEntity.getLinkedCaseReference().toString()) + .value( + CaseLink.builder() + .caseReference(linkEntity.getLinkedCaseReference().toString()) + .caseType(linkEntity.getCcdListId()) + .reasonForLink( + linkEntity.getReasons().stream() + .map(reasonEntity -> ListValue.builder() + .id(reasonEntity.getId().toString()) + .value( + LinkReason.builder() + .reason(reasonEntity.getReasonCode()) + .description(reasonEntity.getReasonText()) + .build() + ) + .build() + ) + .toList() + ) + .build() + ) + .build() + ) + .toList(); + } + pcsCase.setCaseLinks(caseLinks); + } +} diff --git a/src/main/resources/db/migration/V071__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V071__create_case_link_and_link_reason_tables.sql deleted file mode 100644 index 041286d3bd..0000000000 --- a/src/main/resources/db/migration/V071__create_case_link_and_link_reason_tables.sql +++ /dev/null @@ -1,47 +0,0 @@ --- ============================================ --- DROP OLD CASE LINK STRUCTURE --- ============================================ - --- Drop index on case_link if it exists -DROP INDEX IF EXISTS ux_case_link_unique; - --- Drop index on case_link_reason if it exists -DROP INDEX IF EXISTS idx_case_link_reason_link; - --- Drop case_link_reason table first (child table) -DROP TABLE IF EXISTS case_link_reason CASCADE; - --- Drop case_link table -DROP TABLE IF EXISTS case_link CASCADE; - --- ============================================ --- PCS CASE LINK + LINK REASONS --- ============================================ --- ============================================ --- PCS CASE LINK + LINK REASONS --- ============================================ - --- Table: case_link -CREATE TABLE case_link ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - case_link_reference UUID NOT NULL REFERENCES pcs_case(id) ON DELETE CASCADE, -- Source case - linked_case_id BIGINT NOT NULL, -- Linked case number (BIGINT) - ccd_list_id VARCHAR(50), -- CCD ListValue ID - created_at TIMESTAMP DEFAULT now() -); - --- Unique constraint on source + linked case -CREATE UNIQUE INDEX ux_case_link_unique - ON case_link(case_link_reference, linked_case_id); - --- Table: case_link_reason -CREATE TABLE case_link_reason ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - case_link_id UUID NOT NULL REFERENCES case_link(id) ON DELETE CASCADE, -- FK to case_link - reason_code VARCHAR(100) NOT NULL, - reason_text VARCHAR(255) -); - --- Optional index for performance -CREATE INDEX idx_case_link_reason_link - ON case_link_reason(case_link_id); diff --git a/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql new file mode 100644 index 0000000000..9ba9d322e8 --- /dev/null +++ b/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql @@ -0,0 +1,24 @@ +-- ============================================ +-- PCS CASE LINK + LINK REASONS +-- ============================================ +CREATE TABLE case_link ( + id UUID PRIMARY KEY, + case_link_reference UUID NOT NULL REFERENCES pcs_case(id) ON DELETE CASCADE, + linked_case_id BIGINT NOT NULL, + ccd_list_id VARCHAR(50), + created_at TIMESTAMP DEFAULT now() +); + +CREATE UNIQUE INDEX ux_case_link_unique + ON case_link(case_link_reference, linked_case_id); + + +CREATE TABLE case_link_reason ( + id UUID PRIMARY KEY, + case_link_id UUID NOT NULL REFERENCES case_link(id) ON DELETE CASCADE, + reason_code VARCHAR(100) NOT NULL, + reason_text VARCHAR(255) +); + +CREATE INDEX idx_case_link_reason_link + ON case_link_reason(case_link_id); diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java index 561173d364..00f7fdcb2a 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseViewTest.java @@ -9,8 +9,6 @@ import org.modelmapper.ModelMapper; import uk.gov.hmcts.ccd.sdk.CaseViewRequest; import uk.gov.hmcts.ccd.sdk.type.AddressUK; -import uk.gov.hmcts.ccd.sdk.type.CaseLink; -import uk.gov.hmcts.ccd.sdk.type.LinkReason; import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; import uk.gov.hmcts.reform.pcs.ccd.domain.Party; @@ -22,8 +20,6 @@ import uk.gov.hmcts.reform.pcs.ccd.entity.party.ClaimPartyEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyRole; -import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkEntity; -import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkReasonEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.party.ClaimPartyId; import uk.gov.hmcts.reform.pcs.ccd.repository.PcsCaseRepository; import uk.gov.hmcts.reform.pcs.ccd.service.CaseTitleService; @@ -39,6 +35,7 @@ import uk.gov.hmcts.reform.pcs.ccd.view.RentDetailsView; import uk.gov.hmcts.reform.pcs.ccd.view.StatementOfTruthView; import uk.gov.hmcts.reform.pcs.ccd.view.TenancyLicenceView; +import uk.gov.hmcts.reform.pcs.ccd.view.CaseLinkView; import uk.gov.hmcts.reform.pcs.exception.CaseNotFoundException; import uk.gov.hmcts.reform.pcs.postcodecourt.model.LegislativeCountry; import uk.gov.hmcts.reform.pcs.security.SecurityContextService; @@ -100,6 +97,8 @@ class PCSCaseViewTest { private ClaimEntity claimEntity; @Mock private CaseNameHmctsFormatter caseNameHmctsFormatter; + @Mock + private CaseLinkView caseLinkView; private PCSCaseView underTest; @@ -112,7 +111,7 @@ void setUp() { caseTitleService, claimView, tenancyLicenceView, claimGroundsView, rentDetailsView, alternativesToPossessionView, housingActWalesView, asbProhibitedConductView, rentArrearsView, noticeOfPossessionView, - statementOfTruthView, caseNameHmctsFormatter + statementOfTruthView, caseNameHmctsFormatter, caseLinkView ); } @@ -336,96 +335,7 @@ void shouldSetCaseFieldsInViewHelpers() { verify(rentArrearsView).setCaseFields(pcsCase, pcsCaseEntity); verify(noticeOfPossessionView).setCaseFields(pcsCase, pcsCaseEntity); verify(statementOfTruthView).setCaseFields(pcsCase, pcsCaseEntity); - } - - @Test - void shouldMapAndWrapCaseLinks() { - // Given - CaseLinkReasonEntity caseLinkReasonEntity1 = createCaseLinkReasonEntity(UUID.randomUUID(), - "CLR003", - "Same Party"); - CaseLinkReasonEntity caseLinkReasonEntity2 = createCaseLinkReasonEntity(UUID.randomUUID(), - "CLR010", "Bail"); - - CaseLinkEntity caseLinkEntity1 = createCaseLinkEntity(UUID.randomUUID(), List.of(caseLinkReasonEntity1), - "CCD1"); - CaseLinkEntity caseLinkEntity2 = createCaseLinkEntity(UUID.randomUUID(), List.of(caseLinkReasonEntity2), - "CCD2"); - - when(pcsCaseEntity.getCaseLinks()).thenReturn(List.of(caseLinkEntity1, caseLinkEntity2)); - - - LinkReason linkReason1 = createLinkReason(caseLinkReasonEntity1.getReasonCode(), - caseLinkReasonEntity1.getReasonText()); - List> linkReasons1 = List.of( - ListValue.builder() - .id(caseLinkReasonEntity1.getId().toString()) - .value(linkReason1) - .build()); - - LinkReason linkReason2 = createLinkReason(caseLinkReasonEntity2.getReasonCode(), - caseLinkReasonEntity2.getReasonText()); - List> linkReasons2 = List.of( - ListValue.builder() - .id(caseLinkReasonEntity2.getId().toString()) - .value(linkReason2) - .build()); - - CaseLink expectedCaseLink1 = creatCaseLink(String.valueOf(caseLinkEntity1.getLinkedCaseReference()), - caseLinkEntity1.getCcdListId(), linkReasons1); - CaseLink expectedCaseLink2 = creatCaseLink(String.valueOf(caseLinkEntity2.getLinkedCaseReference()), - caseLinkEntity2.getCcdListId(), linkReasons2); - - - // When - PCSCase pcsCase = underTest.getCase(request(CASE_REFERENCE, DEFAULT_STATE)); - - // Then - List> mappedCaseLinks = pcsCase.getCaseLinks(); - assertThat(mappedCaseLinks).hasSize(2); - assertThat(mappedCaseLinks.getFirst().getValue().getCaseReference()).isEqualTo( - expectedCaseLink1.getCaseReference()); - assertThat(mappedCaseLinks.get(1).getValue().getCaseType()).isEqualTo(expectedCaseLink2.getCaseType()); - } - - private CaseLinkReasonEntity createCaseLinkReasonEntity(UUID id, String reasonCode, String reasonText) { - - CaseLinkEntity caseLinkEntity = mock(CaseLinkEntity.class); - - return CaseLinkReasonEntity.builder() - .id(id) - .reasonCode(reasonCode) - .reasonText(reasonText) - .caseLink(caseLinkEntity) - .build(); - } - - private CaseLinkEntity createCaseLinkEntity(UUID id, List linkReasonEntities, String ccdId) { - - return CaseLinkEntity.builder() - .id(id) - .linkedCaseReference(1234L) - .ccdListId(ccdId) - .reasons(linkReasonEntities) - .pcsCase(pcsCaseEntity) - .build(); - } - - private CaseLink creatCaseLink(String ref, String caseType, List> reasons) { - - return CaseLink.builder() - .caseReference(ref) - .caseType(caseType) - .reasonForLink(reasons) - .build(); - } - - private LinkReason createLinkReason(String reason, String description) { - - return LinkReason.builder() - .reason(reason) - .description(description) - .build(); + verify(caseLinkView).setCaseFields(pcsCase, pcsCaseEntity); } @Test diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLinkTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLinkTest.java index 7b68bc375d..7a0be5e7e3 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLinkTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLinkTest.java @@ -46,12 +46,12 @@ void shouldCreateCaseLinksInSubmitCallback() { .build()); PCSCase pcsCase = PCSCase.builder().caseLinks(caseLists).build(); - doNothing().when(pcsCaseService).patchCase(TEST_CASE_REFERENCE, pcsCase); + doNothing().when(pcsCaseService).patchCaseLinks(TEST_CASE_REFERENCE, pcsCase); // When callSubmitHandler(pcsCase); // Then - verify(pcsCaseService).patchCase(TEST_CASE_REFERENCE, pcsCase); + verify(pcsCaseService).patchCaseLinks(TEST_CASE_REFERENCE, pcsCase); } } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCaseTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCaseTest.java index 6d1e7ad080..ca9e212196 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCaseTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/MaintainLinkCaseTest.java @@ -44,12 +44,12 @@ void shouldModifyCaseLinksInSubmitCallback() { .build()); PCSCase pcsCase = PCSCase.builder().caseLinks(caseLists).build(); - doNothing().when(pcsCaseService).patchCase(TEST_CASE_REFERENCE, pcsCase); + doNothing().when(pcsCaseService).patchCaseLinks(TEST_CASE_REFERENCE, pcsCase); // When callSubmitHandler(pcsCase); // Then - verify(pcsCaseService).patchCase(TEST_CASE_REFERENCE, pcsCase); + verify(pcsCaseService).patchCaseLinks(TEST_CASE_REFERENCE, pcsCase); } } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java index ace2ee7e8a..d2d6fc6933 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java @@ -191,7 +191,7 @@ void shouldPatchCaseData() { when(pcsCaseRepository.findByCaseReference(CASE_REFERENCE)).thenReturn(java.util.Optional.of(pcsCaseEntity)); // When - underTest.patchCase(CASE_REFERENCE, caseData); + underTest.patchCaseLinks(CASE_REFERENCE, caseData); // Then verify(pcsCaseEntity, atLeastOnce()).mergeCaseLinks(caseData.getCaseLinks()); diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java new file mode 100644 index 0000000000..28af8c327d --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java @@ -0,0 +1,77 @@ +package uk.gov.hmcts.reform.pcs.ccd.view; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkReasonEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CaseLinkViewTest { + + private PCSCase pcsCase; + + @Mock + private PcsCaseEntity pcsCaseEntity; + @Mock(strictness = LENIENT) + private CaseLinkEntity caseLinkEntity; + private CaseLinkView underTest; + + @BeforeEach + void setUp() { + pcsCase = PCSCase.builder().build(); + underTest = new CaseLinkView(); + } + + + @Test + void shouldMapAndWrapCaseLinks() { + // Given + CaseLinkReasonEntity caseLinkReasonEntity1 = createCaseLinkReasonEntity(UUID.randomUUID(), "CLR003", + "Same Party"); + CaseLinkReasonEntity caseLinkReasonEntity2 = createCaseLinkReasonEntity(UUID.randomUUID(), + "CLR010", "Bail"); + when(pcsCaseEntity.getCaseLinks()).thenReturn(List.of(caseLinkEntity)); + when(caseLinkEntity.getLinkedCaseReference()).thenReturn(1234L); + when(caseLinkEntity.getReasons()).thenReturn(List.of(caseLinkReasonEntity1, caseLinkReasonEntity2)); + when(caseLinkEntity.getCcdListId()).thenReturn("PCS"); + + // When + underTest.setCaseFields(pcsCase, pcsCaseEntity); + + // Then + List> mappedCaseLinks = pcsCase.getCaseLinks(); + assertThat(mappedCaseLinks).hasSize(1); + assertThat(mappedCaseLinks.getFirst().getValue().getCaseReference()).isEqualTo("1234"); + assertThat(mappedCaseLinks.getFirst().getValue().getCaseType()).isEqualTo("PCS"); + assertThat(mappedCaseLinks.getFirst().getValue().getReasonForLink().getFirst().getValue().getReason()) + .isEqualTo("CLR003"); + assertThat(mappedCaseLinks.getFirst().getValue().getReasonForLink().getFirst().getValue().getDescription()) + .isEqualTo("Same Party"); + } + + private CaseLinkReasonEntity createCaseLinkReasonEntity(UUID id, String reasonCode, String reasonText) { + CaseLinkEntity caseLinkEntity = mock(CaseLinkEntity.class); + + return CaseLinkReasonEntity.builder() + .id(id) + .reasonCode(reasonCode) + .reasonText(reasonText) + .caseLink(caseLinkEntity) + .build(); + } +} From ce7a640c25766175f67b6c2f768d11599b6a466d Mon Sep 17 00:00:00 2001 From: aschalew Date: Fri, 27 Mar 2026 08:44:11 +0000 Subject: [PATCH 14/18] Fixed a code smell from Sonar cube --- .../java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java index 28af8c327d..92f18adf32 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java @@ -17,7 +17,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mock.Strictness.LENIENT; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -65,7 +64,6 @@ void shouldMapAndWrapCaseLinks() { } private CaseLinkReasonEntity createCaseLinkReasonEntity(UUID id, String reasonCode, String reasonText) { - CaseLinkEntity caseLinkEntity = mock(CaseLinkEntity.class); return CaseLinkReasonEntity.builder() .id(id) From 26e23f7375c822943647f43e0faac975cbc7d335 Mon Sep 17 00:00:00 2001 From: aschalew Date: Fri, 27 Mar 2026 13:22:33 +0000 Subject: [PATCH 15/18] Updated PK names in case_link table from PR review comment --- .../hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java | 4 ++-- .../hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java | 12 +++--------- ...V072__create_case_link_and_link_reason_tables.sql | 6 +++--- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java index fb54ce5472..a00ae4f4ae 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntity.java @@ -35,10 +35,10 @@ public class CaseLinkEntity { private UUID id; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "case_link_reference") + @JoinColumn(name = "case_id") private PcsCaseEntity pcsCase; - @Column(name = "linked_case_id", nullable = false) + @Column(name = "linked_case_reference", nullable = false) private Long linkedCaseReference; @Column(name = "ccd_list_id") diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java index ef01ca40c3..97513db359 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java @@ -131,18 +131,13 @@ public void addDefendantResponse(DefendantResponseEntity defendantResponse) { public void mergeCaseLinks(List> incomingLinkedCases) { - if (incomingLinkedCases == null) { - this.caseLinks.clear(); - return; - } - Map existingLinkedCases = this.caseLinks.stream() .collect(Collectors.toMap(CaseLinkEntity::getLinkedCaseReference, Function.identity() )); - List result = new ArrayList<>(); + List mergedCaseLinkEntities = new ArrayList<>(); for (ListValue caseLinkListValue : incomingLinkedCases) { CaseLink dto = caseLinkListValue.getValue(); @@ -174,11 +169,10 @@ public void mergeCaseLinks(List> incomingLinkedCases) { caseLinkEntity.getReasons().addAll(caseLinkReasonEntities); } - result.add(caseLinkEntity); + mergedCaseLinkEntities.add(caseLinkEntity); } this.caseLinks.clear(); - this.caseLinks.addAll(result); - + this.caseLinks.addAll(mergedCaseLinkEntities); } } diff --git a/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql index 9ba9d322e8..aea6b0f46a 100644 --- a/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql +++ b/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql @@ -3,14 +3,14 @@ -- ============================================ CREATE TABLE case_link ( id UUID PRIMARY KEY, - case_link_reference UUID NOT NULL REFERENCES pcs_case(id) ON DELETE CASCADE, - linked_case_id BIGINT NOT NULL, + case_id UUID NOT NULL REFERENCES pcs_case(id) ON DELETE CASCADE, + linked_case_reference BIGINT NOT NULL, ccd_list_id VARCHAR(50), created_at TIMESTAMP DEFAULT now() ); CREATE UNIQUE INDEX ux_case_link_unique - ON case_link(case_link_reference, linked_case_id); + ON case_link(case_id, linked_case_reference); CREATE TABLE case_link_reason ( From 113955378e2fe932f74cf68469d900318221b026 Mon Sep 17 00:00:00 2001 From: aschalew Date: Fri, 27 Mar 2026 15:29:26 +0000 Subject: [PATCH 16/18] Moved mergeCaseLinks() method into CaseLinkService class --- .../reform/pcs/ccd/entity/PcsCaseEntity.java | 53 ---------- .../pcs/ccd/service/CaseLinkService.java | 68 ++++++++++++ .../pcs/ccd/service/PcsCaseService.java | 3 +- .../pcs/ccd/entity/PcsCaseEntityTest.java | 84 --------------- .../pcs/ccd/service/CaseLinkServiceTest.java | 100 ++++++++++++++++++ .../pcs/ccd/service/PcsCaseServiceTest.java | 7 +- 6 files changed, 175 insertions(+), 140 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkService.java create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java index 97513db359..820b362222 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntity.java @@ -16,9 +16,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import uk.gov.hmcts.ccd.sdk.type.CaseLink; -import uk.gov.hmcts.ccd.sdk.type.LinkReason; -import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.reform.pcs.ccd.domain.ClaimantType; import uk.gov.hmcts.reform.pcs.ccd.entity.party.PartyEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.respondpossessionclaim.DefendantResponseEntity; @@ -27,11 +24,8 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.function.Function; -import java.util.stream.Collectors; import static jakarta.persistence.CascadeType.ALL; import static jakarta.persistence.FetchType.LAZY; @@ -128,51 +122,4 @@ public void addDefendantResponse(DefendantResponseEntity defendantResponse) { defendantResponses.add(defendantResponse); defendantResponse.setPcsCase(this); } - - public void mergeCaseLinks(List> incomingLinkedCases) { - - Map existingLinkedCases = - this.caseLinks.stream() - .collect(Collectors.toMap(CaseLinkEntity::getLinkedCaseReference, - Function.identity() - )); - - List mergedCaseLinkEntities = new ArrayList<>(); - - for (ListValue caseLinkListValue : incomingLinkedCases) { - CaseLink dto = caseLinkListValue.getValue(); - Long incomingCaseRef = Long.valueOf(dto.getCaseReference()); - - CaseLinkEntity caseLinkEntity = existingLinkedCases.remove(incomingCaseRef); - - if (caseLinkEntity == null) { - caseLinkEntity = new CaseLinkEntity(); - caseLinkEntity.setPcsCase(this); - caseLinkEntity.setLinkedCaseReference(incomingCaseRef); - } - - caseLinkEntity.setCcdListId(dto.getCaseType()); - - caseLinkEntity.getReasons().clear(); - - if (dto.getReasonForLink() != null) { - List caseLinkReasonEntities = new ArrayList<>(); - for (ListValue incomingLinkReason : dto.getReasonForLink()) { - CaseLinkReasonEntity caseLinkReasonEntity = CaseLinkReasonEntity.builder() - .caseLink(caseLinkEntity) - .reasonCode(incomingLinkReason.getValue().getReason()) - .reasonText(incomingLinkReason.getValue().getDescription()) - .build(); - caseLinkReasonEntities.add(caseLinkReasonEntity); - } - caseLinkEntity.getReasons().clear(); - caseLinkEntity.getReasons().addAll(caseLinkReasonEntities); - } - - mergedCaseLinkEntities.add(caseLinkEntity); - } - - this.caseLinks.clear(); - this.caseLinks.addAll(mergedCaseLinkEntities); - } } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkService.java new file mode 100644 index 0000000000..0adb8f69bc --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkService.java @@ -0,0 +1,68 @@ +package uk.gov.hmcts.reform.pcs.ccd.service; + +import org.springframework.stereotype.Service; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.LinkReason; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkReasonEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +public class CaseLinkService { + + public void mergeCaseLinks(List> incomingLinkedCases, PcsCaseEntity pcsCaseEntity) { + + Map existingLinkedCases = + pcsCaseEntity.getCaseLinks().stream() + .collect(Collectors.toMap(CaseLinkEntity::getLinkedCaseReference, + Function.identity() + )); + + List mergedCaseLinkEntities = new ArrayList<>(); + + for (ListValue caseLinkListValue : incomingLinkedCases) { + CaseLink dto = caseLinkListValue.getValue(); + Long incomingCaseRef = Long.valueOf(dto.getCaseReference()); + + CaseLinkEntity caseLinkEntity = existingLinkedCases.remove(incomingCaseRef); + + if (caseLinkEntity == null) { + caseLinkEntity = new CaseLinkEntity(); + caseLinkEntity.setPcsCase(pcsCaseEntity); + caseLinkEntity.setLinkedCaseReference(incomingCaseRef); + } + + caseLinkEntity.setCcdListId(dto.getCaseType()); + + caseLinkEntity.getReasons().clear(); + + if (dto.getReasonForLink() != null) { + List caseLinkReasonEntities = new ArrayList<>(); + for (ListValue incomingLinkReason : dto.getReasonForLink()) { + CaseLinkReasonEntity caseLinkReasonEntity = CaseLinkReasonEntity.builder() + .caseLink(caseLinkEntity) + .reasonCode(incomingLinkReason.getValue().getReason()) + .reasonText(incomingLinkReason.getValue().getDescription()) + .build(); + caseLinkReasonEntities.add(caseLinkReasonEntity); + } + caseLinkEntity.getReasons().clear(); + caseLinkEntity.getReasons().addAll(caseLinkReasonEntities); + } + + mergedCaseLinkEntities.add(caseLinkEntity); + } + + pcsCaseEntity.getCaseLinks().clear(); + pcsCaseEntity.getCaseLinks().addAll(mergedCaseLinkEntities); + + } +} + diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java index 34221bfc92..d9ad85e848 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseService.java @@ -28,6 +28,7 @@ public class PcsCaseService { private final DocumentService documentService; private final TenancyLicenceService tenancyLicenceService; private final AddressMapper addressMapper; + private final CaseLinkService caseLinkService; public PcsCaseEntity createCase(long caseReference, AddressUK propertyAddress, @@ -68,7 +69,7 @@ public void patchCaseLinks(long caseReference, PCSCase pcsCase) { log.info("Patching linked cases for {}", caseReference); if (pcsCase.getCaseLinks() != null) { - pcsCaseEntity.mergeCaseLinks(pcsCase.getCaseLinks()); + caseLinkService.mergeCaseLinks(pcsCase.getCaseLinks(), pcsCaseEntity); } } } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntityTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntityTest.java index a457246c4c..fae4b13195 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntityTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/PcsCaseEntityTest.java @@ -2,17 +2,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import uk.gov.hmcts.ccd.sdk.type.CaseLink; -import uk.gov.hmcts.ccd.sdk.type.LinkReason; -import uk.gov.hmcts.ccd.sdk.type.ListValue; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.UUID; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.assertj.core.api.Assertions.assertThat; class PcsCaseEntityTest { @@ -37,80 +29,4 @@ void shouldUpdateCaseOnTenancyLicenceWhenSet() { verify(existingTenancyLicence).setPcsCase(null); verify(updatedTenancyLicence).setPcsCase(underTest); } - - @Test - void shouldMergeCaseLinks() { - //Given - CaseLinkReasonEntity caseLinkReasonEntity = createCaseLinkReasonEntity(UUID.randomUUID(), - "CLR003", "Same Party"); - - CaseLinkEntity caseLinkEntity = createCaseLinkEntity(UUID.randomUUID(), List.of(caseLinkReasonEntity), - 1234L, "CCD1"); - - LinkReason linkReason = createLinkReason(caseLinkReasonEntity.getReasonCode(), - caseLinkReasonEntity.getReasonText()); - List> linkReasons = List.of( - ListValue.builder() - .id(caseLinkReasonEntity.getId().toString()) - .value(linkReason) - .build()); - CaseLink caseLink = creatCaseLink(String.valueOf(caseLinkEntity.getLinkedCaseReference()), - caseLinkEntity.getCcdListId(), linkReasons, null); - - List> caseLists = List.of( - ListValue.builder() - .id(UUID.randomUUID().toString()) - .value(caseLink) - .build()); - - // When - underTest.mergeCaseLinks(caseLists); - - // Then - assertThat(underTest.getCaseLinks()).hasSize(1); - } - - - private CaseLinkReasonEntity createCaseLinkReasonEntity(UUID id, String reasonCode, String reasonText) { - - CaseLinkEntity caseLinkEntity = mock(CaseLinkEntity.class); - - return CaseLinkReasonEntity.builder() - .id(id) - .reasonCode(reasonCode) - .reasonText(reasonText) - .caseLink(caseLinkEntity) - .build(); - } - - private CaseLinkEntity createCaseLinkEntity(UUID id, List linkReasonEntities, - Long linkedCaseRef, String ccdId) { - - return CaseLinkEntity.builder() - .id(id) - .linkedCaseReference(linkedCaseRef) - .ccdListId(ccdId) - .reasons(linkReasonEntities) - .build(); - } - - private CaseLink creatCaseLink(String ref, String caseType, List> reasons, - LocalDateTime time) { - - return CaseLink.builder() - .caseReference(ref) - .caseType(caseType) - .reasonForLink(reasons) - .createdDateTime(time) - .build(); - } - - private LinkReason createLinkReason(String reason, String description) { - - return LinkReason.builder() - .reason(reason) - .description(description) - .build(); - } - } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java new file mode 100644 index 0000000000..5de30b9c01 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java @@ -0,0 +1,100 @@ +package uk.gov.hmcts.reform.pcs.ccd.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import uk.gov.hmcts.ccd.sdk.type.CaseLink; +import uk.gov.hmcts.ccd.sdk.type.LinkReason; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.CaseLinkReasonEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; + +import java.util.List; +import java.util.UUID; + +import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThat; + +class CaseLinkServiceTest { + + private PcsCaseEntity pcsCaseEntity; + private CaseLinkService underTest; + + @BeforeEach + void setUp() { + pcsCaseEntity = PcsCaseEntity.builder().build(); + underTest = new CaseLinkService(); + } + + @Test + void shouldMergeCaseLinks() { + //Given + CaseLinkReasonEntity caseLinkReasonEntity = createCaseLinkReasonEntity(); + + CaseLinkEntity caseLinkEntity = createCaseLinkEntity( List.of(caseLinkReasonEntity)); + + LinkReason linkReason = createLinkReason(caseLinkReasonEntity.getReasonCode(), + caseLinkReasonEntity.getReasonText()); + List> linkReasons = List.of( + ListValue.builder() + .id(caseLinkReasonEntity.getId().toString()) + .value(linkReason) + .build()); + CaseLink caseLink = creatCaseLink(String.valueOf(caseLinkEntity.getLinkedCaseReference()), + caseLinkEntity.getCcdListId(), linkReasons); + + List> caseLists = List.of( + ListValue.builder() + .id(UUID.randomUUID().toString()) + .value(caseLink) + .build()); + + // When + underTest.mergeCaseLinks(caseLists, pcsCaseEntity); + + // Then + assertThat(pcsCaseEntity.getCaseLinks()).hasSize(1); + assertThat(pcsCaseEntity.getCaseLinks().getFirst().getCcdListId()).isEqualTo("PCS"); + assertThat(pcsCaseEntity.getCaseLinks().getFirst().getReasons().getFirst().getReasonCode()).isEqualTo("CLR003"); + } + + private CaseLinkReasonEntity createCaseLinkReasonEntity() { + + CaseLinkEntity caseLinkEntity = mock(CaseLinkEntity.class); + + return CaseLinkReasonEntity.builder() + .id(UUID.randomUUID()) + .reasonCode("CLR003") + .reasonText("Same Party") + .caseLink(caseLinkEntity) + .build(); + } + + private CaseLinkEntity createCaseLinkEntity(List linkReasonEntities) { + + return CaseLinkEntity.builder() + .id(UUID.randomUUID()) + .linkedCaseReference(1234L) + .ccdListId("PCS") + .reasons(linkReasonEntities) + .build(); + } + + private CaseLink creatCaseLink(String ref, String caseType, List> reasons) { + + return CaseLink.builder() + .caseReference(ref) + .caseType(caseType) + .reasonForLink(reasons) + .build(); + } + + private LinkReason createLinkReason(String reason, String description) { + + return LinkReason.builder() + .reason(reason) + .description(description) + .build(); + } + +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java index d2d6fc6933..3d18137c8a 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/PcsCaseServiceTest.java @@ -48,6 +48,8 @@ class PcsCaseServiceTest { private TenancyLicenceService tenancyLicenceService; @Mock private AddressMapper addressMapper; + @Mock + private CaseLinkService caseLinkService; @Captor private ArgumentCaptor pcsCaseEntityCaptor; @@ -62,7 +64,8 @@ void setUp() { partyService, documentService, tenancyLicenceService, - addressMapper + addressMapper, + caseLinkService ); } @@ -194,7 +197,7 @@ void shouldPatchCaseData() { underTest.patchCaseLinks(CASE_REFERENCE, caseData); // Then - verify(pcsCaseEntity, atLeastOnce()).mergeCaseLinks(caseData.getCaseLinks()); + verify(caseLinkService, atLeastOnce()).mergeCaseLinks(caseData.getCaseLinks(), pcsCaseEntity); } private PcsCaseEntity stubFindCase() { From f8d4e888de05dd73a7fdea716bab5cd9f4f2ed69 Mon Sep 17 00:00:00 2001 From: aschalew Date: Fri, 27 Mar 2026 16:16:58 +0000 Subject: [PATCH 17/18] Updated CaseLinkServiceTest --- .../gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java index 5de30b9c01..611893e075 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java @@ -31,7 +31,7 @@ void shouldMergeCaseLinks() { //Given CaseLinkReasonEntity caseLinkReasonEntity = createCaseLinkReasonEntity(); - CaseLinkEntity caseLinkEntity = createCaseLinkEntity( List.of(caseLinkReasonEntity)); + CaseLinkEntity caseLinkEntity = createCaseLinkEntity(List.of(caseLinkReasonEntity)); LinkReason linkReason = createLinkReason(caseLinkReasonEntity.getReasonCode(), caseLinkReasonEntity.getReasonText()); From 05939c71d4e44b4ca30376e5b179b1b4de8a93f7 Mon Sep 17 00:00:00 2001 From: aschalew Date: Mon, 30 Mar 2026 10:59:45 +0100 Subject: [PATCH 18/18] Removed reasonText field from CaseLinkReasonEntity, updated permission for solicitor in CreateCaseLink --- .../reform/pcs/ccd/entity/CaseLinkReasonEntity.java | 4 ---- .../hmcts/reform/pcs/ccd/event/CreateCaseLink.java | 2 +- .../hmcts/reform/pcs/ccd/service/CaseLinkService.java | 1 - .../gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java | 1 - ...V073__create_case_link_and_link_reason_tables.sql} | 3 +-- .../reform/pcs/ccd/entity/CaseLinkEntityTest.java | 3 --- .../reform/pcs/ccd/service/CaseLinkServiceTest.java | 7 ++----- .../hmcts/reform/pcs/ccd/view/CaseLinkViewTest.java | 11 +++-------- 8 files changed, 7 insertions(+), 25 deletions(-) rename src/main/resources/db/migration/{V072__create_case_link_and_link_reason_tables.sql => V073__create_case_link_and_link_reason_tables.sql} (90%) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntity.java index 7c4af127e5..296de7c6f0 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntity.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkReasonEntity.java @@ -37,8 +37,4 @@ public class CaseLinkReasonEntity { @Column(name = "reason_code", nullable = false) private String reasonCode; - - @Column(name = "reason_text") - private String reasonText; - } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java index db81b1d6ca..37917ea8cf 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/CreateCaseLink.java @@ -30,7 +30,7 @@ public void configureDecentralised(DecentralisedConfigBuilder> incomingLinkedCases, PcsCas CaseLinkReasonEntity caseLinkReasonEntity = CaseLinkReasonEntity.builder() .caseLink(caseLinkEntity) .reasonCode(incomingLinkReason.getValue().getReason()) - .reasonText(incomingLinkReason.getValue().getDescription()) .build(); caseLinkReasonEntities.add(caseLinkReasonEntity); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java index 01136a16fe..0ce225123d 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/CaseLinkView.java @@ -37,7 +37,6 @@ private void setCaseLinks(PCSCase pcsCase, PcsCaseEntity pcsCaseEntity) { .value( LinkReason.builder() .reason(reasonEntity.getReasonCode()) - .description(reasonEntity.getReasonText()) .build() ) .build() diff --git a/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql b/src/main/resources/db/migration/V073__create_case_link_and_link_reason_tables.sql similarity index 90% rename from src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql rename to src/main/resources/db/migration/V073__create_case_link_and_link_reason_tables.sql index aea6b0f46a..ac650702f5 100644 --- a/src/main/resources/db/migration/V072__create_case_link_and_link_reason_tables.sql +++ b/src/main/resources/db/migration/V073__create_case_link_and_link_reason_tables.sql @@ -16,8 +16,7 @@ CREATE UNIQUE INDEX ux_case_link_unique CREATE TABLE case_link_reason ( id UUID PRIMARY KEY, case_link_id UUID NOT NULL REFERENCES case_link(id) ON DELETE CASCADE, - reason_code VARCHAR(100) NOT NULL, - reason_text VARCHAR(255) + reason_code VARCHAR(100) NOT NULL ); CREATE INDEX idx_case_link_reason_link diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java index 413ec6aaf8..03e97347f8 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/entity/CaseLinkEntityTest.java @@ -24,12 +24,10 @@ void setUp() { void shouldAddCaseLinkReason() { // Given String reasonCode = "CLR003"; - String reasonText = "Same Party"; CaseLinkEntity caseLinkEntity = CaseLinkEntity.builder().build(); CaseLinkReasonEntity caseLinkReasonEntity = CaseLinkReasonEntity.builder() .caseLink(caseLinkEntity) .reasonCode(reasonCode) - .reasonText(reasonText) .build(); @@ -39,6 +37,5 @@ void shouldAddCaseLinkReason() { // Then assertThat(underTest.getReasons()).hasSize(1); assertThat(underTest.getReasons().getFirst().getReasonCode()).isEqualTo("CLR003"); - assertThat(underTest.getReasons().getFirst().getReasonText()).isEqualTo("Same Party"); } } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java index 611893e075..d1906c8168 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/CaseLinkServiceTest.java @@ -33,8 +33,7 @@ void shouldMergeCaseLinks() { CaseLinkEntity caseLinkEntity = createCaseLinkEntity(List.of(caseLinkReasonEntity)); - LinkReason linkReason = createLinkReason(caseLinkReasonEntity.getReasonCode(), - caseLinkReasonEntity.getReasonText()); + LinkReason linkReason = createLinkReason(caseLinkReasonEntity.getReasonCode()); List> linkReasons = List.of( ListValue.builder() .id(caseLinkReasonEntity.getId().toString()) @@ -65,7 +64,6 @@ private CaseLinkReasonEntity createCaseLinkReasonEntity() { return CaseLinkReasonEntity.builder() .id(UUID.randomUUID()) .reasonCode("CLR003") - .reasonText("Same Party") .caseLink(caseLinkEntity) .build(); } @@ -89,11 +87,10 @@ private CaseLink creatCaseLink(String ref, String caseType, List