Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6d15c22
Added logic for creation, mangemnet and display of case links
Mar 18, 2026
e0a2d81
Reverted AUthMode back to Local
Mar 18, 2026
931e7ea
Merge branch 'master' into HDPI-4510-create-and-view-proposed-linked-…
Mar 18, 2026
645a043
Renamed sql script for case linking from V063 to V067
Mar 18, 2026
e84bdcd
Renamed epims_id back to empimsid
Mar 18, 2026
0f64f6a
Updated Unit tests to remove some code smells
Mar 19, 2026
9519c00
Renamed epimsid to epims_id
Mar 19, 2026
67afd9e
Renamed epims_id back to epimsid
Mar 20, 2026
a925e2f
Resolved merge conflicts
Mar 20, 2026
aeddb7a
Renamed V067 to V070 after merging master
Mar 20, 2026
4d49560
Update V002.1__postcode_test_data.sql
adusumillipraveen Mar 20, 2026
d2115fa
Refactored Code to meet PR comments
Mar 23, 2026
c1966d3
Merge branch 'master' into HDPI-4510-create-and-view-proposed-linked-…
Mar 23, 2026
81d21cd
Merge branch 'master' into HDPI-4510-create-and-view-proposed-linked-…
Mar 23, 2026
91f8f06
Updated code for PR comments
Mar 24, 2026
993ccf4
Resolved merge conflicts
Mar 25, 2026
6e13ebb
Renamed V070 to V071
Mar 25, 2026
ac43cd0
Merge branch 'master' into HDPI-4510-create-and-view-proposed-linked-…
Mar 26, 2026
3922036
Updated code for PR comments
Mar 26, 2026
cbdee92
Merge branch 'master' into HDPI-4510-create-and-view-proposed-linked-…
Mar 27, 2026
ce7a640
Fixed a code smell from Sonar cube
Mar 27, 2026
26e23f7
Updated PK names in case_link table from PR review comment
Mar 27, 2026
b011d5a
Merge branch 'master' into HDPI-4510-create-and-view-proposed-linked-…
Mar 27, 2026
1139553
Moved mergeCaseLinks() method into CaseLinkService class
Mar 27, 2026
f8d4e88
Updated CaseLinkServiceTest
Mar 27, 2026
354c908
Merge branch 'master' into HDPI-4510-create-and-view-proposed-linked-…
Mar 30, 2026
05939c7
Removed reasonText field from CaseLinkReasonEntity, updated permissio…
Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ 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," +
"**/entities/**," + "**/model/**," + "**/uk/gov/hmcts/reform/pcs/testingsupport/**/*.java"
Expand Down
2 changes: 1 addition & 1 deletion charts/pcs-api/values.ccd.preview.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/uk/gov/hmcts/reform/pcs/ccd/CaseType.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,10 @@ public void configure(final ConfigBuilder<PCSCase, State, UserRole> 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)");
}
}
6 changes: 4 additions & 2 deletions src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import uk.gov.hmcts.ccd.sdk.type.AddressUK;
import uk.gov.hmcts.ccd.sdk.type.Document;
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;
Expand All @@ -34,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;

Expand Down Expand Up @@ -69,6 +70,7 @@ public class PCSCaseView implements CaseView<PCSCase, State> {
private final NoticeOfPossessionView noticeOfPossessionView;
private final StatementOfTruthView statementOfTruthView;
private final CaseNameHmctsFormatter caseNameHmctsFormatter;
private final CaseLinkView caseLinkView;


/**
Expand Down Expand Up @@ -129,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;
}
Expand Down Expand Up @@ -253,5 +256,4 @@ private List<ListValue<Document>> mapAndWrapDocuments(PcsCaseEntity pcsCaseEntit
.build())
.collect(Collectors.toList());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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_CASE_WORKER;
import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole.PCS_SOLICITOR;

public class CaseLinkingAccess implements HasAccessControl {


@Override
public SetMultimap<HasRole, Permission> getGrants() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would returning an immutable set be approriate?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this matches what the other Access classes have at the moment, so if there was to be a change then it should be done to all of them. Perhaps as a separate house keeping ticket?

SetMultimap<HasRole, Permission> grants = HashMultimap.create();
grants.putAll(PCS_SOLICITOR, Permission.CRU);
Copy link
Copy Markdown
Contributor

@guygrewal77 guygrewal77 Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the Delete missing for permissions ? In the event - CreateCaseLink class, solicitor has full CRUD access.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The access profile in CaseLinkinAccess is applied to the fields in PCSCase (for updating cases) do not need the Delete permission in association with the events CreateCaseLink and MaintainLinkCase. In the event class CreateCaseLink, we only need the permissions CRU, whereas in the event class MaintainLinkCase, we need the Delete permission as well. Hence I will remove the Delete permission in CreateCaseLink and keep it in MaintainLinkCase. I will keep CRU in CaseLinkinAccess class

grants.put(PCS_CASE_WORKER, Permission.R);

return grants;
}
}
22 changes: 20 additions & 2 deletions src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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<ListValue<CaseLink>> caseLinks = new ArrayList<>();

@CCD(
access = {CaseLinkingAccess.class},
label = "Component Launcher (for displaying Linked Cases data)"
)
@JsonProperty("LinkedCasesComponentLauncher")
private ComponentLauncher linkedCasesComponentLauncher;

@CCD(
searchable = false,
access = {CitizenAccess.class}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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 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")
@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_reference", 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<CaseLinkReasonEntity> reasons = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ public class PcsCaseEntity {
@JsonManagedReference
private List<DocumentEntity> documents = new ArrayList<>();

@OneToMany(mappedBy = "pcsCase",
cascade = ALL,
orphanRemoval = true)
@Builder.Default
private List<CaseLinkEntity> caseLinks = new ArrayList<>();

public void setTenancyLicence(TenancyLicenceEntity tenancyLicence) {
if (this.tenancyLicence != null) {
this.tenancyLicence.setPcsCase(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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<PCSCase, State, UserRole> {

private final PcsCaseService pcsCaseService;


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra space

@Override
public void configureDecentralised(DecentralisedConfigBuilder<PCSCase, State, UserRole> configBuilder) {
new PageBuilder(configBuilder
.decentralisedEvent(EventId.createCaseLink.name(), this::submit)
.forAllStates()
.name("Link cases")
.description("To link related cases")
.grant(Permission.R, UserRole.PCS_CASE_WORKER)
.grant(Permission.CRU,UserRole.PCS_SOLICITOR))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space needed

.page("createCaseLink")
.pageLabel("Case Link")
.optional(PCSCase::getCaseLinks,"LinkedCasesComponentLauncher = \"DONOTSHOW\"",null,true)
.optional(PCSCase::getLinkedCasesComponentLauncher,
null,null,null,null,"#ARGUMENT(CREATE,LinkedCases)");

}

private SubmitResponse<State> submit(EventPayload<PCSCase, State> eventPayload) {
long caseReference = eventPayload.caseReference();
PCSCase pcsCase = eventPayload.caseData();

log.info("Caseworker created case link for {}", caseReference);

pcsCaseService.patchCaseLinks(caseReference, pcsCase);

return SubmitResponse.defaultResponse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public enum EventId {
enforceTheOrder,
respondPossessionClaim,
submitDefendantResponse,
createTestCase
createTestCase,
createCaseLink,
maintainCaseLink
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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<PCSCase, State, UserRole> {

private final PcsCaseService pcsCaseService;

@Override
public void configureDecentralised(DecentralisedConfigBuilder<PCSCase, State, UserRole> configBuilder) {
new PageBuilder(configBuilder
.decentralisedEvent(EventId.maintainCaseLink.name(), this::submit)
.forAllStates()
.name("Manage case links")
.description("To manage link related cases")
.grant(Permission.R, UserRole.PCS_CASE_WORKER)
.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<State> submit(EventPayload<PCSCase, State> eventPayload) {
long caseReference = eventPayload.caseReference();
PCSCase pcsCase = eventPayload.caseData();

log.info("Caseworker updated case link for {}", caseReference);

pcsCaseService.patchCaseLinks(caseReference, pcsCase);

return SubmitResponse.defaultResponse();
}


}
Loading
Loading