From a2dfcc264de7be6eda9f0b606821a14186273de5 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 16 Jul 2026 10:32:52 +0100 Subject: [PATCH 01/37] HDPI-6585: add camunda api, task creation and dmns --- .../hmcts/reform/pcs/camunda/CamundaApi.java | 19 + .../reform/pcs/camunda/CamundaService.java | 76 ++ .../hmcts/reform/pcs/camunda/DmnValue.java | 13 + .../pcs/camunda/SendMessageRequest.java | 19 + .../hmcts/reform/pcs/camunda/TaskType.java | 14 + ...a-task-cancellation-possession_service.dmn | 103 ++ .../wa-task-completion-possession_service.dmn | 186 ++++ .../wa-task-initiation-possession_service.dmn | 945 ++++++++++++++++++ ...wa-task-permissions-possession_servcie.dmn | 152 +++ .../dmn/wa-task-types-possession_service.dmn | 208 ++++ 10 files changed, 1735 insertions(+) create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaApi.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/camunda/DmnValue.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/camunda/SendMessageRequest.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java create mode 100644 src/main/resources/dmn/wa-task-cancellation-possession_service.dmn create mode 100644 src/main/resources/dmn/wa-task-completion-possession_service.dmn create mode 100644 src/main/resources/dmn/wa-task-initiation-possession_service.dmn create mode 100644 src/main/resources/dmn/wa-task-permissions-possession_servcie.dmn create mode 100644 src/main/resources/dmn/wa-task-types-possession_service.dmn diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaApi.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaApi.java new file mode 100644 index 0000000000..0b59066b61 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaApi.java @@ -0,0 +1,19 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient(name = "camunda", url = "${camunda.url}") +public interface CamundaApi { + String SERVICE_AUTHORIZATION = "ServiceAuthorization"; + + @PostMapping( + value = "/message", + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + void sendMessage(@RequestHeader(SERVICE_AUTHORIZATION) String serviceAuthorisation, + SendMessageRequest sendMessageRequest); + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java new file mode 100644 index 0000000000..73093c6c70 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java @@ -0,0 +1,76 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; +import uk.gov.hmcts.reform.pcs.ccd.CaseType; + +import java.time.LocalDateTime; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME; + +@Slf4j +@AllArgsConstructor +@Service +public class CamundaService { + + private final CamundaApi camundaApi; + private final AuthTokenGenerator authTokenGenerator; + + private static final String CREATE = "createTaskMessage"; + private static final String UNCONFIGURED = "unconfigured"; + private static final String EMPTY_WARNINGS_LIST = "[]"; + + public void createTask( + Long caseId, + TaskType taskType + ) { + log.info("Creating task for {}", caseId); + Map> processVariables = new ConcurrentHashMap<>(); + + LocalDateTime delayUntil = LocalDateTime.now(); + LocalDateTime dueDate = LocalDateTime.now().plusDays(taskType.getWorkingDays()); + + processVariables.put("taskState", dmnStringValue(UNCONFIGURED)); + processVariables.put("caseTypeId", dmnStringValue(CaseType.getCaseType())); + processVariables.put("dueDate", dmnStringValue(dueDate.format(ISO_LOCAL_DATE_TIME))); + processVariables.put("workingDaysAllowed", dmnIntegerValue(taskType.getWorkingDays())); + processVariables.put("jurisdiction", dmnStringValue(CaseType.getJurisdictionId())); + processVariables.put("name", dmnStringValue(taskType.getName())); + processVariables.put("taskId", dmnStringValue(taskType.getId())); + processVariables.put("caseId", dmnStringValue(caseId.toString())); + processVariables.put("delayUntil", dmnStringValue(delayUntil.format(ISO_LOCAL_DATE_TIME))); + processVariables.put("hasWarnings", dmnBooleanValue(false)); + processVariables.put("warningList", dmnStringValue(EMPTY_WARNINGS_LIST)); + + SendMessageRequest request = SendMessageRequest.builder() + .messageName(CREATE) + .processVariables(processVariables) + .build(); + + String s2sToken = authTokenGenerator.generate(); + + try { + log.info("Camunda request for case id {}: {}", caseId, request); + camundaApi.sendMessage(s2sToken, request); + } catch (Exception e) { + log.error("Failed to send Camunda request for caseId {}", caseId, e); + } + + } + + private DmnValue dmnStringValue(String value) { + return DmnValue.builder().value(value).type("String").build(); + } + + private DmnValue dmnIntegerValue(Integer value) { + return DmnValue.builder().value(value).type("Integer").build(); + } + + private DmnValue dmnBooleanValue(Boolean value) { + return DmnValue.builder().value(value).type("Boolean").build(); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/DmnValue.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/DmnValue.java new file mode 100644 index 0000000000..eff870c43e --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/DmnValue.java @@ -0,0 +1,13 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +@AllArgsConstructor +public class DmnValue { + private T value; + private String type; +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/SendMessageRequest.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/SendMessageRequest.java new file mode 100644 index 0000000000..10addfde04 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/SendMessageRequest.java @@ -0,0 +1,19 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SendMessageRequest { + + private String messageName; + + private Map> processVariables; +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java new file mode 100644 index 0000000000..d1556fc251 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java @@ -0,0 +1,14 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum TaskType { + NEW_CLAIM_CREATE_NEW_HEARING("NewClaimCreateNewHearing", "New Claim – Create new hearing", 5); + + private final String id; + private final String name; + private final Integer workingDays; +} diff --git a/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn b/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn new file mode 100644 index 0000000000..8fa2acaf8a --- /dev/null +++ b/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn @@ -0,0 +1,103 @@ + + + + + + + fromState + + + + + event + + + + + state + + + + + + + + + + + + + "changeCaseState" + + + "CASE_ISSUED" + + + "Cancel" + + + "NewClaimCreateNewHearing" + + + + + + + + + + + + + + "changeCaseState" + + + "CASE_STAYED" + + + "Cancel" + + + "NewClaimCreateNewHearing" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/dmn/wa-task-completion-possession_service.dmn b/src/main/resources/dmn/wa-task-completion-possession_service.dmn new file mode 100644 index 0000000000..144ae3fa0f --- /dev/null +++ b/src/main/resources/dmn/wa-task-completion-possession_service.dmn @@ -0,0 +1,186 @@ + + + + + + + eventId + + + + + + + + + + "NewClaimCreateNewHearing" + + + "manual" + + + + + + + + "ReviewDefendantResponseAndCounterclaim" + + + "manual" + + + + + + + + "ReviewAdjournGenApp" + + + "manual" + + + + + + + + "ReviewSetAsideGenApp" + + + "manual" + + + + + + + + "ReviewGenApp" + + + "manual" + + + + + + + + "ReviewAdditionalDocumentsClaim" + + + "manual" + + + + + + + + "ReviewAdditionalDocumentsGenApp" + + + "manual" + + + + + + + + "ReviewAdditionalDocumentsCounterclaim" + + + "manual" + + + + + "Approve or reject case flag" + + + "ReviewCaseFlagRequest" + + + "Auto" + + + + + + + + "ReviewCaseFlag" + + + "manual" + + + + + + + + "ReviewFailedPayment" + + + "manual" + + + + + + + + "ReviewDateDue" + + + "manual" + + + + + + + + "TranslateDefendantSubmittedDocument" + + + "manual" + + + + + + + + "TranslateClaimantSubmittedDocument" + + + "manual" + + + + + + + + "TranslateCaseworkerUploadedDocument" + + + "manual" + + + + + + + + + + + + diff --git a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn new file mode 100644 index 0000000000..41c327c858 --- /dev/null +++ b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn @@ -0,0 +1,945 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if(additionalData != null and additionalData.Data != null and additionalData.Data.cicCaseAdminActionRequired + != null and additionalData.Data.cicCaseAdminActionRequired = ["AdminActionRequired"]) then + true +else + false + + + + + if(additionalData.Data.cicCaseFirstOrderDueDate != null and additionalData.Data.cicCaseFirstOrderDueDate != "") then + true +else + false + + + + + + + + + + + + "Notify claim" + + + "Case issued" + + + + + + + + + + + + + + + + + + "NewClaimCreateNewHearing" + + + "New Claim – Create new hearing" + + + + + + 5 + + + "NewClaimCreateNewHearing" + + + "decision_making_work" + + + "ADMIN" + + + + This rule would also be required for when this task is cancelled from a user putting the case in case stayed and then putting it back into case issued state. + + "Update case state" + + + "Case issued" + + + + + + + + + + + + + + + + + + "NewClaimCreateNewHearing" + + + "New Claim – Create new hearing" + + + + + + 5 + + + "NewClaimCreateNewHearing" + + + "decision_making_work" + + + "ADMIN" + + + + Possible process for welsh translation when user selects they have selected completed claim in english and welsh or just welsh + + Tech team update case state + + + Case moves from "pending issued" to "Case issued" + + + + + + + + + + + + + + + + + + "NewClaimCreateNewHearing" + + + "New Claim – Create new hearing" + + + + + + 5 + + + "NewClaimCreateNewHearing" + + + "decision_making_work" + + + "ADMIN" + + + + + "Respond to claim" (Respone and counterclaim submitted) + + + + + + "Pending Case issued (counterclaim)" + + + + + + User selects (Help with fees) + + + + + + + + + "Review DefendantResponseAndCounterclaim " + + + "Review Defendant response and counterclaim" + + + + + + 5 + + + "Review DefendantResponseAndCounterclaim " + + + "routine_work" + + + "ADMIN" + + + + + "Upload Document - Citizen dashboard" (NEED NAME OF EVENT) + + + + + + + + + + + + User selects(No, the documents I'm uploading relate to the main claim) + + + + + + + + + "ReviewAdditionalDocumentsClaim" + + + "Review additional documents - claim" + + + + + + 5 + + + "ReviewAdditionalDocumentsClaim" + + + "routine_work" + + + "ADMIN" + + + + + "Upload Document (Defendant LR)", "Upload Document Claimant LR" + + + + + + + + + + + + User selects("Other" for Type of Doc) + + + + + + + + + "ReviewAdditionalDocumentsClaim" + + + "Review additional documents - claim" + + + + + + 5 + + + "ReviewAdditionalDocumentsClaim" + + + "routine_work" + + + "ADMIN" + + + + + "Upload Document - Citizen dashboard" (NEED NAME OF EVENT) + + + + + + + + + + + + User selects("Yes XXX", for Do these documents relate to an existing application?) + + + + + + + + + "ReviewAdditionalDocumentsGenApp" + + + "Review additional documents – gen app" + + + + + + 5 + + + "ReviewAdditionalDocumentsGenApp" + + + "routine_work" + + + "ADMIN" + + + + + "Upload Document (Defendant LR)", "Upload Document Claimant LR" + + + + + + + + + + + + User selects("Yes XXX", for Do these documents relate to an existing application?) + + + + + + + + + "ReviewAdditionalDocumentsGenApp" + + + "Review additional documents – gen app" + + + + + + 5 + + + "ReviewAdditionalDocumentsGenApp" + + + "routine_work" + + + "ADMIN" + + + + + "Upload Document - Citizen dashboard" (NEED NAME OF EVENT) + + + + + + + + + + + + User selects("Yes XXX", for Do these documents relate to an existing counterclaim?) + + + + + + + + + "ReviewAdditionalDocumentsCounterclaim" + + + "Review additional documents – counterclaim" + + + + + + 5 + + + "ReviewAdditionalDocumentsCounterclaim" + + + "routine_work" + + + "ADMIN" + + + + + "Upload Document (Defendant LR)", "Upload Document Claimant LR" + + + + + + + + + + + + User selects("Yes XXX", for Do these documents relate to an existing counterclaim?) + + + + + + + + + "ReviewAdditionalDocumentsCounterclaim" + + + "Review additional documents – counterclaim" + + + + + + 5 + + + "ReviewAdditionalDocumentsCounterclaim" + + + "routine_work" + + + "ADMIN" + + + + + "Submit General Application" + + + + + + + + + "Application Issued" + + + User selects("Ask to adjourn the hearing" for Choose an application) + + + + + + + + + "ReviewAdjournGenApp" + + + "Review adjourn gen app" + + + + + + 1 + + + "ReviewAdjournGenApp" + + + "applications" + + + "ADMIN" + + + + + "Submit General Application" + + + + + + + + + "Application Issued" + + + User selects("Ask the court to set aside" for Choose an application) + + + + + + + + + "ReviewSetAsideGenApp" + + + "Review set aside gen app" + + + + + + 1 + + + "ReviewSetAsideGenApp" + + + "applications" + + + "ADMIN" + + + + + "Submit General Application" + + + + + + + + + "Application Issued" + + + User selects("Something else" for Choose an application) + + + + + + + + + "ReviewGenApp" + + + "Review gen app" + + + + + + 1 + + + "ReviewGenApp" + + + "applications" + + + "ADMIN" + + + + + Defendant Submit response, External professional user adding flag via manage cases + + + + + + + + + + + + Flag default status is 'Requested' + + + + + + + + + "ReviewCaseFlagRequest" + + + "Review Case Flag request" + + + + + + 5 + + + "ReviewCaseFlagRequest" + + + "decision_making_work" + + + "ADMIN" + + + + + Defendant Submit response, External professional user adding flag via manage cases + + + + + + + + + + + + Flag default status is 'Active' + + + + + + + + + "ReviewCaseFlag" + + + "Review Case Flag" + + + + + + 5 + + + "ReviewCaseFlag" + + + "decision_making_work" + + + "ADMIN" + + + + + "Create Service Request (fee)" + + + + + + + + + + + + Charge back occurs (I.e. payment unsuccessful) + + + + + + + + + "ReviewFailedPayment" + + + "Review Failed Payment" + + + + + + 5 + + + "ReviewFailedPayment" + + + "error_management" + + + "ADMIN" + + + + + "Add review date" + + + + + + + + + + + + Date of review is satisfied + + + + + + + + + "ReviewDateDue" + + + "Review Date Due" + + + + + + 5 + + + "ReviewDateDue" + + + "routine_work" + + + "ADMIN" + + + + + Defendant submits Gen app, counterclaim, response or response and counterclaim + + + Submitted + + + + + + + + + User selects claim completed in welsh or english and welsh OR Claimant or defendant had indicated they want to receive communications and documents in Welsh + + + + + + + + + "TranslateDefendantSubmittedDocument" + + + "Translate Defendant Submitted Document" + + + + + + 5 + + + "TranslateDefendantSubmittedDocument" + + + "routine_work" + + + "ADMIN" + + + + + Submit Claim + + + Pending claim issued + + + + + + + + + User selects claim completed in welsh or english and welsh OR Claimant or defendant had indicated they want to receive communications and documents in Welsh + + + + + + + + + "TranslateClaimantSubmittedDocument" + + + "Translate Claimant Submitted Document" + + + + + + 5 + + + "TranslateClaimantSubmittedDocument" + + + "routine_work" + + + "ADMIN" + + + + + Caseworker uploads document + + + + + + + + + + + + Claimant or defendant had indicated they want to receive communications and documents in Welsh + + + + + + + + + "TranslateCaseworkerUploadedDocument" + + + "Translate Caseworker Uploaded Document" + + + + + + 5 + + + "TranslateCaseworkerUploadedDocument" + + + "routine_work" + + + "ADMIN" + + + + + + + + + + + + diff --git a/src/main/resources/dmn/wa-task-permissions-possession_servcie.dmn b/src/main/resources/dmn/wa-task-permissions-possession_servcie.dmn new file mode 100644 index 0000000000..8704d1f8f2 --- /dev/null +++ b/src/main/resources/dmn/wa-task-permissions-possession_servcie.dmn @@ -0,0 +1,152 @@ + + + + + + + + if(taskAttributes != null and taskAttributes.taskType != null) + then taskAttributes.taskType + else "r1" + + + + + + + + + + + + + + + + + + + "NewClaimCreateNewHearing", "ReviewDefendantResponseAndCounterclaim", "ReviewAdjournGenApp", "ReviewSetAsideGenApp", "ReviewGenApp", "ReviewAdditionalDocumentsClaim", "ReviewAdditionalDocumentsGenApp", "ReviewAdditionalDocumentsCounterclaim", "ReviewCaseFlagRequest", "ReviewCaseFlag", "ReviewFailedPayment", "ReviewDateDue" + + + + + + + + + "Hearing admin" + + + "Read,Own,Claim,Unclaim,Manage,Complete" + + + "ADMIN" + + + + + + + + + false + + + + + "NewClaimCreateNewHearing", "ReviewDefendantResponseAndCounterclaim", "ReviewAdjournGenApp", "ReviewSetAsideGenApp", "ReviewGenApp", "ReviewAdditionalDocumentsClaim", "ReviewAdditionalDocumentsGenApp", "ReviewAdditionalDocumentsCounterclaim", "ReviewCaseFlagRequest", "ReviewCaseFlag", "ReviewFailedPayment", "ReviewDateDue" + + + + + + + + + "Hearing team leader" + + + "Read,Own,Manage,Complete,Cancel,Assign,Unassign" + + + "ADMIN" + + + + + + + + + false + + + + + "TranslateDefendantSubmittedDocument", "TranslateClaimantSubmittedDocument", "TranslateCaseworkerUploadedDocument" + + + + + + + + + "WLU admin" + + + "Read,Own,Claim,Unclaim,Manage,Complete" + + + "ADMIN" + + + + + + + + + false + + + + + "TranslateDefendantSubmittedDocument", "TranslateClaimantSubmittedDocument", "TranslateCaseworkerUploadedDocument" + + + + + + + + + "WLU team leader" + + + "Read,Own,Manage,Complete,Cancel,Assign,Unassign" + + + "ADMIN" + + + + + + + + + false + + + + + + + + + + + + diff --git a/src/main/resources/dmn/wa-task-types-possession_service.dmn b/src/main/resources/dmn/wa-task-types-possession_service.dmn new file mode 100644 index 0000000000..c199530473 --- /dev/null +++ b/src/main/resources/dmn/wa-task-types-possession_service.dmn @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + "NewClaimCreateNewHearing" + + + "New Claim – Create new hearing" + + + + + + + + "ReviewDefendantResponseAndCounterclaim" + + + "Review Defendant response and counterclaim" + + + + + + + + "ReviewAdjournGenApp" + + + "Review adjourn gen app" + + + + + + + + "ReviewSetAsideGenApp" + + + "Review set aside gen app" + + + + + + + + "ReviewGenApp" + + + "Review gen app" + + + + + + + + "ReviewAdditionalDocumentsClaim" + + + "Review additional documents - claim" + + + + + + + + "ReviewAdditionalDocumentsGenApp" + + + "Review additional documents – gen app" + + + + + + + + "ReviewAdditionalDocumentsCounterclaim" + + + "Review additional documents – counterclaim" + + + + + + + + "ReviewCaseFlagRequest" + + + "Review Case Flag request" + + + + + + + + "ReviewCaseFlag" + + + "Review Case Flag" + + + + + + + + "ReviewFailedPayment" + + + "Review Failed Payment" + + + + + + + + "ReviewDateDue" + + + "Review Date Due" + + + + + + + + "TranslateDefendantSubmittedDocument" + + + "Translate Defendant Submitted Document" + + + + + + + + "TranslateClaimantSubmittedDocument" + + + "Translate Claimant Submitted Document" + + + + + + + + "TranslateCaseworkerUploadedDocument" + + + "Translate Caseworker Uploaded Document" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ae67522f1066200b55abc4a240298f6285ae838c Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 16 Jul 2026 15:39:22 +0100 Subject: [PATCH 02/37] HDPI-6585: add configuration dmn --- .../wa-task-completion-possession_service.dmn | 2 +- ...-task-configuration-possession_service.dmn | 682 ++++++++++++++++++ ...wa-task-permissions-possession_servcie.dmn | 10 +- 3 files changed, 688 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/dmn/wa-task-configuration-possession_service.dmn diff --git a/src/main/resources/dmn/wa-task-completion-possession_service.dmn b/src/main/resources/dmn/wa-task-completion-possession_service.dmn index 144ae3fa0f..159e10a785 100644 --- a/src/main/resources/dmn/wa-task-completion-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-completion-possession_service.dmn @@ -1,5 +1,5 @@ - + diff --git a/src/main/resources/dmn/wa-task-configuration-possession_service.dmn b/src/main/resources/dmn/wa-task-configuration-possession_service.dmn new file mode 100644 index 0000000000..ae41d59931 --- /dev/null +++ b/src/main/resources/dmn/wa-task-configuration-possession_service.dmn @@ -0,0 +1,682 @@ + + + + + + + + + + + + if(taskAttributes != null and taskAttributes.taskType != null) then + taskAttributes.taskType + else if(taskType != null) then + taskType + else + null + + + + + + + + + + + + + + "caseName" + + + caseData.caseNameHmctsInternal + + + true + + + + + + + + + + + "caseManagementCategory" + + + if (caseData.caseManagementCategory !=null and caseData.caseManagementCategory.selectedLabel !=null) then caseData.caseManagementCategory.selectedLabel else "Property Possession Claims" + + + true + + + + + + + + + + + "region" + + + if(caseData!=null and caseData.caseManagementLocation!=null and caseData.caseManagementLocation.region!=null) then caseData.caseManagementLocation.region else "1" + + + true + + + + Location Id + + + + + + + + "location" + + + if (caseData.caseManagementLocation != null and caseData.caseManagementLocation.baseLocation != null) then caseData.caseManagementLocation.baseLocation else "336559" + + + true + + + + + +Short name + + + + + + + + "locationName" + + + if (caseData.caseManagementLocation != null and caseData.caseManagementLocation.baseLocation != null and caseData.managingOffice != null) then caseData.managingOffice else "Unable to find location" + + + true + + + + + + + + "ReviewAdjournGenApp","ReviewSetAsideGenApp","TranslateDefendantSubmittedDocument","TranslateClaimantSubmittedDocument","TranslateCaseworkerUploadedDocument" + + + "majorPriority" + + + 1000 + + + true + + + + + + + + "ReviewGenApp" + + + "majorPriority" + + + 2000 + + + true + + + + + + + + "NewClaimCreateNewHearing" + + + "majorPriority" + + + 3000 + + + true + + + + + + + + "ReviewCaseFlagRequest","ReviewCaseFlag" + + + "majorPriority" + + + 4000 + + + true + + + + + + + + "ReviewDefendantResponseAndCounterclaim","ReviewAdditionalDocumentsClaim","ReviewAdditionalDocumentsGenApp","ReviewAdditionalDocumentsCounterclaim" + + + "majorPriority" + + + 5000 + + + true + + + + + + + + "ReviewFailedPayment" + + + "majorPriority" + + + 6000 + + + true + + + + + + + + "ReviewDateDue" + + + "majorPriority" + + + 7000 + + + true + + + + + + + + + + + "minorPriority" + + + 500 + + + true + + + + + + + + + + + "dueDateNonWorkingCalendar" + + + "https://www.gov.uk/bank-holidays/england-and-wales.json" + + + true + + + + + + + + + + + "dueDateNonWorkingDaysOfWeek" + + + "SATURDAY,SUNDAY" + + + false + + + + + + + + "ReviewDefendantResponseAndCounterclaim","ReviewAdditionalDocumentsClaim","ReviewAdditionalDocumentsGenApp","ReviewAdditionalDocumentsCounterclaim","ReviewDateDue","TranslateDefendantSubmittedDocument","TranslateClaimantSubmittedDocument","TranslateCaseworkerUploadedDocument" + + + "workType" + + + "routine_work" + + + true + + + + + + + + "NewClaimCreateNewHearing","ReviewCaseFlagRequest","ReviewCaseFlag" + + + "workType" + + + "decision_making_work" + + + true + + + + + + + + "ReviewAdjournGenApp","ReviewSetAsideGenApp","ReviewGenApp" + + + "workType" + + + "applications" + + + true + + + + + + + + "ReviewFailedPayment" + + + "workType" + + + "error_management" + + + true + + + + + + + + + + + "roleCategory" + + + "ADMIN" + + + true + + + + + + + + "NewClaimCreateNewHearing" + + + "description" + + + "List the case on List Assist, create and serve the Notice of Hearing on all parties, and upload a copy of the notice to the digital case file. Only mark the task as complete once all of these steps have been completed." + + + + + + + + + + + "ReviewDefendantResponseAndCounterclaim" + + + "description" + + + "Review the defendant’s response and counterclaim, check whether any further case action is required, and take the appropriate action before closing the task. Only mark the task as complete once the review is finished and any required action has been completed." + + + true + + + + + + + + "ReviewAdjournGenApp" + + + "description" + + + "Review the general application to adjourn the hearing, decide what action is needed, and take the appropriate action. Only mark the task as complete once the application has been reviewed and any required action has been completed." + + + true + + + + + + + + "ReviewSetAsideGenApp" + + + "description" + + + "Review the general application to set aside, decide what action is needed, and take the appropriate action. Only mark the task as complete once the application has been reviewed and any required action has been completed." + + + true + + + + + + + + "ReviewGenApp" + + + "description" + + + "Review the general application, decide what action is needed, and take the appropriate action. Only mark the task as complete once the application has been reviewed and any required action has been completed." + + + true + + + + + + + + "ReviewAdditionalDocumentsClaim" + + + "description" + + + Review the additional documents submitted on the claim, check whether any further case action is required, and take the appropriate action. Only mark the task as complete once the documents have been reviewed and any required action has been completed. + + + true + + + + + + + + "ReviewAdditionalDocumentsGenApp" + + + "description" + + + "Review the additional documents submitted for the general application, check whether any further case action is required, and take the appropriate action. Only mark the task as complete once the documents have been reviewed and any required action has been completed." + + + true + + + + + + + + "ReviewAdditionalDocumentsCounterclaim" + + + "description" + + + "Review the additional documents submitted for the counterclaim, check whether any further case action is required, and take the appropriate action. Only mark the task as complete once the documents have been reviewed and any required action has been completed." + + + true + + + + + + + + "ReviewCaseFlagRequest" + + + "description" + + + "Review the case flag request, decide whether it should be approved or rejected, and record the decision. The task will close when the approval or rejection event is completed." + + + true + + + + + + + + "ReviewCaseFlag" + + + "description" + + + "Review the case flags on the case flags tab, check whether any action is required, and take the appropriate action. Only mark the task as complete once the case flags have been reviewed and any required action has been completed." + + + true + + + + + + + + "ReviewFailedPayment" + + + "description" + + + "Review the failed payment on the service request screen, check whether the payment issue has been resolved or whether further action is required, and take the appropriate action. Only mark the task as complete once the payment issue has been reviewed and any required action has been completed." + + + true + + + + + + + + "ReviewDateDue" + + + "description" + + + "Review the case note about the date due, check whether any follow-up action is required, and take the appropriate action. Only mark the task as complete once the date due has been reviewed and any required action has been completed." + + + true + + + + + + + + "TranslateDefendantSubmittedDocument" + + + "description" + + + "Review the defendant submitted document and arrange translation where required. Upload or record the translated document on the case file and take any further action needed. Only mark the task as complete once the translation has been completed and any required action has been taken." + + + true + + + + + + + + "TranslateClaimantSubmittedDocument" + + + "description" + + + "Review the claimant submitted document and arrange translation. Email the translated document to the court. Only mark the task as complete once the translation has been completed." + + + true + + + + + + + + "TranslateCaseworkerUploadedDocument" + + + "description" + + + "Review the caseworker uploaded document and arrange translation. Email the translated document to the court. Only mark the task as complete once the translation has been completed." + + + true + + + + + + + + + + + "priorityDateOriginRef" + + + caseData.dueDate + + + true + + + + + + + + + + + "dueDateOrigin" + + + now() + + + false + + + + + + + + + + + "dueDateTime" + + + time("17:00") + + + false + + + + + + + + + + + + diff --git a/src/main/resources/dmn/wa-task-permissions-possession_servcie.dmn b/src/main/resources/dmn/wa-task-permissions-possession_servcie.dmn index 8704d1f8f2..eb96e30faf 100644 --- a/src/main/resources/dmn/wa-task-permissions-possession_servcie.dmn +++ b/src/main/resources/dmn/wa-task-permissions-possession_servcie.dmn @@ -1,5 +1,5 @@ - + @@ -35,7 +35,7 @@ - "Hearing admin" + "hearing-centre-admin" "Read,Own,Claim,Unclaim,Manage,Complete" @@ -64,7 +64,7 @@ - "Hearing team leader" + "hearing-centre-team-leader" "Read,Own,Manage,Complete,Cancel,Assign,Unassign" @@ -93,7 +93,7 @@ - "WLU admin" + "wlu-admin" "Read,Own,Claim,Unclaim,Manage,Complete" @@ -122,7 +122,7 @@ - "WLU team leader" + "wlu-team-leader" "Read,Own,Manage,Complete,Cancel,Assign,Unassign" From fe7c8545217762d3cec1aa959cb7bc70c5816d47 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 16 Jul 2026 17:12:18 +0100 Subject: [PATCH 03/37] HDPI-6585: add unit test to camunda service --- .../reform/pcs/camunda/CamundaService.java | 6 +- .../wa-task-initiation-possession_service.dmn | 16 +-- .../pcs/camunda/CamundaServiceTest.java | 105 ++++++++++++++++++ 3 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java index 73093c6c70..51b94aae2a 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java @@ -6,6 +6,7 @@ import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.pcs.ccd.CaseType; +import java.time.Clock; import java.time.LocalDateTime; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -23,6 +24,7 @@ public class CamundaService { private static final String CREATE = "createTaskMessage"; private static final String UNCONFIGURED = "unconfigured"; private static final String EMPTY_WARNINGS_LIST = "[]"; + private final Clock utcClock; public void createTask( Long caseId, @@ -31,8 +33,8 @@ public void createTask( log.info("Creating task for {}", caseId); Map> processVariables = new ConcurrentHashMap<>(); - LocalDateTime delayUntil = LocalDateTime.now(); - LocalDateTime dueDate = LocalDateTime.now().plusDays(taskType.getWorkingDays()); + LocalDateTime delayUntil = LocalDateTime.now(utcClock); + LocalDateTime dueDate = delayUntil.plusDays(taskType.getWorkingDays()); processVariables.put("taskState", dmnStringValue(UNCONFIGURED)); processVariables.put("caseTypeId", dmnStringValue(CaseType.getCaseType())); diff --git a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn index 41c327c858..98faae9ed0 100644 --- a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn @@ -1,5 +1,5 @@ - + @@ -56,7 +56,7 @@ else "Notify claim" - "Case issued" + "CASE_ISSUED" @@ -101,7 +101,7 @@ else "Update case state" - "Case issued" + "CASE_ISSUED" @@ -146,7 +146,7 @@ else Tech team update case state - Case moves from "pending issued" to "Case issued" + "PENDING_CASE_ISSUED to CASE_ISSUED" @@ -193,7 +193,7 @@ else - "Pending Case issued (counterclaim)" + "PENDING_COUNTER_CLAIM_ISSUED" @@ -504,7 +504,7 @@ else - "Application Issued" + "GEN_APP_ISSUED" User selects("Ask to adjourn the hearing" for Choose an application) @@ -548,7 +548,7 @@ else - "Application Issued" + "GEN_APP_ISSUED" User selects("Ask the court to set aside" for Choose an application) @@ -850,7 +850,7 @@ else Submit Claim - Pending claim issued + "PENDING_CASE_ISSUED" diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java new file mode 100644 index 0000000000..28e38b7f64 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java @@ -0,0 +1,105 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; + +import java.time.Clock; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class CamundaServiceTest { + + @Mock + private CamundaApi camundaApi; + + @Mock + private AuthTokenGenerator authTokenGenerator; + + @Mock + private Clock utcClock; + + @InjectMocks + private CamundaService camundaService; + + private static final LocalDateTime TEST_UTC_DATE_TIME = LocalDate.of(2025, 8, 27) + .atTime(12, 51, 19); + + @BeforeEach + void setUp() { + when(authTokenGenerator.generate()).thenReturn("authToken"); + when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); + when(utcClock.getZone()).thenReturn(ZoneOffset.UTC); + } + + @Test + void shouldSendCreateTaskTestToCamunda() { + // Given + Long caseId = 1234L; + TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + + // When + camundaService.createTask(caseId, taskType); + + // Then + ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(SendMessageRequest.class); + verify(camundaApi).sendMessage(eq("authToken"), requestArgumentCaptor.capture()); + SendMessageRequest sendMessageRequest = requestArgumentCaptor.getValue(); + + assertThat(sendMessageRequest).isNotNull(); + assertThat(sendMessageRequest.getMessageName()).isEqualTo("createTaskMessage"); + + Map> processVariables = sendMessageRequest.getProcessVariables(); + assertThat(processVariables).isNotEmpty(); + assertThat(processVariables.get("taskState").getValue()).isEqualTo("unconfigured"); + assertThat(processVariables.get("taskState").getType()).isEqualTo("String"); + assertThat(processVariables.get("caseTypeId").getValue()).isEqualTo("PCS"); + assertThat(processVariables.get("caseTypeId").getType()).isEqualTo("String"); + assertThat(processVariables.get("dueDate").getValue()).isEqualTo("2025-09-01T12:51:19"); + assertThat(processVariables.get("dueDate").getType()).isEqualTo("String"); + assertThat(processVariables.get("workingDaysAllowed").getValue()).isEqualTo(5); + assertThat(processVariables.get("workingDaysAllowed").getType()).isEqualTo("Integer"); + assertThat(processVariables.get("jurisdiction").getValue()).isEqualTo("PCS"); + assertThat(processVariables.get("jurisdiction").getType()).isEqualTo("String"); + assertThat(processVariables.get("name").getValue()).isEqualTo("New Claim – Create new hearing"); + assertThat(processVariables.get("name").getType()).isEqualTo("String"); + assertThat(processVariables.get("taskId").getValue()).isEqualTo("NewClaimCreateNewHearing"); + assertThat(processVariables.get("taskId").getType()).isEqualTo("String"); + assertThat(processVariables.get("caseId").getValue()).isEqualTo(caseId.toString()); + assertThat(processVariables.get("caseId").getType()).isEqualTo("String"); + assertThat(processVariables.get("delayUntil").getValue()).isEqualTo("2025-08-27T12:51:19"); + assertThat(processVariables.get("delayUntil").getType()).isEqualTo("String"); + assertThat(processVariables.get("hasWarnings").getValue()).isEqualTo(false); + assertThat(processVariables.get("hasWarnings").getType()).isEqualTo("Boolean"); + assertThat(processVariables.get("warningList").getValue()).isEqualTo("[]"); + assertThat(processVariables.get("warningList").getType()).isEqualTo("String"); + } + + @Test + void shouldHandleFailedRequestToCamunda() { + // Given + Long caseId = 1234L; + TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + + when(authTokenGenerator.generate()).thenReturn("authToken"); + doThrow(new RuntimeException()).when(camundaApi).sendMessage(any(), any()); + + // When + camundaService.createTask(caseId, taskType); + } +} From d4f6968974c9182e97c89c9fe4d18d03ba51c7dd Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 16 Jul 2026 17:27:14 +0100 Subject: [PATCH 04/37] HDPI-6585: add feature flag and camunda url --- charts/pcs-api/values.wa.preview.template.yaml | 1 + featureFlags/flags.json | 3 ++- src/main/java/uk/gov/hmcts/reform/pcs/Application.java | 4 +++- .../java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java | 1 + .../uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java | 1 + .../java/uk/gov/hmcts/reform/pcs/service/FeatureFlag.java | 3 ++- src/main/resources/application.yaml | 3 +++ 7 files changed, 13 insertions(+), 3 deletions(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index e30b0a1e88..bc8c8d2a94 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -10,6 +10,7 @@ java: SPRING_JMS_SERVICEBUS_ENABLED: true AZURE_SERVICE_BUS_TOPIC_NAME: ${SERVICE_NAME}-asb-ccd-case-events AZURE_SERVICE_BUS_SUBSCRIPTION_NAME: ${SERVICE_NAME}-asb-ccd-case-events + CAMUNDA_BASE_URL: http://pcs-api-pr-${CHANGE_ID}-camunda/engine-rest servicebus: enabled: true diff --git a/featureFlags/flags.json b/featureFlags/flags.json index d10cfbd063..39b143347f 100644 --- a/featureFlags/flags.json +++ b/featureFlags/flags.json @@ -3,6 +3,7 @@ "bulk-print-enabled": false, "caseworker-events-enabled": true, "cui-respond-to-claim-lr-enabled": true, - "release-1.2-enabled": true + "release-1.2-enabled": true, + "caseworker-wa-enabled": false } } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/Application.java b/src/main/java/uk/gov/hmcts/reform/pcs/Application.java index 03a21aafe6..b7f89003a6 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/Application.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/Application.java @@ -8,6 +8,7 @@ import uk.gov.hmcts.reform.ccd.document.am.feign.CaseDocumentClientApi; import uk.gov.hmcts.reform.fees.client.FeesApi; import uk.gov.hmcts.reform.idam.client.IdamApi; +import uk.gov.hmcts.reform.pcs.camunda.CamundaApi; import uk.gov.hmcts.reform.pcs.hearings.service.api.HmcHearingApi; import uk.gov.hmcts.reform.pcs.idam.IdamUserInfoApi; import uk.gov.hmcts.reform.pcs.location.service.api.LocationReferenceApi; @@ -32,7 +33,8 @@ RdProfessionalApi.class, FeesApi.class, - CaseDocumentClientApi.class + CaseDocumentClientApi.class, + CamundaApi.class } ) @EnableJms diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java index 3660ad46b5..64eeb1d8c3 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java @@ -9,5 +9,6 @@ public class FeatureFlags { private VerticalYesNo release1dot2Enabled; private VerticalYesNo caseWorkerEventsEnabled; + private VerticalYesNo caseWorkerWaEnabled; } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java index ef77c79c21..5366224004 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java @@ -18,6 +18,7 @@ public void setCaseFields(PCSCase pcsCase) { pcsCase.setFeatureFlags(FeatureFlags.builder() .release1dot2Enabled(getFlag(FeatureFlag.RELEASE_1_DOT_2)) .caseWorkerEventsEnabled(getFlag(FeatureFlag.CASEWORKER_EVENTS)) + .caseWorkerWaEnabled(getFlag(FeatureFlag.CASEWORKER_WA)) .build()); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/service/FeatureFlag.java b/src/main/java/uk/gov/hmcts/reform/pcs/service/FeatureFlag.java index 14651c685c..017fd1b1d3 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/service/FeatureFlag.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/service/FeatureFlag.java @@ -6,7 +6,8 @@ public enum FeatureFlag { BULK_PRINT("bulk-print-enabled", false), CASEWORKER_EVENTS("caseworker-events-enabled", false), CUI_RESPOND_TO_CLAIM_LR("cui-respond-to-claim-lr-enabled", false), - RELEASE_1_DOT_2("release-1.2-enabled", false); + RELEASE_1_DOT_2("release-1.2-enabled", false), + CASEWORKER_WA("caseworker-wa-enabled", false); private final String key; private final boolean defaultValue; diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index f536562e15..219fa2da57 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -333,5 +333,8 @@ core_case_data: caseApi: url: ${CASE_API_URL:http://localhost:3206} +camunda: + url: ${CAMUNDA_BASE_URL:http://camunda-local-bpm/engine-rest} + shutter: service: ${SHUTTER_SERVICE:false} From b0d233c89fb87a3ab0d42f823bc7b1d422711080 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 16 Jul 2026 17:32:23 +0100 Subject: [PATCH 05/37] HDPI-6585: add access for WA user --- .../uk/gov/hmcts/reform/pcs/CftlibConfig.java | 1 + .../reform/pcs/ccd/accesscontrol/UserRole.java | 3 ++- .../reform/pcs/ccd/accesscontrol/WAAccess.java | 18 ++++++++++++++++++ .../pcs/ccd/config/HighLevelDataSetupApp.java | 3 ++- .../hmcts/reform/pcs/ccd/domain/PCSCase.java | 9 +++++---- .../gov/hmcts/reform/pcs/ccd/domain/State.java | 5 +++-- 6 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/WAAccess.java diff --git a/src/cftlib/java/uk/gov/hmcts/reform/pcs/CftlibConfig.java b/src/cftlib/java/uk/gov/hmcts/reform/pcs/CftlibConfig.java index 9b12decc83..42521e443b 100644 --- a/src/cftlib/java/uk/gov/hmcts/reform/pcs/CftlibConfig.java +++ b/src/cftlib/java/uk/gov/hmcts/reform/pcs/CftlibConfig.java @@ -73,6 +73,7 @@ private void createAccessProfiles(CFTLib lib) { roleNames.add("caseworker"); roleNames.add("caseworker-ras-validation"); roleNames.add("GS_profile"); + roleNames.add("caseworker-wa-task-configuration"); lib.createRoles(roleNames.toArray(new String[0])); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/UserRole.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/UserRole.java index d2220d1733..0e012b1cba 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/UserRole.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/UserRole.java @@ -45,7 +45,8 @@ public enum UserRole implements HasRole { LEADERSHIP_JUDGE("leadership-judge", Set.of(R), RAS), CIRCUIT_JUDGE("circuit-judge", Set.of(R), RAS), JUDGE("judge", Set.of(R), RAS), - SYSTEM_USER("pcs-system-update", Permission.CRU, IDAM); + SYSTEM_USER("pcs-system-update", Permission.CRU, IDAM), + WA_SYSTEM_USER("caseworker-wa-task-configuration", Permission.CRU, IDAM); @JsonValue diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/WAAccess.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/WAAccess.java new file mode 100644 index 0000000000..fc0caae731 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/WAAccess.java @@ -0,0 +1,18 @@ +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.WA_SYSTEM_USER; + +public class WAAccess implements HasAccessControl { + @Override + public SetMultimap getGrants() { + SetMultimap grants = HashMultimap.create(); + grants.put(WA_SYSTEM_USER, Permission.R); + return grants; + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/config/HighLevelDataSetupApp.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/config/HighLevelDataSetupApp.java index 146f7fda76..0daa3f3480 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/config/HighLevelDataSetupApp.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/config/HighLevelDataSetupApp.java @@ -36,7 +36,8 @@ public class HighLevelDataSetupApp extends DataLoaderToDefinitionStore { new CcdRoleConfig("fee-paid-judge", "PUBLIC"), new CcdRoleConfig("circuit-judge", "PUBLIC"), new CcdRoleConfig("leadership-judge", "PUBLIC"), - new CcdRoleConfig("pcs-system-update", "PUBLIC") + new CcdRoleConfig("pcs-system-update", "PUBLIC"), + new CcdRoleConfig("caseworker-wa-task-configuration", "PUBLIC") }; 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 1e4020dd42..40b2b83339 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 @@ -31,6 +31,7 @@ import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.InternalTabAccess; import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.PartyVisibleTabAccess; import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.RasValidationAccess; +import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.WAAccess; import uk.gov.hmcts.reform.pcs.ccd.domain.dashboard.DashboardData; import uk.gov.hmcts.reform.pcs.ccd.domain.enforcetheorder.EnforcementOrder; import uk.gov.hmcts.reform.pcs.ccd.domain.genapp.CitizenGenAppRequest; @@ -638,25 +639,25 @@ public class PCSCase { @CCD( label = "CaseNameHmctsInternal", - access = {GlobalSearchAccess.class} + access = {GlobalSearchAccess.class, WAAccess.class} ) private String caseNameHmctsInternal; @CCD( label = "CaseNamePublic", - access = {GlobalSearchAccess.class} + access = {GlobalSearchAccess.class, WAAccess.class} ) private String caseNamePublic; @CCD( label = "CaseManagementLocation", - access = {GlobalSearchAccess.class, RasValidationAccess.class} + access = {GlobalSearchAccess.class, RasValidationAccess.class, WAAccess.class} ) private CaseLocation caseManagementLocation; @CCD( label = "CaseManagementCategory", - access = {GlobalSearchAccess.class} + access = {GlobalSearchAccess.class, WAAccess.class} ) private DynamicList caseManagementCategory; diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java index 32aeec350c..96ee09d203 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java @@ -10,6 +10,7 @@ import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.InternalCaseFlagAccess; import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.GlobalSearchAccess; import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.RasValidationAccess; +import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.WAAccess; /** * All possible PCS case states. @@ -29,7 +30,7 @@ public enum State { @CCD( label = "Pending Case Issued", access = {ClaimantAccess.class, CitizenAccess.class, RasValidationAccess.class, - InternalCaseFlagAccess.class, GlobalSearchAccess.class}, + InternalCaseFlagAccess.class, GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) PENDING_CASE_ISSUED, @@ -37,7 +38,7 @@ public enum State { @CCD( label = "Case Issued", access = {CaseworkerReadAccess.class, ClaimantAccess.class, DefendantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) CASE_ISSUED From 98810ba53d4c07838b2d38154f43cd923beb48db Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Tue, 21 Jul 2026 14:02:55 +0100 Subject: [PATCH 06/37] HDPI-6585: add feature flag to CamundaService --- .../reform/pcs/camunda/CamundaService.java | 8 +++++ .../pcs/camunda/CamundaServiceTest.java | 36 +++++++++++++++---- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java index 51b94aae2a..e3ddb25ee5 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java @@ -5,6 +5,8 @@ import org.springframework.stereotype.Service; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.pcs.ccd.CaseType; +import uk.gov.hmcts.reform.pcs.service.FeatureFlag; +import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; import java.time.Clock; import java.time.LocalDateTime; @@ -20,6 +22,7 @@ public class CamundaService { private final CamundaApi camundaApi; private final AuthTokenGenerator authTokenGenerator; + private final FeatureToggleService featureToggleService; private static final String CREATE = "createTaskMessage"; private static final String UNCONFIGURED = "unconfigured"; @@ -30,6 +33,11 @@ public void createTask( Long caseId, TaskType taskType ) { + if (!featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)) { + log.info("Skipped creating task for {}", caseId); + return; + } + log.info("Creating task for {}", caseId); Map> processVariables = new ConcurrentHashMap<>(); diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java index 28e38b7f64..e4806f36be 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java @@ -8,6 +8,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; +import uk.gov.hmcts.reform.pcs.service.FeatureFlag; +import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; import java.time.Clock; import java.time.LocalDate; @@ -19,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,25 +37,26 @@ public class CamundaServiceTest { @Mock private Clock utcClock; + @Mock + private FeatureToggleService featureToggleService; + @InjectMocks private CamundaService camundaService; private static final LocalDateTime TEST_UTC_DATE_TIME = LocalDate.of(2025, 8, 27) .atTime(12, 51, 19); - @BeforeEach - void setUp() { - when(authTokenGenerator.generate()).thenReturn("authToken"); - when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); - when(utcClock.getZone()).thenReturn(ZoneOffset.UTC); - } - @Test void shouldSendCreateTaskTestToCamunda() { // Given Long caseId = 1234L; TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + when(authTokenGenerator.generate()).thenReturn("authToken"); + when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); + when(utcClock.getZone()).thenReturn(ZoneOffset.UTC); + when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(true); + // When camundaService.createTask(caseId, taskType); @@ -90,6 +94,21 @@ void shouldSendCreateTaskTestToCamunda() { assertThat(processVariables.get("warningList").getType()).isEqualTo("String"); } + @Test + void shouldSkipCreatingTaskIfWaIsNotEnabled() { + // Given + Long caseId = 1234L; + TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + + when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(false); + + // When + camundaService.createTask(caseId, taskType); + + // Then + verify(camundaApi, never()).sendMessage(any(), any()); + } + @Test void shouldHandleFailedRequestToCamunda() { // Given @@ -97,6 +116,9 @@ void shouldHandleFailedRequestToCamunda() { TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; when(authTokenGenerator.generate()).thenReturn("authToken"); + when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); + when(utcClock.getZone()).thenReturn(ZoneOffset.UTC); + when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(true); doThrow(new RuntimeException()).when(camundaApi).sendMessage(any(), any()); // When From 96cb778b8977d31cf66470b80d7f3d3541a81cea Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Tue, 21 Jul 2026 14:36:53 +0100 Subject: [PATCH 07/37] HDPI-6501: fix checkstyle --- .../reform/pcs/camunda/CamundaServiceTest.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java index e4806f36be..8bf1ef2990 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java @@ -1,6 +1,5 @@ package uk.gov.hmcts.reform.pcs.camunda; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -49,8 +48,8 @@ public class CamundaServiceTest { @Test void shouldSendCreateTaskTestToCamunda() { // Given - Long caseId = 1234L; - TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + final Long caseId = 1234L; + final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; when(authTokenGenerator.generate()).thenReturn("authToken"); when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); @@ -97,8 +96,8 @@ void shouldSendCreateTaskTestToCamunda() { @Test void shouldSkipCreatingTaskIfWaIsNotEnabled() { // Given - Long caseId = 1234L; - TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + final Long caseId = 1234L; + final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(false); @@ -112,8 +111,8 @@ void shouldSkipCreatingTaskIfWaIsNotEnabled() { @Test void shouldHandleFailedRequestToCamunda() { // Given - Long caseId = 1234L; - TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + final Long caseId = 1234L; + final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; when(authTokenGenerator.generate()).thenReturn("authToken"); when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); From 853b1c7ae36cabe24bdeecbeeaaf4d87b724d802 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Tue, 21 Jul 2026 15:45:42 +0100 Subject: [PATCH 08/37] HDPI-6501: add task creation to notify case issued event --- .../notify/service/FeePaymentNotificationService.java | 11 ++++++++++- .../service/FeePaymentNotificationServiceTest.java | 9 ++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/notify/service/FeePaymentNotificationService.java b/src/main/java/uk/gov/hmcts/reform/pcs/notify/service/FeePaymentNotificationService.java index 7f2c56d27f..042f136a06 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/notify/service/FeePaymentNotificationService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/notify/service/FeePaymentNotificationService.java @@ -4,6 +4,10 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import uk.gov.hmcts.reform.pcs.camunda.CamundaService; +import uk.gov.hmcts.reform.pcs.camunda.TaskType; +import uk.gov.hmcts.reform.pcs.ccd.entity.ClaimEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.feesandpay.FeePaymentEntity; import uk.gov.hmcts.reform.pcs.ccd.repository.feeandpay.FeePaymentRepository; import uk.gov.hmcts.reform.pcs.exception.FeePaymentNotFoundException; @@ -17,6 +21,7 @@ public class FeePaymentNotificationService { private final NotificationService notificationService; private final FeePaymentRepository feePaymentRepository; + private final CamundaService camundaService; @Transactional public void sendClaimantPaidCaseIssuedNotification(UUID feePaymentId) { @@ -25,6 +30,10 @@ public void sendClaimantPaidCaseIssuedNotification(UUID feePaymentId) { log.info("Sending claimant paid case issued notification for fee payment: {}", feePaymentId); - notificationService.sendClaimantClaimIssuedEmailNotification(feePayment.getClaim()); + ClaimEntity claimEntity = feePayment.getClaim(); + notificationService.sendClaimantClaimIssuedEmailNotification(claimEntity); + + PcsCaseEntity pcsCaseEntity = claimEntity.getPcsCase(); + camundaService.createTask(pcsCaseEntity.getCaseReference(), TaskType.NEW_CLAIM_CREATE_NEW_HEARING); } } diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/notify/service/FeePaymentNotificationServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/notify/service/FeePaymentNotificationServiceTest.java index 92ceb2d49d..7be00ec68c 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/notify/service/FeePaymentNotificationServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/notify/service/FeePaymentNotificationServiceTest.java @@ -5,7 +5,10 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.reform.pcs.camunda.CamundaService; +import uk.gov.hmcts.reform.pcs.camunda.TaskType; import uk.gov.hmcts.reform.pcs.ccd.entity.ClaimEntity; +import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.feesandpay.FeePaymentEntity; import uk.gov.hmcts.reform.pcs.ccd.repository.feeandpay.FeePaymentRepository; import uk.gov.hmcts.reform.pcs.exception.FeePaymentNotFoundException; @@ -24,6 +27,8 @@ class FeePaymentNotificationServiceTest { private NotificationService notificationService; @Mock private FeePaymentRepository feePaymentRepository; + @Mock + private CamundaService camundaService; @InjectMocks private FeePaymentNotificationService underTest; @@ -31,7 +36,8 @@ class FeePaymentNotificationServiceTest { @Test void shouldSendClaimantClaimIssuedEmailNotification() { UUID feePaymentId = UUID.randomUUID(); - ClaimEntity claim = new ClaimEntity(); + PcsCaseEntity pcsCaseEntity = PcsCaseEntity.builder().caseReference(1234L).build(); + ClaimEntity claim = ClaimEntity.builder().pcsCase(pcsCaseEntity).build(); FeePaymentEntity feePayment = FeePaymentEntity.builder() .id(feePaymentId) .claim(claim) @@ -41,6 +47,7 @@ void shouldSendClaimantClaimIssuedEmailNotification() { underTest.sendClaimantPaidCaseIssuedNotification(feePaymentId); verify(notificationService).sendClaimantClaimIssuedEmailNotification(claim); + verify(camundaService).createTask(1234L, TaskType.NEW_CLAIM_CREATE_NEW_HEARING); } @Test From 1aed3cd7f5f7661cd2b5159809eff18b5a918f6c Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Tue, 21 Jul 2026 17:27:25 +0100 Subject: [PATCH 09/37] HDPI-6501: add role assignment url --- charts/pcs-api/values.ccd.preview.template.yaml | 2 +- charts/pcs-api/values.wa.preview.template.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/charts/pcs-api/values.ccd.preview.template.yaml b/charts/pcs-api/values.ccd.preview.template.yaml index 77a6ca3dc3..f106f488a0 100644 --- a/charts/pcs-api/values.ccd.preview.template.yaml +++ b/charts/pcs-api/values.ccd.preview.template.yaml @@ -177,7 +177,7 @@ xui-webapp: nodejs: imagePullPolicy: Always releaseNameOverride: ${SERVICE_NAME}-xui-webapp - image: hmctsprod.azurecr.io/xui/webapp:latest + image: hmctsprod.azurecr.io/xui/webapp:pr-5239 ingressHost: xui-${SERVICE_FQDN} devmemoryRequests: 2Gi devcpuRequests: 500m diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index bc8c8d2a94..5994d4a58e 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -65,6 +65,7 @@ wa: AZURE_SERVICE_BUS_TOPIC_NAME: ${SERVICE_NAME}-asb-ccd-case-events AZURE_SERVICE_BUS_SUBSCRIPTION_NAME: ${SERVICE_NAME}-asb-ccd-case-events AZURE_SERVICE_BUS_CCD_CASE_EVENTS_SUBSCRIPTION_NAME: ${SERVICE_NAME}-asb-ccd-case-events + ROLE_ASSIGNMENT_URL: http://am-role-assignment-service-aat.service.core-compute-aat.internal keyVaults: wa: secrets: From e23dda053398d4482b32a56696c47d32ad371fc3 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 09:09:10 +0100 Subject: [PATCH 10/37] HDPI-6501: add publish to camunda and add role assignment url to wa-task-management-api --- charts/pcs-api/values.wa.preview.template.yaml | 3 ++- .../pcs/ccd/event/changecasestate/ChangeCaseState.java | 7 +++++++ .../dmn/wa-task-initiation-possession_service.dmn | 2 +- .../pcs/ccd/event/changecasestate/ChangeCaseStateTest.java | 6 +++++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index 5994d4a58e..a87f3750b2 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -82,8 +82,9 @@ wa: java: ingressHost: wa-task-management-api-${SERVICE_FQDN} environment: - ALLOWED_CASE_TYPES: pcs-${CHANGE_ID} + ALLOWED_CASE_TYPES: pcs ALLOWED_JURISDICTIONS: pcs + ROLE_ASSIGNMENT_URL: http://am-role-assignment-service-aat.service.core-compute-aat.internal keyVaults: wa: secrets: diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseState.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseState.java index 5cb3ec676d..2125ce8ea1 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseState.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseState.java @@ -15,6 +15,8 @@ import uk.gov.hmcts.reform.pcs.ccd.domain.State; import uk.gov.hmcts.reform.pcs.ccd.page.changecasestate.ChangeCaseStatePage; import uk.gov.hmcts.reform.pcs.ccd.util.AddressFormatter; +import uk.gov.hmcts.reform.pcs.service.FeatureFlag; +import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.CaseworkerRoles.CASEWORKER_ROLES; import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.JudicialHistoryRoles.JUDICIAL_HISTORY_ROLES; @@ -28,6 +30,7 @@ public class ChangeCaseState implements CCDConfig { private final AddressFormatter addressFormatter; + private final FeatureToggleService featureToggleService; @Override public void configureDecentralised(DecentralisedConfigBuilder configBuilder) { @@ -51,6 +54,10 @@ public void configureDecentralised(DecentralisedConfigBuilder - "Notify claim" + "Notify claim - claimant is notified of case issued" "CASE_ISSUED" diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseStateTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseStateTest.java index 26d469c42e..425fc6995a 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseStateTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseStateTest.java @@ -14,6 +14,7 @@ import uk.gov.hmcts.reform.pcs.ccd.domain.State; import uk.gov.hmcts.reform.pcs.ccd.event.BaseEventTest; import uk.gov.hmcts.reform.pcs.ccd.util.AddressFormatter; +import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; @@ -29,6 +30,9 @@ class ChangeCaseStateTest extends BaseEventTest { @Mock private AddressFormatter addressFormatter; + @Mock + private FeatureToggleService featureToggleService; + @InjectMocks private ChangeCaseState changeCaseState; @@ -73,4 +77,4 @@ void shouldIncludeFormattedAddressInConfirmationBody() { assertThat(response.getConfirmationBody()).contains(FORMATTED_ADDRESS); } -} \ No newline at end of file +} From 6bf772881c3a4e6b65ba8df0ee0763665b573167 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 11:22:16 +0100 Subject: [PATCH 11/37] HDPI-6585: add service bus to ccd preview --- charts/pcs-api/values.wa.preview.template.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index a87f3750b2..50fefbca29 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -27,6 +27,14 @@ servicebus: subscriptionNeeded: yes requiresSession: true +ccd: + servicebus: + destination: ${SERVICE_NAME}-asb-ccd-case-events + scheduler-enabled: true + batch-size: 50 + published-retention-days: 7 + schedule: "*/10 * * * *" + postgresql: enabled: true flexibleserver: pcs-preview From 471dc3310beed420161bdbcfa90214848e5d6bb1 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 11:56:34 +0100 Subject: [PATCH 12/37] HDPI-6585: update cron job config --- charts/pcs-api/values.wa.preview.template.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index 50fefbca29..51ce19ab69 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -144,15 +144,15 @@ wa: job: schedule: "*/1 * * * *" - wa-initiation-batch: + wa-task-batch-initiation: job: schedule: "*/1 * * * *" - wa-reconfiguration-batch: + wa-task-batch-reconfig: job: schedule: "*/1 * * * *" - wa-termination-batch: + wa-task-batch-termination: job: schedule: "*/1 * * * *" From d6786baf9560f26dac3a74919e44d85a9538b3bd Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 13:33:14 +0100 Subject: [PATCH 13/37] HDPI-6585: reverse changes --- charts/pcs-api/values.wa.preview.template.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index 51ce19ab69..50fefbca29 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -144,15 +144,15 @@ wa: job: schedule: "*/1 * * * *" - wa-task-batch-initiation: + wa-initiation-batch: job: schedule: "*/1 * * * *" - wa-task-batch-reconfig: + wa-reconfiguration-batch: job: schedule: "*/1 * * * *" - wa-task-batch-termination: + wa-termination-batch: job: schedule: "*/1 * * * *" From 576668a4951cbb8a9d154a4595a35a2a34068473 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 14:50:33 +0100 Subject: [PATCH 14/37] HDPI-6585: stop suspension of cron jobs --- charts/pcs-api/values.wa.preview.template.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index 50fefbca29..91a7b508c6 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -146,14 +146,17 @@ wa: wa-initiation-batch: job: + suspend: false schedule: "*/1 * * * *" wa-reconfiguration-batch: job: + suspend: false schedule: "*/1 * * * *" wa-termination-batch: job: + suspend: false schedule: "*/1 * * * *" #disable wa db as we are using the postgres flexible server From 222c4434260ebc59e7c626be0d24e06543213769 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 15:32:19 +0100 Subject: [PATCH 15/37] HDPI-6585: add WA access to more states --- charts/pcs-api/values.wa.preview.template.yaml | 3 --- src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index 91a7b508c6..50fefbca29 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -146,17 +146,14 @@ wa: wa-initiation-batch: job: - suspend: false schedule: "*/1 * * * *" wa-reconfiguration-batch: job: - suspend: false schedule: "*/1 * * * *" wa-termination-batch: job: - suspend: false schedule: "*/1 * * * *" #disable wa db as we are using the postgres flexible server 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 500b08f3cc..fc3eed5f53 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 @@ -131,7 +131,7 @@ public class PCSCase { @CCD( label = "Property address", - access = {DefendantAccess.class} + access = {DefendantAccess.class, WAAccess.class} ) @External private AddressUK propertyAddress; From 716b22a77347feb9097d8ef693d88389af0dc806 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 15:32:27 +0100 Subject: [PATCH 16/37] HDPI-6585: add WA access to more states --- .../gov/hmcts/reform/pcs/ccd/domain/State.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java index ea3064fcfe..709c19f228 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java @@ -46,7 +46,7 @@ public enum State { @CCD( label = "Judicial Referral", access = {CaseworkerReadAccess.class, ClaimantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) JUDICIAL_REFERRAL, @@ -54,7 +54,7 @@ public enum State { @CCD( label = "Hearing Readiness", access = {CaseworkerReadAccess.class, ClaimantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) HEARING_READINESS, @@ -62,7 +62,7 @@ public enum State { @CCD( label = "Prepare For Hearing Conduct Hearing", access = {CaseworkerReadAccess.class, ClaimantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) PREPARE_FOR_HEARING_CONDUCT_HEARING, @@ -70,7 +70,7 @@ public enum State { @CCD( label = "Decision Outcome", access = {CaseworkerReadAccess.class, ClaimantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) DECISION_OUTCOME, @@ -78,7 +78,7 @@ public enum State { @CCD( label = "Case Progression", access = {CaseworkerReadAccess.class, ClaimantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) CASE_PROGRESSION, @@ -86,7 +86,7 @@ public enum State { @CCD( label = "All Final Orders", access = {CaseworkerReadAccess.class, ClaimantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) ALL_FINAL_ORDERS_ISSUED, @@ -94,7 +94,7 @@ public enum State { @CCD( label = "Case Stayed", access = {CaseworkerReadAccess.class, ClaimantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) CASE_STAYED, @@ -102,7 +102,7 @@ public enum State { @CCD( label = "Breathing Space", access = {CaseworkerReadAccess.class, ClaimantAccess.class, RasValidationAccess.class, - GlobalSearchAccess.class}, + GlobalSearchAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) BREATHING_SPACE From f95cde51cb1f5f8a6cc8731c16f774e072f0203e Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 15:33:50 +0100 Subject: [PATCH 17/37] HDPI-6585: remove WA access to property address --- src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fc3eed5f53..500b08f3cc 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 @@ -131,7 +131,7 @@ public class PCSCase { @CCD( label = "Property address", - access = {DefendantAccess.class, WAAccess.class} + access = {DefendantAccess.class} ) @External private AddressUK propertyAddress; From 5b486c26479995ec6e526d43acb3f99c6aba4d73 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 16:02:01 +0100 Subject: [PATCH 18/37] HDPI-6585: increase WA access to fields in PCS case --- .../hmcts/reform/pcs/ccd/domain/PCSCase.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 500b08f3cc..1566037477 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 @@ -131,7 +131,7 @@ public class PCSCase { @CCD( label = "Property address", - access = {DefendantAccess.class} + access = {DefendantAccess.class, WAAccess.class} ) @External private AddressUK propertyAddress; @@ -198,7 +198,7 @@ public class PCSCase { ) private Integer regionId; - @CCD(access = {InternalCaseFlagAccess.class}, + @CCD(access = {InternalCaseFlagAccess.class, WAAccess.class}, label = "Party") private List> parties; @@ -270,12 +270,12 @@ public class PCSCase { @CCD( label = "Have you served notice to the defendants?", - access = {CitizenAccess.class} + access = {CitizenAccess.class, WAAccess.class} ) private YesOrNo noticeServed; @JsonUnwrapped(prefix = "notice_") - @CCD(access = {ClaimantAccess.class, CitizenAccess.class}) + @CCD(access = {ClaimantAccess.class, CitizenAccess.class, WAAccess.class}) private NoticeServedDetails noticeServedDetails; private String caseTitleMarkdown; @@ -283,7 +283,7 @@ public class PCSCase { @CCD(access = {DefendantAccess.class}) private DashboardData dashboardData; - @CCD(access = {CitizenAccess.class}) + @CCD(access = {CitizenAccess.class, WAAccess.class}) private LegislativeCountry legislativeCountry; @CCD( @@ -357,7 +357,7 @@ public class PCSCase { /** * Combined list of all defendants in the case (i.e. primary defendant + additional defendants). */ - @CCD(access = {ClaimantAccess.class, CitizenAccess.class, InternalCaseFlagAccess.class}) + @CCD(access = {ClaimantAccess.class, CitizenAccess.class, InternalCaseFlagAccess.class, WAAccess.class}) private List> allDefendants; @JsonUnwrapped(prefix = "tenancy_") @@ -580,7 +580,7 @@ public class PCSCase { private VerticalYesNo isExemptLandlord; @CCD( - access = {DefendantAccess.class}, + access = {DefendantAccess.class, WAAccess.class}, searchable = false ) private PossessionClaimResponse possessionClaimResponse; @@ -604,16 +604,16 @@ public class PCSCase { private String formattedDefendantNames; private String formattedPropertyAddress; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) private List> claimGroundSummaries; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) private LocalDateTime dateSubmitted; @CCD(access = {ClaimantAccess.class, DefendantAccess.class}, label = "Date claim issued") private LocalDate claimIssueDate; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) private LocalDateTime dateIssued; @CCD( From 323e3260e77b588d2cde1494d8338df38a868b47 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 16:40:42 +0100 Subject: [PATCH 19/37] HDPI-6585: add WA access to AWAITING_SUBMISSION_TO_HMCTS --- src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java index 709c19f228..6949693d7b 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/State.java @@ -22,7 +22,7 @@ public enum State { @CCD( label = "Awaiting Submission to HMCTS", - access = {ClaimantAccess.class, CitizenAccess.class, RasValidationAccess.class}, + access = {ClaimantAccess.class, CitizenAccess.class, RasValidationAccess.class, WAAccess.class}, hint = "${caseTitleMarkdown}" ) AWAITING_SUBMISSION_TO_HMCTS, From 82a4575240ca314d6f52bf746abdbfb49bc69a1c Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Wed, 22 Jul 2026 17:15:23 +0100 Subject: [PATCH 20/37] HDPI-6585: increase WA access --- .../gov/hmcts/reform/pcs/ccd/domain/PCSCase.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 1566037477..94a3818ada 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 @@ -111,7 +111,7 @@ public class PCSCase { @JsonUnwrapped private ClaimantInformation claimantInformation; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) private List> allClaimants; @CCD( @@ -177,14 +177,14 @@ public class PCSCase { @CCD( searchable = false, - access = {DefendantAccess.class} + access = {DefendantAccess.class, WAAccess.class} ) @External private String userPcqId; @CCD( searchable = false, - access = {DefendantAccess.class} + access = {DefendantAccess.class, WAAccess.class} ) private YesOrNo userPcqIdSet; @@ -280,7 +280,7 @@ public class PCSCase { private String caseTitleMarkdown; - @CCD(access = {DefendantAccess.class}) + @CCD(access = {DefendantAccess.class, WAAccess.class}) private DashboardData dashboardData; @CCD(access = {CitizenAccess.class, WAAccess.class}) @@ -551,17 +551,17 @@ public class PCSCase { /** * Combined list of all underlessees/mortgagees in the case. */ - @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) private List> allUnderlesseeOrMortgagees; @CCD( searchable = false, label = "Ways to pay", - access = {PartyVisibleTabAccess.class} + access = {PartyVisibleTabAccess.class, WAAccess.class} ) private WaysToPay waysToPay; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) private StatementOfTruthDetails statementOfTruth; @CCD(searchable = false) @@ -610,7 +610,7 @@ public class PCSCase { @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) private LocalDateTime dateSubmitted; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class}, label = "Date claim issued") + @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}, label = "Date claim issued") private LocalDate claimIssueDate; @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) From dd8a2facc758870f141513cca1fc6b6c8d2a113e Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 23 Jul 2026 07:55:35 +0100 Subject: [PATCH 21/37] HDPI-6585: specify camunda version to fix bpm upload problem --- charts/pcs-api/values.wa.preview.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index 50fefbca29..8182592c35 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -61,7 +61,7 @@ wa: camunda-bpm: java: - image: hmctsprod.azurecr.io/camunda/bpm:latest + image: hmctsprod.azurecr.io/camunda/bpm:7.24.0 wa-case-event-handler: java: From ddce3bcab121c241c36a2373b8d17f002f267eba Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 23 Jul 2026 08:16:26 +0100 Subject: [PATCH 22/37] HDPI-6585: reverse change to image version --- charts/pcs-api/values.wa.preview.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index 8182592c35..50fefbca29 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -61,7 +61,7 @@ wa: camunda-bpm: java: - image: hmctsprod.azurecr.io/camunda/bpm:7.24.0 + image: hmctsprod.azurecr.io/camunda/bpm:latest wa-case-event-handler: java: From cb056d42dc88425b305ca53658d75c6376c2b002 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 23 Jul 2026 08:27:31 +0100 Subject: [PATCH 23/37] HDPI-6585: change to a different camunda version --- charts/pcs-api/values.wa.preview.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index 50fefbca29..f09dc72a9e 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -61,7 +61,7 @@ wa: camunda-bpm: java: - image: hmctsprod.azurecr.io/camunda/bpm:latest + image: hmctsprod.azurecr.io/camunda/bpm:pr-1025 wa-case-event-handler: java: From f583ebf25ffb07639f1b0abb10796762dc58f0db Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 23 Jul 2026 10:15:39 +0100 Subject: [PATCH 24/37] HDPI-6585: add caseworker access profile --- .../gov/hmcts/reform/pcs/ccd/accesscontrol/AccessProfile.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/AccessProfile.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/AccessProfile.java index f2fb204fb9..287f91d7d7 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/AccessProfile.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/AccessProfile.java @@ -33,7 +33,8 @@ public enum AccessProfile implements HasRole { WLU_TEAM_LEADER("wlu-team-leader", Permission.CRU), WLU_ADMIN("wlu-admin", Permission.CRU), GS_PROFILE("GS_profile", Set.of(R)), - SYSTEM_USER("pcs-system-update", CRU); + SYSTEM_USER("pcs-system-update", CRU), + WA_SYSTEM_USER("caseworker-wa-task-configuration", CRU); @JsonValue From e0e2ff04e7770afbd0369cd32b4025e2de36c06c Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 23 Jul 2026 11:13:27 +0100 Subject: [PATCH 25/37] HDPI-6585: add interval days and move service bus config to application.yaml --- .../pcs-api/values.wa.preview.template.yaml | 9 +---- .../hmcts/reform/pcs/ccd/domain/PCSCase.java | 36 +++++++++---------- src/main/resources/application.yaml | 8 +++++ ...-task-configuration-possession_service.dmn | 17 +++++++++ 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index f09dc72a9e..aeb1eb485d 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -8,6 +8,7 @@ java: key: connectionString environment: SPRING_JMS_SERVICEBUS_ENABLED: true + PCS_SERVICEBUS_SCHEDULER_ENABLED: true AZURE_SERVICE_BUS_TOPIC_NAME: ${SERVICE_NAME}-asb-ccd-case-events AZURE_SERVICE_BUS_SUBSCRIPTION_NAME: ${SERVICE_NAME}-asb-ccd-case-events CAMUNDA_BASE_URL: http://pcs-api-pr-${CHANGE_ID}-camunda/engine-rest @@ -27,14 +28,6 @@ servicebus: subscriptionNeeded: yes requiresSession: true -ccd: - servicebus: - destination: ${SERVICE_NAME}-asb-ccd-case-events - scheduler-enabled: true - batch-size: 50 - published-retention-days: 7 - schedule: "*/10 * * * *" - postgresql: enabled: true flexibleserver: pcs-preview 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 94a3818ada..500b08f3cc 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 @@ -111,7 +111,7 @@ public class PCSCase { @JsonUnwrapped private ClaimantInformation claimantInformation; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) private List> allClaimants; @CCD( @@ -131,7 +131,7 @@ public class PCSCase { @CCD( label = "Property address", - access = {DefendantAccess.class, WAAccess.class} + access = {DefendantAccess.class} ) @External private AddressUK propertyAddress; @@ -177,14 +177,14 @@ public class PCSCase { @CCD( searchable = false, - access = {DefendantAccess.class, WAAccess.class} + access = {DefendantAccess.class} ) @External private String userPcqId; @CCD( searchable = false, - access = {DefendantAccess.class, WAAccess.class} + access = {DefendantAccess.class} ) private YesOrNo userPcqIdSet; @@ -198,7 +198,7 @@ public class PCSCase { ) private Integer regionId; - @CCD(access = {InternalCaseFlagAccess.class, WAAccess.class}, + @CCD(access = {InternalCaseFlagAccess.class}, label = "Party") private List> parties; @@ -270,20 +270,20 @@ public class PCSCase { @CCD( label = "Have you served notice to the defendants?", - access = {CitizenAccess.class, WAAccess.class} + access = {CitizenAccess.class} ) private YesOrNo noticeServed; @JsonUnwrapped(prefix = "notice_") - @CCD(access = {ClaimantAccess.class, CitizenAccess.class, WAAccess.class}) + @CCD(access = {ClaimantAccess.class, CitizenAccess.class}) private NoticeServedDetails noticeServedDetails; private String caseTitleMarkdown; - @CCD(access = {DefendantAccess.class, WAAccess.class}) + @CCD(access = {DefendantAccess.class}) private DashboardData dashboardData; - @CCD(access = {CitizenAccess.class, WAAccess.class}) + @CCD(access = {CitizenAccess.class}) private LegislativeCountry legislativeCountry; @CCD( @@ -357,7 +357,7 @@ public class PCSCase { /** * Combined list of all defendants in the case (i.e. primary defendant + additional defendants). */ - @CCD(access = {ClaimantAccess.class, CitizenAccess.class, InternalCaseFlagAccess.class, WAAccess.class}) + @CCD(access = {ClaimantAccess.class, CitizenAccess.class, InternalCaseFlagAccess.class}) private List> allDefendants; @JsonUnwrapped(prefix = "tenancy_") @@ -551,17 +551,17 @@ public class PCSCase { /** * Combined list of all underlessees/mortgagees in the case. */ - @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) private List> allUnderlesseeOrMortgagees; @CCD( searchable = false, label = "Ways to pay", - access = {PartyVisibleTabAccess.class, WAAccess.class} + access = {PartyVisibleTabAccess.class} ) private WaysToPay waysToPay; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) private StatementOfTruthDetails statementOfTruth; @CCD(searchable = false) @@ -580,7 +580,7 @@ public class PCSCase { private VerticalYesNo isExemptLandlord; @CCD( - access = {DefendantAccess.class, WAAccess.class}, + access = {DefendantAccess.class}, searchable = false ) private PossessionClaimResponse possessionClaimResponse; @@ -604,16 +604,16 @@ public class PCSCase { private String formattedDefendantNames; private String formattedPropertyAddress; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) private List> claimGroundSummaries; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) private LocalDateTime dateSubmitted; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}, label = "Date claim issued") + @CCD(access = {ClaimantAccess.class, DefendantAccess.class}, label = "Date claim issued") private LocalDate claimIssueDate; - @CCD(access = {ClaimantAccess.class, DefendantAccess.class, WAAccess.class}) + @CCD(access = {ClaimantAccess.class, DefendantAccess.class}) private LocalDateTime dateIssued; @CCD( diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 753a713277..2f23bd1970 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -132,6 +132,14 @@ azure: application-insights: instrumentation-key: ${pcs.AppInsightsInstrumentationKey:00000000-0000-0000-0000-000000000000} +ccd: + servicebus: + destination: ${AZURE_SERVICE_BUS_TOPIC_NAME:ccd-case-events-aat} + scheduler-enabled: ${PCS_SERVICEBUS_SCHEDULER_ENABLED:false} + batch-size: ${PCS_SERVICEBUS_BATCH_SIZE:50} + published-retention-days: ${PCS_SERVICEBUS_PUBLISHED_RETENTION_DAYS:7} + schedule: ${PCS_SERVICEBUS_SCHEDULE:*/10 * * * * *} + hmc: deployment-id: ${HMC_DEPLOYMENT_ID:} api-url: ${HMC_API_URL:http://localhost:8084} diff --git a/src/main/resources/dmn/wa-task-configuration-possession_service.dmn b/src/main/resources/dmn/wa-task-configuration-possession_service.dmn index ae41d59931..bf0cc468fb 100644 --- a/src/main/resources/dmn/wa-task-configuration-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-configuration-possession_service.dmn @@ -619,6 +619,23 @@ Short name true + + + + + + "NewClaimCreateNewHearing" + + + "dueDateIntervalDays" + + + 5 + + + + + From f5392be0fb5e2b1ea701cf45bca02961d26b048d Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 23 Jul 2026 14:49:21 +0100 Subject: [PATCH 26/37] HDPI-6585: add definition-snapshots to dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 7f5c516bbb..56a6ba480e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ USER hmcts COPY lib/applicationinsights.json /opt/app/ COPY build/libs/pcs-api.jar /opt/app/ +COPY build/cftlib/definition-snapshots /opt/app/build/cftlib/definition-snapshots EXPOSE 3206 CMD [ "pcs-api.jar" ] From d6dcd281bcf24a01931c4b9a43c9cb06b33b52aa Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 23 Jul 2026 16:03:52 +0100 Subject: [PATCH 27/37] HDPI-6585: add depend on dumpCCDDefinitions --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 11cc3423d0..19d5aaa185 100644 --- a/build.gradle +++ b/build.gradle @@ -102,6 +102,7 @@ ccd { configDir = file('build/definitions') decentralised = true runtimeIndexing = runtimeIndexingEnabled + caseEventServiceBus = true } tasks.withType(JavaCompile).configureEach { @@ -570,6 +571,8 @@ tasks.withType(CftlibExec).configureEach { environment 'CCD_S2S-AUTHORISED_SERVICES_CASE_USER_ROLES', 'aac_manage_case_assignment,pcs_api' } +assemble.dependsOn(dumpCCDDefinitions) + cftlibTest { environment 'ENABLE_TESTING_SUPPORT', 'true' environment 'DB_SCHEDULER_POLLING_INTERVAL_SECONDS', '2' From ea78b96b6abd1968fa4e456a013f931f367136ab Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Thu, 23 Jul 2026 16:49:03 +0100 Subject: [PATCH 28/37] HDPI-6585: reverse changes to Dockerfile --- Dockerfile | 1 - build.gradle | 3 --- 2 files changed, 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 56a6ba480e..7f5c516bbb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ USER hmcts COPY lib/applicationinsights.json /opt/app/ COPY build/libs/pcs-api.jar /opt/app/ -COPY build/cftlib/definition-snapshots /opt/app/build/cftlib/definition-snapshots EXPOSE 3206 CMD [ "pcs-api.jar" ] diff --git a/build.gradle b/build.gradle index 19d5aaa185..11cc3423d0 100644 --- a/build.gradle +++ b/build.gradle @@ -102,7 +102,6 @@ ccd { configDir = file('build/definitions') decentralised = true runtimeIndexing = runtimeIndexingEnabled - caseEventServiceBus = true } tasks.withType(JavaCompile).configureEach { @@ -571,8 +570,6 @@ tasks.withType(CftlibExec).configureEach { environment 'CCD_S2S-AUTHORISED_SERVICES_CASE_USER_ROLES', 'aac_manage_case_assignment,pcs_api' } -assemble.dependsOn(dumpCCDDefinitions) - cftlibTest { environment 'ENABLE_TESTING_SUPPORT', 'true' environment 'DB_SCHEDULER_POLLING_INTERVAL_SECONDS', '2' From a4093c37f54da97a1612abefd12ce749b13ced9a Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Fri, 24 Jul 2026 07:47:27 +0100 Subject: [PATCH 29/37] HDPI-6585: programmatically send cancel task message --- .../reform/pcs/camunda/CamundaService.java | 35 ++++++++++++++++--- .../pcs/camunda/SendMessageRequest.java | 4 +-- .../hmcts/reform/pcs/camunda/TaskType.java | 8 ++++- .../changecasestate/ChangeCaseState.java | 15 ++++---- ...a-task-cancellation-possession_service.dmn | 4 +-- 5 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java index e3ddb25ee5..e2917ba15b 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java @@ -25,14 +25,13 @@ public class CamundaService { private final FeatureToggleService featureToggleService; private static final String CREATE = "createTaskMessage"; + private static final String CANCEL = "cancelTasks"; private static final String UNCONFIGURED = "unconfigured"; private static final String EMPTY_WARNINGS_LIST = "[]"; + private static final String CANCELLATION_PROCESS = "CASE_EVENT_CANCELLATION"; private final Clock utcClock; - public void createTask( - Long caseId, - TaskType taskType - ) { + public void createTask(Long caseId, TaskType taskType) { if (!featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)) { log.info("Skipped creating task for {}", caseId); return; @@ -55,12 +54,39 @@ public void createTask( processVariables.put("delayUntil", dmnStringValue(delayUntil.format(ISO_LOCAL_DATE_TIME))); processVariables.put("hasWarnings", dmnBooleanValue(false)); processVariables.put("warningList", dmnStringValue(EMPTY_WARNINGS_LIST)); + processVariables.put("__processCategory__" + taskType.getProcessCategory(), dmnBooleanValue(true)); SendMessageRequest request = SendMessageRequest.builder() .messageName(CREATE) .processVariables(processVariables) .build(); + sendCamundaRequest(request, caseId); + } + + public void cancelTask(Long caseId, TaskType taskType) { + if (!featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)) { + log.info("Skipped cancelling task for {}", caseId); + return; + } + + Map> correlationKeys = new ConcurrentHashMap<>(); + correlationKeys.put("caseId", dmnStringValue(caseId.toString())); + correlationKeys.put("__processCategory__" + taskType.getProcessCategory(), dmnBooleanValue(true)); + + Map> processVariables = new ConcurrentHashMap<>(); + processVariables.put("cancellationProcess", dmnStringValue(CANCELLATION_PROCESS)); + + SendMessageRequest request = SendMessageRequest.builder() + .messageName(CANCEL) + .processVariables(processVariables) + .correlationKeys(correlationKeys) + .build(); + + sendCamundaRequest(request, caseId); + } + + private void sendCamundaRequest(SendMessageRequest request, long caseId) { String s2sToken = authTokenGenerator.generate(); try { @@ -69,7 +95,6 @@ public void createTask( } catch (Exception e) { log.error("Failed to send Camunda request for caseId {}", caseId, e); } - } private DmnValue dmnStringValue(String value) { diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/SendMessageRequest.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/SendMessageRequest.java index 10addfde04..47260fd6a8 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/SendMessageRequest.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/SendMessageRequest.java @@ -3,17 +3,17 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; import java.util.Map; @Builder @Data -@NoArgsConstructor @AllArgsConstructor public class SendMessageRequest { private String messageName; private Map> processVariables; + + private final Map> correlationKeys; } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java index d1556fc251..80bd57e1d0 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java @@ -6,9 +6,15 @@ @Getter @AllArgsConstructor public enum TaskType { - NEW_CLAIM_CREATE_NEW_HEARING("NewClaimCreateNewHearing", "New Claim – Create new hearing", 5); + NEW_CLAIM_CREATE_NEW_HEARING( + "NewClaimCreateNewHearing", + "New Claim – Create new hearing", + 5, + "NewClaimCreateNewHearing" + ); private final String id; private final String name; private final Integer workingDays; + private final String processCategory; } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseState.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseState.java index 2125ce8ea1..0457b3ca58 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseState.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseState.java @@ -8,6 +8,8 @@ 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.camunda.CamundaService; +import uk.gov.hmcts.reform.pcs.camunda.TaskType; import uk.gov.hmcts.reform.pcs.ccd.ShowConditions; import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole; import uk.gov.hmcts.reform.pcs.ccd.common.PageBuilder; @@ -15,8 +17,6 @@ import uk.gov.hmcts.reform.pcs.ccd.domain.State; import uk.gov.hmcts.reform.pcs.ccd.page.changecasestate.ChangeCaseStatePage; import uk.gov.hmcts.reform.pcs.ccd.util.AddressFormatter; -import uk.gov.hmcts.reform.pcs.service.FeatureFlag; -import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.CaseworkerRoles.CASEWORKER_ROLES; import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.JudicialHistoryRoles.JUDICIAL_HISTORY_ROLES; @@ -30,7 +30,7 @@ public class ChangeCaseState implements CCDConfig { private final AddressFormatter addressFormatter; - private final FeatureToggleService featureToggleService; + private final CamundaService camundaService; @Override public void configureDecentralised(DecentralisedConfigBuilder configBuilder) { @@ -54,10 +54,6 @@ public void configureDecentralised(DecentralisedConfigBuilder submit(EventPayload eventPayload) { PCSCase pcsCase = eventPayload.caseData(); State targetState = pcsCase.getTargetState().toState(); + + if (targetState == State.CASE_STAYED || targetState == State.CASE_ISSUED) { + camundaService.cancelTask(eventPayload.caseReference(), TaskType.NEW_CLAIM_CREATE_NEW_HEARING); + } + return SubmitResponse.builder() .state(targetState) .confirmationBody(buildConfirmationMarkdown(pcsCase, eventPayload.caseReference())) diff --git a/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn b/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn index 8fa2acaf8a..fba293620f 100644 --- a/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn @@ -42,7 +42,7 @@ - + "NewHearing" @@ -65,7 +65,7 @@ - + "NewHearing" From 7f7fc6ac94ec3387ad1280a4697ed2a155af9370 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Fri, 24 Jul 2026 08:04:32 +0100 Subject: [PATCH 30/37] HDPI-6585: fix unit test --- .../changecasestate/ChangeCaseStateTest.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseStateTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseStateTest.java index 425fc6995a..c276f4a80b 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseStateTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/changecasestate/ChangeCaseStateTest.java @@ -9,16 +9,18 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.hmcts.ccd.sdk.api.callback.SubmitResponse; +import uk.gov.hmcts.reform.pcs.camunda.CamundaService; +import uk.gov.hmcts.reform.pcs.camunda.TaskType; import uk.gov.hmcts.reform.pcs.ccd.domain.CaseStateOption; 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.event.BaseEventTest; import uk.gov.hmcts.reform.pcs.ccd.util.AddressFormatter; -import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static uk.gov.hmcts.reform.pcs.ccd.util.AddressFormatter.COMMA_DELIMITER; @@ -31,7 +33,7 @@ class ChangeCaseStateTest extends BaseEventTest { private AddressFormatter addressFormatter; @Mock - private FeatureToggleService featureToggleService; + private CamundaService camundaService; @InjectMocks private ChangeCaseState changeCaseState; @@ -45,14 +47,19 @@ void setUp() { @ParameterizedTest @EnumSource(CaseStateOption.class) - void shouldTransitionToSelectedTargetState(CaseStateOption targetState) { + void shouldTransitionToSelectedTargetState(CaseStateOption targetStateOption) { PCSCase pcsCase = PCSCase.builder() - .targetState(targetState) + .targetState(targetStateOption) .build(); SubmitResponse response = callSubmitHandler(pcsCase); - assertThat(response.getState()).isEqualTo(targetState.toState()); + State targetState = targetStateOption.toState(); + assertThat(response.getState()).isEqualTo(targetState); + + if (targetState == State.CASE_ISSUED || targetState == State.CASE_STAYED) { + verify(camundaService).cancelTask(1234L, TaskType.NEW_CLAIM_CREATE_NEW_HEARING); + } } @Test From 89d4b27b0b0ffcbd48fb006d7facf89fc9db5336 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Fri, 24 Jul 2026 09:11:48 +0100 Subject: [PATCH 31/37] HDPI-6585: use task id for process categories and add unit test --- .../reform/pcs/camunda/CamundaService.java | 4 +- .../hmcts/reform/pcs/camunda/TaskType.java | 8 +- ...a-task-cancellation-possession_service.dmn | 4 +- .../pcs/camunda/CamundaServiceTest.java | 88 ++++++++++++++++++- 4 files changed, 92 insertions(+), 12 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java index e2917ba15b..1b398d74b0 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java @@ -54,7 +54,7 @@ public void createTask(Long caseId, TaskType taskType) { processVariables.put("delayUntil", dmnStringValue(delayUntil.format(ISO_LOCAL_DATE_TIME))); processVariables.put("hasWarnings", dmnBooleanValue(false)); processVariables.put("warningList", dmnStringValue(EMPTY_WARNINGS_LIST)); - processVariables.put("__processCategory__" + taskType.getProcessCategory(), dmnBooleanValue(true)); + processVariables.put("__processCategory__" + taskType.getId(), dmnBooleanValue(true)); SendMessageRequest request = SendMessageRequest.builder() .messageName(CREATE) @@ -72,7 +72,7 @@ public void cancelTask(Long caseId, TaskType taskType) { Map> correlationKeys = new ConcurrentHashMap<>(); correlationKeys.put("caseId", dmnStringValue(caseId.toString())); - correlationKeys.put("__processCategory__" + taskType.getProcessCategory(), dmnBooleanValue(true)); + correlationKeys.put("__processCategory__" + taskType.getId(), dmnBooleanValue(true)); Map> processVariables = new ConcurrentHashMap<>(); processVariables.put("cancellationProcess", dmnStringValue(CANCELLATION_PROCESS)); diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java index 80bd57e1d0..d1556fc251 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/TaskType.java @@ -6,15 +6,9 @@ @Getter @AllArgsConstructor public enum TaskType { - NEW_CLAIM_CREATE_NEW_HEARING( - "NewClaimCreateNewHearing", - "New Claim – Create new hearing", - 5, - "NewClaimCreateNewHearing" - ); + NEW_CLAIM_CREATE_NEW_HEARING("NewClaimCreateNewHearing", "New Claim – Create new hearing", 5); private final String id; private final String name; private final Integer workingDays; - private final String processCategory; } diff --git a/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn b/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn index fba293620f..15db9a7238 100644 --- a/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-cancellation-possession_service.dmn @@ -42,7 +42,7 @@ - "NewHearing" + "NewClaimCreateNewHearing" @@ -65,7 +65,7 @@ - "NewHearing" + "NewClaimCreateNewHearing" diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java index 8bf1ef2990..80bccca65b 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java @@ -1,11 +1,18 @@ package uk.gov.hmcts.reform.pcs.camunda; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.pcs.service.FeatureFlag; import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; @@ -14,6 +21,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; @@ -39,6 +47,22 @@ public class CamundaServiceTest { @Mock private FeatureToggleService featureToggleService; + private Logger componentLogger; + private ListAppender logAppender; + + @BeforeEach + void setUp() { + componentLogger = (Logger) LoggerFactory.getLogger(CamundaService.class); + logAppender = new ListAppender<>(); + logAppender.start(); + componentLogger.addAppender(logAppender); + } + + @AfterEach + void tearDown() { + componentLogger.detachAppender(logAppender); + } + @InjectMocks private CamundaService camundaService; @@ -46,7 +70,7 @@ public class CamundaServiceTest { .atTime(12, 51, 19); @Test - void shouldSendCreateTaskTestToCamunda() { + void shouldSendCreateTaskToCamunda() { // Given final Long caseId = 1234L; final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; @@ -105,6 +129,11 @@ void shouldSkipCreatingTaskIfWaIsNotEnabled() { camundaService.createTask(caseId, taskType); // Then + List infoMessages = logAppender.list.stream() + .filter(e -> e.getLevel() == Level.INFO) + .filter(e -> e.getFormattedMessage().contains("Skipped creating task for " + caseId)) + .toList(); + assertThat(infoMessages).hasSize(1); verify(camundaApi, never()).sendMessage(any(), any()); } @@ -122,5 +151,62 @@ void shouldHandleFailedRequestToCamunda() { // When camundaService.createTask(caseId, taskType); + + // Then + List terminalErrors = logAppender.list.stream() + .filter(e -> e.getLevel() == Level.ERROR) + .filter(e -> e.getFormattedMessage().contains("Failed to send Camunda request for caseId " + caseId)) + .toList(); + assertThat(terminalErrors).hasSize(1); + } + + @Test + void shouldSendCancelTaskToCamunda() { + // Given + final Long caseId = 1234L; + final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + + when(authTokenGenerator.generate()).thenReturn("authToken"); + when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(true); + + // When + camundaService.cancelTask(caseId, taskType); + + // Then + ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(SendMessageRequest.class); + verify(camundaApi).sendMessage(eq("authToken"), requestArgumentCaptor.capture()); + SendMessageRequest sendMessageRequest = requestArgumentCaptor.getValue(); + + assertThat(sendMessageRequest).isNotNull(); + assertThat(sendMessageRequest.getMessageName()).isEqualTo("cancelTasks"); + + Map> processVariables = sendMessageRequest.getProcessVariables(); + assertThat(processVariables).isNotEmpty(); + assertThat(processVariables.get("cancellationProcess").getValue()).isEqualTo("CASE_EVENT_CANCELLATION"); + + Map> correlationKeys = sendMessageRequest.getCorrelationKeys(); + assertThat(correlationKeys).isNotEmpty(); + assertThat(correlationKeys.get("caseId").getValue()).isEqualTo(caseId.toString()); + assertThat(correlationKeys.get("__processCategory__NewClaimCreateNewHearing").getValue()).isEqualTo(true); + } + + @Test + void shouldSkipCancellingTaskIfWaIsNotEnabled() { + // Given + final Long caseId = 1234L; + final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; + + when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(false); + + // When + camundaService.cancelTask(caseId, taskType); + + // Then + List infoMessages = logAppender.list.stream() + .filter(e -> e.getLevel() == Level.INFO) + .filter(e -> e.getFormattedMessage().contains("Skipped cancelling task for " + caseId)) + .toList(); + assertThat(infoMessages).hasSize(1); + verify(camundaApi, never()).sendMessage(any(), any()); } } From ce6fde994125f2acc680c7cb70b1849405b4c55e Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Fri, 24 Jul 2026 13:21:31 +0100 Subject: [PATCH 32/37] HDPI-6585: update dmns and remove service bus config --- .../pcs-api/values.wa.preview.template.yaml | 26 ------------------- src/main/resources/application.yaml | 8 ------ ...-task-configuration-possession_service.dmn | 19 +++++++++++++- .../wa-task-initiation-possession_service.dmn | 4 +-- 4 files changed, 20 insertions(+), 37 deletions(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index aeb1eb485d..ee09132a87 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -1,33 +1,7 @@ java: - secrets: - AZURE_SERVICE_BUS_CONNECTION_STRING: - secretRef: pcs-sb-preview - key: connectionString - AZURE_SERVICE_BUS_PREVIEW_CONNECTION_STRING: - secretRef: pcs-sb-preview - key: connectionString environment: - SPRING_JMS_SERVICEBUS_ENABLED: true - PCS_SERVICEBUS_SCHEDULER_ENABLED: true - AZURE_SERVICE_BUS_TOPIC_NAME: ${SERVICE_NAME}-asb-ccd-case-events - AZURE_SERVICE_BUS_SUBSCRIPTION_NAME: ${SERVICE_NAME}-asb-ccd-case-events CAMUNDA_BASE_URL: http://pcs-api-pr-${CHANGE_ID}-camunda/engine-rest -servicebus: - enabled: true - releaseNameOverride: ${SERVICE_NAME}-asb - resourceGroup: pcs-aso-preview-rg - sbNamespace: pcs-servicebus-preview - setup: - topics: - - name: crd-topic - subscriptionNeeded: yes - - name: jrd-topic - subscriptionNeeded: yes - - name: ccd-case-events - subscriptionNeeded: yes - requiresSession: true - postgresql: enabled: true flexibleserver: pcs-preview diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 2f23bd1970..753a713277 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -132,14 +132,6 @@ azure: application-insights: instrumentation-key: ${pcs.AppInsightsInstrumentationKey:00000000-0000-0000-0000-000000000000} -ccd: - servicebus: - destination: ${AZURE_SERVICE_BUS_TOPIC_NAME:ccd-case-events-aat} - scheduler-enabled: ${PCS_SERVICEBUS_SCHEDULER_ENABLED:false} - batch-size: ${PCS_SERVICEBUS_BATCH_SIZE:50} - published-retention-days: ${PCS_SERVICEBUS_PUBLISHED_RETENTION_DAYS:7} - schedule: ${PCS_SERVICEBUS_SCHEDULE:*/10 * * * * *} - hmc: deployment-id: ${HMC_DEPLOYMENT_ID:} api-url: ${HMC_API_URL:http://localhost:8084} diff --git a/src/main/resources/dmn/wa-task-configuration-possession_service.dmn b/src/main/resources/dmn/wa-task-configuration-possession_service.dmn index bf0cc468fb..a7d9a8eaf2 100644 --- a/src/main/resources/dmn/wa-task-configuration-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-configuration-possession_service.dmn @@ -619,12 +619,29 @@ Short name true + + + + + + "ReviewAdjournGenApp", "ReviewSetAsideGenApp", "ReviewGenApp" + + + "dueDateIntervalDays" + + + 1 + + + + + - "NewClaimCreateNewHearing" + "NewClaimCreateNewHearing", "Review DefendantResponseAndCounterclaim", "ReviewAdditionalDocumentsClaim", "ReviewAdditionalDocumentsGenApp", "ReviewAdditionalDocumentsCounterclaim", "ReviewCaseFlagRequest", "ReviewCaseFlag", "ReviewFailedPayment", "ReviewDateDue", "TranslateDefendantSubmittedDocument", "TranslateClaimantSubmittedDocument", "TranslateCaseworkerUploadedDocument" "dueDateIntervalDays" diff --git a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn index 8952b3e5b0..6a6da71fcb 100644 --- a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn @@ -208,7 +208,7 @@ else - "Review DefendantResponseAndCounterclaim " + "Review DefendantResponseAndCounterclaim" "Review Defendant response and counterclaim" @@ -220,7 +220,7 @@ else 5 - "Review DefendantResponseAndCounterclaim " + "Review DefendantResponseAndCounterclaim" "routine_work" From 701a2d017a384d480881eab3640113c7031ba95d Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Fri, 24 Jul 2026 13:31:12 +0100 Subject: [PATCH 33/37] HDPI-6585: update dmn id --- .../resources/dmn/wa-task-initiation-possession_service.dmn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn index 6a6da71fcb..cd8a1d50cf 100644 --- a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn @@ -1,6 +1,6 @@ - + @@ -937,7 +937,7 @@ else - + From 88dda10a96a8061a16f68b1244bb60da75b0d162 Mon Sep 17 00:00:00 2001 From: scottstewart-sl <202802468+scottstewart-sl@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:22:43 +0100 Subject: [PATCH 34/37] HDPI-6585: Schedule Camunda task creation via db-scheduler (#2258) --- .../camunda/CamundaRequestTaskComponent.java | 63 ++++++++ .../pcs/camunda/CamundaRequestTaskData.java | 23 +++ .../reform/pcs/camunda/CamundaService.java | 45 +++++- src/main/resources/application.yaml | 3 + .../CamundaRequestTaskComponentTest.java | 85 +++++++++++ .../pcs/camunda/CamundaServiceTest.java | 142 +++++++++++++++--- 6 files changed, 335 insertions(+), 26 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskComponent.java create mode 100644 src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskData.java create mode 100644 src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskComponentTest.java diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskComponent.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskComponent.java new file mode 100644 index 0000000000..ad0eba8ae1 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskComponent.java @@ -0,0 +1,63 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import com.github.kagkarlsson.scheduler.task.CompletionHandler; +import com.github.kagkarlsson.scheduler.task.FailureHandler; +import com.github.kagkarlsson.scheduler.task.TaskDescriptor; +import com.github.kagkarlsson.scheduler.task.helper.CustomTask; +import com.github.kagkarlsson.scheduler.task.helper.Tasks; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +@Slf4j +@Component +public class CamundaRequestTaskComponent { + + private static final String CAMUNDA_REQUEST_TASK_NAME = "camunda-request-task"; + + public static final TaskDescriptor CAMUNDA_REQUEST_TASK_DESCRIPTOR = + TaskDescriptor.of(CAMUNDA_REQUEST_TASK_NAME, CamundaRequestTaskData.class); + + private final CamundaService camundaService; + private final int maxRetries; + private final Duration backoffDelay; + + public CamundaRequestTaskComponent(CamundaService camundaService, + @Value("${camunda.request.max-retries}") int maxRetries, + @Value("${camunda.request.backoff-delay-duration}") Duration backoffDelay) { + + this.camundaService = camundaService; + this.maxRetries = maxRetries; + this.backoffDelay = backoffDelay; + } + + @Bean + public CustomTask camundaRequestTask() { + return Tasks.custom(CAMUNDA_REQUEST_TASK_DESCRIPTOR) + .onFailure( + FailureHandler.maxRetries(maxRetries) + .withBackoff(backoffDelay) + .thenRemove(complete -> + log.warn( + "Execution has failed {} times. Giving up.", + complete.getExecution().consecutiveFailures + ) + )) + .execute((taskInstance, executionContext) -> { + CamundaRequestTaskData taskData = taskInstance.getData(); + log.debug("Executing Camunda request task for {}", taskData); + try { + camundaService.handleRequest(taskData); + return new CompletionHandler.OnCompleteRemove<>(); + } catch (Exception e) { + log.error("Failed to create Camunda request for: {}. Attempt {}/{}", + taskData, executionContext.getExecution().consecutiveFailures + 1, maxRetries, e); + throw e; + } + }); + } + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskData.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskData.java new file mode 100644 index 0000000000..bf3e41f0dc --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskData.java @@ -0,0 +1,23 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@AllArgsConstructor +public class CamundaRequestTaskData { + + enum Action { + CREATE, + CANCEL + } + + private final Action action; + + private final long caseReference; + + private final TaskType taskType; + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java index 1b398d74b0..c2ae9dd2bd 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/camunda/CamundaService.java @@ -1,19 +1,24 @@ package uk.gov.hmcts.reform.pcs.camunda; +import com.github.kagkarlsson.scheduler.SchedulerClient; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; +import uk.gov.hmcts.reform.pcs.camunda.CamundaRequestTaskData.Action; import uk.gov.hmcts.reform.pcs.ccd.CaseType; import uk.gov.hmcts.reform.pcs.service.FeatureFlag; import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; import java.time.Clock; +import java.time.Instant; import java.time.LocalDateTime; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME; +import static uk.gov.hmcts.reform.pcs.camunda.CamundaRequestTaskComponent.CAMUNDA_REQUEST_TASK_DESCRIPTOR; @Slf4j @AllArgsConstructor @@ -22,6 +27,7 @@ public class CamundaService { private final CamundaApi camundaApi; private final AuthTokenGenerator authTokenGenerator; + private final SchedulerClient schedulerClient; private final FeatureToggleService featureToggleService; private static final String CREATE = "createTaskMessage"; @@ -31,7 +37,41 @@ public class CamundaService { private static final String CANCELLATION_PROCESS = "CASE_EVENT_CANCELLATION"; private final Clock utcClock; - public void createTask(Long caseId, TaskType taskType) { + public void createTask(long caseId, TaskType taskType) { + scheduleCamundaRequest(Action.CREATE, caseId, taskType); + } + + public void cancelTask(long caseId, TaskType taskType) { + scheduleCamundaRequest(Action.CANCEL, caseId, taskType); + } + + void handleRequest(CamundaRequestTaskData taskData) { + switch (taskData.getAction()) { + case CREATE -> requestTaskCreation(taskData.getCaseReference(), taskData.getTaskType()); + case CANCEL -> requestTaskCancellation(taskData.getCaseReference(), taskData.getTaskType()); + } + } + + private void scheduleCamundaRequest(Action action, long caseId, TaskType taskType) { + if (!featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)) { + log.info("Skipped scheduling Camunda request for {}", caseId); + return; + } + + CamundaRequestTaskData taskData = CamundaRequestTaskData.builder() + .action(action) + .caseReference(caseId) + .taskType(taskType) + .build(); + + schedulerClient.scheduleIfNotExists( + CAMUNDA_REQUEST_TASK_DESCRIPTOR + .instance(UUID.randomUUID().toString()) + .data(taskData) + .scheduledTo(Instant.now(utcClock))); + } + + private void requestTaskCreation(Long caseId, TaskType taskType) { if (!featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)) { log.info("Skipped creating task for {}", caseId); return; @@ -64,7 +104,7 @@ public void createTask(Long caseId, TaskType taskType) { sendCamundaRequest(request, caseId); } - public void cancelTask(Long caseId, TaskType taskType) { + private void requestTaskCancellation(Long caseId, TaskType taskType) { if (!featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)) { log.info("Skipped cancelling task for {}", caseId); return; @@ -108,4 +148,5 @@ private DmnValue dmnIntegerValue(Integer value) { private DmnValue dmnBooleanValue(Boolean value) { return DmnValue.builder().value(value).type("Boolean").build(); } + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 753a713277..da596dc496 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -336,6 +336,9 @@ caseApi: camunda: url: ${CAMUNDA_BASE_URL:http://camunda-local-bpm/engine-rest} + request: + max-retries: ${CAMUNDA_REQUEST_TASK_MAX_RETRIES:3} + backoff-delay-duration: ${CAMUNDA_REQUEST_TASK_BACKOFF_DURATION:20m} shutter: service: ${SHUTTER_SERVICE:false} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskComponentTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskComponentTest.java new file mode 100644 index 0000000000..6b12bcc931 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaRequestTaskComponentTest.java @@ -0,0 +1,85 @@ +package uk.gov.hmcts.reform.pcs.camunda; + +import com.github.kagkarlsson.scheduler.task.CompletionHandler; +import com.github.kagkarlsson.scheduler.task.Execution; +import com.github.kagkarlsson.scheduler.task.ExecutionContext; +import com.github.kagkarlsson.scheduler.task.TaskInstance; +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 java.time.Duration; + +import static java.time.temporal.ChronoUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CamundaRequestTaskComponentTest { + + @Mock + private CamundaService camundaService; + @Mock + private TaskInstance taskInstance; + @Mock + private ExecutionContext executionContext; + + private CamundaRequestTaskComponent underTest; + + @BeforeEach + void setUp() { + underTest = new CamundaRequestTaskComponent(camundaService, 3, Duration.of(2, MINUTES)); + } + + @Test + void shouldCallCamundaService() { + // Given + CamundaRequestTaskData taskData = mock(CamundaRequestTaskData.class); + when(taskInstance.getData()).thenReturn(taskData); + + // When + underTest.camundaRequestTask().execute(taskInstance, executionContext); + + // Then + verify(camundaService).handleRequest(taskData); + } + + @Test + void shouldRemoveTaskAfterSuccessfulExecution() { + // When + CompletionHandler completionHandler + = underTest.camundaRequestTask().execute(taskInstance, executionContext); + + // Then + assertThat(completionHandler).isInstanceOf(CompletionHandler.OnCompleteRemove.class); + } + + @Test + void shouldPropagateExceptionForFailedExecution() { + // Given + CamundaRequestTaskData taskData = mock(CamundaRequestTaskData.class); + when(taskInstance.getData()).thenReturn(taskData); + + Execution execution = mock(Execution.class); + when(executionContext.getExecution()).thenReturn(execution); + + RuntimeException expectedException = mock(RuntimeException.class); + doThrow(expectedException).when(camundaService).handleRequest(taskData); + + // When + Throwable throwable = catchThrowable(() -> underTest.camundaRequestTask().execute( + taskInstance, + executionContext + )); + + // Then + assertThat(throwable).isEqualTo(expectedException); + } + +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java index 80bccca65b..177db4b339 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/camunda/CamundaServiceTest.java @@ -4,16 +4,20 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; +import com.github.kagkarlsson.scheduler.SchedulerClient; +import com.github.kagkarlsson.scheduler.task.SchedulableInstance; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.LoggerFactory; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; +import uk.gov.hmcts.reform.pcs.camunda.CamundaRequestTaskData.Action; import uk.gov.hmcts.reform.pcs.service.FeatureFlag; import uk.gov.hmcts.reform.pcs.service.FeatureToggleService; @@ -27,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -35,6 +40,8 @@ @ExtendWith(MockitoExtension.class) public class CamundaServiceTest { + private static final long CASE_REFERENCE = 1234L; + @Mock private CamundaApi camundaApi; @@ -42,11 +49,18 @@ public class CamundaServiceTest { private AuthTokenGenerator authTokenGenerator; @Mock + private SchedulerClient schedulerClient; + + @Mock(strictness = LENIENT) private Clock utcClock; @Mock private FeatureToggleService featureToggleService; + @Captor + private ArgumentCaptor> schedulableInstanceCaptor; + + private Logger componentLogger; private ListAppender logAppender; @@ -56,6 +70,9 @@ void setUp() { logAppender = new ListAppender<>(); logAppender.start(); componentLogger.addAppender(logAppender); + + when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); + when(utcClock.getZone()).thenReturn(ZoneOffset.UTC); } @AfterEach @@ -69,19 +86,72 @@ void tearDown() { private static final LocalDateTime TEST_UTC_DATE_TIME = LocalDate.of(2025, 8, 27) .atTime(12, 51, 19); + @Test + void shouldScheduleCamundaCreateRequestTask() { + // When + stubWaFeatureFlag(true); + camundaService.createTask(CASE_REFERENCE, TaskType.NEW_CLAIM_CREATE_NEW_HEARING); + + // Then + verify(schedulerClient).scheduleIfNotExists(schedulableInstanceCaptor.capture()); + + SchedulableInstance schedulableInstance = schedulableInstanceCaptor.getValue(); + + CamundaRequestTaskData taskData = schedulableInstance.getTaskInstance().getData(); + assertThat(taskData.getAction()).isEqualTo(Action.CREATE); + assertThat(taskData.getCaseReference()).isEqualTo(CASE_REFERENCE); + assertThat(taskData.getTaskType()).isEqualTo(TaskType.NEW_CLAIM_CREATE_NEW_HEARING); + } + + @Test + void shouldScheduleCamundaCancelRequestTask() { + // When + stubWaFeatureFlag(true); + camundaService.cancelTask(CASE_REFERENCE, TaskType.NEW_CLAIM_CREATE_NEW_HEARING); + + // Then + verify(schedulerClient).scheduleIfNotExists(schedulableInstanceCaptor.capture()); + + SchedulableInstance schedulableInstance = schedulableInstanceCaptor.getValue(); + + CamundaRequestTaskData taskData = schedulableInstance.getTaskInstance().getData(); + assertThat(taskData.getAction()).isEqualTo(Action.CANCEL); + assertThat(taskData.getCaseReference()).isEqualTo(CASE_REFERENCE); + assertThat(taskData.getTaskType()).isEqualTo(TaskType.NEW_CLAIM_CREATE_NEW_HEARING); + } + + @Test + void shouldNotScheduleCamundaCreateRequestTaskWhenWaNotEnabled() { + // When + stubWaFeatureFlag(false); + camundaService.createTask(CASE_REFERENCE, TaskType.NEW_CLAIM_CREATE_NEW_HEARING); + + // Then + verify(schedulerClient, never()).scheduleIfNotExists(any()); + } + + @Test + void shouldNotScheduleCamundaCancelRequestTaskWhenWaNotEnabled() { + // When + stubWaFeatureFlag(false); + camundaService.cancelTask(CASE_REFERENCE, TaskType.NEW_CLAIM_CREATE_NEW_HEARING); + + // Then + verify(schedulerClient, never()).scheduleIfNotExists(any()); + } + @Test void shouldSendCreateTaskToCamunda() { // Given - final Long caseId = 1234L; final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; when(authTokenGenerator.generate()).thenReturn("authToken"); - when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); - when(utcClock.getZone()).thenReturn(ZoneOffset.UTC); - when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(true); + stubWaFeatureFlag(true); + + CamundaRequestTaskData taskData = buildTaskDataForCreate(taskType); // When - camundaService.createTask(caseId, taskType); + camundaService.handleRequest(taskData); // Then ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(SendMessageRequest.class); @@ -107,7 +177,7 @@ void shouldSendCreateTaskToCamunda() { assertThat(processVariables.get("name").getType()).isEqualTo("String"); assertThat(processVariables.get("taskId").getValue()).isEqualTo("NewClaimCreateNewHearing"); assertThat(processVariables.get("taskId").getType()).isEqualTo("String"); - assertThat(processVariables.get("caseId").getValue()).isEqualTo(caseId.toString()); + assertThat(processVariables.get("caseId").getValue()).isEqualTo(Long.toString(CASE_REFERENCE)); assertThat(processVariables.get("caseId").getType()).isEqualTo("String"); assertThat(processVariables.get("delayUntil").getValue()).isEqualTo("2025-08-27T12:51:19"); assertThat(processVariables.get("delayUntil").getType()).isEqualTo("String"); @@ -120,18 +190,19 @@ void shouldSendCreateTaskToCamunda() { @Test void shouldSkipCreatingTaskIfWaIsNotEnabled() { // Given - final Long caseId = 1234L; final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; - when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(false); + stubWaFeatureFlag(false); + + CamundaRequestTaskData taskData = buildTaskDataForCreate(taskType); // When - camundaService.createTask(caseId, taskType); + camundaService.handleRequest(taskData); // Then List infoMessages = logAppender.list.stream() .filter(e -> e.getLevel() == Level.INFO) - .filter(e -> e.getFormattedMessage().contains("Skipped creating task for " + caseId)) + .filter(e -> e.getFormattedMessage().contains("Skipped creating task for " + CASE_REFERENCE)) .toList(); assertThat(infoMessages).hasSize(1); verify(camundaApi, never()).sendMessage(any(), any()); @@ -140,22 +211,22 @@ void shouldSkipCreatingTaskIfWaIsNotEnabled() { @Test void shouldHandleFailedRequestToCamunda() { // Given - final Long caseId = 1234L; final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; when(authTokenGenerator.generate()).thenReturn("authToken"); - when(utcClock.instant()).thenReturn(TEST_UTC_DATE_TIME.toInstant(ZoneOffset.UTC)); - when(utcClock.getZone()).thenReturn(ZoneOffset.UTC); - when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(true); + stubWaFeatureFlag(true); doThrow(new RuntimeException()).when(camundaApi).sendMessage(any(), any()); + CamundaRequestTaskData taskData = buildTaskDataForCreate(taskType); + // When - camundaService.createTask(caseId, taskType); + camundaService.handleRequest(taskData); // Then List terminalErrors = logAppender.list.stream() .filter(e -> e.getLevel() == Level.ERROR) - .filter(e -> e.getFormattedMessage().contains("Failed to send Camunda request for caseId " + caseId)) + .filter(e -> e.getFormattedMessage() + .contains("Failed to send Camunda request for caseId " + CASE_REFERENCE)) .toList(); assertThat(terminalErrors).hasSize(1); } @@ -163,14 +234,15 @@ void shouldHandleFailedRequestToCamunda() { @Test void shouldSendCancelTaskToCamunda() { // Given - final Long caseId = 1234L; final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; when(authTokenGenerator.generate()).thenReturn("authToken"); - when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(true); + stubWaFeatureFlag(true); + + CamundaRequestTaskData taskData = buildTaskDataForCancel(taskType); // When - camundaService.cancelTask(caseId, taskType); + camundaService.handleRequest(taskData); // Then ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(SendMessageRequest.class); @@ -186,27 +258,49 @@ void shouldSendCancelTaskToCamunda() { Map> correlationKeys = sendMessageRequest.getCorrelationKeys(); assertThat(correlationKeys).isNotEmpty(); - assertThat(correlationKeys.get("caseId").getValue()).isEqualTo(caseId.toString()); + assertThat(correlationKeys.get("caseId").getValue()).isEqualTo(Long.toString(CASE_REFERENCE)); assertThat(correlationKeys.get("__processCategory__NewClaimCreateNewHearing").getValue()).isEqualTo(true); } @Test void shouldSkipCancellingTaskIfWaIsNotEnabled() { // Given - final Long caseId = 1234L; final TaskType taskType = TaskType.NEW_CLAIM_CREATE_NEW_HEARING; - when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(false); + stubWaFeatureFlag(false); + + CamundaRequestTaskData taskData = buildTaskDataForCancel(taskType); // When - camundaService.cancelTask(caseId, taskType); + camundaService.handleRequest(taskData); // Then List infoMessages = logAppender.list.stream() .filter(e -> e.getLevel() == Level.INFO) - .filter(e -> e.getFormattedMessage().contains("Skipped cancelling task for " + caseId)) + .filter(e -> e.getFormattedMessage().contains("Skipped cancelling task for " + CASE_REFERENCE)) .toList(); assertThat(infoMessages).hasSize(1); verify(camundaApi, never()).sendMessage(any(), any()); } + + private static CamundaRequestTaskData buildTaskDataForCreate(TaskType taskType) { + return CamundaRequestTaskData.builder() + .action(Action.CREATE) + .caseReference(CASE_REFERENCE) + .taskType(taskType) + .build(); + } + + private static CamundaRequestTaskData buildTaskDataForCancel(TaskType taskType) { + return CamundaRequestTaskData.builder() + .action(Action.CANCEL) + .caseReference(CASE_REFERENCE) + .taskType(taskType) + .build(); + } + + private void stubWaFeatureFlag(boolean enabled) { + when(featureToggleService.isEnabled(FeatureFlag.CASEWORKER_WA)).thenReturn(enabled); + } + } From 7c847defa5246fe1e55f4d93b3cdd00729df6cb9 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Fri, 24 Jul 2026 15:03:47 +0100 Subject: [PATCH 35/37] HDPI-6585: remove feature flag show condition and add CAMUNDA_BASE_URL to values.yaml --- charts/pcs-api/values.yaml | 1 + .../java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java | 1 - .../java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/charts/pcs-api/values.yaml b/charts/pcs-api/values.yaml index 1da5034c53..ac53b2d4c3 100644 --- a/charts/pcs-api/values.yaml +++ b/charts/pcs-api/values.yaml @@ -54,6 +54,7 @@ java: CCD_DATA_STORE_URL: "http://ccd-data-store-api-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal" CASE_DOCUMENT_AM_URL: "http://ccd-case-document-am-api-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal" RD_PROFESSIONAL_API_URL: "http://rd-professional-api-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal" + CAMUNDA_BASE_URL: "http://camunda-api-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal" servicebus: enabled: false ccd: diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java index 64eeb1d8c3..3660ad46b5 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/FeatureFlags.java @@ -9,6 +9,5 @@ public class FeatureFlags { private VerticalYesNo release1dot2Enabled; private VerticalYesNo caseWorkerEventsEnabled; - private VerticalYesNo caseWorkerWaEnabled; } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java index 5366224004..ef77c79c21 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/FeatureFlagView.java @@ -18,7 +18,6 @@ public void setCaseFields(PCSCase pcsCase) { pcsCase.setFeatureFlags(FeatureFlags.builder() .release1dot2Enabled(getFlag(FeatureFlag.RELEASE_1_DOT_2)) .caseWorkerEventsEnabled(getFlag(FeatureFlag.CASEWORKER_EVENTS)) - .caseWorkerWaEnabled(getFlag(FeatureFlag.CASEWORKER_WA)) .build()); } From 75ef88298a94387141da2c812e98a5357454ae00 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Fri, 24 Jul 2026 16:21:15 +0100 Subject: [PATCH 36/37] HDPI-6585: disableActiveClusterCheck and set suspend to false --- charts/pcs-api/values.wa.preview.template.yaml | 4 ++++ .../resources/dmn/wa-task-initiation-possession_service.dmn | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/charts/pcs-api/values.wa.preview.template.yaml b/charts/pcs-api/values.wa.preview.template.yaml index ee09132a87..7ea6a267ef 100644 --- a/charts/pcs-api/values.wa.preview.template.yaml +++ b/charts/pcs-api/values.wa.preview.template.yaml @@ -114,6 +114,8 @@ wa: wa-initiation-batch: job: schedule: "*/1 * * * *" + disableActiveClusterCheck: true + suspend: false wa-reconfiguration-batch: job: @@ -122,6 +124,8 @@ wa: wa-termination-batch: job: schedule: "*/1 * * * *" + disableActiveClusterCheck: true + suspend: false #disable wa db as we are using the postgres flexible server wa: diff --git a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn index cd8a1d50cf..6a6da71fcb 100644 --- a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn @@ -1,6 +1,6 @@ - + @@ -937,7 +937,7 @@ else - + From bed6271c980b8c98b3cc0818d89e4397ecc854f5 Mon Sep 17 00:00:00 2001 From: gmmagruder Date: Fri, 24 Jul 2026 16:21:53 +0100 Subject: [PATCH 37/37] HDPI-6585: reverse change to dmn --- .../resources/dmn/wa-task-initiation-possession_service.dmn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn index 6a6da71fcb..cd8a1d50cf 100644 --- a/src/main/resources/dmn/wa-task-initiation-possession_service.dmn +++ b/src/main/resources/dmn/wa-task-initiation-possession_service.dmn @@ -1,6 +1,6 @@ - + @@ -937,7 +937,7 @@ else - +