From 35e12192a5caa78e2127abdfa85bc094afaee8f5 Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Tue, 12 May 2026 17:06:43 +0100 Subject: [PATCH 1/9] Add new table date_case_closed --- .../data/caseclosed/DateCaseClosedEntity.java | 39 +++++++++++++++++++ .../caseclosed/DateCaseClosedRepository.java | 15 +++++++ ...43__CME-943_add_date_case_closed_table.sql | 27 +++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedEntity.java create mode 100644 src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java create mode 100644 src/main/resources/db/migration/V20250508_943__CME-943_add_date_case_closed_table.sql diff --git a/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedEntity.java b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedEntity.java new file mode 100644 index 0000000000..c67ef58f79 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedEntity.java @@ -0,0 +1,39 @@ +package uk.gov.hmcts.ccd.data.caseclosed; + +import lombok.Data; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import java.time.LocalDateTime; + +@Entity +@Data +@Table(name = "date_case_closed") +public class DateCaseClosedEntity { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "date_case_closed_id_seq_generator") + @SequenceGenerator( + name = "date_case_closed_id_seq_generator", + sequenceName = "date_case_closed_id_seq", + allocationSize = 1 + ) + private Long id; + + @Column(name = "ccd_case_number", nullable = false) + private Long ccdCaseNumber; + + @Column(name = "state") + private String state; + + @Column(name = "state_category") + private String stateCategory; + + @Column(name = "state_changed_date") + private LocalDateTime stateChangedDate; +} diff --git a/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java new file mode 100644 index 0000000000..b16dbc8cb5 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java @@ -0,0 +1,15 @@ +package uk.gov.hmcts.ccd.data.caseclosed; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Transactional +@Repository +public interface DateCaseClosedRepository extends CrudRepository { + + List findClosedCasesByStateChangedDate(LocalDateTime stateChangedDate); +} diff --git a/src/main/resources/db/migration/V20250508_943__CME-943_add_date_case_closed_table.sql b/src/main/resources/db/migration/V20250508_943__CME-943_add_date_case_closed_table.sql new file mode 100644 index 0000000000..5f0f209fdd --- /dev/null +++ b/src/main/resources/db/migration/V20250508_943__CME-943_add_date_case_closed_table.sql @@ -0,0 +1,27 @@ +CREATE TABLE public.date_case_closed ( + id bigint NOT NULL, + ccd_case_number bigint NOT NULL, + state character varying(255), + state_category character varying(500), + state_changed_date timestamp, + PRIMARY KEY(id) +); + +CREATE SEQUENCE public.date_case_closed_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE public.date_case_closed_id_seq OWNED BY public.date_case_closed.id; + +ALTER TABLE ONLY public.date_case_closed + ALTER COLUMN id SET DEFAULT nextval('public.date_case_closed_id_seq'::regclass); + +ALTER TABLE public.date_case_closed + ADD CONSTRAINT fk_date_case_closed_ccd_case_number_case_data + FOREIGN KEY (ccd_case_number) REFERENCES public.case_data(reference); + +CREATE INDEX idx_date_case_closed_state_changed_date + ON public.date_case_closed (state_changed_date); From a24bbdd4cb80c28edabe7039fa19ab1f4bacd65b Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Thu, 14 May 2026 10:24:13 +0100 Subject: [PATCH 2/9] Update DateCaseClosedRepository.java --- .../hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java index b16dbc8cb5..55a5908176 100644 --- a/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java +++ b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java @@ -2,14 +2,12 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; -@Transactional @Repository public interface DateCaseClosedRepository extends CrudRepository { - List findClosedCasesByStateChangedDate(LocalDateTime stateChangedDate); + List findByStateChangedDateLessThanEqual(LocalDateTime stateChangedDate); } From f265abbb44f37ca37cd4069419bcff9e19f3f582 Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:01:43 +0100 Subject: [PATCH 3/9] CME-942: Create a new endpoint /getCloseCases/{date} (#2901) * Add /getCloseCases/{date} * Add tests * Add BEFTA tests * Update DefaultClosedCaseSearchOperation.java * Update dates * Change API URL to date only * Use Date instead of LocalDateTime * Cleanup test * Address comments * Update UICaseSearchController.java * Update S-942.1_CreateDateCaseClosed.td.json * CME-944 - Insert into DATE_CASE_CLOSED when new state category is "Closed for payment" (#2910) * Allow to get cases from current time * insert into DATE_CASE_CLOSED * Add BEFTA tests * Update TestingSupportController.java * Change to caps CLOSED FOR PAYMENT * Add pre-release branch and new test * Make state_category deserialisable * debug log * Update values.preview.template.yaml * Cleanup * Rename tests * CME-945 - Update date case closed records on case event state changes (#2915) * Add updateDateCaseClosed and delete Update CreateCaseEventServiceTest.java comments * Add BEFTA tests * Checkstyle * Update S-942.1_DeleteExistingDateCaseClosed.td.json * Broaden date case closed cleanup --- build.gradle | 2 +- .../values.preview.template.yaml | 2 +- .../F-942 - Get Closed Cases/F-942.feature | 56 ++++++++ ...F-942_DeleteExistingDateCaseClosed.td.json | 32 +++++ .../F-942_Test_Data_Base.td.json | 28 ++++ .../F-942 - Get Closed Cases/S-942.1.td.json | 18 +++ .../S-942.1_CreateDateCaseClosed.td.json | 42 ++++++ .../F-942 - Get Closed Cases/S-942.2.td.json | 31 +++++ .../F-942 - Get Closed Cases/S-942.3.td.json | 23 ++++ .../F-942 - Get Closed Cases/S-942.4.td.json | 31 +++++ .../F-944.feature | 33 +++++ .../F-944_Create_Case_Base.td.json | 66 +++++++++ ..._Get_Closed_For_Payment_Cases_Base.td.json | 28 ++++ .../S-944.1.td.json | 18 +++ .../S-944.1_Create_Case.td.json | 20 +++ .../S-944.1_GetToken_CaseCreate.td.json | 62 +++++++++ .../S-944.2.td.json | 25 ++++ .../S-944.2_Create_Case_NotClosed.td.json | 20 +++ ...44.2_GetToken_CaseCreate_NotClosed.td.json | 17 +++ .../F-945.feature | 57 ++++++++ .../F-945_GetToken_UpdateCase_Base.td.json | 47 +++++++ .../F-945_Update_Case_Base.td.json | 70 ++++++++++ .../S-945.1.td.json | 18 +++ .../S-945.1_GetToken_UpdateCase.td.json | 18 +++ ...945.1_Update_Case_ClosedForPayment.td.json | 27 ++++ .../S-945.2.td.json | 18 +++ ...ken_UpdateCase_NotClosedForPayment.td.json | 18 +++ ....2_Update_Case_NotClosedForPayment.td.json | 27 ++++ .../S-945.3.td.json | 18 +++ ...tToken_UpdateCase_ClosedForPayment.td.json | 18 +++ ...ken_UpdateCase_NotClosedForPayment.td.json | 21 +++ ...945.3_Update_Case_ClosedForPayment.td.json | 26 ++++ ....3_Update_Case_NotClosedForPayment.td.json | 27 ++++ .../AuthorisationCaseEvent.json | 42 ++++++ .../AuthorisationCaseField.json | 10 ++ .../AuthorisationCaseState.json | 18 +++ .../AuthorisationCaseType.json | 9 ++ .../FT_DateCaseClosed/CaseEvent.json | 127 +++++++++++++++++ .../FT_DateCaseClosed/CaseEventToFields.json | 1 + .../FT_DateCaseClosed/CaseField.json | 21 +++ .../FT_DateCaseClosed/CaseTypeTab.json | 16 +++ .../FT_DateCaseClosed/SearchInputFields.json | 10 ++ .../FT_DateCaseClosed/SearchResultFields.json | 11 ++ .../BEFTA_MASTER/FT_DateCaseClosed/State.json | 24 ++++ .../WorkBasketInputFields.json | 10 ++ .../WorkBasketResultFields.json | 11 ++ .../FT_Decentralisation/State.json | 3 +- .../valid/BEFTA_MASTER/common/CaseType.json | 13 ++ .../ContractTestSubmitCaseTransaction.java | 6 +- .../data/caseclosed/DateCaseClosedEntity.java | 4 +- .../caseclosed/DateCaseClosedRepository.java | 4 +- .../model/definition/CaseStateDefinition.java | 12 ++ .../model/search/DateCaseClosedResponse.java | 16 +++ ...thorisedClosedCaseSearchOperationImpl.java | 52 +++++++ .../caseclosed/ClosedCaseSearchOperation.java | 10 ++ .../DefaultClosedCaseSearchOperation.java | 36 +++++ .../createcase/SubmitCaseTransaction.java | 34 ++++- .../createevent/CreateCaseEventService.java | 39 +++++- .../getcase/CaseNotFoundException.java | 4 + .../controller/TestingSupportController.java | 94 +++++++++++-- .../controller/UICaseSearchController.java | 39 +++++- ...isedClosedCaseSearchOperationImplTest.java | 108 +++++++++++++++ .../DefaultClosedCaseSearchOperationTest.java | 91 ++++++++++++ ...mitCaseTransactionCaseAccessGroupTest.java | 6 +- .../createcase/SubmitCaseTransactionTest.java | 69 +++++++++- .../CreateCaseEventServiceTest.java | 129 ++++++++++++++++++ .../TestingSupportControllerTest.java | 56 +++++++- .../UICaseSearchControllerTest.java | 55 ++++++++ 68 files changed, 2127 insertions(+), 27 deletions(-) create mode 100644 src/aat/resources/features/F-942 - Get Closed Cases/F-942.feature create mode 100644 src/aat/resources/features/F-942 - Get Closed Cases/F-942_DeleteExistingDateCaseClosed.td.json create mode 100644 src/aat/resources/features/F-942 - Get Closed Cases/F-942_Test_Data_Base.td.json create mode 100644 src/aat/resources/features/F-942 - Get Closed Cases/S-942.1.td.json create mode 100644 src/aat/resources/features/F-942 - Get Closed Cases/S-942.1_CreateDateCaseClosed.td.json create mode 100644 src/aat/resources/features/F-942 - Get Closed Cases/S-942.2.td.json create mode 100644 src/aat/resources/features/F-942 - Get Closed Cases/S-942.3.td.json create mode 100644 src/aat/resources/features/F-942 - Get Closed Cases/S-942.4.td.json create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944.feature create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Create_Case_Base.td.json create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Get_Closed_For_Payment_Cases_Base.td.json create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1.td.json create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_Create_Case.td.json create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_GetToken_CaseCreate.td.json create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2.td.json create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2_Create_Case_NotClosed.td.json create mode 100644 src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2_GetToken_CaseCreate_NotClosed.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945.feature create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_GetToken_UpdateCase_Base.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_Update_Case_Base.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1_GetToken_UpdateCase.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1_Update_Case_ClosedForPayment.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2_GetToken_UpdateCase_NotClosedForPayment.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2_Update_Case_NotClosedForPayment.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_GetToken_UpdateCase_ClosedForPayment.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_GetToken_UpdateCase_NotClosedForPayment.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_Update_Case_ClosedForPayment.td.json create mode 100644 src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_Update_Case_NotClosedForPayment.td.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseEvent.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseField.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseState.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseType.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseEvent.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseEventToFields.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseField.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseTypeTab.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/SearchInputFields.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/SearchResultFields.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/State.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/WorkBasketInputFields.json create mode 100644 src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/WorkBasketResultFields.json create mode 100644 src/main/java/uk/gov/hmcts/ccd/domain/model/search/DateCaseClosedResponse.java create mode 100644 src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/AuthorisedClosedCaseSearchOperationImpl.java create mode 100644 src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/ClosedCaseSearchOperation.java create mode 100644 src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/DefaultClosedCaseSearchOperation.java create mode 100644 src/test/java/uk/gov/hmcts/ccd/domain/service/caseclosed/AuthorisedClosedCaseSearchOperationImplTest.java create mode 100644 src/test/java/uk/gov/hmcts/ccd/domain/service/caseclosed/DefaultClosedCaseSearchOperationTest.java diff --git a/build.gradle b/build.gradle index 9d7c36c2f1..84bd58e32f 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ ext { testContainersVersion = '2.0.2' cucumber = '7.34.3' beftaFwVersion = '9.2.4' - ccdTestDefinitionVersion = '7.26.2' + ccdTestDefinitionVersion = '7.26.0-SNAPSHOT-CME-986_1' limits = [ 'instruction': 90, 'branch' : 85, diff --git a/charts/ccd-data-store-api/values.preview.template.yaml b/charts/ccd-data-store-api/values.preview.template.yaml index 18d7cf2553..40df5b9de7 100644 --- a/charts/ccd-data-store-api/values.preview.template.yaml +++ b/charts/ccd-data-store-api/values.preview.template.yaml @@ -92,7 +92,7 @@ ccd: ccd-definition-store-api: java: ingressHost: ccd-definition-store-${SERVICE_FQDN} - image: hmctsprod.azurecr.io/ccd/definition-store-api:latest + image: hmctsprod.azurecr.io/ccd/definition-store-api:pr-1800 #image: hmctsprod.azurecr.io/ccd/definition-store-api:pr-1611 imagePullPolicy: Always devmemoryRequests: 2048Mi diff --git a/src/aat/resources/features/F-942 - Get Closed Cases/F-942.feature b/src/aat/resources/features/F-942 - Get Closed Cases/F-942.feature new file mode 100644 index 0000000000..8e4dab3d5c --- /dev/null +++ b/src/aat/resources/features/F-942 - Get Closed Cases/F-942.feature @@ -0,0 +1,56 @@ +@F-942 +Feature: F-942: Get closed cases + + Background: Load test data for the scenario + Given an appropriate test context as detailed in the test data source + And a call [to delete existing date case closed records] will get the expected response as in [F-942_DeleteExistingDateCaseClosed], + + @S-942.1 + Scenario: Return 200 when get closed cases requested + Given a case that has just been created as in [Standard_Full_Case_Creation_Data], + And a successful call [to create a date case closed record] as in [S-942.1_CreateDateCaseClosed], + And a user with [an active profile in CCD], + + When a request is prepared with appropriate values, + And the request [contains Date field], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a positive response is received, + And the response has all other details as expected. + + @S-942.2 + Scenario: Return 400 if date validation fails + Given a user with [an active profile in CCD], + + When a request is prepared with appropriate values, + And the request [contains Date field], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a negative response is received, + And the response [code is HTTP-400 Bad Request], + And the response has all the details as expected. + + @S-942.3 + Scenario: Return 401 if user is unauthorised + Given a user with [an active profile in CCD], + + When a request is prepared with appropriate values, + And the request [contains a dummy user id], + And the request [contains Date field], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a negative response is received, + And the response [code is HTTP-401], + And the response has all the details as expected. + + @S-942.4 + Scenario: Return 404 if no closed cases are found + Given a user with [an active profile in CCD], + + When a request is prepared with appropriate values, + And the request [contains Date field for which there are no cases available], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a negative response is received, + And the response [code is HTTP-404], + And the response has all the details as expected. diff --git a/src/aat/resources/features/F-942 - Get Closed Cases/F-942_DeleteExistingDateCaseClosed.td.json b/src/aat/resources/features/F-942 - Get Closed Cases/F-942_DeleteExistingDateCaseClosed.td.json new file mode 100644 index 0000000000..d917870015 --- /dev/null +++ b/src/aat/resources/features/F-942 - Get Closed Cases/F-942_DeleteExistingDateCaseClosed.td.json @@ -0,0 +1,32 @@ +{ + "_guid_": "F-942_DeleteExistingDateCaseClosed", + "title": "Delete existing date case closed records for get closed cases", + "specs": [ + "to delete existing date case closed records" + ], + + "productName": "CCD Data Store", + "operationName": "delete existing date case closed records", + + "method": "DELETE", + "uri": "/testing-support/date-case-closed", + + "users": { + "invokingUser": { + "_extends_": "CaseworkerAutotest" + } + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "queryParams": { + "stateChangedDate": "${[scenarioContext][customValues][today]}" + } + }, + + "expectedResponse": { + "responseCode": 204 + } +} diff --git a/src/aat/resources/features/F-942 - Get Closed Cases/F-942_Test_Data_Base.td.json b/src/aat/resources/features/F-942 - Get Closed Cases/F-942_Test_Data_Base.td.json new file mode 100644 index 0000000000..6b9fd18037 --- /dev/null +++ b/src/aat/resources/features/F-942 - Get Closed Cases/F-942_Test_Data_Base.td.json @@ -0,0 +1,28 @@ +{ + "_guid_": "F-942_Test_Data_Base", + + "productName": "CCD Data Store", + "operationName": "get closed cases", + + "method": "GET", + "uri": "/internal/searchCases/getClosedCases/{date}", + + "users": { + "invokingUser": { + "_extends_": "CaseworkerAutotest" + } + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "pathVariables": { + "date": "2025-01-01" + } + }, + + "expectedResponse": { + "responseCode": 200 + } +} diff --git a/src/aat/resources/features/F-942 - Get Closed Cases/S-942.1.td.json b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.1.td.json new file mode 100644 index 0000000000..510d27049c --- /dev/null +++ b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.1.td.json @@ -0,0 +1,18 @@ +{ + "_guid_": "S-942.1", + "_extends_": "F-942_Test_Data_Base", + "title": "Return list of closed cases successfully", + + "specs": [ + "an active profile in CCD", + "contains Date field" + ], + + "expectedResponse": { + "body": { + "caseReferences": [ + "${[scenarioContext][customValues][caseIdAsStringFrom_Standard_Full_Case_Creation_Data]}" + ] + } + } +} diff --git a/src/aat/resources/features/F-942 - Get Closed Cases/S-942.1_CreateDateCaseClosed.td.json b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.1_CreateDateCaseClosed.td.json new file mode 100644 index 0000000000..c06944272c --- /dev/null +++ b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.1_CreateDateCaseClosed.td.json @@ -0,0 +1,42 @@ +{ + "_guid_": "S-942.1_CreateDateCaseClosed", + "title": "Create date case closed record for get closed cases", + "specs": [ + "to create a date case closed record" + ], + + "productName": "CCD Data Store", + "operationName": "create date case closed record", + + "method": "POST", + "uri": "/testing-support/date-case-closed", + + "users": { + "invokingUser": { + "_extends_": "CaseworkerAutotest" + } + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "body": { + "ccdCaseNumber": "${[scenarioContext][parentContext][childContexts][Standard_Full_Case_Creation_Data][testData][actualResponse][body][id]}", + "state": "Closed", + "stateCategory": "Closed", + "stateChangedDate": "2025-01-01T00:00:00" + } + }, + + "expectedResponse": { + "responseCode": 201, + "body": { + "id": "[[ANYTHING_PRESENT]]", + "ccdCaseNumber": "${[scenarioContext][parentContext][childContexts][Standard_Full_Case_Creation_Data][testData][actualResponse][body][id]}", + "state": "Closed", + "stateCategory": "Closed", + "stateChangedDate": "2025-01-01T00:00:00" + } + } +} diff --git a/src/aat/resources/features/F-942 - Get Closed Cases/S-942.2.td.json b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.2.td.json new file mode 100644 index 0000000000..24da65d9d4 --- /dev/null +++ b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.2.td.json @@ -0,0 +1,31 @@ +{ + "_guid_": "S-942.2", + "_extends_": "F-942_Test_Data_Base", + "title": "Return 400 if date is not before current date", + + "specs": [ + "an active profile in CCD", + "contains Date field", + "code is HTTP-400 Bad Request" + ], + + "request": { + "pathVariables": { + "date": "2999-01-01" + } + }, + + "expectedResponse": { + "_extends_": "Common_400_Response", + "body": { + "exception": "uk.gov.hmcts.ccd.endpoint.exceptions.BadSearchRequest", + "status": 400, + "error": "Bad Request", + "message": "Date is not valid", + "path": "/internal/searchCases/getClosedCases/2999-01-01", + "details": null, + "callbackErrors": null, + "callbackWarnings": null + } + } +} diff --git a/src/aat/resources/features/F-942 - Get Closed Cases/S-942.3.td.json b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.3.td.json new file mode 100644 index 0000000000..3e9409a607 --- /dev/null +++ b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.3.td.json @@ -0,0 +1,23 @@ +{ + "_guid_": "S-942.3", + "_extends_": "F-942_Test_Data_Base", + "title": "Return 401 if user is unauthorised", + + "specs": [ + "an active profile in CCD", + "contains a dummy user id", + "contains Date field", + "code is HTTP-401" + ], + + "request": { + "headers": { + "Authorization": "Bearer abc" + } + }, + + "expectedResponse": { + "_extends_": "Common_401_Response", + "body": null + } +} diff --git a/src/aat/resources/features/F-942 - Get Closed Cases/S-942.4.td.json b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.4.td.json new file mode 100644 index 0000000000..bd4b8e3d74 --- /dev/null +++ b/src/aat/resources/features/F-942 - Get Closed Cases/S-942.4.td.json @@ -0,0 +1,31 @@ +{ + "_guid_": "S-942.4", + "_extends_": "F-942_Test_Data_Base", + "title": "Return 404 not found if no closed cases are found", + + "specs": [ + "an active profile in CCD", + "contains Date field for which there are no cases available", + "code is HTTP-404" + ], + + "request": { + "pathVariables": { + "date": "2024-01-01" + } + }, + + "expectedResponse": { + "_extends_": "Common_404_Response", + "body": { + "exception": "uk.gov.hmcts.ccd.domain.service.getcase.CaseNotFoundException", + "status": 404, + "error": "Not Found", + "message": "Case data not found", + "path": "/internal/searchCases/getClosedCases/2024-01-01", + "details": null, + "callbackErrors": null, + "callbackWarnings": null + } + } +} diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944.feature b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944.feature new file mode 100644 index 0000000000..a0fa187298 --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944.feature @@ -0,0 +1,33 @@ +@F-944 +Feature: F-944: Get closed for payment cases after creation + + Background: Load test data for the scenario + Given an appropriate test context as detailed in the test data source + And a call [to delete existing date case closed records] will get the expected response as in [F-942_DeleteExistingDateCaseClosed], + + @S-944.1 + Scenario: Return created case when created case state category is closed for payment + Given a user with [an active profile in CCD], + And a successful call [to create a token for case creation] as in [S-944.1_GetToken_CaseCreate], + And a successful call [to create a case] as in [S-944.1_Create_Case], + + When a request is prepared with appropriate values, + And the request [contains today's Date field], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a positive response is received, + And the response has all other details as expected. + + @S-944.2 + Scenario: Do not return created case when created case state category is not closed for payment + Given a user with [an active profile in CCD], + And a successful call [to create a token for case creation] as in [S-944.2_GetToken_CaseCreate_NotClosed], + And a successful call [to create a case] as in [S-944.2_Create_Case_NotClosed], + + When a request is prepared with appropriate values, + And the request [contains today's Date field], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a negative response is received, + And the response [code is HTTP-404], + And the response has all other details as expected. diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Create_Case_Base.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Create_Case_Base.td.json new file mode 100644 index 0000000000..2cc005bd40 --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Create_Case_Base.td.json @@ -0,0 +1,66 @@ +{ + "_guid_": "F-944_Create_Case_Base", + + "productName": "CCD Data Store", + "operationName": "Submit case creation as Case worker", + + "method": "POST", + "uri": "/caseworkers/{uid}/jurisdictions/{jid}/case-types/{ctid}/cases", + + "specs": [ + "to create a case" + ], + + "users": { + "invokingUser": { + "_extends_": "BeftaMasterCaseworker" + } + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "pathVariables": { + "uid": "[[DEFAULT_AUTO_VALUE]]", + "jid": "BEFTA_MASTER", + "ctid": "FT_DateCaseClosed" + }, + "body": { + "data": {}, + "event": { + "summary": "", + "description": "" + }, + "ignore_warning": false, + "draft_id": null + } + }, + + "expectedResponse": { + "_extends_": "Common_201_Response", + "headers": { + "Content-Encoding": "gzip", + "vary": "accept-encoding", + "Content-Length": "[[ANYTHING_PRESENT]]" + }, + "body": { + "id": "[[ANYTHING_PRESENT]]", + "jurisdiction": "BEFTA_MASTER", + "version": 0, + "case_type_id": "FT_DateCaseClosed", + "last_state_modified_date": "[[ANYTHING_PRESENT]]", + "created_date": "[[ANYTHING_PRESENT]]", + "last_modified": "[[ANYTHING_PRESENT]]", + "security_classification": "PUBLIC", + "case_data": {}, + "data_classification": {}, + "supplementary_data": null, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + } + } +} diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Get_Closed_For_Payment_Cases_Base.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Get_Closed_For_Payment_Cases_Base.td.json new file mode 100644 index 0000000000..49853805cb --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Get_Closed_For_Payment_Cases_Base.td.json @@ -0,0 +1,28 @@ +{ + "_guid_": "F-944_Get_Closed_For_Payment_Cases_Base", + + "productName": "CCD Data Store", + "operationName": "get closed cases", + + "method": "GET", + "uri": "/internal/searchCases/getClosedCases/{date}", + + "users": { + "invokingUser": { + "_extends_": "BeftaMasterCaseworker" + } + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "pathVariables": { + "date": "${[scenarioContext][customValues][today]}" + } + }, + + "expectedResponse": { + "responseCode": 200 + } +} diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1.td.json new file mode 100644 index 0000000000..d4f43ed893 --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1.td.json @@ -0,0 +1,18 @@ +{ + "_guid_": "S-944.1", + "_extends_": "F-944_Get_Closed_For_Payment_Cases_Base", + "title": "Return created case when state category is closed for payment", + + "specs": [ + "an active profile in CCD", + "contains today's Date field" + ], + + "expectedResponse": { + "body": { + "caseReferences": [ + "${}${[scenarioContext][childContexts][S-944.1_Create_Case][testData][actualResponse][body][id]}" + ] + } + } +} diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_Create_Case.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_Create_Case.td.json new file mode 100644 index 0000000000..9551a7ff66 --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_Create_Case.td.json @@ -0,0 +1,20 @@ +{ + "_guid_": "S-944.1_Create_Case", + "_extends_": "F-944_Create_Case_Base", + "title": "Create a case in state category closed for payment", + + "request": { + "body": { + "event_token": "${[scenarioContext][parentContext][childContexts][S-944.1_GetToken_CaseCreate][testData][actualResponse][body][token]}", + "event": { + "id": "createClosedForPaymentCase" + } + } + }, + + "expectedResponse": { + "body": { + "state": "CaseClosedForPayment" + } + } +} diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_GetToken_CaseCreate.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_GetToken_CaseCreate.td.json new file mode 100644 index 0000000000..c060780c73 --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_GetToken_CaseCreate.td.json @@ -0,0 +1,62 @@ +{ + "_guid_": "S-944.1_GetToken_CaseCreate", + "title": "Create event token for state category closed for payment case creation", + "specs": [ + "to create a token for case creation" + ], + + "productName": "CCD Data Store", + "operationName": "Start event creation as Case worker", + + "method": "GET", + "uri": "/caseworkers/{uid}/jurisdictions/{jid}/case-types/{ctid}/event-triggers/{etid}/token", + + "users": { + "invokingUser": { + "_extends_": "BeftaMasterCaseworker" + } + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "pathVariables": { + "uid": "[[DEFAULT_AUTO_VALUE]]", + "jid": "BEFTA_MASTER", + "ctid": "FT_DateCaseClosed", + "etid": "createClosedForPaymentCase" + } + }, + + "expectedResponse": { + "_extends_": "Common_200_Response", + "headers": { + "Content-Encoding": "gzip", + "Content-Length": "[[ANYTHING_PRESENT]]" + }, + "body": { + "token": "[[ANYTHING_PRESENT]]", + "case_details": { + "id": null, + "jurisdiction": "BEFTA_MASTER", + "state": null, + "version": null, + "case_type_id": "FT_DateCaseClosed", + "created_date": null, + "last_modified": null, + "last_state_modified_date": null, + "security_classification": null, + "case_data": {}, + "data_classification": {}, + "supplementary_data": null, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + }, + "event_id": "createClosedForPaymentCase" + } + } +} diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2.td.json new file mode 100644 index 0000000000..6bdca7739a --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2.td.json @@ -0,0 +1,25 @@ +{ + "_guid_": "S-944.2", + "_extends_": "F-944_Get_Closed_For_Payment_Cases_Base", + "title": "Do not return created case when state category is not closed for payment", + + "specs": [ + "an active profile in CCD", + "contains today's Date field", + "code is HTTP-404" + ], + + "expectedResponse": { + "_extends_": "Common_404_Response", + "body": { + "exception": "uk.gov.hmcts.ccd.domain.service.getcase.CaseNotFoundException", + "status": 404, + "error": "Not Found", + "message": "Case data not found", + "path": "/internal/searchCases/getClosedCases/${[scenarioContext][customValues][today]}", + "details": null, + "callbackErrors": null, + "callbackWarnings": null + } + } +} diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2_Create_Case_NotClosed.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2_Create_Case_NotClosed.td.json new file mode 100644 index 0000000000..23d4c0ce5b --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2_Create_Case_NotClosed.td.json @@ -0,0 +1,20 @@ +{ + "_guid_": "S-944.2_Create_Case_NotClosed", + "_extends_": "F-944_Create_Case_Base", + "title": "Create a case in state category not closed for payment", + + "request": { + "body": { + "event_token": "${[scenarioContext][parentContext][childContexts][S-944.2_GetToken_CaseCreate_NotClosed][testData][actualResponse][body][token]}", + "event": { + "id": "createNotClosedForPaymentCase" + } + } + }, + + "expectedResponse": { + "body": { + "state": "CaseNotClosedForPayment" + } + } +} diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2_GetToken_CaseCreate_NotClosed.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2_GetToken_CaseCreate_NotClosed.td.json new file mode 100644 index 0000000000..5ddf9c8597 --- /dev/null +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.2_GetToken_CaseCreate_NotClosed.td.json @@ -0,0 +1,17 @@ +{ + "_guid_": "S-944.2_GetToken_CaseCreate_NotClosed", + "_extends_": "S-944.1_GetToken_CaseCreate", + "title": "Create event token for state category not closed for payment case creation", + + "request": { + "pathVariables": { + "etid": "createNotClosedForPaymentCase" + } + }, + + "expectedResponse": { + "body": { + "event_id": "createNotClosedForPaymentCase" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945.feature b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945.feature new file mode 100644 index 0000000000..2b1bd38720 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945.feature @@ -0,0 +1,57 @@ +@F-945 +Feature: F-945: Update closed for payment cases + + Background: Load test data for the scenario + Given an appropriate test context as detailed in the test data source + And a call [to delete existing date case closed records] will get the expected response as in [F-942_DeleteExistingDateCaseClosed], + + @S-945.1 + Scenario: Return updated case when update event post state is closed for payment + Given a user with [an active profile in CCD], + And a successful call [to create a token for case creation] as in [S-944.2_GetToken_CaseCreate_NotClosed], + And a successful call [to create a case] as in [S-944.2_Create_Case_NotClosed], + And a successful call [to get an event token for the case just created] as in [S-945.1_GetToken_UpdateCase], + And a successful call [to submit updateCase event with post state closed for payment] as in [S-945.1_Update_Case_ClosedForPayment], + + When a request is prepared with appropriate values, + And the request [contains today's Date field], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a positive response is received, + And the response has all other details as expected. + + @S-945.2 + Scenario: Do not return updated case when update event post state is not closed for payment + Given a user with [an active profile in CCD], + And a successful call [to create a token for case creation] as in [S-944.1_GetToken_CaseCreate], + And a successful call [to create a case] as in [S-944.1_Create_Case], + And a successful call [to create a token for case creation] as in [S-944.2_GetToken_CaseCreate_NotClosed], + And a successful call [to create a case] as in [S-944.2_Create_Case_NotClosed], + And a successful call [to get an event token for the case just created] as in [S-945.2_GetToken_UpdateCase_NotClosedForPayment], + And a successful call [to submit updateCase event with post state not closed for payment] as in [S-945.2_Update_Case_NotClosedForPayment], + + When a request is prepared with appropriate values, + And the request [contains today's Date field], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a positive response is received, + And the response has all other details as expected. + + @S-945.3 + Scenario: Do not return updated case after moving out of closed for payment state + Given a user with [an active profile in CCD], + And a successful call [to create a token for case creation] as in [S-944.1_GetToken_CaseCreate], + And a successful call [to create a case] as in [S-944.1_Create_Case], + And a successful call [to create a token for case creation] as in [S-944.2_GetToken_CaseCreate_NotClosed], + And a successful call [to create a case] as in [S-944.2_Create_Case_NotClosed], + And a successful call [to get an event token for the case just created] as in [S-945.3_GetToken_UpdateCase_ClosedForPayment], + And a successful call [to submit updateCase event with post state closed for payment] as in [S-945.3_Update_Case_ClosedForPayment], + And a successful call [to get an event token for the updated case] as in [S-945.3_GetToken_UpdateCase_NotClosedForPayment], + And a successful call [to submit updateCase event with post state not closed for payment] as in [S-945.3_Update_Case_NotClosedForPayment], + + When a request is prepared with appropriate values, + And the request [contains today's Date field], + And it is submitted to call the [get closed cases] operation of [CCD Data Store], + + Then a positive response is received, + And the response has all other details as expected. diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_GetToken_UpdateCase_Base.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_GetToken_UpdateCase_Base.td.json new file mode 100644 index 0000000000..32cd00ebb0 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_GetToken_UpdateCase_Base.td.json @@ -0,0 +1,47 @@ +{ + "_guid_": "F-945_GetToken_UpdateCase_Base", + "title": "Create event token for date case closed case update", + + "productName": "CCD Data Store", + "operationName": "Retrieve an update token", + + "method": "GET", + "uri": "/caseworkers/{uid}/jurisdictions/{jid}/case-types/{ctid}/cases/{cid}/event-triggers/{etid}/token", + + "specs": [ + "to get an event token for the case just created" + ], + + "users": { + "invokingUser": { + "_extends_": "BeftaMasterCaseworker" + } + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "pathVariables": { + "uid": "[[DEFAULT_AUTO_VALUE]]", + "jid": "BEFTA_MASTER", + "ctid": "FT_DateCaseClosed", + "cid": "OVERRIDE", + "etid": "OVERRIDE" + } + }, + + "expectedResponse": { + "_extends_": "Common_200_Response", + "headers": { + "Content-Encoding": "gzip", + "Content-Type": "application/json", + "Content-Length": "[[ANYTHING_PRESENT]]" + }, + "body": { + "token": "[[ANYTHING_PRESENT]]", + "case_details": "[[ANYTHING_PRESENT]]", + "event_id": "OVERRIDE" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_Update_Case_Base.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_Update_Case_Base.td.json new file mode 100644 index 0000000000..0398d06990 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_Update_Case_Base.td.json @@ -0,0 +1,70 @@ +{ + "_guid_": "F-945_Update_Case_Base", + "title": "Submit update event for date case closed case", + + "productName": "CCD data store", + "operationName": "submit updateCase event", + + "method": "POST", + "uri": "/caseworkers/{uid}/jurisdictions/{jid}/case-types/{ctid}/cases/{cid}/events", + + "specs": [ + "an active profile in CCD", + "contains a case Id that has just been created" + ], + + "user": { + "_extends_": "BeftaMasterCaseworker" + }, + + "request": { + "headers": { + "_extends_": "Common_Request_Headers" + }, + "pathVariables": { + "uid": "[[DEFAULT_AUTO_VALUE]]", + "jid": "BEFTA_MASTER", + "ctid": "FT_DateCaseClosed", + "cid": "OVERRIDE" + }, + "body": { + "data": {}, + "event": { + "id": "OVERRIDE", + "summary": "", + "description": "" + }, + "security_classification": "PUBLIC", + "event_token": "OVERRIDE", + "ignore_warning": true + } + }, + + "expectedResponse": { + "responseCode": 201, + "responseMessage": "OK", + "headers": { + "_extends_": "Common_Response_Headers", + "Vary": "Accept-Encoding" + }, + "body": { + "id": "[[ANYTHING_PRESENT]]", + "jurisdiction": "BEFTA_MASTER", + "state": "OVERRIDE", + "version": "[[ANYTHING_PRESENT]]", + "case_type_id": "FT_DateCaseClosed", + "created_date": "[[ANYTHING_PRESENT]]", + "last_modified": "[[ANYTHING_PRESENT]]", + "last_state_modified_date": "[[ANYTHING_PRESENT]]", + "security_classification": "PUBLIC", + "case_data": {}, + "data_classification": "[[ANYTHING_PRESENT]]", + "supplementary_data": null, + "after_submit_callback_response": null, + "callback_response_status_code": null, + "callback_response_status": null, + "delete_draft_response_status_code": null, + "delete_draft_response_status": null + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1.td.json new file mode 100644 index 0000000000..2553a9c59f --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1.td.json @@ -0,0 +1,18 @@ +{ + "_guid_": "S-945.1", + "_extends_": "F-944_Get_Closed_For_Payment_Cases_Base", + "title": "Return updated case when update event post state is closed for payment", + + "specs": [ + "an active profile in CCD", + "contains today's Date field" + ], + + "expectedResponse": { + "body": { + "caseReferences": [ + "${}${[scenarioContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}" + ] + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1_GetToken_UpdateCase.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1_GetToken_UpdateCase.td.json new file mode 100644 index 0000000000..3843fb7154 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1_GetToken_UpdateCase.td.json @@ -0,0 +1,18 @@ +{ + "_guid_": "S-945.1_GetToken_UpdateCase", + "_extends_": "F-945_GetToken_UpdateCase_Base", + "title": "Create event token for updating case to closed for payment", + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}", + "etid": "updateToClosedForPaymentCase" + } + }, + + "expectedResponse": { + "body": { + "event_id": "updateToClosedForPaymentCase" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1_Update_Case_ClosedForPayment.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1_Update_Case_ClosedForPayment.td.json new file mode 100644 index 0000000000..5ff191d318 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.1_Update_Case_ClosedForPayment.td.json @@ -0,0 +1,27 @@ +{ + "_guid_": "S-945.1_Update_Case_ClosedForPayment", + "_extends_": "F-945_Update_Case_Base", + "title": "Update case to state category closed for payment", + "specs": [ + "to submit updateCase event with post state closed for payment", + "contains Update token created as in S-945.1_GetToken_UpdateCase" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}" + }, + "body": { + "event_token": "${[scenarioContext][parentContext][childContexts][S-945.1_GetToken_UpdateCase][testData][actualResponse][body][token]}", + "event": { + "id": "updateToClosedForPaymentCase" + } + } + }, + + "expectedResponse": { + "body": { + "state": "CaseClosedForPayment" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2.td.json new file mode 100644 index 0000000000..4ae4e33185 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2.td.json @@ -0,0 +1,18 @@ +{ + "_guid_": "S-945.2", + "_extends_": "F-944_Get_Closed_For_Payment_Cases_Base", + "title": "Do not return updated case when update event post state is not closed for payment", + + "specs": [ + "an active profile in CCD", + "contains today's Date field" + ], + + "expectedResponse": { + "body": { + "caseReferences": [ + "${}${[scenarioContext][childContexts][S-944.1_Create_Case][testData][actualResponse][body][id]}" + ] + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2_GetToken_UpdateCase_NotClosedForPayment.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2_GetToken_UpdateCase_NotClosedForPayment.td.json new file mode 100644 index 0000000000..8ae0701760 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2_GetToken_UpdateCase_NotClosedForPayment.td.json @@ -0,0 +1,18 @@ +{ + "_guid_": "S-945.2_GetToken_UpdateCase_NotClosedForPayment", + "_extends_": "F-945_GetToken_UpdateCase_Base", + "title": "Create event token for updating case to not closed for payment", + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}", + "etid": "updateToNotClosedForPaymentCaseFromNotClosed" + } + }, + + "expectedResponse": { + "body": { + "event_id": "updateToNotClosedForPaymentCaseFromNotClosed" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2_Update_Case_NotClosedForPayment.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2_Update_Case_NotClosedForPayment.td.json new file mode 100644 index 0000000000..bc766752e1 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.2_Update_Case_NotClosedForPayment.td.json @@ -0,0 +1,27 @@ +{ + "_guid_": "S-945.2_Update_Case_NotClosedForPayment", + "_extends_": "F-945_Update_Case_Base", + "title": "Update case to state category not closed for payment", + "specs": [ + "to submit updateCase event with post state not closed for payment", + "contains Update token created as in S-945.2_GetToken_UpdateCase_NotClosedForPayment" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}" + }, + "body": { + "event_token": "${[scenarioContext][parentContext][childContexts][S-945.2_GetToken_UpdateCase_NotClosedForPayment][testData][actualResponse][body][token]}", + "event": { + "id": "updateToNotClosedForPaymentCaseFromNotClosed" + } + } + }, + + "expectedResponse": { + "body": { + "state": "CaseNotClosedForPayment" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3.td.json new file mode 100644 index 0000000000..7192f40c6a --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3.td.json @@ -0,0 +1,18 @@ +{ + "_guid_": "S-945.3", + "_extends_": "F-944_Get_Closed_For_Payment_Cases_Base", + "title": "Do not return updated case after moving out of closed for payment state", + + "specs": [ + "an active profile in CCD", + "contains today's Date field" + ], + + "expectedResponse": { + "body": { + "caseReferences": [ + "${}${[scenarioContext][childContexts][S-944.1_Create_Case][testData][actualResponse][body][id]}" + ] + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_GetToken_UpdateCase_ClosedForPayment.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_GetToken_UpdateCase_ClosedForPayment.td.json new file mode 100644 index 0000000000..c976ef9f22 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_GetToken_UpdateCase_ClosedForPayment.td.json @@ -0,0 +1,18 @@ +{ + "_guid_": "S-945.3_GetToken_UpdateCase_ClosedForPayment", + "_extends_": "F-945_GetToken_UpdateCase_Base", + "title": "Create event token for moving case into closed for payment", + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}", + "etid": "updateToClosedForPaymentCase" + } + }, + + "expectedResponse": { + "body": { + "event_id": "updateToClosedForPaymentCase" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_GetToken_UpdateCase_NotClosedForPayment.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_GetToken_UpdateCase_NotClosedForPayment.td.json new file mode 100644 index 0000000000..a111a600f5 --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_GetToken_UpdateCase_NotClosedForPayment.td.json @@ -0,0 +1,21 @@ +{ + "_guid_": "S-945.3_GetToken_UpdateCase_NotClosedForPayment", + "_extends_": "F-945_GetToken_UpdateCase_Base", + "title": "Create event token for moving case out of closed for payment", + "specs": [ + "to get an event token for the updated case" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}", + "etid": "updateToNotClosedForPaymentCase" + } + }, + + "expectedResponse": { + "body": { + "event_id": "updateToNotClosedForPaymentCase" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_Update_Case_ClosedForPayment.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_Update_Case_ClosedForPayment.td.json new file mode 100644 index 0000000000..173fc440dc --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_Update_Case_ClosedForPayment.td.json @@ -0,0 +1,26 @@ +{ + "_guid_": "S-945.3_Update_Case_ClosedForPayment", + "_extends_": "F-945_Update_Case_Base", + "title": "Move case into closed for payment", + "specs": [ + "to submit updateCase event with post state closed for payment" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}" + }, + "body": { + "event_token": "${[scenarioContext][parentContext][childContexts][S-945.3_GetToken_UpdateCase_ClosedForPayment][testData][actualResponse][body][token]}", + "event": { + "id": "updateToClosedForPaymentCase" + } + } + }, + + "expectedResponse": { + "body": { + "state": "CaseClosedForPayment" + } + } +} diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_Update_Case_NotClosedForPayment.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_Update_Case_NotClosedForPayment.td.json new file mode 100644 index 0000000000..1ac34bfd0a --- /dev/null +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/S-945.3_Update_Case_NotClosedForPayment.td.json @@ -0,0 +1,27 @@ +{ + "_guid_": "S-945.3_Update_Case_NotClosedForPayment", + "_extends_": "F-945_Update_Case_Base", + "title": "Move case out of closed for payment", + "specs": [ + "to submit updateCase event with post state not closed for payment", + "contains Update token created as in S-945.3_GetToken_UpdateCase_NotClosedForPayment" + ], + + "request": { + "pathVariables": { + "cid": "${[scenarioContext][parentContext][childContexts][S-944.2_Create_Case_NotClosed][testData][actualResponse][body][id]}" + }, + "body": { + "event_token": "${[scenarioContext][parentContext][childContexts][S-945.3_GetToken_UpdateCase_NotClosedForPayment][testData][actualResponse][body][token]}", + "event": { + "id": "updateToNotClosedForPaymentCase" + } + } + }, + + "expectedResponse": { + "body": { + "state": "CaseNotClosedForPayment" + } + } +} diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseEvent.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseEvent.json new file mode 100644 index 0000000000..5850630498 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseEvent.json @@ -0,0 +1,42 @@ +[ + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "CaseEventID": "createClosedForPaymentCase", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "CaseEventID": "createNotClosedForPaymentCase", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "CaseEventID": "updateToClosedForPaymentCase", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "CaseEventID": "updateToNotClosedForPaymentCase", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "CaseEventID": "updateToNotClosedForPaymentCaseFromNotClosed", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + } +] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseField.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseField.json new file mode 100644 index 0000000000..f56c2de298 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseField.json @@ -0,0 +1,10 @@ +[ + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "CaseFieldID": "TextField", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + } +] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseState.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseState.json new file mode 100644 index 0000000000..b4ba1014f3 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseState.json @@ -0,0 +1,18 @@ +[ + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "CaseStateID": "CaseClosedForPayment", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "CaseStateID": "CaseNotClosedForPayment", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + } +] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseType.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseType.json new file mode 100644 index 0000000000..4c3ff225e2 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/AuthorisationCaseType.json @@ -0,0 +1,9 @@ +[ + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "UserRole": "caseworker-befta_master", + "CRUD": "CRUD" + } +] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseEvent.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseEvent.json new file mode 100644 index 0000000000..ab7d577685 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseEvent.json @@ -0,0 +1,127 @@ +[ + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "ID": "createClosedForPaymentCase", + "Name": "Create closed for payment case", + "Description": "Create a case in a closed for payment state category", + "DisplayOrder": 1, + "PreConditionState(s)": "", + "PostConditionState": "CaseClosedForPayment", + "EventEnablingCondition": "", + "CallBackURLAboutToStartEvent": "", + "RetriesTimeoutAboutToStartEvent": "", + "CallBackURLAboutToSubmitEvent": "", + "RetriesTimeoutURLAboutToSubmitEvent": "", + "CallBackURLSubmittedEvent": "", + "RetriesTimeoutURLSubmittedEvent": "", + "SecurityClassification": "Public", + "ShowSummary": "Y", + "ShowEventNotes": "", + "CanSaveDraft": "N", + "EndButtonLabel": "", + "Publish": "", + "TTLIncrement": "" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "ID": "createNotClosedForPaymentCase", + "Name": "Create non-closed payment case", + "Description": "Create a case in a state category that is not closed for payment", + "DisplayOrder": 2, + "PreConditionState(s)": "", + "PostConditionState": "CaseNotClosedForPayment", + "EventEnablingCondition": "", + "CallBackURLAboutToStartEvent": "", + "RetriesTimeoutAboutToStartEvent": "", + "CallBackURLAboutToSubmitEvent": "", + "RetriesTimeoutURLAboutToSubmitEvent": "", + "CallBackURLSubmittedEvent": "", + "RetriesTimeoutURLSubmittedEvent": "", + "SecurityClassification": "Public", + "ShowSummary": "Y", + "ShowEventNotes": "", + "CanSaveDraft": "N", + "EndButtonLabel": "", + "Publish": "", + "TTLIncrement": "" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "ID": "updateToClosedForPaymentCase", + "Name": "Update closed payment case", + "Description": "Update a case to a closed for payment state category", + "DisplayOrder": 3, + "PreConditionState(s)": "CaseNotClosedForPayment", + "PostConditionState": "CaseClosedForPayment", + "EventEnablingCondition": "", + "CallBackURLAboutToStartEvent": "", + "RetriesTimeoutAboutToStartEvent": "", + "CallBackURLAboutToSubmitEvent": "", + "RetriesTimeoutURLAboutToSubmitEvent": "", + "CallBackURLSubmittedEvent": "", + "RetriesTimeoutURLSubmittedEvent": "", + "SecurityClassification": "Public", + "ShowSummary": "Y", + "ShowEventNotes": "", + "CanSaveDraft": "N", + "EndButtonLabel": "", + "Publish": "", + "TTLIncrement": "" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "ID": "updateToNotClosedForPaymentCase", + "Name": "Update non-closed payment case", + "Description": "Update a case to a state category that is not closed for payment", + "DisplayOrder": 4, + "PreConditionState(s)": "CaseClosedForPayment", + "PostConditionState": "CaseNotClosedForPayment", + "EventEnablingCondition": "", + "CallBackURLAboutToStartEvent": "", + "RetriesTimeoutAboutToStartEvent": "", + "CallBackURLAboutToSubmitEvent": "", + "RetriesTimeoutURLAboutToSubmitEvent": "", + "CallBackURLSubmittedEvent": "", + "RetriesTimeoutURLSubmittedEvent": "", + "SecurityClassification": "Public", + "ShowSummary": "Y", + "ShowEventNotes": "", + "CanSaveDraft": "N", + "EndButtonLabel": "", + "Publish": "", + "TTLIncrement": "" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "ID": "updateToNotClosedForPaymentCaseFromNotClosed", + "Name": "Update unchanged payment case", + "Description": "Update a case that remains in a non-closed for payment state category", + "DisplayOrder": 5, + "PreConditionState(s)": "CaseNotClosedForPayment", + "PostConditionState": "CaseNotClosedForPayment", + "EventEnablingCondition": "", + "CallBackURLAboutToStartEvent": "", + "RetriesTimeoutAboutToStartEvent": "", + "CallBackURLAboutToSubmitEvent": "", + "RetriesTimeoutURLAboutToSubmitEvent": "", + "CallBackURLSubmittedEvent": "", + "RetriesTimeoutURLSubmittedEvent": "", + "SecurityClassification": "Public", + "ShowSummary": "Y", + "ShowEventNotes": "", + "CanSaveDraft": "N", + "EndButtonLabel": "", + "Publish": "", + "TTLIncrement": "" + } +] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseEventToFields.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseEventToFields.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseEventToFields.json @@ -0,0 +1 @@ +[] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseField.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseField.json new file mode 100644 index 0000000000..4ecfe093c4 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseField.json @@ -0,0 +1,21 @@ +[ + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "ID": "TextField", + "Label": "Text Field", + "HintText": "", + "FieldType": "Text", + "CategoryID": "", + "FieldTypeParameter": "", + "RegularExpression": "", + "SecurityClassification": "Public", + "Min": "", + "Max": "", + "DefaultHidden": "No", + "Searchable": "", + "FieldShowCondition": "", + "TabShowCondition": "" + } +] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseTypeTab.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseTypeTab.json new file mode 100644 index 0000000000..276d07b4d4 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/CaseTypeTab.json @@ -0,0 +1,16 @@ +[ + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "Channel": "notApplicable", + "TabID": "NameTab", + "TabLabel": "Details", + "TabDisplayOrder": 2, + "CaseFieldID": "TextField", + "TabFieldDisplayOrder": 1, + "FieldShowCondition": "", + "TabShowCondition": "", + "DisplayContextParameter": "" + } +] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/SearchInputFields.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/SearchInputFields.json new file mode 100644 index 0000000000..f5a6686a58 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/SearchInputFields.json @@ -0,0 +1,10 @@ +[ { + "LiveFrom" : "01/01/2017", + "LiveTo" : "", + "CaseTypeID" : "FT_DateCaseClosed", + "CaseFieldID" : "TextField", + "ListElementCode" : "", + "Label" : "Text Field", + "DisplayOrder" : 1, + "DisplayContextParameter" : "" +} ] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/SearchResultFields.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/SearchResultFields.json new file mode 100644 index 0000000000..8ea061d18b --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/SearchResultFields.json @@ -0,0 +1,11 @@ +[ { + "LiveFrom" : "01/01/2017", + "LiveTo" : "", + "CaseTypeID" : "FT_DateCaseClosed", + "CaseFieldID" : "TextField", + "Label" : "Text Field", + "ListElementCode" : "", + "DisplayOrder" : 1, + "ResultsOrdering" : "", + "DisplayContextParameter" : "" +} ] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/State.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/State.json new file mode 100644 index 0000000000..ceba3ce777 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/State.json @@ -0,0 +1,24 @@ +[ + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "ID": "CaseClosedForPayment", + "Name": "Closed for payment purpose", + "Description": "", + "DisplayOrder": 1, + "TitleDisplay": "", + "StateCategory": "CLOSED FOR PAYMENT,End" + }, + { + "LiveFrom": "01/01/2017", + "LiveTo": "", + "CaseTypeID": "FT_DateCaseClosed", + "ID": "CaseNotClosedForPayment", + "Name": "Not closed for payment purpose", + "Description": "", + "DisplayOrder": 2, + "TitleDisplay": "", + "StateCategory": "End" + } +] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/WorkBasketInputFields.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/WorkBasketInputFields.json new file mode 100644 index 0000000000..f5a6686a58 --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/WorkBasketInputFields.json @@ -0,0 +1,10 @@ +[ { + "LiveFrom" : "01/01/2017", + "LiveTo" : "", + "CaseTypeID" : "FT_DateCaseClosed", + "CaseFieldID" : "TextField", + "ListElementCode" : "", + "Label" : "Text Field", + "DisplayOrder" : 1, + "DisplayContextParameter" : "" +} ] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/WorkBasketResultFields.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/WorkBasketResultFields.json new file mode 100644 index 0000000000..d7c97e92cc --- /dev/null +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_DateCaseClosed/WorkBasketResultFields.json @@ -0,0 +1,11 @@ +[ { + "LiveFrom" : "01/01/2017", + "LiveTo" : "", + "CaseTypeID" : "FT_DateCaseClosed", + "CaseFieldID" : "TextField", + "ListElementCode" : "", + "Label" : "Text Field", + "DisplayOrder" : 1, + "ResultsOrdering" : "", + "DisplayContextParameter" : "" +} ] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_Decentralisation/State.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_Decentralisation/State.json index 17f456ba51..e8844234a3 100644 --- a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_Decentralisation/State.json +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/FT_Decentralisation/State.json @@ -6,5 +6,6 @@ "Name" : "Create case", "Description" : "", "DisplayOrder" : 1, - "TitleDisplay" : "" + "TitleDisplay" : "", + "StateCategory" : "" } ] diff --git a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/common/CaseType.json b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/common/CaseType.json index db750c58ff..18f62e6461 100644 --- a/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/common/CaseType.json +++ b/src/aat/resources/uk/gov/hmcts/ccd/test_definitions/valid/BEFTA_MASTER/common/CaseType.json @@ -662,6 +662,19 @@ "CallbackGetCaseUrl" : "", "RetriesGetCaseUrl" : "" }, + { + "LiveFrom" : "01/01/2017", + "LiveTo" : "", + "ID" : "FT_DateCaseClosed", + "Name" : "FT-Date Case Closed", + "Description" : "Minimal case type for date case closed state category feature tests", + "JurisdictionID" : "BEFTA_MASTER", + "PrintableDocumentsUrl" : "", + "RetriesTimeoutURLPrintEvent" : "", + "SecurityClassification" : "Public", + "CallbackGetCaseUrl" : "", + "RetriesGetCaseUrl" : "" + }, { "LiveFrom" : "01/01/2017", "LiveTo" : "", diff --git a/src/contractTest/java/uk/gov/hmcts/ccd/v2/external/controller/ContractTestSubmitCaseTransaction.java b/src/contractTest/java/uk/gov/hmcts/ccd/v2/external/controller/ContractTestSubmitCaseTransaction.java index 64e99a7faa..b1c2827d76 100644 --- a/src/contractTest/java/uk/gov/hmcts/ccd/v2/external/controller/ContractTestSubmitCaseTransaction.java +++ b/src/contractTest/java/uk/gov/hmcts/ccd/v2/external/controller/ContractTestSubmitCaseTransaction.java @@ -5,6 +5,7 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import uk.gov.hmcts.ccd.ApplicationParams; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseAuditEventRepository; import uk.gov.hmcts.ccd.data.casedetails.DefaultCaseDetailsRepository; import uk.gov.hmcts.ccd.data.persistence.CasePointerRepository; @@ -43,12 +44,13 @@ public ContractTestSubmitCaseTransaction(DefaultCaseDetailsRepository caseDetail DecentralisedCreateCaseEventService decentralisedCreateCaseEventService, PersistenceStrategyResolver resolver, CasePointerRepository creator, - SynchronisedCaseProcessor synchronisedCaseProcessor) { + SynchronisedCaseProcessor synchronisedCaseProcessor, + DateCaseClosedRepository dateCaseClosedRepository) { super(caseDetailsRepository, caseAuditEventRepository, caseTypeService, callbackInvoker, uidService, securityClassificationService, caseDataAccessControl, messageService, caseDocumentService, applicationParams, caseAccessGroupUtils, caseDocumentTimestampService, decentralisedCreateCaseEventService, resolver, creator, - synchronisedCaseProcessor); + synchronisedCaseProcessor, dateCaseClosedRepository); } } diff --git a/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedEntity.java b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedEntity.java index c67ef58f79..496b13562f 100644 --- a/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedEntity.java +++ b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedEntity.java @@ -1,7 +1,5 @@ package uk.gov.hmcts.ccd.data.caseclosed; -import lombok.Data; - import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -9,6 +7,8 @@ import jakarta.persistence.Id; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; +import lombok.Data; + import java.time.LocalDateTime; @Entity diff --git a/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java index 55a5908176..ce71cbeb8c 100644 --- a/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java +++ b/src/main/java/uk/gov/hmcts/ccd/data/caseclosed/DateCaseClosedRepository.java @@ -9,5 +9,7 @@ @Repository public interface DateCaseClosedRepository extends CrudRepository { - List findByStateChangedDateLessThanEqual(LocalDateTime stateChangedDate); + List findByStateChangedDateBefore(LocalDateTime stateChangedDate); + + void deleteByCcdCaseNumber(Long ccdCaseNumber); } diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/model/definition/CaseStateDefinition.java b/src/main/java/uk/gov/hmcts/ccd/domain/model/definition/CaseStateDefinition.java index 124bdf8761..d75c3fbd6a 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/model/definition/CaseStateDefinition.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/model/definition/CaseStateDefinition.java @@ -20,6 +20,7 @@ public class CaseStateDefinition implements Serializable, Copyable accessControlLists; + private String stateCategory = null; @Schema(required = true) @JsonProperty("id") @@ -83,6 +84,16 @@ public void setAccessControlLists(List accessControlLists) { this.accessControlLists = accessControlLists; } + @JsonIgnore + public String getStateCategory() { + return stateCategory; + } + + @JsonProperty("state_category") + public void setStateCategory(String stateCategory) { + this.stateCategory = stateCategory; + } + @JsonIgnore @Override public CaseStateDefinition createCopy() { @@ -93,6 +104,7 @@ public CaseStateDefinition createCopy() { copy.setDisplayOrder(this.getDisplayOrder()); copy.setTitleDisplay(this.getTitleDisplay()); copy.setAccessControlLists(createACLCopyList(this.getAccessControlLists())); + copy.setStateCategory(this.getStateCategory()); return copy; } diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/model/search/DateCaseClosedResponse.java b/src/main/java/uk/gov/hmcts/ccd/domain/model/search/DateCaseClosedResponse.java new file mode 100644 index 0000000000..20a1959edb --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/model/search/DateCaseClosedResponse.java @@ -0,0 +1,16 @@ +package uk.gov.hmcts.ccd.domain.model.search; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +public class DateCaseClosedResponse { + private List caseReferences; + + public DateCaseClosedResponse(List caseReferences) { + this.caseReferences = caseReferences == null || caseReferences.isEmpty() ? null : caseReferences; + } +} diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/AuthorisedClosedCaseSearchOperationImpl.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/AuthorisedClosedCaseSearchOperationImpl.java new file mode 100644 index 0000000000..c3bafe3965 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/AuthorisedClosedCaseSearchOperationImpl.java @@ -0,0 +1,52 @@ +package uk.gov.hmcts.ccd.domain.service.caseclosed; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import uk.gov.hmcts.ccd.domain.model.search.DateCaseClosedResponse; +import uk.gov.hmcts.ccd.domain.service.getcase.CaseNotFoundException; +import uk.gov.hmcts.ccd.domain.service.getcase.GetCaseOperation; + +import java.time.LocalDate; +import java.util.List; + +import static uk.gov.hmcts.ccd.v2.V2.Error.CASE_DATA_NOT_FOUND; + +@Service +@Qualifier("authorised") +public class AuthorisedClosedCaseSearchOperationImpl implements ClosedCaseSearchOperation { + + private final ClosedCaseSearchOperation closedCaseSearchOperation; + private final GetCaseOperation getCaseOperation; + + public AuthorisedClosedCaseSearchOperationImpl( + @Qualifier(DefaultClosedCaseSearchOperation.QUALIFIER) ClosedCaseSearchOperation closedCaseSearchOperation, + @Qualifier("authorised") GetCaseOperation getCaseOperation) { + this.closedCaseSearchOperation = closedCaseSearchOperation; + this.getCaseOperation = getCaseOperation; + } + + @Override + @Transactional + public DateCaseClosedResponse execute(LocalDate closedCaseDate) { + List closedCaseReferences = closedCaseSearchOperation.execute(closedCaseDate).getCaseReferences(); + + if (closedCaseReferences == null || closedCaseReferences.isEmpty()) { + throw new CaseNotFoundException(CASE_DATA_NOT_FOUND, null); + } + + List caseReferences = closedCaseReferences.stream() + .filter(this::hasReadAccess) + .toList(); + + if (caseReferences.isEmpty()) { + throw new CaseNotFoundException(CASE_DATA_NOT_FOUND, null); + } + + return new DateCaseClosedResponse(caseReferences); + } + + private boolean hasReadAccess(String caseReference) { + return getCaseOperation.execute(caseReference).isPresent(); + } +} diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/ClosedCaseSearchOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/ClosedCaseSearchOperation.java new file mode 100644 index 0000000000..e52f7e7c11 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/ClosedCaseSearchOperation.java @@ -0,0 +1,10 @@ +package uk.gov.hmcts.ccd.domain.service.caseclosed; + +import uk.gov.hmcts.ccd.domain.model.search.DateCaseClosedResponse; + +import java.time.LocalDate; + +public interface ClosedCaseSearchOperation { + + DateCaseClosedResponse execute(LocalDate closedCaseDate); +} diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/DefaultClosedCaseSearchOperation.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/DefaultClosedCaseSearchOperation.java new file mode 100644 index 0000000000..f77999eab0 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/caseclosed/DefaultClosedCaseSearchOperation.java @@ -0,0 +1,36 @@ +package uk.gov.hmcts.ccd.domain.service.caseclosed; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; +import uk.gov.hmcts.ccd.domain.model.search.DateCaseClosedResponse; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +@Service +@Qualifier(DefaultClosedCaseSearchOperation.QUALIFIER) +public class DefaultClosedCaseSearchOperation implements ClosedCaseSearchOperation { + + public static final String QUALIFIER = "default"; + private final DateCaseClosedRepository dateCaseClosedRepository; + + public DefaultClosedCaseSearchOperation(DateCaseClosedRepository dateCaseClosedRepository) { + this.dateCaseClosedRepository = dateCaseClosedRepository; + } + + @Override + public DateCaseClosedResponse execute(LocalDate closedCaseDate) { + LocalDateTime nextDayStart = closedCaseDate.plusDays(1).atStartOfDay(); + List caseReferences = dateCaseClosedRepository.findByStateChangedDateBefore(nextDayStart) + .stream() + .map(DateCaseClosedEntity::getCcdCaseNumber) + .map(String::valueOf) + .distinct() + .toList(); + + return new DateCaseClosedResponse(caseReferences); + } +} diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransaction.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransaction.java index a6641a3628..d7177d8558 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransaction.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransaction.java @@ -7,6 +7,8 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import feign.FeignException; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.data.casedetails.CachedCaseDetailsRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseAuditEventRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseDetailsRepository; @@ -42,6 +44,7 @@ import jakarta.inject.Inject; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -67,6 +70,8 @@ public class SubmitCaseTransaction implements AccessControl { private final PersistenceStrategyResolver resolver; private final CasePointerRepository casePointerRepository; private final SynchronisedCaseProcessor synchronisedCaseProcessor; + private final DateCaseClosedRepository dateCaseClosedRepository; + private static final String CLOSED_FOR_PAYMENT = "CLOSED FOR PAYMENT"; @Inject public SubmitCaseTransaction(@Qualifier(CachedCaseDetailsRepository.QUALIFIER) @@ -85,8 +90,9 @@ public SubmitCaseTransaction(@Qualifier(CachedCaseDetailsRepository.QUALIFIER) final DecentralisedCreateCaseEventService decentralisedSubmitCaseTransaction, final PersistenceStrategyResolver resolver, final CasePointerRepository casePointerRepository, - final SynchronisedCaseProcessor synchronisedCaseProcessor - ) { + final SynchronisedCaseProcessor synchronisedCaseProcessor, + final DateCaseClosedRepository dateCaseClosedRepository + ) { this.caseDetailsRepository = caseDetailsRepository; this.caseAuditEventRepository = caseAuditEventRepository; this.caseTypeService = caseTypeService; @@ -103,6 +109,7 @@ public SubmitCaseTransaction(@Qualifier(CachedCaseDetailsRepository.QUALIFIER) this.resolver = resolver; this.casePointerRepository = casePointerRepository; this.synchronisedCaseProcessor = synchronisedCaseProcessor; + this.dateCaseClosedRepository = dateCaseClosedRepository; } @Transactional(propagation = Propagation.REQUIRES_NEW) @@ -188,10 +195,33 @@ public CaseDetails submitCase(Event event, ); } + updateDateCaseClosed(savedCaseDetails, caseTypeDefinition); return savedCaseDetails; } + private void updateDateCaseClosed(CaseDetails caseDetails, CaseTypeDefinition caseTypeDefinition) { + CaseStateDefinition caseStateDefinition = caseTypeService.findState(caseTypeDefinition, caseDetails.getState()); + String stateCategory = caseStateDefinition.getStateCategory(); + + if (hasClosedForPaymentCategory(stateCategory)) { + DateCaseClosedEntity dateCaseClosedEntity = new DateCaseClosedEntity(); + dateCaseClosedEntity.setCcdCaseNumber(caseDetails.getReference()); + dateCaseClosedEntity.setState(caseDetails.getState()); + dateCaseClosedEntity.setStateCategory(stateCategory); + dateCaseClosedEntity.setStateChangedDate(caseDetails.getLastStateModifiedDate()); + + dateCaseClosedRepository.save(dateCaseClosedEntity); + } + } + + private boolean hasClosedForPaymentCategory(String stateCategory) { + return stateCategory != null + && Arrays.stream(stateCategory.split(",")) + .map(String::trim) + .anyMatch(CLOSED_FOR_PAYMENT::equalsIgnoreCase); + } + private CaseDetails submitDecentralisedCase(Event event, CaseTypeDefinition caseTypeDefinition, IdamUser idamUser, CaseEventDefinition caseEventDefinition, IdamUser onBehalfOfUser, CaseDetails newCaseDetails, diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventService.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventService.java index 7d52cc528f..5b27189f69 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventService.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventService.java @@ -8,6 +8,8 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import uk.gov.hmcts.ccd.ApplicationParams; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.data.casedetails.CachedCaseDetailsRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseAuditEventRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseDetailsRepository; @@ -64,6 +66,7 @@ import java.time.Clock; import java.time.LocalDateTime; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -112,6 +115,8 @@ public class CreateCaseEventService { private final CasePointerRepository pointerRepository; private final ConditionalFieldRestorer conditionalFieldRestorer; private final CaseAccessService caseAccessService; + private final DateCaseClosedRepository dateCaseClosedRepository; + private static final String CLOSED_FOR_PAYMENT = "CLOSED FOR PAYMENT"; @Inject public CreateCaseEventService(@Qualifier(CachedUserRepository.QUALIFIER) final UserRepository userRepository, @@ -152,7 +157,8 @@ public CreateCaseEventService(@Qualifier(CachedUserRepository.QUALIFIER) final U final CasePointerRepository pointerRepository, final SynchronisedCaseProcessor synchronisedCaseProcessor, final ConditionalFieldRestorer conditionalFieldRestorer, - final CaseAccessService caseAccessService) { + final CaseAccessService caseAccessService, + final DateCaseClosedRepository dateCaseClosedRepository) { this.userRepository = userRepository; this.caseDetailsRepository = caseDetailsRepository; @@ -189,6 +195,7 @@ public CreateCaseEventService(@Qualifier(CachedUserRepository.QUALIFIER) final U this.synchronisedCaseProcessor = synchronisedCaseProcessor; this.conditionalFieldRestorer = conditionalFieldRestorer; this.caseAccessService = caseAccessService; + this.dateCaseClosedRepository = dateCaseClosedRepository; } @Transactional(propagation = Propagation.REQUIRES_NEW) @@ -323,6 +330,8 @@ public CreateCaseEventResult createCaseEvent(final String caseReference, final C } + updateDateCaseClosed(finalCaseDetails, caseDetailsInDatabase, caseTypeDefinition); + return CreateCaseEventResult.caseEventWith() .caseDetailsBefore(caseDetailsInDatabase) .savedCaseDetails(finalCaseDetails) @@ -496,6 +505,34 @@ private CaseDetails saveCaseDetails(final CaseDetails caseDetailsBefore, return caseDetailsRepository.set(caseDetails); } + private void updateDateCaseClosed(CaseDetails caseDetails, + CaseDetails caseDetailsBefore, + CaseTypeDefinition caseTypeDefinition) { + String stateCategory = caseTypeService.findState(caseTypeDefinition, + caseDetails.getState()).getStateCategory(); + String previousStateCategory = caseTypeService.findState(caseTypeDefinition, + caseDetailsBefore.getState()).getStateCategory(); + + if (hasClosedForPaymentCategory(stateCategory)) { + DateCaseClosedEntity dateCaseClosedEntity = new DateCaseClosedEntity(); + dateCaseClosedEntity.setCcdCaseNumber(caseDetails.getReference()); + dateCaseClosedEntity.setState(caseDetails.getState()); + dateCaseClosedEntity.setStateCategory(stateCategory); + dateCaseClosedEntity.setStateChangedDate(caseDetails.getLastStateModifiedDate()); + + dateCaseClosedRepository.save(dateCaseClosedEntity); + } else if (hasClosedForPaymentCategory(previousStateCategory)) { + dateCaseClosedRepository.deleteByCcdCaseNumber(caseDetails.getReference()); + } + } + + private boolean hasClosedForPaymentCategory(String stateCategory) { + return stateCategory != null + && Arrays.stream(stateCategory.split(",")) + .map(String::trim) + .anyMatch(CLOSED_FOR_PAYMENT::equalsIgnoreCase); + } + private void updateCaseState(CaseDetails caseDetails, CaseEventDefinition caseEventDefinition) { final String postState = casePostStateService.evaluateCaseState(caseEventDefinition, caseDetails); if (shouldChangeState(postState)) { diff --git a/src/main/java/uk/gov/hmcts/ccd/domain/service/getcase/CaseNotFoundException.java b/src/main/java/uk/gov/hmcts/ccd/domain/service/getcase/CaseNotFoundException.java index 97e83efc7a..7382fc8cfb 100644 --- a/src/main/java/uk/gov/hmcts/ccd/domain/service/getcase/CaseNotFoundException.java +++ b/src/main/java/uk/gov/hmcts/ccd/domain/service/getcase/CaseNotFoundException.java @@ -14,4 +14,8 @@ public CaseNotFoundException(String jurisdictionId, String caseTypeId, String ca public CaseNotFoundException(String caseReference) { super(String.format("No case found for reference: %s", caseReference)); } + + public CaseNotFoundException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/uk/gov/hmcts/ccd/v2/external/controller/TestingSupportController.java b/src/main/java/uk/gov/hmcts/ccd/v2/external/controller/TestingSupportController.java index 592bbe6a2e..1fd65ce738 100644 --- a/src/main/java/uk/gov/hmcts/ccd/v2/external/controller/TestingSupportController.java +++ b/src/main/java/uk/gov/hmcts/ccd/v2/external/controller/TestingSupportController.java @@ -1,27 +1,38 @@ package uk.gov.hmcts.ccd.v2.external.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.inject.Inject; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.hibernate.Session; import org.hibernate.SessionFactory; +import org.hibernate.type.BasicTypeReference; import org.hibernate.type.StandardBasicTypes; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.responses.ApiResponse; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.domain.model.caselinking.CaseLinksResource; import uk.gov.hmcts.ccd.domain.service.caselinking.CaseLinkService; import uk.gov.hmcts.ccd.v2.V2; -import jakarta.inject.Inject; import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; @@ -35,11 +46,15 @@ public class TestingSupportController { private final SessionFactory sessionFactory; + private final DateCaseClosedRepository dateCaseClosedRepository; + @Inject public TestingSupportController(CaseLinkService caseLinkService, - SessionFactory sessionFactory) { + SessionFactory sessionFactory, + DateCaseClosedRepository dateCaseClosedRepository) { this.caseLinkService = caseLinkService; this.sessionFactory = sessionFactory; + this.dateCaseClosedRepository = dateCaseClosedRepository; } @GetMapping( @@ -72,26 +87,83 @@ public void dataCaseTypeIdDelete( executeSql( session, "DELETE FROM case_link WHERE case_type_id IN (:caseTypeReferences)", - caseTypesWithChangeIds); + "caseTypeReferences", + caseTypesWithChangeIds, + StandardBasicTypes.STRING); executeSql( session, "DELETE FROM case_event WHERE case_type_id IN (:caseTypeReferences)", - caseTypesWithChangeIds); + "caseTypeReferences", + caseTypesWithChangeIds, + StandardBasicTypes.STRING); + executeSql( + session, + "DELETE FROM date_case_closed WHERE ccd_case_number IN (" + + "SELECT reference FROM case_data WHERE case_type_id IN (:caseTypeReferences)" + + ")", + "caseTypeReferences", + caseTypesWithChangeIds, + StandardBasicTypes.STRING); executeSql( session, "DELETE FROM case_data WHERE case_type_id IN (:caseTypeReferences)", - caseTypesWithChangeIds); + "caseTypeReferences", + caseTypesWithChangeIds, + StandardBasicTypes.STRING); session.close(); log.info("Deleted records for changeId {} and caseTypeIds {} ", changeId, caseTypeIds); } - private void executeSql(Session session, String sql, List ids) { + @PostMapping(value = "/date-case-closed", consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Create a DATE_CASE_CLOSED record for functional tests") + @ApiResponse(responseCode = "201", description = "Success") + public ResponseEntity dateCaseClosedPost(@RequestBody DateCaseClosedRequest request) { + DateCaseClosedEntity entity = new DateCaseClosedEntity(); + entity.setCcdCaseNumber(request.getCcdCaseNumber()); + entity.setState(request.getState()); + entity.setStateCategory(request.getStateCategory()); + entity.setStateChangedDate(request.getStateChangedDate()); + + return ResponseEntity.status(HttpStatus.CREATED).body(dateCaseClosedRepository.save(entity)); + } + + @DeleteMapping(value = "/date-case-closed") + @Operation(summary = "Delete DATE_CASE_CLOSED records for functional tests") + @ApiResponse(responseCode = "204", description = "Success") + public ResponseEntity dateCaseClosedDelete( + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) + @Parameter(name = "State Changed Date", required = true) LocalDate stateChangedDate) { + Session session = sessionFactory.openSession(); + + session.beginTransaction(); + session.createNativeQuery( + "DELETE FROM date_case_closed " + + "WHERE state_changed_date < :stateChangedDateEnd") + .setParameter("stateChangedDateEnd", Timestamp.valueOf(stateChangedDate.plusDays(1).atStartOfDay())) + .executeUpdate(); + session.getTransaction().commit(); + + session.close(); + + return ResponseEntity.noContent().build(); + } + + private void executeSql(Session session, String sql, String parameterName, List ids, + BasicTypeReference type) { session.beginTransaction(); session.createNativeQuery(sql) - .setParameterList("caseTypeReferences", ids, StandardBasicTypes.STRING) + .setParameterList(parameterName, ids, type) .executeUpdate(); session.getTransaction().commit(); } + @Data + public static class DateCaseClosedRequest { + private Long ccdCaseNumber; + private String state; + private String stateCategory; + private LocalDateTime stateChangedDate; + } + } diff --git a/src/main/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchController.java b/src/main/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchController.java index d25c9b623d..c2a47d1d4c 100644 --- a/src/main/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchController.java +++ b/src/main/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchController.java @@ -1,7 +1,6 @@ package uk.gov.hmcts.ccd.v2.internal.controller; import com.google.common.base.Strings; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -12,8 +11,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -22,19 +24,25 @@ import uk.gov.hmcts.ccd.auditlog.AuditOperationType; import uk.gov.hmcts.ccd.auditlog.LogAudit; import uk.gov.hmcts.ccd.domain.model.search.CaseSearchResult; +import uk.gov.hmcts.ccd.domain.model.search.DateCaseClosedResponse; import uk.gov.hmcts.ccd.domain.model.search.elasticsearch.CaseSearchResultView; import uk.gov.hmcts.ccd.domain.model.search.elasticsearch.ElasticsearchRequest; import uk.gov.hmcts.ccd.domain.model.search.elasticsearch.SearchResultViewItem; +import uk.gov.hmcts.ccd.domain.service.caseclosed.ClosedCaseSearchOperation; import uk.gov.hmcts.ccd.domain.service.search.CaseSearchResultViewGenerator; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.CaseSearchOperation; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.CrossCaseTypeSearchRequest; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.ElasticsearchQueryHelper; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.ElasticsearchSortService; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.security.AuthorisedCaseSearchOperation; +import uk.gov.hmcts.ccd.endpoint.exceptions.BadSearchRequest; +import uk.gov.hmcts.ccd.v2.V2; import uk.gov.hmcts.ccd.v2.internal.resource.CaseSearchResultViewResource; import java.time.Duration; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneOffset; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -55,6 +63,7 @@ public class UICaseSearchController { private final ElasticsearchQueryHelper elasticsearchQueryHelper; private final CaseSearchResultViewGenerator caseSearchResultViewGenerator; private final ElasticsearchSortService elasticsearchSortService; + private final ClosedCaseSearchOperation closedCaseSearchOperation; @Autowired @SuppressWarnings("checkstyle:LineLength") //don't want to break message @@ -63,11 +72,13 @@ public UICaseSearchController( @Qualifier(AuthorisedCaseSearchOperation.QUALIFIER) CaseSearchOperation caseSearchOperation, ElasticsearchQueryHelper elasticsearchQueryHelper, CaseSearchResultViewGenerator caseSearchResultViewGenerator, - ElasticsearchSortService elasticsearchSortService) { + ElasticsearchSortService elasticsearchSortService, + @Qualifier("authorised") ClosedCaseSearchOperation closedCaseSearchOperation) { this.caseSearchOperation = caseSearchOperation; this.elasticsearchQueryHelper = elasticsearchQueryHelper; this.caseSearchResultViewGenerator = caseSearchResultViewGenerator; this.elasticsearchSortService = elasticsearchSortService; + this.closedCaseSearchOperation = closedCaseSearchOperation; } @PostMapping(path = "") @@ -170,6 +181,30 @@ public ResponseEntity searchCases( return ResponseEntity.ok(new CaseSearchResultViewResource(caseSearchResultView)); } + @GetMapping(path = "/getClosedCases/{date}") + @Operation(description = "Retrieve closed cases from date_case_closed by state changed date.") + @ApiResponse( + responseCode = "200", + description = "Success" + ) + @ApiResponse( + responseCode = "400", + description = "Date is not valid." + ) + @ApiResponse( + responseCode = "404", + description = V2.Error.CASE_DATA_NOT_FOUND + ) + public ResponseEntity getClosedCases( + @Parameter(name = "date", required = true) + @PathVariable("date") + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate closedCasesDate) { + if (closedCasesDate.isAfter(LocalDate.now(ZoneOffset.UTC))) { + throw new BadSearchRequest("Date is not valid"); + } + return ResponseEntity.ok(closedCaseSearchOperation.execute(closedCasesDate)); + } + public static String buildCaseIds(ResponseEntity response) { CaseSearchResultViewResource body = response.getBody(); return body == null ? null diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/caseclosed/AuthorisedClosedCaseSearchOperationImplTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/caseclosed/AuthorisedClosedCaseSearchOperationImplTest.java new file mode 100644 index 0000000000..87446310ac --- /dev/null +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/caseclosed/AuthorisedClosedCaseSearchOperationImplTest.java @@ -0,0 +1,108 @@ +package uk.gov.hmcts.ccd.domain.service.caseclosed; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.domain.model.definition.CaseDetails; +import uk.gov.hmcts.ccd.domain.model.search.DateCaseClosedResponse; +import uk.gov.hmcts.ccd.domain.service.getcase.GetCaseOperation; +import uk.gov.hmcts.ccd.domain.service.getcase.CaseNotFoundException; + +import java.time.LocalDate; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class AuthorisedClosedCaseSearchOperationImplTest { + + private static final LocalDate CLOSED_CASES_DATE = LocalDate.of(2026, 5, 18); + + @Mock + private ClosedCaseSearchOperation closedCaseSearchOperation; + + @Mock + private GetCaseOperation getCaseOperation; + + @InjectMocks + private AuthorisedClosedCaseSearchOperationImpl authorisedClosedCaseSearchOperation; + + @Test + void shouldReturnClosedCaseReferences() { + when(closedCaseSearchOperation.execute(CLOSED_CASES_DATE)) + .thenReturn(new DateCaseClosedResponse(List.of("1234567890123456", "2345678901234567"))); + when(getCaseOperation.execute("1234567890123456")).thenReturn(Optional.of(new CaseDetails())); + when(getCaseOperation.execute("2345678901234567")).thenReturn(Optional.of(new CaseDetails())); + + DateCaseClosedResponse response = authorisedClosedCaseSearchOperation.execute(CLOSED_CASES_DATE); + + assertAll( + () -> verify(closedCaseSearchOperation).execute(CLOSED_CASES_DATE), + () -> verify(getCaseOperation).execute("1234567890123456"), + () -> verify(getCaseOperation).execute("2345678901234567"), + () -> assertThat(response.getCaseReferences(), is(List.of("1234567890123456", "2345678901234567"))) + ); + } + + @Test + void shouldThrowCaseNotFoundExceptionWhenNoClosedCasesFound() { + when(closedCaseSearchOperation.execute(CLOSED_CASES_DATE)) + .thenReturn(new DateCaseClosedResponse(Collections.emptyList())); + + CaseNotFoundException exception = assertThrows( + CaseNotFoundException.class, + () -> authorisedClosedCaseSearchOperation.execute(CLOSED_CASES_DATE) + ); + + assertAll( + () -> verify(closedCaseSearchOperation).execute(CLOSED_CASES_DATE), + () -> verifyNoInteractions(getCaseOperation), + () -> assertThat(exception.getMessage(), is("Case data not found")) + ); + } + + @Test + void shouldReturnOnlyClosedCaseReferencesUserHasPermissionToRead() { + when(closedCaseSearchOperation.execute(CLOSED_CASES_DATE)) + .thenReturn(new DateCaseClosedResponse(List.of("1234567890123456", "2345678901234567"))); + when(getCaseOperation.execute("1234567890123456")).thenReturn(Optional.of(new CaseDetails())); + when(getCaseOperation.execute("2345678901234567")).thenReturn(Optional.empty()); + + DateCaseClosedResponse response = authorisedClosedCaseSearchOperation.execute(CLOSED_CASES_DATE); + + assertAll( + () -> verify(closedCaseSearchOperation).execute(CLOSED_CASES_DATE), + () -> verify(getCaseOperation).execute("1234567890123456"), + () -> verify(getCaseOperation).execute("2345678901234567"), + () -> assertThat(response.getCaseReferences(), is(List.of("1234567890123456"))) + ); + } + + @Test + void shouldThrowCaseNotFoundExceptionWhenUserDoesNotHaveReadPermission() { + when(closedCaseSearchOperation.execute(CLOSED_CASES_DATE)) + .thenReturn(new DateCaseClosedResponse(List.of("1234567890123456"))); + when(getCaseOperation.execute("1234567890123456")).thenReturn(Optional.empty()); + + CaseNotFoundException exception = assertThrows( + CaseNotFoundException.class, + () -> authorisedClosedCaseSearchOperation.execute(CLOSED_CASES_DATE) + ); + + assertAll( + () -> verify(closedCaseSearchOperation).execute(CLOSED_CASES_DATE), + () -> verify(getCaseOperation).execute("1234567890123456"), + () -> assertThat(exception.getMessage(), is("Case data not found")) + ); + } +} diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/caseclosed/DefaultClosedCaseSearchOperationTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/caseclosed/DefaultClosedCaseSearchOperationTest.java new file mode 100644 index 0000000000..2dfd729845 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/caseclosed/DefaultClosedCaseSearchOperationTest.java @@ -0,0 +1,91 @@ +package uk.gov.hmcts.ccd.domain.service.caseclosed; + +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.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; +import uk.gov.hmcts.ccd.domain.model.search.DateCaseClosedResponse; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class DefaultClosedCaseSearchOperationTest { + + private static final LocalDate CLOSED_CASES_DATE = LocalDate.of(2026, 5, 18); + private static final LocalDateTime NEXT_DAY_START = LocalDateTime.of(2026, 5, 19, 0, 0); + + @Mock + private DateCaseClosedRepository dateCaseClosedRepository; + + private DefaultClosedCaseSearchOperation defaultClosedCaseSearchOperation; + + @BeforeEach + void setUp() { + defaultClosedCaseSearchOperation = new DefaultClosedCaseSearchOperation(dateCaseClosedRepository); + } + + @Test + void shouldReturnClosedCaseReferences() { + DateCaseClosedEntity firstClosedCase = createDateCaseClosedEntity(1234567890123456L); + DateCaseClosedEntity secondClosedCase = createDateCaseClosedEntity(2345678901234567L); + when(dateCaseClosedRepository.findByStateChangedDateBefore(NEXT_DAY_START)) + .thenReturn(List.of(firstClosedCase, secondClosedCase)); + + DateCaseClosedResponse response = defaultClosedCaseSearchOperation.execute(CLOSED_CASES_DATE); + + assertAll( + () -> verify(dateCaseClosedRepository).findByStateChangedDateBefore(NEXT_DAY_START), + () -> assertThat(response.getCaseReferences(), is(List.of("1234567890123456", "2345678901234567"))) + ); + } + + @Test + void shouldReturnNullCaseReferencesWhenNoClosedCasesFound() { + when(dateCaseClosedRepository.findByStateChangedDateBefore(NEXT_DAY_START)) + .thenReturn(Collections.emptyList()); + + DateCaseClosedResponse response = defaultClosedCaseSearchOperation.execute(CLOSED_CASES_DATE); + + assertAll( + () -> verify(dateCaseClosedRepository).findByStateChangedDateBefore(NEXT_DAY_START), + () -> assertThat(response.getCaseReferences(), is(nullValue())) + ); + } + + @Test + void shouldUseNextDayStartAsCutoffForCurrentDate() { + ArgumentCaptor stateChangedDateCaptor = ArgumentCaptor.forClass(LocalDateTime.class); + DateCaseClosedEntity closedCase = createDateCaseClosedEntity(1234567890123456L); + LocalDate currentDate = LocalDate.now(); + when(dateCaseClosedRepository.findByStateChangedDateBefore(currentDate.plusDays(1).atStartOfDay())) + .thenReturn(List.of(closedCase)); + + DateCaseClosedResponse response = defaultClosedCaseSearchOperation.execute(currentDate); + + assertAll( + () -> verify(dateCaseClosedRepository).findByStateChangedDateBefore(stateChangedDateCaptor.capture()), + () -> assertThat(stateChangedDateCaptor.getValue(), is(currentDate.plusDays(1).atStartOfDay())), + () -> assertThat(response.getCaseReferences(), is(List.of("1234567890123456"))) + ); + } + + private DateCaseClosedEntity createDateCaseClosedEntity(Long caseReference) { + DateCaseClosedEntity dateCaseClosedEntity = new DateCaseClosedEntity(); + dateCaseClosedEntity.setCcdCaseNumber(caseReference); + return dateCaseClosedEntity; + } +} diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionCaseAccessGroupTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionCaseAccessGroupTest.java index 58ff6ca896..9f54f0872a 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionCaseAccessGroupTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionCaseAccessGroupTest.java @@ -17,6 +17,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import uk.gov.hmcts.ccd.ApplicationParams; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.config.JacksonUtils; import uk.gov.hmcts.ccd.data.casedetails.CaseAuditEventRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseDetailsRepository; @@ -102,6 +103,8 @@ class SubmitCaseTransactionCaseAccessGroupTest { @Mock private CaseAuditEventRepository caseAuditEventRepository; @Mock + private DateCaseClosedRepository dateCaseClosedRepository; + @Mock private CaseTypeService caseTypeService; @Mock private CallbackInvoker callbackInvoker; @@ -183,7 +186,8 @@ void setup() throws IOException { decentralisedSubmitCaseTransaction, resolver, casePointerRepository, - synchronisedCaseProcessor + synchronisedCaseProcessor, + dateCaseClosedRepository ); idamUser = buildIdamUser(); diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionTest.java index 85f4c5b020..5f6229140f 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionTest.java @@ -11,6 +11,8 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseAuditEventRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseDetailsRepository; import uk.gov.hmcts.ccd.decentralised.dto.DecentralisedCaseDetails; @@ -47,6 +49,7 @@ import java.io.IOException; import java.io.InputStream; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @@ -110,6 +113,8 @@ class SubmitCaseTransactionTest { @Mock private CaseAuditEventRepository caseAuditEventRepository; @Mock + private DateCaseClosedRepository dateCaseClosedRepository; + @Mock private CaseTypeService caseTypeService; @Mock private CallbackInvoker callbackInvoker; @@ -186,7 +191,8 @@ void setup() { decentralisedSubmitCaseTransaction, resolver, casePointerRepository, - synchronisedCaseProcessor + synchronisedCaseProcessor, + dateCaseClosedRepository ); idamUser = buildIdamUser(); @@ -475,6 +481,67 @@ void shouldGrantAccessAndAttachDocumentsForDecentralisedCases() { eq("12345"), eq("TestType"), eq("TestJurisdiction"), anyList()); } + @Test + @DisplayName("should save date case closed when state category contains closed for payment") + void shouldSaveDateCaseClosedWhenStateCategoryContainsClosedForPayment() { + LocalDateTime stateChangedDate = LocalDateTime.of(2026, 6, 16, 10, 30); + state.setStateCategory("CLOSED FOR PAYMENT, End"); + doReturn(stateChangedDate).when(savedCaseDetails).getLastStateModifiedDate(); + + ArgumentCaptor dateCaseClosedEntityCaptor = + ArgumentCaptor.forClass(DateCaseClosedEntity.class); + + submitCaseTransaction.submitCase(event, + caseTypeDefinition, + idamUser, + caseEventDefinition, + this.caseDetails, + IGNORE_WARNING, + null); + + verify(dateCaseClosedRepository).save(dateCaseClosedEntityCaptor.capture()); + DateCaseClosedEntity dateCaseClosedEntity = dateCaseClosedEntityCaptor.getValue(); + + assertAll( + () -> assertThat(dateCaseClosedEntity.getCcdCaseNumber(), is(1234567890L)), + () -> assertThat(dateCaseClosedEntity.getState(), is(STATE_ID)), + () -> assertThat(dateCaseClosedEntity.getStateCategory(), is("CLOSED FOR PAYMENT, End")), + () -> assertThat(dateCaseClosedEntity.getStateChangedDate(), is(stateChangedDate)) + ); + } + + @Test + @DisplayName("should not save date case closed when state category does not contain closed for payment") + void shouldNotSaveDateCaseClosedWhenStateCategoryDoesNotContainsClosedForPayment() { + state.setStateCategory("End"); + + submitCaseTransaction.submitCase(event, + caseTypeDefinition, + idamUser, + caseEventDefinition, + this.caseDetails, + IGNORE_WARNING, + null); + + verify(dateCaseClosedRepository, never()).save(any(DateCaseClosedEntity.class)); + } + + @Test + @DisplayName("should not save date case closed when state category only partially matches closed for payment") + void shouldNotSaveDateCaseClosedWhenStateCategoryOnlyPartiallyMatchesClosedForPayment() { + state.setStateCategory("CLOSED FOR PAYMENT EXAMPLE, End"); + + submitCaseTransaction.submitCase(event, + caseTypeDefinition, + idamUser, + caseEventDefinition, + this.caseDetails, + IGNORE_WARNING, + null); + + verify(dateCaseClosedRepository, never()).save(any(DateCaseClosedEntity.class)); + } + private void assertAuditEventProxyByUser(final AuditEvent auditEvent) { assertAll("Audit event", () -> assertThat(auditEvent.getCaseDataId(), is(savedCaseDetails.getId())), diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventServiceTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventServiceTest.java index 9b2e079246..61156eb7dc 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventServiceTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/createevent/CreateCaseEventServiceTest.java @@ -12,11 +12,14 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import uk.gov.hmcts.ccd.ApplicationParams; import uk.gov.hmcts.ccd.TestFixtures; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseAuditEventRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseDetailsRepository; import uk.gov.hmcts.ccd.data.casedetails.DefaultCaseDetailsRepository; @@ -94,6 +97,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -188,6 +192,8 @@ class CreateCaseEventServiceTest extends TestFixtures { private ConditionalFieldRestorer conditionalFieldRestorer; @Mock private CaseAccessService caseAccessService; + @Mock + private DateCaseClosedRepository dateCaseClosedRepository; @Spy private CaseDocumentTimestampService caseDocumentTimestampService = @@ -241,6 +247,8 @@ void setUp() throws Exception { caseDetails.setLastStateModifiedDate(LAST_MODIFIED); caseDetailsBefore = caseDetails.shallowClone(); + CaseStateDefinition preState = new CaseStateDefinition(); + preState.setId(PRE_STATE_ID); CaseStateDefinition postState = new CaseStateDefinition(); postState.setId(POST_STATE); IdamUser user = new IdamUser(); @@ -260,6 +268,7 @@ void setUp() throws Exception { doReturn(caseDetails).when(caseDetailsRepository).set(caseDetails); doReturn(postState).when(caseTypeService).findState(caseTypeDefinition, POST_STATE); + doReturn(preState).when(caseTypeService).findState(caseTypeDefinition, PRE_STATE_ID); doReturn(user).when(userRepository).getUser(); doReturn(user).when(userRepository).getUserByUserId(anyString()); doReturn(user).when(userRepository).getUser(anyString()); @@ -370,6 +379,119 @@ void shouldNotUpdateLastStateModifiedWhenStateTransitionNotOccurred() { assertThat(caseEventResult.getSavedCaseDetails().getLastStateModifiedDate()).isEqualTo(LAST_MODIFIED); } + @Test + @DisplayName("should save date case closed when case moves into closed for payment state category") + void shouldSaveDateCaseClosedWhenCaseMovesIntoClosedForPaymentStateCategory() throws Exception { + // Case object after save + CaseDetails savedCaseDetails = caseDetails.shallowClone(); + savedCaseDetails.setState(POST_STATE); + savedCaseDetails.setReference(Long.parseLong(CASE_REFERENCE)); + savedCaseDetails.setLastStateModifiedDate(lastModifiedTimestamp); + + // Case object during update before save + CaseDetails updatedCaseDetails = caseDetails.shallowClone(); + + CaseStateDefinition previousState = state(PRE_STATE_ID, "End"); + CaseStateDefinition newState = state(POST_STATE, "CLOSED FOR PAYMENT,End"); + + doReturn(caseDetailsBefore, updatedCaseDetails).when(caseService).clone(caseDetails); + doReturn(previousState).when(caseTypeService).findState(caseTypeDefinition, PRE_STATE_ID); + doReturn(newState).when(caseTypeService).findState(caseTypeDefinition, POST_STATE); + doReturn(savedCaseDetails).when(caseDetailsRepository).set(any(CaseDetails.class)); + + underTest.createCaseEvent(CASE_REFERENCE, caseDataContent); + ArgumentCaptor dateCaseClosedEntityCaptor = + ArgumentCaptor.forClass(DateCaseClosedEntity.class); + + verify(dateCaseClosedRepository).save(dateCaseClosedEntityCaptor.capture()); + verify(dateCaseClosedRepository, never()).deleteByCcdCaseNumber(anyLong()); + DateCaseClosedEntity dateCaseClosedEntity = dateCaseClosedEntityCaptor.getValue(); + assertAll( + () -> assertThat(dateCaseClosedEntity.getCcdCaseNumber()).isEqualTo(Long.parseLong(CASE_REFERENCE)), + () -> assertThat(dateCaseClosedEntity.getState()).isEqualTo(POST_STATE), + () -> assertThat(dateCaseClosedEntity.getStateCategory()).isEqualTo("CLOSED FOR PAYMENT,End"), + () -> assertThat(dateCaseClosedEntity.getStateChangedDate()).isEqualTo(lastModifiedTimestamp) + ); + } + + @Test + @DisplayName("should remove date case closed when case moves out of closed for payment state category") + void shouldRemoveDateCaseClosedWhenCaseMovesOutOfClosedForPaymentStateCategory() throws Exception { + String closedForPaymentState = "ClosedForPayment"; + caseDetails.setState(closedForPaymentState); + caseDetailsBefore.setState(closedForPaymentState); + + CaseDetails updatedCaseDetails = caseDetails.shallowClone(); + CaseDetails savedCaseDetails = caseDetails.shallowClone(); + savedCaseDetails.setState(POST_STATE); + savedCaseDetails.setReference(Long.parseLong(CASE_REFERENCE)); + + CaseStateDefinition previousState = state(closedForPaymentState, "CLOSED FOR PAYMENT,End"); + CaseStateDefinition newState = state(POST_STATE, "End"); + + doReturn(caseDetailsBefore, updatedCaseDetails).when(caseService).clone(caseDetails); + doReturn(true).when(eventTriggerService).isPreStateValid(closedForPaymentState, caseEventDefinition); + doReturn(previousState).when(caseTypeService).findState(caseTypeDefinition, closedForPaymentState); + doReturn(newState).when(caseTypeService).findState(caseTypeDefinition, POST_STATE); + doReturn(savedCaseDetails).when(caseDetailsRepository).set(any(CaseDetails.class)); + + underTest.createCaseEvent(CASE_REFERENCE, caseDataContent); + + verify(dateCaseClosedRepository).deleteByCcdCaseNumber(Long.parseLong(CASE_REFERENCE)); + } + + @Test + @DisplayName("should use about to submit state override when updating date case closed") + void shouldUseAboutToSubmitStateOverrideWhenUpdatingDateCaseClosed() throws Exception { + String callbackState = "CallbackClosedForPayment"; + + CaseStateDefinition callbackStateDefinition = new CaseStateDefinition(); + callbackStateDefinition.setId(callbackState); + callbackStateDefinition.setStateCategory("CLOSED FOR PAYMENT,End"); + // Case object passed into the about-to-submit callback + CaseDetails updatedCaseDetails = caseDetails.shallowClone(); + + // createCaseEvent() clones case for before snapshot, then clones again for update + doReturn(caseDetailsBefore, updatedCaseDetails).when(caseService).clone(caseDetails); + doAnswer(invocation -> invocation.getArgument(0)) + .when(caseDocumentService).stripDocumentHashes(any(CaseDetails.class)); + doAnswer(invocation -> { + CaseDetails callbackCaseDetails = invocation.getArgument(2); + callbackCaseDetails.setState(callbackState); + aboutToSubmitCallbackResponse.setState(Optional.of(callbackState)); + return aboutToSubmitCallbackResponse; + }).when(callbackInvoker).invokeAboutToSubmitCallback(any(), + any(), + any(), + any(), + any()); + // Return the callback-mutated case as the saved final case + doAnswer(invocation -> invocation.getArgument(0)).when(caseDetailsRepository).set(any(CaseDetails.class)); + CaseStateDefinition previousState = state(PRE_STATE_ID, "End"); + CaseStateDefinition newState = state(POST_STATE, "End"); + doReturn(previousState).when(caseTypeService).findState(caseTypeDefinition, PRE_STATE_ID); + doReturn(newState).when(caseTypeService).findState(caseTypeDefinition, POST_STATE); + doReturn(callbackStateDefinition).when(caseTypeService).findState(caseTypeDefinition, callbackState); + + underTest.createCaseEvent(CASE_REFERENCE, caseDataContent); + ArgumentCaptor dateCaseClosedEntityCaptor = + ArgumentCaptor.forClass(DateCaseClosedEntity.class); + + InOrder inOrder = inOrder(casePostStateService, callbackInvoker); + inOrder.verify(casePostStateService).evaluateCaseState(caseEventDefinition, updatedCaseDetails); + inOrder.verify(callbackInvoker).invokeAboutToSubmitCallback(caseEventDefinition, + caseDetailsBefore, + updatedCaseDetails, + caseTypeDefinition, + IGNORE_WARNING); + verify(dateCaseClosedRepository).save(dateCaseClosedEntityCaptor.capture()); + DateCaseClosedEntity dateCaseClosedEntity = dateCaseClosedEntityCaptor.getValue(); + assertAll( + () -> assertThat(dateCaseClosedEntity.getState()).isEqualTo(callbackState), + () -> assertThat(dateCaseClosedEntity.getStateCategory()).isEqualTo("CLOSED FOR PAYMENT,End") + ); + } + @Test @DisplayName("should invoke about to submit callback") void shouldInvokeAboutToSubmitCallback() { @@ -746,4 +868,11 @@ private ObjectNode createBasicDoc() { return node; } + private CaseStateDefinition state(String id, String stateCategory) { + CaseStateDefinition state = new CaseStateDefinition(); + state.setId(id); + state.setStateCategory(stateCategory); + return state; + } + } diff --git a/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/TestingSupportControllerTest.java b/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/TestingSupportControllerTest.java index 3de8e1df7d..64361f2cdb 100644 --- a/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/TestingSupportControllerTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/v2/external/controller/TestingSupportControllerTest.java @@ -11,6 +11,9 @@ import static org.mockito.Mockito.when; import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import java.util.Objects; import org.hibernate.Session; @@ -23,7 +26,10 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedEntity; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.domain.model.caselinking.CaseLink; import uk.gov.hmcts.ccd.domain.model.caselinking.CaseLinksResource; import uk.gov.hmcts.ccd.domain.service.caselinking.CaseLinkService; @@ -41,6 +47,8 @@ class TestingSupportControllerTest { @Mock private CaseLinkService caseLinkService; + @Mock + private DateCaseClosedRepository dateCaseClosedRepository; @InjectMocks private TestingSupportController testingSupportController; @@ -72,7 +80,53 @@ void shouldDeleteCaseTypeIds() { when(session.getTransaction()) .thenReturn(transaction); testingSupportController.dataCaseTypeIdDelete(BigInteger.ONE, "Benefit"); - verify(session, times(3)) + verify(session, times(4)) .createNativeQuery(anyString()); } + + @Test + void shouldCreateDateCaseClosedRecord() { + final LocalDateTime stateChangedDate = LocalDateTime.of(2025, 5, 8, 12, 30); + TestingSupportController.DateCaseClosedRequest request = + new TestingSupportController.DateCaseClosedRequest(); + request.setCcdCaseNumber(1234567890123456L); + request.setState("Closed"); + request.setStateCategory("Closed"); + request.setStateChangedDate(stateChangedDate); + when(dateCaseClosedRepository.save(any(DateCaseClosedEntity.class))).thenAnswer(invocation -> { + DateCaseClosedEntity entity = invocation.getArgument(0); + entity.setId(1L); + return entity; + }); + + ResponseEntity response = testingSupportController.dateCaseClosedPost(request); + + DateCaseClosedEntity responseBody = Objects.requireNonNull(response.getBody()); + assertEquals(HttpStatus.CREATED, response.getStatusCode()); + assertEquals(1234567890123456L, responseBody.getCcdCaseNumber()); + assertEquals("Closed", responseBody.getState()); + assertEquals("Closed", responseBody.getStateCategory()); + assertEquals(stateChangedDate, responseBody.getStateChangedDate()); + } + + @Test + void shouldDeleteExistingDateCaseClosedRecords() { + when(sessionFactory.openSession()) + .thenReturn(session); + when(session.createNativeQuery(anyString())) + .thenReturn(nativeQuery); + when(nativeQuery.setParameter(anyString(), any())) + .thenReturn(nativeQuery); + when(session.getTransaction()) + .thenReturn(transaction); + + ResponseEntity response = testingSupportController.dateCaseClosedDelete(LocalDate.of(2025, 1, 1)); + + assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); + verify(session).createNativeQuery( + "DELETE FROM date_case_closed " + + "WHERE state_changed_date < :stateChangedDateEnd" + ); + verify(nativeQuery).setParameter("stateChangedDateEnd", Timestamp.valueOf("2025-01-02 00:00:00")); + } } diff --git a/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java b/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java index a5997ce1df..ad21baf2ff 100644 --- a/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java @@ -11,16 +11,21 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import uk.gov.hmcts.ccd.domain.model.search.CaseSearchResult; +import uk.gov.hmcts.ccd.domain.model.search.DateCaseClosedResponse; import uk.gov.hmcts.ccd.domain.model.search.elasticsearch.CaseSearchResultView; import uk.gov.hmcts.ccd.domain.model.search.elasticsearch.ElasticsearchRequest; import uk.gov.hmcts.ccd.domain.model.search.elasticsearch.SearchResultViewItem; +import uk.gov.hmcts.ccd.domain.service.caseclosed.ClosedCaseSearchOperation; import uk.gov.hmcts.ccd.domain.service.search.CaseSearchResultViewGenerator; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.CaseSearchOperation; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.CrossCaseTypeSearchRequest; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.ElasticsearchQueryHelper; import uk.gov.hmcts.ccd.domain.service.search.elasticsearch.ElasticsearchSortService; +import uk.gov.hmcts.ccd.endpoint.exceptions.BadSearchRequest; import uk.gov.hmcts.ccd.v2.internal.resource.CaseSearchResultViewResource; +import java.time.LocalDate; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -30,11 +35,13 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static uk.gov.hmcts.ccd.domain.service.aggregated.SearchQueryOperation.WORKBASKET; @@ -55,6 +62,9 @@ class UICaseSearchControllerTest { @Mock private ElasticsearchSortService elasticsearchSortService; + @Mock + private ClosedCaseSearchOperation closedCaseSearchOperation; + @InjectMocks private UICaseSearchController controller; @@ -123,6 +133,51 @@ void shouldBuildCaseIdListWithCappedLimit() { ); } + @Test + void shouldGetClosedCases() { + LocalDate closedCasesDate = LocalDate.now(ZoneOffset.UTC).minusDays(1); + DateCaseClosedResponse dateCaseClosedResponse = + new DateCaseClosedResponse(List.of("1234567890123456", "2345678901234567")); + when(closedCaseSearchOperation.execute(closedCasesDate)).thenReturn(dateCaseClosedResponse); + + ResponseEntity response = controller.getClosedCases(closedCasesDate); + + verify(closedCaseSearchOperation).execute(closedCasesDate); + assertAll( + () -> assertThat(response.getStatusCode(), is(HttpStatus.OK)), + () -> assertThat(response.getBody(), is(dateCaseClosedResponse)) + ); + } + + @Test + void shouldGetClosedCasesForCurrentDate() { + LocalDate closedCasesDate = LocalDate.now(ZoneOffset.UTC); + DateCaseClosedResponse dateCaseClosedResponse = + new DateCaseClosedResponse(List.of("1234567890123456")); + when(closedCaseSearchOperation.execute(closedCasesDate)).thenReturn(dateCaseClosedResponse); + + ResponseEntity response = controller.getClosedCases(closedCasesDate); + + verify(closedCaseSearchOperation).execute(closedCasesDate); + assertAll( + () -> assertThat(response.getStatusCode(), is(HttpStatus.OK)), + () -> assertThat(response.getBody(), is(dateCaseClosedResponse)) + ); + } + + @Test + void shouldRejectClosedCasesDateThatIsAfterCurrentDate() { + LocalDate closedCasesDate = LocalDate.now(ZoneOffset.UTC).plusDays(1); + + BadSearchRequest exception = assertThrows( + BadSearchRequest.class, + () -> controller.getClosedCases(closedCasesDate) + ); + + verify(closedCaseSearchOperation, never()).execute(any()); + assertThat(exception.getMessage(), is("Date is not valid")); + } + private List searchResultViewItems(int numberOfEntries) { List items = new ArrayList<>(); IntStream.range(1, numberOfEntries + 1).forEach(idx -> From 547eddfd14a851cf04f186a25e89fef75eb19777 Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Thu, 2 Jul 2026 13:29:39 +0100 Subject: [PATCH 4/9] Update UICaseSearchController.java --- .../ccd/v2/internal/controller/UICaseSearchController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchController.java b/src/main/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchController.java index d5ccbe633d..78999db8e6 100644 --- a/src/main/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchController.java +++ b/src/main/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchController.java @@ -75,7 +75,7 @@ public UICaseSearchController( ElasticsearchQueryHelper elasticsearchQueryHelper, CaseSearchResultViewGenerator caseSearchResultViewGenerator, ElasticsearchSortService elasticsearchSortService, - @Qualifier("authorised") ClosedCaseSearchOperation closedCaseSearchOperation) { + @Qualifier("authorised") ClosedCaseSearchOperation closedCaseSearchOperation, CrossCaseTypeSearchRequestHelper crossCaseTypeSearchRequestHelper) { this.caseSearchOperation = caseSearchOperation; this.elasticsearchQueryHelper = elasticsearchQueryHelper; From da1312518491afe2d09b85aba4f707df12e135a4 Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:07:44 +0100 Subject: [PATCH 5/9] Fix build failures --- .../createcase/SubmitCaseTransactionNewCaseTest.java | 7 ++++++- .../internal/controller/UICaseSearchControllerTest.java | 8 +++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionNewCaseTest.java b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionNewCaseTest.java index e63bb6e9bf..4365b9b0d4 100644 --- a/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionNewCaseTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/domain/service/createcase/SubmitCaseTransactionNewCaseTest.java @@ -18,6 +18,7 @@ import org.mockito.MockitoAnnotations; import uk.gov.hmcts.ccd.ApplicationParams; import uk.gov.hmcts.ccd.config.JacksonUtils; +import uk.gov.hmcts.ccd.data.caseclosed.DateCaseClosedRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseAuditEventRepository; import uk.gov.hmcts.ccd.data.casedetails.CaseDetailsRepository; import uk.gov.hmcts.ccd.data.casedetails.SecurityClassification; @@ -148,6 +149,9 @@ class SubmitCaseTransactionNewCaseTest { @Mock private SynchronisedCaseProcessor synchronisedCaseProcessor; + @Mock + private DateCaseClosedRepository dateCaseClosedRepository; + @InjectMocks private SubmitCaseTransaction submitCaseTransaction; private Event event; @@ -183,7 +187,8 @@ void setup() throws IOException { decentralisedSubmitCaseTransaction, resolver, casePointerRepository, - synchronisedCaseProcessor + synchronisedCaseProcessor, + dateCaseClosedRepository ); idamUser = buildIdamUser(); diff --git a/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java b/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java index 16d642b8d3..4d4ab3bf16 100644 --- a/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.http.HttpStatus; @@ -66,11 +67,11 @@ class UICaseSearchControllerTest { @Mock private ClosedCaseSearchOperation closedCaseSearchOperation; + private CrossCaseTypeSearchRequestHelper crossCaseTypeSearchRequestHelper; + @InjectMocks private ApplicationParams applicationParams; - private CrossCaseTypeSearchRequestHelper crossCaseTypeSearchRequestHelper; - private UICaseSearchController controller; @BeforeEach @@ -79,7 +80,8 @@ void setUp() { crossCaseTypeSearchRequestHelper = new CrossCaseTypeSearchRequestHelper(applicationParams); controller = new UICaseSearchController(caseSearchOperation, elasticsearchQueryHelper, - caseSearchResultViewGenerator,elasticsearchSortService, crossCaseTypeSearchRequestHelper); + caseSearchResultViewGenerator,elasticsearchSortService, closedCaseSearchOperation, + crossCaseTypeSearchRequestHelper); } @Test From 018f284c40883faf54c363adb02cadcba48a281f Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:34:57 +0100 Subject: [PATCH 6/9] Update UICaseSearchControllerTest.java --- .../v2/internal/controller/UICaseSearchControllerTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java b/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java index 4d4ab3bf16..f7ea880c63 100644 --- a/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java +++ b/src/test/java/uk/gov/hmcts/ccd/v2/internal/controller/UICaseSearchControllerTest.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.http.HttpStatus; @@ -67,11 +66,11 @@ class UICaseSearchControllerTest { @Mock private ClosedCaseSearchOperation closedCaseSearchOperation; - private CrossCaseTypeSearchRequestHelper crossCaseTypeSearchRequestHelper; - - @InjectMocks + @Mock private ApplicationParams applicationParams; + private CrossCaseTypeSearchRequestHelper crossCaseTypeSearchRequestHelper; + private UICaseSearchController controller; @BeforeEach From fbc632488ff0ffc2ee78b26ebd10744f3fb2d93d Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:39:40 +0100 Subject: [PATCH 7/9] Update test-definitions --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d62f5fd449..a1ba0c9f74 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,7 @@ ext { testContainersVersion = '2.0.5' cucumber = '7.34.3' beftaFwVersion = '9.2.6' - ccdTestDefinitionVersion = '7.26.0-SNAPSHOT-CME-986_1' + ccdTestDefinitionVersion = '7.26.0-SNAPSHOT-CME-986_2' limits = [ 'instruction': 90, 'branch' : 85, From a1a7848d222214ca8c5cd7e577312a4647579691 Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:59:00 +0100 Subject: [PATCH 8/9] update test-definitions --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a1ba0c9f74..a23423210a 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,7 @@ ext { testContainersVersion = '2.0.5' cucumber = '7.34.3' beftaFwVersion = '9.2.6' - ccdTestDefinitionVersion = '7.26.0-SNAPSHOT-CME-986_2' + ccdTestDefinitionVersion = '7.26.0-rc1' limits = [ 'instruction': 90, 'branch' : 85, From 19dd1dd7904676f57be010a1cc60ac0b0a28f342 Mon Sep 17 00:00:00 2001 From: Sammi Chong <133241942+SammiChong@users.noreply.github.com> Date: Fri, 3 Jul 2026 12:21:50 +0100 Subject: [PATCH 9/9] Add "callback_error_message" --- .../F-944_Create_Case_Base.td.json | 1 + .../S-944.1_GetToken_CaseCreate.td.json | 1 + .../F-945_Update_Case_Base.td.json | 1 + 3 files changed, 3 insertions(+) diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Create_Case_Base.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Create_Case_Base.td.json index 2cc005bd40..c1042feade 100644 --- a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Create_Case_Base.td.json +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/F-944_Create_Case_Base.td.json @@ -59,6 +59,7 @@ "after_submit_callback_response": null, "callback_response_status_code": null, "callback_response_status": null, + "callback_error_message": null, "delete_draft_response_status_code": null, "delete_draft_response_status": null } diff --git a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_GetToken_CaseCreate.td.json b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_GetToken_CaseCreate.td.json index c060780c73..49452815de 100644 --- a/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_GetToken_CaseCreate.td.json +++ b/src/aat/resources/features/F-944 - Get Closed for Payment Cases After Creation/S-944.1_GetToken_CaseCreate.td.json @@ -53,6 +53,7 @@ "after_submit_callback_response": null, "callback_response_status_code": null, "callback_response_status": null, + "callback_error_message": null, "delete_draft_response_status_code": null, "delete_draft_response_status": null }, diff --git a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_Update_Case_Base.td.json b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_Update_Case_Base.td.json index 0398d06990..b9a1f30bc6 100644 --- a/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_Update_Case_Base.td.json +++ b/src/aat/resources/features/F-945: Get Closed For Payment Cases After Update/F-945_Update_Case_Base.td.json @@ -63,6 +63,7 @@ "after_submit_callback_response": null, "callback_response_status_code": null, "callback_response_status": null, + "callback_error_message": null, "delete_draft_response_status_code": null, "delete_draft_response_status": null }