diff --git a/build.gradle b/build.gradle index 67ab987bb7..d66f8bf0dc 100644 --- a/build.gradle +++ b/build.gradle @@ -338,7 +338,9 @@ sonarqube { "**/model/**," + "**/uk/gov/hmcts/reform/pcs/testingsupport/**/*.java" property "sonar.cpd.exclusions", "**/uk/gov/hmcts/reform/pcs/ccd/domain/enforcetheorder/warrant/StatementOfTruthDetails.java," + - "**/uk/gov/hmcts/reform/pcs/ccd/domain/genapp/*.java," + "**/uk/gov/hmcts/reform/pcs/ccd/domain/genapp/*.java," + + "**/uk/gov/hmcts/reform/pcs/ccd/domain/documentamend/DocumentAmendDetails.java," + + "**/uk/gov/hmcts/reform/pcs/ccd/domain/documentremoval/DocumentRemovalDetails.java," } } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/ManageDocumentStates.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/ManageDocumentStates.java new file mode 100644 index 0000000000..55a77ea180 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/accesscontrol/ManageDocumentStates.java @@ -0,0 +1,22 @@ +package uk.gov.hmcts.reform.pcs.ccd.accesscontrol; + +import uk.gov.hmcts.reform.pcs.ccd.domain.State; + +public final class ManageDocumentStates { + + public static final State[] MANAGE_DOCUMENT_STATES = { + State.PENDING_CASE_ISSUED, + State.CASE_ISSUED, + State.CASE_PROGRESSION, + State.CASE_STAYED, + State.BREATHING_SPACE, + State.JUDICIAL_REFERRAL, + State.HEARING_READINESS, + State.PREPARE_FOR_HEARING_CONDUCT_HEARING, + State.DECISION_OUTCOME, + State.ALL_FINAL_ORDERS_ISSUED + }; + + private ManageDocumentStates() { + } +} \ No newline at end of file diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/DocumentSelectionDetails.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/DocumentSelectionDetails.java new file mode 100644 index 0000000000..975fdea54a --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/DocumentSelectionDetails.java @@ -0,0 +1,20 @@ +package uk.gov.hmcts.reform.pcs.ccd.domain; + +import uk.gov.hmcts.ccd.sdk.type.YesOrNo; + +public interface DocumentSelectionDetails { + + CaseFileCategory getSelectedFolder(); + + void setPropertyAddressSummary(String propertyAddressSummary); + + void setEmptyForCategory(CaseFileCategory category, YesOrNo empty); + + void setSelectedFolderId(String id); + + void setSelectedFolderLabel(String label); + + void setSelectedDocumentId(String id); + + void setSelectedDocumentFileName(String fileName); +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java index 074bb4ec46..93ed5e3a0a 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/PCSCase.java @@ -34,6 +34,8 @@ import uk.gov.hmcts.reform.pcs.ccd.domain.dashboard.DashboardData; import uk.gov.hmcts.reform.pcs.ccd.domain.documentupload.DocumentUploadDetails; import uk.gov.hmcts.reform.pcs.ccd.domain.documentamend.DocumentAmendDetails; +import uk.gov.hmcts.reform.pcs.ccd.domain.documentremoval.DocumentRemovalDetails; +import uk.gov.hmcts.reform.pcs.ccd.domain.documentupload.DocumentUploadDetails; import uk.gov.hmcts.reform.pcs.ccd.domain.enforcetheorder.EnforcementOrder; import uk.gov.hmcts.reform.pcs.ccd.domain.genapp.CitizenGenAppRequest; import uk.gov.hmcts.reform.pcs.ccd.domain.genapp.GeneralApplication; @@ -486,6 +488,36 @@ public class PCSCase { @JsonUnwrapped(prefix = "documentAmend_") private DocumentAmendDetails documentAmendDetails; + @JsonUnwrapped(prefix = "documentRemoval_") + private DocumentRemovalDetails documentRemovalDetails; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList statementsOfCaseDocuments; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList propertyDocuments; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList evidenceDocuments; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList hearingDocuments; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList ordersAndNoticeOfHearingsDocuments; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList applicationsDocuments; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList appealsDocuments; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList correspondenceDocuments; + + @CCD(searchable = false, typeOverride = DynamicRadioList) + private DynamicList uncategorisedDocuments; + @CCD( label = "Are you planning to make an application at the same time as your claim?", hint = "After you’ve submitted your claim, there will be instructions on how to make an application" diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/documentamend/DocumentAmendDetails.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/documentamend/DocumentAmendDetails.java index bd0f751490..cce2a10d95 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/documentamend/DocumentAmendDetails.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/documentamend/DocumentAmendDetails.java @@ -1,5 +1,6 @@ package uk.gov.hmcts.reform.pcs.ccd.domain.documentamend; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.PropertyNamingStrategies; @@ -10,10 +11,9 @@ import lombok.NoArgsConstructor; import uk.gov.hmcts.ccd.sdk.api.CCD; import uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory; -import uk.gov.hmcts.ccd.sdk.type.DynamicList; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.reform.pcs.ccd.domain.DocumentSelectionDetails; -import static uk.gov.hmcts.ccd.sdk.type.FieldType.DynamicRadioList; import static uk.gov.hmcts.ccd.sdk.type.FieldType.FixedList; @Builder @@ -22,7 +22,7 @@ @AllArgsConstructor @JsonInclude(Include.NON_NULL) @JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) -public class DocumentAmendDetails { +public class DocumentAmendDetails implements DocumentSelectionDetails { @CCD( label = "Which folder is the document in?", @@ -35,69 +35,6 @@ public class DocumentAmendDetails { @CCD(searchable = false) private String propertyAddressSummary; - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList statementsOfCaseDocuments; - - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList propertyDocuments; - - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList evidenceDocuments; - - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList hearingDocuments; - - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList ordersAndNoticeOfHearingsDocuments; - - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList applicationsDocuments; - - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList appealsDocuments; - - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList correspondenceDocuments; - - @CCD( - label = "Which document do you want to amend?", - searchable = false, - typeOverride = DynamicRadioList - ) - private DynamicList uncategorisedDocuments; - @CCD(searchable = false) private YesOrNo statementsOfCaseEmpty; @@ -136,4 +73,20 @@ public class DocumentAmendDetails { @CCD(searchable = false) private String selectedDocumentFileName; + + @JsonIgnore + @Override + public void setEmptyForCategory(CaseFileCategory category, YesOrNo empty) { + switch (category) { + case STATEMENTS_OF_CASE -> statementsOfCaseEmpty = empty; + case PROPERTY_DOCUMENTS -> propertyDocumentsEmpty = empty; + case EVIDENCE -> evidenceEmpty = empty; + case HEARING_DOCUMENTS -> hearingDocumentsEmpty = empty; + case ORDERS_AND_NOTICE_OF_HEARINGS -> ordersAndNoticeOfHearingsEmpty = empty; + case APPLICATIONS -> applicationsEmpty = empty; + case APPEALS -> appealsEmpty = empty; + case CORRESPONDENCE -> correspondenceEmpty = empty; + case UNCATEGORISED_DOCUMENTS -> uncategorisedDocumentsEmpty = empty; + } + } } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/documentremoval/DocumentRemovalDetails.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/documentremoval/DocumentRemovalDetails.java new file mode 100644 index 0000000000..6d0c111286 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/domain/documentremoval/DocumentRemovalDetails.java @@ -0,0 +1,135 @@ +package uk.gov.hmcts.reform.pcs.ccd.domain.documentremoval; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.ccd.sdk.api.CCD; +import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory; +import uk.gov.hmcts.reform.pcs.ccd.domain.DocumentSelectionDetails; + +import static uk.gov.hmcts.ccd.sdk.type.FieldType.FixedList; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.TextArea; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) +public class DocumentRemovalDetails implements DocumentSelectionDetails { + + @CCD( + label = "Which folder is the document in?", + searchable = false, + typeOverride = FixedList, + typeParameterOverride = "CaseFileCategory" + ) + private CaseFileCategory selectedFolder; + + @CCD(searchable = false) + private String propertyAddressSummary; + + @CCD(searchable = false) + private YesOrNo statementsOfCaseEmpty; + + @CCD(searchable = false) + private YesOrNo propertyDocumentsEmpty; + + @CCD(searchable = false) + private YesOrNo evidenceEmpty; + + @CCD(searchable = false) + private YesOrNo hearingDocumentsEmpty; + + @CCD(searchable = false) + private YesOrNo ordersAndNoticeOfHearingsEmpty; + + @CCD(searchable = false) + private YesOrNo applicationsEmpty; + + @CCD(searchable = false) + private YesOrNo appealsEmpty; + + @CCD(searchable = false) + private YesOrNo correspondenceEmpty; + + @CCD(searchable = false) + private YesOrNo uncategorisedDocumentsEmpty; + + @CCD(searchable = false) + private String selectedFolderId; + + @CCD(searchable = false) + private String selectedFolderLabel; + + @CCD(searchable = false) + private String selectedDocumentId; + + @CCD(searchable = false) + private String selectedDocumentFileName; + + @CCD(searchable = false, typeOverride = TextArea) + private String statementsOfCaseReason; + + @CCD(searchable = false, typeOverride = TextArea) + private String propertyDocumentsReason; + + @CCD(searchable = false, typeOverride = TextArea) + private String evidenceReason; + + @CCD(searchable = false, typeOverride = TextArea) + private String hearingDocumentsReason; + + @CCD(searchable = false, typeOverride = TextArea) + private String ordersAndNoticeOfHearingsReason; + + @CCD(searchable = false, typeOverride = TextArea) + private String applicationsReason; + + @CCD(searchable = false, typeOverride = TextArea) + private String appealsReason; + + @CCD(searchable = false, typeOverride = TextArea) + private String correspondenceReason; + + @CCD(searchable = false, typeOverride = TextArea) + private String uncategorisedDocumentsReason; + + @JsonIgnore + @Override + public void setEmptyForCategory(CaseFileCategory category, YesOrNo empty) { + switch (category) { + case STATEMENTS_OF_CASE -> statementsOfCaseEmpty = empty; + case PROPERTY_DOCUMENTS -> propertyDocumentsEmpty = empty; + case EVIDENCE -> evidenceEmpty = empty; + case HEARING_DOCUMENTS -> hearingDocumentsEmpty = empty; + case ORDERS_AND_NOTICE_OF_HEARINGS -> ordersAndNoticeOfHearingsEmpty = empty; + case APPLICATIONS -> applicationsEmpty = empty; + case APPEALS -> appealsEmpty = empty; + case CORRESPONDENCE -> correspondenceEmpty = empty; + case UNCATEGORISED_DOCUMENTS -> uncategorisedDocumentsEmpty = empty; + } + } + + @JsonIgnore + public String getReasonForCategory(CaseFileCategory category) { + return switch (category) { + case STATEMENTS_OF_CASE -> statementsOfCaseReason; + case PROPERTY_DOCUMENTS -> propertyDocumentsReason; + case EVIDENCE -> evidenceReason; + case HEARING_DOCUMENTS -> hearingDocumentsReason; + case ORDERS_AND_NOTICE_OF_HEARINGS -> ordersAndNoticeOfHearingsReason; + case APPLICATIONS -> applicationsReason; + case APPEALS -> appealsReason; + case CORRESPONDENCE -> correspondenceReason; + case UNCATEGORISED_DOCUMENTS -> uncategorisedDocumentsReason; + }; + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/DocumentEntity.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/DocumentEntity.java index b5d92ae2ec..6bd80e6f04 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/DocumentEntity.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/entity/DocumentEntity.java @@ -26,6 +26,7 @@ import uk.gov.hmcts.reform.pcs.ccd.entity.respondpossessionclaim.DefendantResponseEntity; import java.time.Instant; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -103,6 +104,12 @@ public class DocumentEntity { @JoinColumn(name = "counter_claim_id") private CounterClaimEntity counterClaim; + private boolean removed; + + private String removalReason; + + private LocalDateTime removedAt; + @PrePersist void prePersist() { if (submittedDate == null) { diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/AmendDocuments.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/AmendDocuments.java index d117a6e8c1..dc09d27fb7 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/AmendDocuments.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/AmendDocuments.java @@ -13,9 +13,10 @@ import uk.gov.hmcts.reform.pcs.ccd.common.PageBuilder; import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; import uk.gov.hmcts.reform.pcs.ccd.domain.State; +import uk.gov.hmcts.reform.pcs.ccd.domain.documentamend.DocumentAmendDetails; import uk.gov.hmcts.reform.pcs.ccd.page.documentamend.AmendDocumentDetailsPlaceholderPage; import uk.gov.hmcts.reform.pcs.ccd.page.documentamend.SelectDocumentPage; -import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentAmendSelectionService; +import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentSelectionService; import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.CaseworkerRoles.CASEWORKER_ROLES; import static uk.gov.hmcts.reform.pcs.ccd.event.EventId.amendDocuments; @@ -26,7 +27,7 @@ @AllArgsConstructor public class AmendDocuments implements CCDConfig { - private final DocumentAmendSelectionService documentAmendSelectionService; + private final DocumentSelectionService documentSelectionService; private final SelectDocumentPage selectDocumentPage; private final AmendDocumentDetailsPlaceholderPage amendDocumentDetailsPlaceholderPage; @@ -48,7 +49,13 @@ public void configureDecentralised(DecentralisedConfigBuilder eventPayload) { PCSCase caseData = eventPayload.caseData(); - documentAmendSelectionService.initialise(eventPayload.caseReference(), caseData); + + if (caseData.getDocumentAmendDetails() == null) { + caseData.setDocumentAmendDetails(new DocumentAmendDetails()); + } + DocumentAmendDetails details = caseData.getDocumentAmendDetails(); + + documentSelectionService.initialise(eventPayload.caseReference(), caseData, details); return caseData; } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/EventId.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/EventId.java index fae47b205b..259377f3d4 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/EventId.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/EventId.java @@ -20,5 +20,6 @@ public enum EventId { createFlags, amendFlags, claimIssuePayment, - changeCaseState + changeCaseState, + removeDocument } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/caseworker/removedocument/RemoveDocument.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/caseworker/removedocument/RemoveDocument.java new file mode 100644 index 0000000000..9bca4aed88 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/event/caseworker/removedocument/RemoveDocument.java @@ -0,0 +1,95 @@ +package uk.gov.hmcts.reform.pcs.ccd.event.caseworker.removedocument; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.DecentralisedConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.EventPayload; +import uk.gov.hmcts.ccd.sdk.api.Permission; +import uk.gov.hmcts.ccd.sdk.api.callback.SubmitResponse; +import uk.gov.hmcts.reform.pcs.ccd.ShowConditions; +import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.UserRole; +import uk.gov.hmcts.reform.pcs.ccd.common.PageBuilder; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.domain.State; +import uk.gov.hmcts.reform.pcs.ccd.domain.documentremoval.DocumentRemovalDetails; +import uk.gov.hmcts.reform.pcs.ccd.page.caseworker.removedocument.SelectDocumentToRemovePage; +import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentRemovalService; +import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentSelectionService; + +import java.util.UUID; + +import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.CaseworkerRoles.CASEWORKER_ROLES; +import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.JudicialHistoryRoles.JUDICIAL_HISTORY_ROLES; +import static uk.gov.hmcts.reform.pcs.ccd.accesscontrol.ManageDocumentStates.MANAGE_DOCUMENT_STATES; +import static uk.gov.hmcts.reform.pcs.ccd.event.EventId.removeDocument; +import static uk.gov.hmcts.reform.pcs.service.FeatureFlag.CASEWORKER_EVENTS; +import static uk.gov.hmcts.reform.pcs.service.FeatureFlag.RELEASE_1_DOT_2; + +@Component +@AllArgsConstructor +public class RemoveDocument implements CCDConfig { + + private final DocumentSelectionService documentSelectionService; + private final DocumentRemovalService documentRemovalService; + private final SelectDocumentToRemovePage selectDocumentToRemovePage; + + @Override + public void configureDecentralised(DecentralisedConfigBuilder configBuilder) { + Event.EventBuilder eventBuilder = + configBuilder + .decentralisedEvent(removeDocument.name(), this::submit, this::start) + .forStates(MANAGE_DOCUMENT_STATES) + .name("Manage documents: Remove") + .showCondition(ShowConditions.featureFlagsEnabled(RELEASE_1_DOT_2, CASEWORKER_EVENTS)) + .grant(Permission.CRU, CASEWORKER_ROLES) + .grantHistoryOnly(JUDICIAL_HISTORY_ROLES) + .showSummary() + .endButtonLabel("Submit"); + + PageBuilder pageBuilder = new PageBuilder(eventBuilder); + selectDocumentToRemovePage.addTo(pageBuilder); + } + + private PCSCase start(EventPayload eventPayload) { + PCSCase caseData = eventPayload.caseData(); + + if (caseData.getDocumentRemovalDetails() == null) { + caseData.setDocumentRemovalDetails(new DocumentRemovalDetails()); + } + DocumentRemovalDetails details = caseData.getDocumentRemovalDetails(); + + documentSelectionService.initialise(eventPayload.caseReference(), caseData, details); + return caseData; + } + + private SubmitResponse submit(EventPayload eventPayload) { + PCSCase caseData = eventPayload.caseData(); + DocumentRemovalDetails details = eventPayload.caseData().getDocumentRemovalDetails(); + String caseName = caseData.getCaseNameHmctsInternal(); + String reason = details.getReasonForCategory(details.getSelectedFolder()); + + documentRemovalService.removeDocument(UUID.fromString(details.getSelectedDocumentId()), reason); + + return SubmitResponse.builder() + .confirmationBody(buildConfirmationMarkdown(details, eventPayload.caseReference(),caseName)) + .build(); + } + + private String buildConfirmationMarkdown(DocumentRemovalDetails details, long caseReference, String caseName) { + return """ + --- +
+ %s removed
+ Case number: %s
+ Property address: %s + %s +
+ +

What happens next

+

The document will no longer appear in case file view

+ """.formatted(details.getSelectedDocumentFileName(), caseReference, + details.getPropertyAddressSummary(),caseName); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/DocumentRemovalCategoryField.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/DocumentRemovalCategoryField.java new file mode 100644 index 0000000000..8e99d70114 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/DocumentRemovalCategoryField.java @@ -0,0 +1,104 @@ +package uk.gov.hmcts.reform.pcs.ccd.page.caseworker.removedocument; + +import uk.gov.hmcts.ccd.sdk.api.TypedPropertyGetter; +import uk.gov.hmcts.ccd.sdk.type.DynamicList; +import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.domain.documentremoval.DocumentRemovalDetails; + +enum DocumentRemovalCategoryField { + STATEMENTS_OF_CASE( + CaseFileCategory.STATEMENTS_OF_CASE, + "StatementsOfCaseDocuments", + "StatementsOfCaseEmpty", + PCSCase::getStatementsOfCaseDocuments, + DocumentRemovalDetails::getStatementsOfCaseEmpty, + DocumentRemovalDetails::getStatementsOfCaseReason + ), + PROPERTY_DOCUMENTS( + CaseFileCategory.PROPERTY_DOCUMENTS, + "PropertyDocuments", + "PropertyDocumentsEmpty", + PCSCase::getPropertyDocuments, + DocumentRemovalDetails::getPropertyDocumentsEmpty, + DocumentRemovalDetails::getPropertyDocumentsReason + ), + EVIDENCE( + CaseFileCategory.EVIDENCE, + "EvidenceDocuments", + "EvidenceEmpty", + PCSCase::getEvidenceDocuments, + DocumentRemovalDetails::getEvidenceEmpty, + DocumentRemovalDetails::getEvidenceReason + ), + HEARING_DOCUMENTS( + CaseFileCategory.HEARING_DOCUMENTS, + "HearingDocuments", + "HearingDocumentsEmpty", + PCSCase::getHearingDocuments, + DocumentRemovalDetails::getHearingDocumentsEmpty, + DocumentRemovalDetails::getHearingDocumentsReason + ), + ORDERS_AND_NOTICE_OF_HEARINGS( + CaseFileCategory.ORDERS_AND_NOTICE_OF_HEARINGS, + "OrdersAndNoticeOfHearingsDocuments", + "OrdersAndNoticeOfHearingsEmpty", + PCSCase::getOrdersAndNoticeOfHearingsDocuments, + DocumentRemovalDetails::getOrdersAndNoticeOfHearingsEmpty, + DocumentRemovalDetails::getOrdersAndNoticeOfHearingsReason + ), + APPLICATIONS( + CaseFileCategory.APPLICATIONS, + "ApplicationsDocuments", + "ApplicationsEmpty", + PCSCase::getApplicationsDocuments, + DocumentRemovalDetails::getApplicationsEmpty, + DocumentRemovalDetails::getApplicationsReason + ), + APPEALS( + CaseFileCategory.APPEALS, + "AppealsDocuments", + "AppealsEmpty", + PCSCase::getAppealsDocuments, + DocumentRemovalDetails::getAppealsEmpty, + DocumentRemovalDetails::getAppealsReason + ), + CORRESPONDENCE( + CaseFileCategory.CORRESPONDENCE, + "CorrespondenceDocuments", + "CorrespondenceEmpty", + PCSCase::getCorrespondenceDocuments, + DocumentRemovalDetails::getCorrespondenceEmpty, + DocumentRemovalDetails::getCorrespondenceReason + ), + UNCATEGORISED_DOCUMENTS( + CaseFileCategory.UNCATEGORISED_DOCUMENTS, + "UncategorisedDocuments", + "UncategorisedDocumentsEmpty", + PCSCase::getUncategorisedDocuments, + DocumentRemovalDetails::getUncategorisedDocumentsEmpty, + DocumentRemovalDetails::getUncategorisedDocumentsReason + ); + + final CaseFileCategory category; + final String documentsFieldId; + final String emptyFieldId; + final TypedPropertyGetter documentsGetter; + final TypedPropertyGetter emptyGetter; + final TypedPropertyGetter reasonGetter; + + DocumentRemovalCategoryField(CaseFileCategory category, + String documentsFieldId, + String emptyFieldId, + TypedPropertyGetter documentsGetter, + TypedPropertyGetter emptyGetter, + TypedPropertyGetter reasonGetter) { + this.category = category; + this.documentsFieldId = documentsFieldId; + this.emptyFieldId = emptyFieldId; + this.documentsGetter = documentsGetter; + this.emptyGetter = emptyGetter; + this.reasonGetter = reasonGetter; + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/SelectDocumentToRemovePage.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/SelectDocumentToRemovePage.java new file mode 100644 index 0000000000..934810006d --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/SelectDocumentToRemovePage.java @@ -0,0 +1,148 @@ +package uk.gov.hmcts.reform.pcs.ccd.page.caseworker.removedocument; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.FieldCollection.FieldCollectionBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.reform.pcs.ccd.common.CcdPageConfiguration; +import uk.gov.hmcts.reform.pcs.ccd.common.PageBuilder; +import uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.domain.State; +import uk.gov.hmcts.reform.pcs.ccd.domain.documentremoval.DocumentRemovalDetails; +import uk.gov.hmcts.reform.pcs.ccd.service.TextAreaValidationService; +import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentSelectionService; +import uk.gov.hmcts.reform.pcs.ccd.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +import static uk.gov.hmcts.reform.pcs.ccd.ShowConditions.NEVER_SHOW; + +@AllArgsConstructor +@Component +public class SelectDocumentToRemovePage implements CcdPageConfiguration { + + private static final String PAGE_ID = "selectDocumentToRemove"; + private static final String FIELD_PREFIX = "documentRemoval_"; + private static final String YES = "=\"Yes\""; + private static final String NO = "=\"No\""; + private static final String DOCUMENT_QUESTION_LABEL = "Which document do you want to remove?"; + private static final String REASON_LABEL = "Why are you removing this document?"; + + private final DocumentSelectionService documentSelectionService; + private final TextAreaValidationService textAreaValidationService; + + @Override + public void addTo(PageBuilder pageBuilder) { + FieldCollectionBuilder page = pageBuilder + .page(PAGE_ID, this::midEvent) + .pageLabel("Select document") + .label(PAGE_ID + "-separator", "---"); + + page.complex(PCSCase::getDocumentRemovalDetails) + .readonly(DocumentRemovalDetails::getPropertyAddressSummary, NEVER_SHOW, true) + .mandatory(DocumentRemovalDetails::getSelectedFolder) + .label("emptyFolderDocumentError", "", NEVER_SHOW) + .label("selectedFolderEmptyErrorMessage", "", NEVER_SHOW) + .label("emptyFolderDocumentQuestion", "", NEVER_SHOW) + .done(); + + for (DocumentRemovalCategoryField categoryField : DocumentRemovalCategoryField.values()) { + addCategoryFields(page, categoryField); + } + + FieldCollectionBuilder documentRemovalDetailsFields = + page.complex(PCSCase::getDocumentRemovalDetails); + + for (DocumentRemovalCategoryField categoryField : DocumentRemovalCategoryField.values()) { + documentRemovalDetailsFields.readonly(categoryField.emptyGetter, NEVER_SHOW, true); + } + + for (DocumentRemovalCategoryField categoryField : DocumentRemovalCategoryField.values()) { + documentRemovalDetailsFields.mandatoryWithoutDefaultValue(categoryField.reasonGetter, + reasonShowCondition(categoryField), REASON_LABEL, false); + } + + documentRemovalDetailsFields + .readonly(DocumentRemovalDetails::getSelectedDocumentId, NEVER_SHOW, true) + .readonly(DocumentRemovalDetails::getSelectedDocumentFileName, NEVER_SHOW, true) + .done(); + } + + private void addCategoryFields(FieldCollectionBuilder page, + DocumentRemovalCategoryField categoryField) { + page + .label(categoryField.documentsFieldId + "EmptyFolderMessage", + emptyFolderMessage(categoryField.category), noDocumentsShowCondition(categoryField)) + .mandatoryWithoutDefaultValue(categoryField.documentsGetter, documentsShowCondition(categoryField), + DOCUMENT_QUESTION_LABEL, false); + } + + private AboutToStartOrSubmitResponse midEvent(CaseDetails details, + CaseDetails detailsBefore) { + PCSCase caseData = details.getData(); + + if (caseData.getDocumentRemovalDetails() == null) { + caseData.setDocumentRemovalDetails(new DocumentRemovalDetails()); + } + DocumentRemovalDetails documentRemovalDetails = caseData.getDocumentRemovalDetails(); + + documentSelectionService.initialise(details.getId(), caseData, documentRemovalDetails); + List errors = new ArrayList<>( + documentSelectionService.validateAndStoreSelection(caseData, documentRemovalDetails)); + + CaseFileCategory selectedFolder = documentRemovalDetails.getSelectedFolder(); + if (selectedFolder != null) { + String reason = documentRemovalDetails.getReasonForCategory(selectedFolder); + errors.addAll(textAreaValidationService.validateSingleTextArea( + reason, REASON_LABEL, TextAreaValidationService.SHORT_TEXT_LIMIT)); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .errorMessageOverride(StringUtils.joinIfNotEmpty("\n", errors)) + .build(); + } + + private String documentsShowCondition(DocumentRemovalCategoryField categoryField) { + return selectedFolderCondition(categoryField.category) + " AND " + FIELD_PREFIX + + categoryField.emptyFieldId + NO; + } + + private String noDocumentsShowCondition(DocumentRemovalCategoryField categoryField) { + return selectedFolderCondition(categoryField.category) + " AND " + FIELD_PREFIX + + categoryField.emptyFieldId + YES; + } + + private String reasonShowCondition(DocumentRemovalCategoryField categoryField) { + return selectedFolderCondition(categoryField.category) + " AND " + + sharedDocumentsFieldId(categoryField.category) + "!=\"\""; + } + + private String sharedDocumentsFieldId(CaseFileCategory category) { + return switch (category) { + case STATEMENTS_OF_CASE -> "statementsOfCaseDocuments"; + case PROPERTY_DOCUMENTS -> "propertyDocuments"; + case EVIDENCE -> "evidenceDocuments"; + case HEARING_DOCUMENTS -> "hearingDocuments"; + case ORDERS_AND_NOTICE_OF_HEARINGS -> "ordersAndNoticeOfHearingsDocuments"; + case APPLICATIONS -> "applicationsDocuments"; + case APPEALS -> "appealsDocuments"; + case CORRESPONDENCE -> "correspondenceDocuments"; + case UNCATEGORISED_DOCUMENTS -> "uncategorisedDocuments"; + }; + } + + private String selectedFolderCondition(CaseFileCategory category) { + return FIELD_PREFIX + "SelectedFolder=\"" + category.name() + "\""; + } + + private String emptyFolderMessage(CaseFileCategory category) { + return DOCUMENT_QUESTION_LABEL + + "
" + + "No documents in '" + category.getLabel() + "'"; + } + +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/DocumentCategoryField.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/DocumentAmendCategoryField.java similarity index 73% rename from src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/DocumentCategoryField.java rename to src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/DocumentAmendCategoryField.java index f00bd1792c..0ce646d605 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/DocumentCategoryField.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/DocumentAmendCategoryField.java @@ -4,72 +4,73 @@ import uk.gov.hmcts.ccd.sdk.type.DynamicList; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; import uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; import uk.gov.hmcts.reform.pcs.ccd.domain.documentamend.DocumentAmendDetails; -enum DocumentCategoryField { +enum DocumentAmendCategoryField { STATEMENTS_OF_CASE( CaseFileCategory.STATEMENTS_OF_CASE, "statementsOfCase", - DocumentAmendDetails::getStatementsOfCaseDocuments, + PCSCase::getStatementsOfCaseDocuments, DocumentAmendDetails::getStatementsOfCaseEmpty ), PROPERTY_DOCUMENTS( CaseFileCategory.PROPERTY_DOCUMENTS, "propertyDocuments", - DocumentAmendDetails::getPropertyDocuments, + PCSCase::getPropertyDocuments, DocumentAmendDetails::getPropertyDocumentsEmpty ), EVIDENCE( CaseFileCategory.EVIDENCE, "evidence", - DocumentAmendDetails::getEvidenceDocuments, + PCSCase::getEvidenceDocuments, DocumentAmendDetails::getEvidenceEmpty ), HEARING_DOCUMENTS( CaseFileCategory.HEARING_DOCUMENTS, "hearingDocuments", - DocumentAmendDetails::getHearingDocuments, + PCSCase::getHearingDocuments, DocumentAmendDetails::getHearingDocumentsEmpty ), ORDERS_AND_NOTICE_OF_HEARINGS( CaseFileCategory.ORDERS_AND_NOTICE_OF_HEARINGS, "ordersAndNoticeOfHearings", - DocumentAmendDetails::getOrdersAndNoticeOfHearingsDocuments, + PCSCase::getOrdersAndNoticeOfHearingsDocuments, DocumentAmendDetails::getOrdersAndNoticeOfHearingsEmpty ), APPLICATIONS( CaseFileCategory.APPLICATIONS, "applications", - DocumentAmendDetails::getApplicationsDocuments, + PCSCase::getApplicationsDocuments, DocumentAmendDetails::getApplicationsEmpty ), APPEALS( CaseFileCategory.APPEALS, "appeals", - DocumentAmendDetails::getAppealsDocuments, + PCSCase::getAppealsDocuments, DocumentAmendDetails::getAppealsEmpty ), CORRESPONDENCE( CaseFileCategory.CORRESPONDENCE, "correspondence", - DocumentAmendDetails::getCorrespondenceDocuments, + PCSCase::getCorrespondenceDocuments, DocumentAmendDetails::getCorrespondenceEmpty ), UNCATEGORISED_DOCUMENTS( CaseFileCategory.UNCATEGORISED_DOCUMENTS, "uncategorisedDocuments", - DocumentAmendDetails::getUncategorisedDocuments, + PCSCase::getUncategorisedDocuments, DocumentAmendDetails::getUncategorisedDocumentsEmpty ); final CaseFileCategory category; final String idPrefix; - final TypedPropertyGetter documentsGetter; + final TypedPropertyGetter documentsGetter; final TypedPropertyGetter emptyGetter; - DocumentCategoryField(CaseFileCategory category, + DocumentAmendCategoryField(CaseFileCategory category, String idPrefix, - TypedPropertyGetter documentsGetter, + TypedPropertyGetter documentsGetter, TypedPropertyGetter emptyGetter) { this.category = category; this.idPrefix = idPrefix; diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/SelectDocumentPage.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/SelectDocumentPage.java index c15e9da9aa..4612f33f99 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/SelectDocumentPage.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/page/documentamend/SelectDocumentPage.java @@ -11,7 +11,7 @@ import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; import uk.gov.hmcts.reform.pcs.ccd.domain.State; import uk.gov.hmcts.reform.pcs.ccd.domain.documentamend.DocumentAmendDetails; -import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentAmendSelectionService; +import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentSelectionService; import uk.gov.hmcts.reform.pcs.ccd.util.StringUtils; import java.util.List; @@ -24,13 +24,14 @@ public class SelectDocumentPage implements CcdPageConfiguration { private static final String PAGE_ID = "selectDocument"; private static final String FIELD_PREFIX = "documentAmend_"; + private static final String DOCUMENT_QUESTION_LABEL = "Which document do you want to amend?"; private static final String YES = "=\"Yes\""; private static final String NO = "=\"No\""; - private final DocumentAmendSelectionService documentAmendSelectionService; + private final DocumentSelectionService documentSelectionService; @Override public void addTo(PageBuilder pageBuilder) { - FieldCollectionBuilder documentAmendPage = pageBuilder + FieldCollectionBuilder page = pageBuilder .page(PAGE_ID, this::midEvent) .pageLabel("Select document") .label(PAGE_ID + "-separator", "---") @@ -39,17 +40,21 @@ public void addTo(PageBuilder pageBuilder) { .mandatory(DocumentAmendDetails::getSelectedFolder) .label("emptyFolderDocumentError", "", NEVER_SHOW) .label("selectedFolderEmptyErrorMessage", "", NEVER_SHOW) - .label("emptyFolderDocumentQuestion", "", NEVER_SHOW); + .label("emptyFolderDocumentQuestion", "", NEVER_SHOW) + .done(); - for (DocumentCategoryField categoryField : DocumentCategoryField.values()) { - addCategoryFields(documentAmendPage, categoryField); + for (DocumentAmendCategoryField categoryField : DocumentAmendCategoryField.values()) { + addCategoryFields(page, categoryField); } - for (DocumentCategoryField categoryField : DocumentCategoryField.values()) { - documentAmendPage.readonly(categoryField.emptyGetter, NEVER_SHOW, true); + FieldCollectionBuilder documentAmendDetailsFields = + page.complex(PCSCase::getDocumentAmendDetails); + + for (DocumentAmendCategoryField categoryField : DocumentAmendCategoryField.values()) { + documentAmendDetailsFields.readonly(categoryField.emptyGetter, NEVER_SHOW, true); } - documentAmendPage + documentAmendDetailsFields .readonly(DocumentAmendDetails::getSelectedFolderId, NEVER_SHOW, true) .readonly(DocumentAmendDetails::getSelectedFolderLabel, NEVER_SHOW, true) .readonly(DocumentAmendDetails::getSelectedDocumentId, NEVER_SHOW, true) @@ -57,21 +62,27 @@ public void addTo(PageBuilder pageBuilder) { .done(); } - private void addCategoryFields(FieldCollectionBuilder page, - DocumentCategoryField categoryField) { + private void addCategoryFields(FieldCollectionBuilder page, + DocumentAmendCategoryField categoryField) { CaseFileCategory category = categoryField.category; page .label(categoryField.idPrefix + "EmptyFolderMessage", emptyFolderMessage(category), noDocumentsShowCondition(category)) - .mandatory(categoryField.documentsGetter, documentsShowCondition(category), true); + .mandatoryWithoutDefaultValue(categoryField.documentsGetter, documentsShowCondition(category), + DOCUMENT_QUESTION_LABEL, true); } private AboutToStartOrSubmitResponse midEvent(CaseDetails details, CaseDetails detailsBefore) { PCSCase caseData = details.getData(); - documentAmendSelectionService.initialise(details.getId(), caseData); - List errors = documentAmendSelectionService.validateAndStoreSelection(caseData); + if (caseData.getDocumentAmendDetails() == null) { + caseData.setDocumentAmendDetails(new DocumentAmendDetails()); + } + DocumentAmendDetails documentAmendDetails = caseData.getDocumentAmendDetails(); + + documentSelectionService.initialise(details.getId(), caseData, documentAmendDetails); + List errors = documentSelectionService.validateAndStoreSelection(caseData, documentAmendDetails); return AboutToStartOrSubmitResponse.builder() .data(caseData) @@ -106,7 +117,7 @@ private String emptyFieldId(CaseFileCategory category) { } private String emptyFolderMessage(CaseFileCategory category) { - return "Which document do you want to amend?" + return DOCUMENT_QUESTION_LABEL + "
" + "No documents in '" + category.getLabel() + "'"; } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentRemovalService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentRemovalService.java new file mode 100644 index 0000000000..77adcf3b9a --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentRemovalService.java @@ -0,0 +1,34 @@ +package uk.gov.hmcts.reform.pcs.ccd.service.document; + +import org.springframework.stereotype.Service; +import uk.gov.hmcts.reform.pcs.ccd.entity.DocumentEntity; +import uk.gov.hmcts.reform.pcs.ccd.repository.DocumentRepository; +import uk.gov.hmcts.reform.pcs.exception.DocumentNotFoundException; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Service +public class DocumentRemovalService { + + private final DocumentRepository documentRepository; + private final DocumentImportService documentImportService; + + public DocumentRemovalService(DocumentRepository documentRepository, + DocumentImportService documentImportService) { + this.documentRepository = documentRepository; + this.documentImportService = documentImportService; + } + + public void removeDocument(UUID documentEntityId, String reason) { + DocumentEntity documentEntity = documentRepository.findById(documentEntityId) + .orElseThrow(() -> new DocumentNotFoundException(documentEntityId)); + + documentEntity.setRemoved(true); + documentEntity.setRemovalReason(reason); + documentEntity.setRemovedAt(LocalDateTime.now()); + documentRepository.save(documentEntity); + + documentImportService.deleteDocument(documentEntity.getUrl()); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentAmendSelectionService.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentSelectionService.java similarity index 54% rename from src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentAmendSelectionService.java rename to src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentSelectionService.java index 3cfbd8e8cc..566cdd9822 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentAmendSelectionService.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentSelectionService.java @@ -6,9 +6,9 @@ import uk.gov.hmcts.ccd.sdk.type.DynamicListElement; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; import uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory; +import uk.gov.hmcts.reform.pcs.ccd.domain.DocumentSelectionDetails; import uk.gov.hmcts.reform.pcs.ccd.domain.DocumentType; import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; -import uk.gov.hmcts.reform.pcs.ccd.domain.documentamend.DocumentAmendDetails; import uk.gov.hmcts.reform.pcs.ccd.entity.DocumentEntity; import uk.gov.hmcts.reform.pcs.ccd.entity.PcsCaseEntity; import uk.gov.hmcts.reform.pcs.ccd.service.PcsCaseService; @@ -21,7 +21,7 @@ import static uk.gov.hmcts.reform.pcs.ccd.util.AddressFormatter.COMMA_DELIMITER; @Service -public class DocumentAmendSelectionService { +public class DocumentSelectionService { public static final String SELECT_DIFFERENT_FOLDER_ERROR = "Select a different folder to continue"; private static final Comparator DOCUMENT_ORDER = Comparator @@ -31,30 +31,28 @@ public class DocumentAmendSelectionService { private final PcsCaseService pcsCaseService; private final AddressFormatter addressFormatter; - public DocumentAmendSelectionService(PcsCaseService pcsCaseService, AddressFormatter addressFormatter) { + public DocumentSelectionService(PcsCaseService pcsCaseService, AddressFormatter addressFormatter) { this.pcsCaseService = pcsCaseService; this.addressFormatter = addressFormatter; } - public void initialise(long caseReference, PCSCase caseData) { - DocumentAmendDetails details = getOrCreateDetails(caseData); + public void initialise(long caseReference, PCSCase caseData, DocumentSelectionDetails details) { PcsCaseEntity pcsCase = pcsCaseService.loadCase(caseReference); + details.setPropertyAddressSummary( + addressFormatter.formatShortAddress(caseData.getPropertyAddress(), COMMA_DELIMITER)); - details.setPropertyAddressSummary(addressFormatter.formatShortAddress(caseData.getPropertyAddress(), - COMMA_DELIMITER)); for (CaseFileCategory category : CaseFileCategory.values()) { - setDocumentsForCategory(details, pcsCase, category); + setDocumentsForCategory(details, caseData, pcsCase, category); } } - public List validateAndStoreSelection(PCSCase caseData) { - DocumentAmendDetails details = caseData.getDocumentAmendDetails(); + public List validateAndStoreSelection(PCSCase caseData, DocumentSelectionDetails details) { if (details == null || details.getSelectedFolder() == null) { return List.of(); } CaseFileCategory selectedFolder = details.getSelectedFolder(); - DynamicList selectedDocuments = documentsForCategory(details, selectedFolder); + DynamicList selectedDocuments = documentsForCategory(caseData, selectedFolder); details.setSelectedFolderId(selectedFolder.getId()); details.setSelectedFolderLabel(selectedFolder.getLabel()); @@ -76,11 +74,11 @@ public List validateAndStoreSelection(PCSCase caseData) { return List.of(); } - private void setDocumentsForCategory(DocumentAmendDetails details, PcsCaseEntity pcsCase, + private void setDocumentsForCategory(DocumentSelectionDetails details, PCSCase caseData, PcsCaseEntity pcsCase, CaseFileCategory category) { - DynamicList documents = documentList(pcsCase, category, documentsForCategory(details, category)); - applyDocumentsForCategory(details, category, documents); - setEmptyForCategory(details, category, YesOrNo.from(isEmpty(documents))); + DynamicList documents = documentList(pcsCase, category, documentsForCategory(caseData, category)); + applyDocumentsForCategory(caseData, category, documents); + details.setEmptyForCategory(category, YesOrNo.from(isEmpty(documents))); } private DynamicList documentList(PcsCaseEntity pcsCase, CaseFileCategory category, @@ -93,6 +91,7 @@ private DynamicList documentList(PcsCaseEntity pcsCase, CaseFileCategory categor .filter(Objects::nonNull) .filter(document -> isInCategory(document, category)) .filter(document -> document.getType() != DocumentType.DEFENDANT_ACCESS_CODE) + .filter(document -> !document.isRemoved()) .sorted(DOCUMENT_ORDER) .map(document -> DynamicListElement.builder() .code(document.getId()) @@ -126,53 +125,31 @@ private boolean isEmpty(DynamicList documents) { return documents == null || CollectionUtils.isEmpty(documents.getListItems()); } - private DocumentAmendDetails getOrCreateDetails(PCSCase caseData) { - if (caseData.getDocumentAmendDetails() == null) { - caseData.setDocumentAmendDetails(new DocumentAmendDetails()); - } - return caseData.getDocumentAmendDetails(); - } - - private DynamicList documentsForCategory(DocumentAmendDetails details, CaseFileCategory category) { + private DynamicList documentsForCategory(PCSCase caseData, CaseFileCategory category) { return switch (category) { - case STATEMENTS_OF_CASE -> details.getStatementsOfCaseDocuments(); - case PROPERTY_DOCUMENTS -> details.getPropertyDocuments(); - case EVIDENCE -> details.getEvidenceDocuments(); - case HEARING_DOCUMENTS -> details.getHearingDocuments(); - case ORDERS_AND_NOTICE_OF_HEARINGS -> details.getOrdersAndNoticeOfHearingsDocuments(); - case APPLICATIONS -> details.getApplicationsDocuments(); - case APPEALS -> details.getAppealsDocuments(); - case CORRESPONDENCE -> details.getCorrespondenceDocuments(); - case UNCATEGORISED_DOCUMENTS -> details.getUncategorisedDocuments(); + case STATEMENTS_OF_CASE -> caseData.getStatementsOfCaseDocuments(); + case PROPERTY_DOCUMENTS -> caseData.getPropertyDocuments(); + case EVIDENCE -> caseData.getEvidenceDocuments(); + case HEARING_DOCUMENTS -> caseData.getHearingDocuments(); + case ORDERS_AND_NOTICE_OF_HEARINGS -> caseData.getOrdersAndNoticeOfHearingsDocuments(); + case APPLICATIONS -> caseData.getApplicationsDocuments(); + case APPEALS -> caseData.getAppealsDocuments(); + case CORRESPONDENCE -> caseData.getCorrespondenceDocuments(); + case UNCATEGORISED_DOCUMENTS -> caseData.getUncategorisedDocuments(); }; } - private void applyDocumentsForCategory(DocumentAmendDetails details, CaseFileCategory category, - DynamicList documents) { - switch (category) { - case STATEMENTS_OF_CASE -> details.setStatementsOfCaseDocuments(documents); - case PROPERTY_DOCUMENTS -> details.setPropertyDocuments(documents); - case EVIDENCE -> details.setEvidenceDocuments(documents); - case HEARING_DOCUMENTS -> details.setHearingDocuments(documents); - case ORDERS_AND_NOTICE_OF_HEARINGS -> details.setOrdersAndNoticeOfHearingsDocuments(documents); - case APPLICATIONS -> details.setApplicationsDocuments(documents); - case APPEALS -> details.setAppealsDocuments(documents); - case CORRESPONDENCE -> details.setCorrespondenceDocuments(documents); - case UNCATEGORISED_DOCUMENTS -> details.setUncategorisedDocuments(documents); - } - } - - private void setEmptyForCategory(DocumentAmendDetails details, CaseFileCategory category, YesOrNo empty) { + private void applyDocumentsForCategory(PCSCase caseData, CaseFileCategory category, DynamicList documents) { switch (category) { - case STATEMENTS_OF_CASE -> details.setStatementsOfCaseEmpty(empty); - case PROPERTY_DOCUMENTS -> details.setPropertyDocumentsEmpty(empty); - case EVIDENCE -> details.setEvidenceEmpty(empty); - case HEARING_DOCUMENTS -> details.setHearingDocumentsEmpty(empty); - case ORDERS_AND_NOTICE_OF_HEARINGS -> details.setOrdersAndNoticeOfHearingsEmpty(empty); - case APPLICATIONS -> details.setApplicationsEmpty(empty); - case APPEALS -> details.setAppealsEmpty(empty); - case CORRESPONDENCE -> details.setCorrespondenceEmpty(empty); - case UNCATEGORISED_DOCUMENTS -> details.setUncategorisedDocumentsEmpty(empty); + case STATEMENTS_OF_CASE -> caseData.setStatementsOfCaseDocuments(documents); + case PROPERTY_DOCUMENTS -> caseData.setPropertyDocuments(documents); + case EVIDENCE -> caseData.setEvidenceDocuments(documents); + case HEARING_DOCUMENTS -> caseData.setHearingDocuments(documents); + case ORDERS_AND_NOTICE_OF_HEARINGS -> caseData.setOrdersAndNoticeOfHearingsDocuments(documents); + case APPLICATIONS -> caseData.setApplicationsDocuments(documents); + case APPEALS -> caseData.setAppealsDocuments(documents); + case CORRESPONDENCE -> caseData.setCorrespondenceDocuments(documents); + case UNCATEGORISED_DOCUMENTS -> caseData.setUncategorisedDocuments(documents); } } } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/ClaimView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/ClaimView.java index 2e6eb0dfa8..28235b8527 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/ClaimView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/ClaimView.java @@ -100,6 +100,7 @@ private static List> getEnergyPerformanceCertificate(PcsCase return pcsCaseEntity.getDocuments().stream() .filter(ClaimView::isEnergyPerformanceCertificate) .filter(DocumentsView::isDescriptionEmpty) + .filter(DocumentsView::isNotRemoved) .map(ClaimView::toDocument) .toList(); } @@ -112,6 +113,7 @@ private static List> getGasSafetyReport(PcsCaseEntity pcsCas return pcsCaseEntity.getDocuments().stream() .filter(ClaimView::isGasSafetyReport) .filter(DocumentsView::isDescriptionEmpty) + .filter(DocumentsView::isNotRemoved) .map(ClaimView::toDocument) .toList(); } @@ -124,6 +126,7 @@ private static List> getElectricalInstallationCondition(PcsC return pcsCaseEntity.getDocuments().stream() .filter(ClaimView::isElectricalInstallationCondition) .filter(DocumentsView::isDescriptionEmpty) + .filter(DocumentsView::isNotRemoved) .map(ClaimView::toDocument) .toList(); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/DocumentsView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/DocumentsView.java index 3a0ffd2307..0139c8172b 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/DocumentsView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/DocumentsView.java @@ -77,7 +77,8 @@ public boolean isDocumentVisibleToUser(DocumentEntity documentEntity, UUID curre } private boolean isExcludedFromCaseFile(DocumentEntity documentEntity) { - return documentEntity.getType() == DocumentType.DEFENDANT_ACCESS_CODE; + return documentEntity.getType() == DocumentType.DEFENDANT_ACCESS_CODE + || documentEntity.isRemoved(); } public static boolean isDescriptionEmpty(DocumentEntity documentEntity) { @@ -85,6 +86,10 @@ public static boolean isDescriptionEmpty(DocumentEntity documentEntity) { || documentEntity.getDescription().trim().isEmpty(); } + public static boolean isNotRemoved(DocumentEntity documentEntity) { + return !documentEntity.isRemoved(); + } + private boolean isNotInCaseDetailsTab(DocumentEntity documentEntity) { List caseDetailsDocuments = List.of( DocumentType.TENANCY_AGREEMENT, diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/NoticeOfPossessionView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/NoticeOfPossessionView.java index eae6346760..0243153c64 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/NoticeOfPossessionView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/NoticeOfPossessionView.java @@ -96,6 +96,7 @@ private static List> getNoticeStatement(PcsCaseEntity pcsCas return pcsCaseEntity.getDocuments().stream() .filter(NoticeOfPossessionView::isNoticeStatement) .filter(DocumentsView::isDescriptionEmpty) + .filter(DocumentsView::isNotRemoved) .map(NoticeOfPossessionView::toDocument) .toList(); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/RentArrearsView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/RentArrearsView.java index 06a0369cd3..77a04557ea 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/RentArrearsView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/RentArrearsView.java @@ -48,6 +48,7 @@ private static List> getRentStatement(PcsCaseEntity pcsCaseE return pcsCaseEntity.getDocuments().stream() .filter(RentArrearsView::isRentStatement) .filter(DocumentsView::isDescriptionEmpty) + .filter(DocumentsView::isNotRemoved) .map(RentArrearsView::toDocument) .toList(); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/TenancyLicenceView.java b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/TenancyLicenceView.java index 6c6dd3570f..4a27a5947a 100644 --- a/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/TenancyLicenceView.java +++ b/src/main/java/uk/gov/hmcts/reform/pcs/ccd/view/TenancyLicenceView.java @@ -75,6 +75,7 @@ private static List> getTenancyLicenceDocument(PcsCaseEntity return pcsCaseEntity.getDocuments().stream() .filter(TenancyLicenceView::isTenancyLicence) .filter(DocumentsView::isDescriptionEmpty) + .filter(DocumentsView::isNotRemoved) .map(TenancyLicenceView::toDocument) .toList(); } @@ -91,6 +92,7 @@ private static List> getOccupationLicenceDocument(PcsCaseEnt return pcsCaseEntity.getDocuments().stream() .filter(TenancyLicenceView::isOccupationLicence) .filter(DocumentsView::isDescriptionEmpty) + .filter(DocumentsView::isNotRemoved) .map(TenancyLicenceView::toDocument) .toList(); } diff --git a/src/main/java/uk/gov/hmcts/reform/pcs/exception/DocumentNotFoundException.java b/src/main/java/uk/gov/hmcts/reform/pcs/exception/DocumentNotFoundException.java new file mode 100644 index 0000000000..06e13c5c2e --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/pcs/exception/DocumentNotFoundException.java @@ -0,0 +1,10 @@ +package uk.gov.hmcts.reform.pcs.exception; + +import java.util.UUID; + +public class DocumentNotFoundException extends RuntimeException { + + public DocumentNotFoundException(UUID documentId) { + super("No document found with id: " + documentId); + } +} diff --git a/src/main/resources/db/migration/V002__add_document_removal_columns.sql b/src/main/resources/db/migration/V002__add_document_removal_columns.sql new file mode 100644 index 0000000000..e0b0b7b6c5 --- /dev/null +++ b/src/main/resources/db/migration/V002__add_document_removal_columns.sql @@ -0,0 +1,12 @@ +ALTER TABLE document +ADD COLUMN removed BOOLEAN, +ADD COLUMN removal_reason VARCHAR(250), +ADD COLUMN removed_at TIMESTAMP; + +UPDATE document +SET removed = FALSE +WHERE removed IS NULL; + +ALTER TABLE document +ALTER COLUMN removed SET DEFAULT FALSE, +ALTER COLUMN removed SET NOT NULL; \ No newline at end of file diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/caseworker/removedocument/RemoveDocumentTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/caseworker/removedocument/RemoveDocumentTest.java new file mode 100644 index 0000000000..9354d8c167 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/event/caseworker/removedocument/RemoveDocumentTest.java @@ -0,0 +1,94 @@ +package uk.gov.hmcts.reform.pcs.ccd.event.caseworker.removedocument; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.api.callback.SubmitResponse; +import uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.domain.State; +import uk.gov.hmcts.reform.pcs.ccd.domain.documentremoval.DocumentRemovalDetails; +import uk.gov.hmcts.reform.pcs.ccd.event.BaseEventTest; +import uk.gov.hmcts.reform.pcs.ccd.page.caseworker.removedocument.SelectDocumentToRemovePage; +import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentRemovalService; +import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentSelectionService; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class RemoveDocumentTest extends BaseEventTest { + + @Mock + private DocumentSelectionService documentSelectionService; + @Mock + private DocumentRemovalService documentRemovalService; + @Mock + private SelectDocumentToRemovePage selectDocumentToRemovePage; + + @BeforeEach + void setUp() { + RemoveDocument underTest = new RemoveDocument( + documentSelectionService, documentRemovalService, selectDocumentToRemovePage + ); + + setEventUnderTest(underTest); + } + + @Test + void shouldCreateDocumentRemovalDetailsWhenAbsentOnStart() { + PCSCase caseData = PCSCase.builder().build(); + + PCSCase result = callStartHandler(caseData); + + assertThat(result.getDocumentRemovalDetails()).isNotNull(); + verify(documentSelectionService).initialise( + eq(TEST_CASE_REFERENCE), eq(result), eq(result.getDocumentRemovalDetails())); + } + + @Test + void shouldReuseExistingDocumentRemovalDetailsOnStart() { + DocumentRemovalDetails existingDetails = DocumentRemovalDetails.builder() + .selectedFolder(CaseFileCategory.EVIDENCE) + .build(); + PCSCase caseData = PCSCase.builder() + .documentRemovalDetails(existingDetails) + .build(); + + PCSCase result = callStartHandler(caseData); + + assertThat(result.getDocumentRemovalDetails()).isSameAs(existingDetails); + verify(documentSelectionService).initialise(TEST_CASE_REFERENCE, result, existingDetails); + } + + @Test + void shouldBuildConfirmationPageWithRemovedDocumentDetails() { + UUID documentId = UUID.randomUUID(); + String reason = "No longer required"; + DocumentRemovalDetails details = DocumentRemovalDetails.builder() + .selectedFolder(CaseFileCategory.EVIDENCE) + .selectedDocumentId(documentId.toString()) + .selectedDocumentFileName("evidence.pdf") + .propertyAddressSummary("123 Test Street, Testville, TE1 1ST") + .evidenceReason(reason) + .build(); + PCSCase caseData = PCSCase.builder() + .documentRemovalDetails(details) + .caseNameHmctsInternal("Smith v Jones") + .build(); + + SubmitResponse response = callSubmitHandler(caseData); + + verify(documentRemovalService).removeDocument(documentId, reason); + assertThat(response.getConfirmationBody()) + .contains("evidence.pdf") + .contains("Case number: " + TEST_CASE_REFERENCE) + .contains("123 Test Street, Testville, TE1 1ST") + .contains("Smith v Jones"); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/SelectDocumentToRemovePageTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/SelectDocumentToRemovePageTest.java new file mode 100644 index 0000000000..86b9585e7f --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/page/caseworker/removedocument/SelectDocumentToRemovePageTest.java @@ -0,0 +1,100 @@ +package uk.gov.hmcts.reform.pcs.ccd.page.caseworker.removedocument; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory; +import uk.gov.hmcts.reform.pcs.ccd.domain.PCSCase; +import uk.gov.hmcts.reform.pcs.ccd.domain.State; +import uk.gov.hmcts.reform.pcs.ccd.domain.documentremoval.DocumentRemovalDetails; +import uk.gov.hmcts.reform.pcs.ccd.page.BasePageTest; +import uk.gov.hmcts.reform.pcs.ccd.service.TextAreaValidationService; +import uk.gov.hmcts.reform.pcs.ccd.service.document.DocumentSelectionService; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SelectDocumentToRemovePageTest extends BasePageTest { + + @Mock + private DocumentSelectionService documentSelectionService; + + @BeforeEach + void setUp() { + setPageUnderTest(new SelectDocumentToRemovePage(documentSelectionService, new TextAreaValidationService())); + } + + @Test + void shouldCreateDocumentRemovalDetailsWhenAbsent() { + PCSCase caseData = PCSCase.builder().build(); + + callMidEventHandler(caseData); + + assertThat(caseData.getDocumentRemovalDetails()).isNotNull(); + verify(documentSelectionService).initialise( + TEST_CASE_REFERENCE, caseData, caseData.getDocumentRemovalDetails()); + } + + @Test + void shouldReuseExistingDocumentRemovalDetails() { + DocumentRemovalDetails existingDetails = DocumentRemovalDetails.builder() + .selectedFolder(CaseFileCategory.EVIDENCE) + .build(); + PCSCase caseData = PCSCase.builder() + .documentRemovalDetails(existingDetails) + .build(); + + callMidEventHandler(caseData); + + assertThat(caseData.getDocumentRemovalDetails()).isSameAs(existingDetails); + verify(documentSelectionService).initialise(TEST_CASE_REFERENCE, caseData, existingDetails); + } + + @Test + void shouldSurfaceErrorsFromDocumentSelectionService() { + PCSCase caseData = PCSCase.builder().build(); + when(documentSelectionService.validateAndStoreSelection(eq(caseData), any())) + .thenReturn(List.of("Select a different folder to continue")); + + AboutToStartOrSubmitResponse response = callMidEventHandler(caseData); + + assertThat(response.getErrorMessageOverride()).isEqualTo("Select a different folder to continue"); + } + + @Test + void shouldNotValidateReasonWhenNoFolderSelected() { + PCSCase caseData = PCSCase.builder() + .documentRemovalDetails(DocumentRemovalDetails.builder().build()) + .build(); + + AboutToStartOrSubmitResponse response = callMidEventHandler(caseData); + + assertThat(response.getErrorMessageOverride()).isNull(); + } + + @Test + void shouldValidateRemovalReasonLengthForSelectedDocument() { + String tooLongReason = "a".repeat(TextAreaValidationService.SHORT_TEXT_LIMIT + 1); + DocumentRemovalDetails details = DocumentRemovalDetails.builder() + .selectedFolder(CaseFileCategory.EVIDENCE) + .evidenceReason(tooLongReason) + .build(); + PCSCase caseData = PCSCase.builder() + .documentRemovalDetails(details) + .build(); + + AboutToStartOrSubmitResponse response = callMidEventHandler(caseData); + + assertThat(response.getErrorMessageOverride()) + .contains("Why are you removing this document?"); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentRemovalServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentRemovalServiceTest.java new file mode 100644 index 0000000000..0a26df1a01 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentRemovalServiceTest.java @@ -0,0 +1,90 @@ +package uk.gov.hmcts.reform.pcs.ccd.service.document; + +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.reform.pcs.ccd.entity.DocumentEntity; +import uk.gov.hmcts.reform.pcs.ccd.repository.DocumentRepository; +import uk.gov.hmcts.reform.pcs.exception.DocumentNotFoundException; + +import java.time.LocalDateTime; +import java.util.Optional; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class DocumentRemovalServiceTest { + + @Mock + private DocumentRepository documentRepository; + @Mock + private DocumentImportService documentImportService; + + private DocumentRemovalService underTest; + + @BeforeEach + void setUp() { + underTest = new DocumentRemovalService(documentRepository, documentImportService); + } + + @Test + void shouldSoftDeleteDocumentAndRemoveFromDocumentStore() { + UUID documentId = UUID.randomUUID(); + String reason = "Duplicate upload"; + DocumentEntity documentEntity = DocumentEntity.builder() + .id(documentId) + .url("http://dm-store/documents/" + documentId) + .removed(false) + .build(); + when(documentRepository.findById(documentId)).thenReturn(Optional.of(documentEntity)); + + underTest.removeDocument(documentId, reason); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DocumentEntity.class); + verify(documentRepository).save(captor.capture()); + DocumentEntity saved = captor.getValue(); + + assertThat(saved.isRemoved()).isTrue(); + assertThat(saved.getRemovalReason()).isEqualTo(reason); + assertThat(saved.getRemovedAt()).isNotNull(); + assertThat(saved.getRemovedAt()).isBeforeOrEqualTo(LocalDateTime.now()); + } + + @Test + void shouldDeleteDocumentFromDocumentStoreUsingItsUrl() { + UUID documentId = UUID.randomUUID(); + String url = "http://dm-store/documents/" + documentId; + DocumentEntity documentEntity = DocumentEntity.builder() + .id(documentId) + .url(url) + .build(); + when(documentRepository.findById(documentId)).thenReturn(Optional.of(documentEntity)); + + underTest.removeDocument(documentId, "No longer relevant"); + + verify(documentImportService).deleteDocument(url); + } + + @Test + void shouldThrowWhenDocumentNotFound() { + UUID documentId = UUID.randomUUID(); + when(documentRepository.findById(documentId)).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> underTest.removeDocument(documentId, "reason")) + .isInstanceOf(DocumentNotFoundException.class) + .hasMessageContaining(documentId.toString()); + + verifyNoInteractions(documentImportService); + verify(documentRepository, never()).save(any()); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentAmendSelectionServiceTest.java b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentSelectionServiceTest.java similarity index 68% rename from src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentAmendSelectionServiceTest.java rename to src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentSelectionServiceTest.java index bd89aa8488..2229226aa5 100644 --- a/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentAmendSelectionServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/pcs/ccd/service/document/DocumentSelectionServiceTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.type.AddressUK; import uk.gov.hmcts.ccd.sdk.type.DynamicList; import uk.gov.hmcts.ccd.sdk.type.DynamicListElement; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; @@ -32,18 +33,18 @@ import static uk.gov.hmcts.reform.pcs.ccd.domain.CaseFileCategory.UNCATEGORISED_DOCUMENTS; @ExtendWith(MockitoExtension.class) -class DocumentAmendSelectionServiceTest { +class DocumentSelectionServiceTest { private static final long CASE_REFERENCE = 1234567890123456L; @Mock private PcsCaseService pcsCaseService; - private DocumentAmendSelectionService underTest; + private DocumentSelectionService underTest; @BeforeEach void setUp() { - underTest = new DocumentAmendSelectionService(pcsCaseService, new AddressFormatter()); + underTest = new DocumentSelectionService(pcsCaseService, new AddressFormatter()); } @Test @@ -67,16 +68,17 @@ void shouldDefineFolderDropdownWithCaseFileViewFolders() { void shouldPopulatePropertyAddressSummary() { when(pcsCaseService.loadCase(CASE_REFERENCE)).thenReturn(PcsCaseEntity.builder().build()); PCSCase caseData = PCSCase.builder() - .propertyAddress(uk.gov.hmcts.ccd.sdk.type.AddressUK.builder() + .propertyAddress(AddressUK.builder() .addressLine1("15 Garden Drive") .postTown("Luton") .postCode("LU1 1AB") .build()) .build(); + DocumentAmendDetails details = new DocumentAmendDetails(); - underTest.initialise(CASE_REFERENCE, caseData); + underTest.initialise(CASE_REFERENCE, caseData, details); - assertThat(caseData.getDocumentAmendDetails().getPropertyAddressSummary()) + assertThat(details.getPropertyAddressSummary()) .isEqualTo("15 Garden Drive, Luton, LU1 1AB"); } @@ -90,15 +92,15 @@ void shouldSerialiseDocumentAmendFieldsWithGeneratedCcdFieldIds() throws JsonPro .build())) .build(); PCSCase caseData = PCSCase.builder() + .applicationsDocuments(applicationsDocuments) .documentAmendDetails(DocumentAmendDetails.builder() - .applicationsDocuments(applicationsDocuments) .applicationsEmpty(YesOrNo.NO) .build()) .build(); String serialisedCaseData = new ObjectMapper().writeValueAsString(caseData); - assertThat(serialisedCaseData).contains("\"documentAmend_ApplicationsDocuments\""); + assertThat(serialisedCaseData).contains("\"applicationsDocuments\""); assertThat(serialisedCaseData).contains("\"documentAmend_ApplicationsEmpty\""); assertThat(serialisedCaseData).doesNotContain("documentAmend_applicationsDocuments"); } @@ -121,17 +123,17 @@ void shouldPopulateDocumentsForSelectedCategoryAndIncludeWithoutNoticeApplicatio .build(); when(pcsCaseService.loadCase(CASE_REFERENCE)).thenReturn(pcsCase); PCSCase caseData = PCSCase.builder().build(); + DocumentAmendDetails details = new DocumentAmendDetails(); - underTest.initialise(CASE_REFERENCE, caseData); + underTest.initialise(CASE_REFERENCE, caseData, details); - DocumentAmendDetails details = caseData.getDocumentAmendDetails(); - assertThat(details.getEvidenceDocuments().getListItems()) + assertThat(caseData.getEvidenceDocuments().getListItems()) .extracting(DynamicListElement::getLabel) .containsExactly("visible evidence.pdf"); - assertThat(details.getApplicationsDocuments().getListItems()) + assertThat(caseData.getApplicationsDocuments().getListItems()) .extracting(DynamicListElement::getLabel) .containsExactly("visible application.pdf", "without notice application.pdf"); - assertThat(details.getApplicationsDocuments().getValue()).isNull(); + assertThat(caseData.getApplicationsDocuments().getValue()).isNull(); } @Test @@ -146,38 +148,59 @@ void shouldExcludeDocumentsWithNullCategoryIdFromUncategorisedDocuments() { .documents(List.of(nullCategoryDocument, categorisedDocument)) .build()); PCSCase caseData = PCSCase.builder().build(); + DocumentAmendDetails details = new DocumentAmendDetails(); - underTest.initialise(CASE_REFERENCE, caseData); + underTest.initialise(CASE_REFERENCE, caseData, details); - assertThat(caseData.getDocumentAmendDetails().getUncategorisedDocuments().getListItems()) + assertThat(caseData.getUncategorisedDocuments().getListItems()) .extracting(DynamicListElement::getLabel) .containsExactly("uncategorised document.pdf"); - assertThat(caseData.getDocumentAmendDetails().getUncategorisedDocumentsEmpty()).isEqualTo(YesOrNo.NO); + assertThat(details.getUncategorisedDocumentsEmpty()).isEqualTo(YesOrNo.NO); } @Test void shouldExcludeDefendantAccessCodeDocumentsFromUncategorisedDocuments() { - DocumentEntity accessCodeDocument = documentWithType( + DocumentEntity accessCodeDocument = buildDocumentEntity( null, UNCATEGORISED_DOCUMENTS.getId(), - DocumentType.DEFENDANT_ACCESS_CODE + DocumentType.DEFENDANT_ACCESS_CODE, + false ); - DocumentEntity visibleDocument = documentWithType( + DocumentEntity visibleDocument = buildDocumentEntity( "uncategorised document.pdf", UNCATEGORISED_DOCUMENTS.getId(), - DocumentType.OTHER + DocumentType.OTHER, + false ); when(pcsCaseService.loadCase(CASE_REFERENCE)).thenReturn(PcsCaseEntity.builder() .documents(List.of(accessCodeDocument, visibleDocument)) .build()); PCSCase caseData = PCSCase.builder().build(); + DocumentAmendDetails details = new DocumentAmendDetails(); - underTest.initialise(CASE_REFERENCE, caseData); + underTest.initialise(CASE_REFERENCE, caseData, details); - assertThat(caseData.getDocumentAmendDetails().getUncategorisedDocuments().getListItems()) + assertThat(caseData.getUncategorisedDocuments().getListItems()) .extracting(DynamicListElement::getLabel) .containsExactly("uncategorised document.pdf"); - assertThat(caseData.getDocumentAmendDetails().getUncategorisedDocumentsEmpty()).isEqualTo(YesOrNo.NO); + assertThat(details.getUncategorisedDocumentsEmpty()).isEqualTo(YesOrNo.NO); + } + + @Test + void shouldExcludeRemovedDocumentsFromSelection() { + DocumentEntity removedDocument = buildDocumentEntity("removed evidence.pdf", EVIDENCE.getId(), null, true); + DocumentEntity activeDocument = buildDocumentEntity("active evidence.pdf", EVIDENCE.getId(), null, false); + when(pcsCaseService.loadCase(CASE_REFERENCE)).thenReturn(PcsCaseEntity.builder() + .documents(List.of(removedDocument, activeDocument)) + .build()); + PCSCase caseData = PCSCase.builder().build(); + DocumentAmendDetails details = new DocumentAmendDetails(); + + underTest.initialise(CASE_REFERENCE, caseData, details); + + assertThat(caseData.getEvidenceDocuments().getListItems()) + .extracting(DynamicListElement::getLabel) + .containsExactly("active evidence.pdf"); } @Test @@ -205,10 +228,11 @@ void shouldOrderDocumentsBySubmittedDateDescendingThenFileNameWithNullDatesLast( .documents(List.of(older, nullDate, newerB, newerA)) .build()); PCSCase caseData = PCSCase.builder().build(); + DocumentAmendDetails details = new DocumentAmendDetails(); - underTest.initialise(CASE_REFERENCE, caseData); + underTest.initialise(CASE_REFERENCE, caseData, details); - assertThat(caseData.getDocumentAmendDetails().getEvidenceDocuments().getListItems()) + assertThat(caseData.getEvidenceDocuments().getListItems()) .extracting(DynamicListElement::getLabel) .containsExactly( "a newer evidence.pdf", @@ -220,7 +244,7 @@ void shouldOrderDocumentsBySubmittedDateDescendingThenFileNameWithNullDatesLast( @Test void shouldReturnNoErrorsWhenDocumentAmendDetailsIsNull() { - List errors = underTest.validateAndStoreSelection(PCSCase.builder().build()); + List errors = underTest.validateAndStoreSelection(PCSCase.builder().build(),null); assertThat(errors).isEmpty(); } @@ -228,22 +252,19 @@ void shouldReturnNoErrorsWhenDocumentAmendDetailsIsNull() { @Test void shouldReturnDifferentFolderErrorWhenSelectedFolderHasNoDocuments() { when(pcsCaseService.loadCase(CASE_REFERENCE)).thenReturn(PcsCaseEntity.builder().build()); - PCSCase caseData = PCSCase.builder() - .documentAmendDetails(DocumentAmendDetails.builder() - .selectedFolder(UNCATEGORISED_DOCUMENTS) - .build()) + PCSCase caseData = PCSCase.builder().build(); + DocumentAmendDetails details = DocumentAmendDetails.builder() + .selectedFolder(UNCATEGORISED_DOCUMENTS) .build(); - underTest.initialise(CASE_REFERENCE, caseData); + underTest.initialise(CASE_REFERENCE, caseData, details); - List errors = underTest.validateAndStoreSelection(caseData); + List errors = underTest.validateAndStoreSelection(caseData, details); assertThat(errors).containsExactly("Select a different folder to continue"); - assertThat(caseData.getDocumentAmendDetails().getSelectedFolderId()) - .isEqualTo(UNCATEGORISED_DOCUMENTS.getId()); - assertThat(caseData.getDocumentAmendDetails().getSelectedFolderLabel()) - .isEqualTo(UNCATEGORISED_DOCUMENTS.getLabel()); - assertThat(caseData.getDocumentAmendDetails().getSelectedDocumentId()).isNull(); - assertThat(caseData.getDocumentAmendDetails().getSelectedDocumentFileName()).isNull(); + assertThat(details.getSelectedFolderId()).isEqualTo(UNCATEGORISED_DOCUMENTS.getId()); + assertThat(details.getSelectedFolderLabel()).isEqualTo(UNCATEGORISED_DOCUMENTS.getLabel()); + assertThat(details.getSelectedDocumentId()).isNull(); + assertThat(details.getSelectedDocumentFileName()).isNull(); } @Test @@ -253,19 +274,18 @@ void shouldTreatEmptyDocumentSelectionAsNoSelection() { .documents(List.of(document)) .build()); PCSCase caseData = PCSCase.builder() - .documentAmendDetails(DocumentAmendDetails.builder() - .selectedFolder(EVIDENCE) - .evidenceDocuments(DynamicList.builder() - .build()) - .build()) + .evidenceDocuments(DynamicList.builder().build()) .build(); - underTest.initialise(CASE_REFERENCE, caseData); + DocumentAmendDetails details = DocumentAmendDetails.builder() + .selectedFolder(EVIDENCE) + .build(); + underTest.initialise(CASE_REFERENCE, caseData, details); - List errors = underTest.validateAndStoreSelection(caseData); + List errors = underTest.validateAndStoreSelection(caseData, details); assertThat(errors).isEmpty(); - assertThat(caseData.getDocumentAmendDetails().getSelectedDocumentId()).isNull(); - assertThat(caseData.getDocumentAmendDetails().getSelectedDocumentFileName()).isNull(); + assertThat(details.getSelectedDocumentId()).isNull(); + assertThat(details.getSelectedDocumentFileName()).isNull(); } @Test @@ -275,20 +295,20 @@ void shouldPersistSelectedFolderAndDocumentDetails() { .documents(List.of(document)) .build()); PCSCase caseData = PCSCase.builder() - .documentAmendDetails(DocumentAmendDetails.builder() - .selectedFolder(EVIDENCE) - .evidenceDocuments(selectedDocument(document)) - .build()) + .evidenceDocuments(selectedDocument(document)) .build(); - underTest.initialise(CASE_REFERENCE, caseData); + DocumentAmendDetails details = DocumentAmendDetails.builder() + .selectedFolder(EVIDENCE) + .build(); + underTest.initialise(CASE_REFERENCE, caseData, details); - List errors = underTest.validateAndStoreSelection(caseData); + List errors = underTest.validateAndStoreSelection(caseData, details); assertThat(errors).isEmpty(); - assertThat(caseData.getDocumentAmendDetails().getSelectedFolderId()).isEqualTo(EVIDENCE.getId()); - assertThat(caseData.getDocumentAmendDetails().getSelectedFolderLabel()).isEqualTo(EVIDENCE.getLabel()); - assertThat(caseData.getDocumentAmendDetails().getSelectedDocumentId()).isEqualTo(document.getId().toString()); - assertThat(caseData.getDocumentAmendDetails().getSelectedDocumentFileName()).isEqualTo("photo.pdf"); + assertThat(details.getSelectedFolderId()).isEqualTo(EVIDENCE.getId()); + assertThat(details.getSelectedFolderLabel()).isEqualTo(EVIDENCE.getLabel()); + assertThat(details.getSelectedDocumentId()).isEqualTo(document.getId().toString()); + assertThat(details.getSelectedDocumentFileName()).isEqualTo("photo.pdf"); } @Test @@ -297,17 +317,16 @@ void shouldLeaveMissingDocumentSelectionToExuiMandatoryValidation() { when(pcsCaseService.loadCase(CASE_REFERENCE)).thenReturn(PcsCaseEntity.builder() .documents(List.of(document)) .build()); - PCSCase caseData = PCSCase.builder() - .documentAmendDetails(DocumentAmendDetails.builder() - .selectedFolder(EVIDENCE) - .build()) + PCSCase caseData = PCSCase.builder().build(); + DocumentAmendDetails details = DocumentAmendDetails.builder() + .selectedFolder(EVIDENCE) .build(); - underTest.initialise(CASE_REFERENCE, caseData); + underTest.initialise(CASE_REFERENCE, caseData, details); - List errors = underTest.validateAndStoreSelection(caseData); + List errors = underTest.validateAndStoreSelection(caseData, details); assertThat(errors).isEmpty(); - assertThat(caseData.getDocumentAmendDetails().getSelectedDocumentId()).isNull(); + assertThat(details.getSelectedDocumentId()).isNull(); } private static DynamicList selectedDocument(DocumentEntity document) { @@ -337,12 +356,14 @@ private static DocumentEntity document(String fileName, String categoryId, GenAp .build(); } - private static DocumentEntity documentWithType(String fileName, String categoryId, DocumentType type) { + private static DocumentEntity buildDocumentEntity(String fileName, String categoryId, DocumentType type, + boolean removed) { return DocumentEntity.builder() .id(UUID.randomUUID()) .fileName(fileName) .categoryId(categoryId) .type(type) + .removed(removed) .submittedDate(Instant.now()) .build(); }