From 18a13249468e291e6110637a734d16976670b24f Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Tue, 29 Oct 2024 10:30:15 +0000 Subject: [PATCH 01/16] NFDIV-4356 Initial changes --- .../CaseworkerUpdateApplicant1Email.java | 119 ++++++++++ .../CaseworkerUpdateApplicant2Email.java | 116 ++++++++++ .../service/EmailUpdateService.java | 49 +++++ .../task/SendCaseInviteToApplicant1.java | 34 +++ .../task/SendCaseInviteToApplicant2.java | 33 +++ .../service/task/SetCaseInviteApplicant1.java | 31 +++ .../service/task/SetCaseInviteApplicant2.java | 30 +++ .../EmailUpdatedNotification.java | 42 ++++ .../ReInviteApplicant1Notification.java | 65 ++++++ .../ReInviteApplicant2Notification.java | 44 ++++ .../divorce/divorcecase/model/CaseData.java | 4 + .../divorcecase/model/CaseInviteApp1.java | 21 ++ .../divorce/notification/CommonContent.java | 14 ++ .../notification/EmailTemplateName.java | 4 +- .../solicitor/service/CcdAccessService.java | 17 ++ .../event/SystemLinkApplicant1.java | 64 ++++++ src/main/resources/application.yaml | 5 + .../CaseworkerUpdateApplicant1EmailTest.java | 205 ++++++++++++++++++ .../CaseworkerUpdateApplicant2EmailTest.java | 202 +++++++++++++++++ .../service/EmailUpdateServiceTest.java | 53 +++++ 20 files changed, 1151 insertions(+), 1 deletion(-) create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant1.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant2.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant1.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant2.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant1Notification.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant2Notification.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java new file mode 100644 index 00000000000..0b8d6671049 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java @@ -0,0 +1,119 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import static java.util.Collections.singletonList; +import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE; + +@Component +@Slf4j +public class CaseworkerUpdateApplicant1Email implements CCDConfig { + + public static final String CASEWORKER_UPDATE_APP1_EMAIL = "caseworker-update-app1-email"; + + @Autowired + private EmailUpdateService emailUpdateService; + + private static final String EMAIL_LABEL = "${%s} email address"; + private static final String APPLICANTS_OR_APPLICANT1S = "labelContentApplicantsOrApplicant1s"; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CASEWORKER_UPDATE_APP1_EMAIL) + .forStates(POST_SUBMISSION_STATES) + .name("Update applicant1 email") + .description("Update applicant/applicant1 email") + .aboutToSubmitCallback(this::aboutToSubmit) + .showSummary() + .showEventNotes() + .grant(CREATE_READ_UPDATE_DELETE, SUPER_USER, CASE_WORKER) + .grantHistoryOnly( + SOLICITOR, + LEGAL_ADVISOR)) + .page("updateApp1Email") + .pageLabel("Update applicant/applicant1 email") + .complex(CaseData::getApplicant1) + .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, APPLICANTS_OR_APPLICANT1S)) + .done(); + } + + public AboutToStartOrSubmitResponse midEvent(final CaseDetails details, + final CaseDetails detailsBefore) { + log.info("midEvent callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId()); + + CaseData caseData = details.getData(); + CaseData caseDataBefore = detailsBefore.getData(); + + if (!validApplicant1ContactDetails(caseDataBefore, caseData)) { + + return AboutToStartOrSubmitResponse.builder() + .errors(singletonList("Please use the 'Update offline status' event before removing the email address.")) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .build(); + } + + public AboutToStartOrSubmitResponse aboutToSubmit( + final CaseDetails details, + final CaseDetails beforeDetails + ) { + log.info("aboutToSubmit callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId()); + + CaseData caseData = details.getData(); + CaseData caseDataBefore = beforeDetails.getData(); + + if (!caseData.getApplicant1().isRepresented()) { + final CaseDetails result = emailUpdateService.processUpdateForApplicant1(details); + String newEmail = caseData.getApplicant1().getEmail(); + + if (caseDataBefore.getApplicant1().getEmail() != null + && !caseDataBefore.getApplicant1().getEmail().isBlank()) { + emailUpdateService.sendNotificationToOldEmail(beforeDetails, newEmail, true); + } + return AboutToStartOrSubmitResponse.builder() + .data(result.getData()) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .build(); + } + + private boolean validApplicant1ContactDetails(CaseData caseDataBefore, CaseData caseData) { + + if (caseDataBefore.getApplicant1().getEmail() != null && !caseDataBefore.getApplicant1().getEmail().isBlank()) { + if (!caseDataBefore.getApplicant1().isRepresented() + && !caseData.getApplicant1().isApplicantOffline() + && (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank())) { + return false; + } + } + return true; + } + + private String getLabel(final String label, final Object... value) { + return String.format(label, value); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java new file mode 100644 index 00000000000..67a37c448f3 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java @@ -0,0 +1,116 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import static java.util.Collections.singletonList; +import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.*; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE; + +@Component +@Slf4j +public class CaseworkerUpdateApplicant2Email implements CCDConfig { + + public static final String CASEWORKER_UPDATE_APP2_EMAIL = "caseworker-update-app2-email"; + + @Autowired + private EmailUpdateService emailUpdateService; + + private static final String EMAIL_LABEL = "${%s} email address"; + private static final String RESPONDENTS_OR_APPLICANT2S = "labelContentRespondentsOrApplicant2s"; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CASEWORKER_UPDATE_APP2_EMAIL) + .forStates(POST_SUBMISSION_STATES) + .name("Update applicant2 email") + .description("Update respondent/applicant2 email") + .aboutToSubmitCallback(this::aboutToSubmit) + .showSummary() + .showEventNotes() + .grant(CREATE_READ_UPDATE_DELETE, SUPER_USER, CASE_WORKER) + .grantHistoryOnly( + SOLICITOR, + LEGAL_ADVISOR)) + .page("updateApp2Email") + .pageLabel("Update respondent/applicant2 email") + .complex(CaseData::getApplicant2) + .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, RESPONDENTS_OR_APPLICANT2S)) + .done(); + } + + public AboutToStartOrSubmitResponse midEvent(final CaseDetails details, + final CaseDetails detailsBefore) { + log.info("midEvent callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId()); + + CaseData caseData = details.getData(); + CaseData caseDataBefore = detailsBefore.getData(); + + if (!validApplicant2ContactDetails(caseDataBefore, caseData)) { + + return AboutToStartOrSubmitResponse.builder() + .errors(singletonList("Please use the 'Update offline status' event before removing the email address.")) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .build(); + } + + public AboutToStartOrSubmitResponse aboutToSubmit( + final CaseDetails details, + final CaseDetails beforeDetails + ) { + log.info("aboutToSubmit callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId()); + + CaseData caseData = details.getData(); + CaseData caseDataBefore = beforeDetails.getData(); + + if (!caseData.getApplicant2().isRepresented()) { + final CaseDetails result = emailUpdateService.processUpdateForApplicant2(details); + String newEmail = caseData.getApplicant2().getEmail(); + + if (caseDataBefore.getApplicant2().getEmail() != null + && !caseDataBefore.getApplicant2().getEmail().isBlank()) { + emailUpdateService.sendNotificationToOldEmail(beforeDetails, newEmail, false); + } + return AboutToStartOrSubmitResponse.builder() + .data(result.getData()) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .build(); + } + + private boolean validApplicant2ContactDetails(CaseData caseDataBefore, CaseData caseData) { + + if (caseDataBefore.getApplicant2().getEmail() != null && !caseDataBefore.getApplicant2().getEmail().isBlank()) { + if (!caseDataBefore.getApplicant2().isRepresented() + && !caseData.getApplicant2().isApplicantOffline() + && (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank())) { + return false; + } + } + return true; + } + + private String getLabel(final String label, final Object... value) { + return String.format(label, value); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java new file mode 100644 index 00000000000..bc0e9fadfd3 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java @@ -0,0 +1,49 @@ +package uk.gov.hmcts.divorce.caseworker.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.divorce.caseworker.service.notification.GeneralEmailNotification; +import uk.gov.hmcts.divorce.caseworker.service.task.*; +import uk.gov.hmcts.divorce.common.notification.EmailUpdatedNotification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.document.task.DivorceApplicationRemover; +import uk.gov.hmcts.divorce.systemupdate.service.task.GenerateD84Form; + +import static uk.gov.hmcts.divorce.divorcecase.task.CaseTaskRunner.caseTasks; + +@Service +@Slf4j +public class EmailUpdateService { + + @Autowired + private SendCaseInviteToApplicant1 sendCaseInviteToApplicant1; + @Autowired + private SendCaseInviteToApplicant2 sendCaseInviteToApplicant2; + @Autowired + private SetCaseInviteApplicant1 setCaseInviteApplicant1; + @Autowired + private SetCaseInviteApplicant1 setCaseInviteApplicant2; + @Autowired + private EmailUpdatedNotification emailUpdatedNotification; + public CaseDetails processUpdateForApplicant1(final CaseDetails caseDetails) { + return caseTasks( + setCaseInviteApplicant1, + sendCaseInviteToApplicant1 + ).run(caseDetails); + } + + public CaseDetails processUpdateForApplicant2(final CaseDetails caseDetails) { + return caseTasks( + setCaseInviteApplicant2, + sendCaseInviteToApplicant2 + ).run(caseDetails); + } + + public void sendNotificationToOldEmail(final CaseDetails caseDetails, + String newEmail, boolean isApplicant1) { + emailUpdatedNotification.send(caseDetails.getData(), caseDetails.getId(), newEmail, isApplicant1); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant1.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant1.java new file mode 100644 index 00000000000..3472c7dfdd0 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant1.java @@ -0,0 +1,34 @@ +package uk.gov.hmcts.divorce.caseworker.service.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.divorce.common.notification.ReInviteApplicant1Notification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; + +@Component +@Slf4j +public class SendCaseInviteToApplicant1 implements CaseTask { + + @Autowired + ReInviteApplicant1Notification reInviteApplicant1Notification; + + @Autowired + NotificationDispatcher notificationDispatcher; + + @Override + public CaseDetails apply(final CaseDetails details) { + + final CaseData caseData = details.getData(); + final Long caseId = details.getId(); + + notificationDispatcher.send(reInviteApplicant1Notification, caseData, caseId); + + return details; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant2.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant2.java new file mode 100644 index 00000000000..0d0bdad413b --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant2.java @@ -0,0 +1,33 @@ +package uk.gov.hmcts.divorce.caseworker.service.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.divorce.common.notification.ReInviteApplicant2Notification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; + +@Component +@Slf4j +public class SendCaseInviteToApplicant2 implements CaseTask { + + @Autowired + ReInviteApplicant2Notification reInviteApplicant2Notification; + + @Autowired + NotificationDispatcher notificationDispatcher; + + @Override + public CaseDetails apply(final CaseDetails details) { + + final CaseData caseData = details.getData(); + final Long caseId = details.getId(); + + notificationDispatcher.send(reInviteApplicant2Notification, caseData, caseId); + + return details; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant1.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant1.java new file mode 100644 index 00000000000..c629d2b3d46 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant1.java @@ -0,0 +1,31 @@ +package uk.gov.hmcts.divorce.caseworker.service.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; + +import java.time.Clock; +import java.time.LocalDate; + +@Component +@Slf4j +public class SetCaseInviteApplicant1 implements CaseTask { + + @Override + public CaseDetails apply(final CaseDetails details) { + + final CaseData data = details.getData(); + CaseInviteApp1 invite = CaseInviteApp1.builder() + .applicant1InviteEmailAddress(data.getApplicant1().getEmail()) + .build() + .generateAccessCode(); + data.setCaseInviteApp1(invite); + + return details; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant2.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant2.java new file mode 100644 index 00000000000..3ffbfe49499 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant2.java @@ -0,0 +1,30 @@ +package uk.gov.hmcts.divorce.caseworker.service.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; + +import java.time.Clock; + +@Component +@Slf4j +public class SetCaseInviteApplicant2 implements CaseTask { + + @Override + public CaseDetails apply(final CaseDetails details) { + + final CaseData data = details.getData(); + CaseInvite invite = CaseInvite.builder() + .applicant2InviteEmailAddress(data.getApplicant2().getEmail()) + .build() + .generateAccessCode(); + data.setCaseInvite(invite); + + return details; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java new file mode 100644 index 00000000000..12ce5c857ba --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java @@ -0,0 +1,42 @@ +package uk.gov.hmcts.divorce.common.notification; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.util.Map; + +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.CITIZEN_EMAIL_UPDATED; + +@Component +@Slf4j +public class EmailUpdatedNotification { + + @Autowired + private CommonContent commonContent; + + @Autowired + private NotificationService notificationService; + + public void send(final CaseData caseData, final Long caseId, final String newEmail, final boolean isApplicant1) { + + Applicant applicant = isApplicant1 ? caseData.getApplicant1() : caseData.getApplicant2(); + Applicant partner = isApplicant1 ? caseData.getApplicant2() : caseData.getApplicant1(); + Map templateVars = commonContent.mainTemplateVars(caseData, caseId, applicant, partner); + templateVars.put("old email", applicant.getEmail()); + templateVars.put("new email", newEmail); + + notificationService.sendEmail( + applicant.getEmail(), + CITIZEN_EMAIL_UPDATED, + templateVars, + applicant.getLanguagePreference(), + caseId + ); + log.info("Successfully sent email updated notification for case id: {}", caseId); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant1Notification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant1Notification.java new file mode 100644 index 00000000000..2d29c72cca6 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant1Notification.java @@ -0,0 +1,65 @@ +package uk.gov.hmcts.divorce.common.notification; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.divorce.common.config.EmailTemplatesConfig; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.notification.ApplicantNotification; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.util.Map; + +import static uk.gov.hmcts.divorce.notification.CommonContent.*; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_FIRM; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REINVITE_CITIZEN_TO_CASE; +import static uk.gov.hmcts.divorce.notification.FormatUtil.getDateTimeFormatterForPreferredLanguage; + +@Component +@Slf4j +public class ReInviteApplicant1Notification implements ApplicantNotification { + + public static final String APPLICANT_2_SIGN_IN_DIVORCE_URL = "applicant2SignInDivorceUrl"; + public static final String APPLICANT_2_SIGN_IN_DISSOLUTION_URL = "applicant2SignInDissolutionUrl"; + + @Autowired + private CommonContent commonContent; + + @Autowired + private EmailTemplatesConfig config; + + @Autowired + private NotificationService notificationService; + + @Override + public void sendToApplicant1(final CaseData caseData, final Long id) { + + log.info("Notifying applicant 1 to invite to case: {}", id); + + final Applicant applicant1 = caseData.getApplicant1(); + + notificationService.sendEmail( + applicant1.getEmail(), + REINVITE_CITIZEN_TO_CASE, + templateVars(caseData, id, applicant1, caseData.getApplicant2()), + applicant1.getLanguagePreference(), + id + ); + } + + @Override + public void sendToApplicant1Offline(final CaseData caseData, final Long id) { + + } + + private Map templateVars(CaseData caseData, Long id, Applicant applicant, Applicant partner) { + Map templateVars = commonContent.mainTemplateVars(caseData, id, applicant, partner); + templateVars.put(ACCESS_CODE, caseData.getCaseInviteApp1().accessCode()); + templateVars.put(CREATE_ACCOUNT_LINK, + config.getTemplateVars().get(caseData.isDivorce() ? APPLICANT_2_SIGN_IN_DIVORCE_URL : APPLICANT_2_SIGN_IN_DISSOLUTION_URL)); + + return templateVars; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant2Notification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant2Notification.java new file mode 100644 index 00000000000..a1d232003a0 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant2Notification.java @@ -0,0 +1,44 @@ +package uk.gov.hmcts.divorce.common.notification; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.notification.ApplicantNotification; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REINVITE_CITIZEN_TO_CASE; + +@Component +@Slf4j +public class ReInviteApplicant2Notification implements ApplicantNotification { + + @Autowired + private CommonContent commonContent; + + @Autowired + private NotificationService notificationService; + + @Override + public void sendToApplicant2(final CaseData caseData, final Long id) { + + log.info("Notifying applicant 2 to invite to case: {}", id); + + final Applicant applicant2 = caseData.getApplicant2(); + + notificationService.sendEmail( + applicant2.getEmail(), + REINVITE_CITIZEN_TO_CASE, + commonContent.mainTemplateVars(caseData, id, applicant2, caseData.getApplicant1()), + applicant2.getLanguagePreference(), + id + ); + } + + @Override + public void sendToApplicant2Offline(final CaseData caseData, final Long id) { + + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java index 39261e35561..23158f2e20f 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java @@ -126,6 +126,10 @@ public class CaseData { @CCD(access = {DefaultAccess.class}) private CaseInvite caseInvite; + @JsonUnwrapped() + @CCD(access = {DefaultAccess.class}) + private CaseInviteApp1 caseInviteApp1; + @JsonUnwrapped() @Builder.Default private AcknowledgementOfService acknowledgementOfService = new AcknowledgementOfService(); diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java new file mode 100644 index 00000000000..4d1c55ee577 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java @@ -0,0 +1,21 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import lombok.Builder; +import uk.gov.hmcts.divorce.divorcecase.util.AccessCodeGenerator; + +public record CaseInviteApp1( + String applicant1InviteEmailAddress, + String accessCode, + String applicant1UserId) { + + @Builder() + public CaseInviteApp1 {} + + public CaseInviteApp1 generateAccessCode() { + return new CaseInviteApp1(applicant1InviteEmailAddress, AccessCodeGenerator.generateAccessCode(), applicant1UserId); + } + + public CaseInviteApp1 useAccessCode() { + return new CaseInviteApp1(applicant1InviteEmailAddress, null, applicant1UserId); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java b/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java index a889e3d7eac..8933e8fa993 100644 --- a/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java +++ b/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java @@ -62,6 +62,7 @@ public class CommonContent { public static final String CREATE_ACCOUNT_LINK = "create account link"; public static final String SIGN_IN_URL = "signin url"; public static final String WEBFORM_URL = "webformUrl"; + public static final String WEBFORM_CY_URL = "webformCyUrl"; public static final String SIGN_IN_DIVORCE_URL = "signInDivorceUrl"; public static final String SIGN_IN_DISSOLUTION_URL = "signInDissolutionUrl"; public static final String SIGN_IN_PROFESSIONAL_USERS_URL = "signInProfessionalUsersUrl"; @@ -133,6 +134,9 @@ public class CommonContent { public static final String SMART_SURVEY = "smartSurvey"; public static final String GENERAL_FEE = "generalFee"; public static final String FINAL_ORDER_FEE = "fee"; + public static final String WEB_FORM_TEXT = "webformText"; + public static final String CONTACT_TEXT = "[Contact us using our online form]"; + public static final String CONTACT_TEXT_WELSH = "[Cysylltwch รข ni drwy ddefnyddio ein ffurflen ar-lein]"; @Autowired private DocmosisCommonContent docmosisCommonContent; @@ -155,6 +159,8 @@ public Map mainTemplateVars(final CaseData caseData, config.getTemplateVars().get(caseData.isDivorce() ? DIVORCE_COURT_EMAIL : DISSOLUTION_COURT_EMAIL)); templateVars.put(SIGN_IN_URL, getSignInUrl(caseData)); templateVars.put(WEBFORM_URL, config.getTemplateVars().get(WEBFORM_URL)); + templateVars.put(WEB_FORM_TEXT, getContactWebFormText(applicant.getLanguagePreference())); + templateVars.put(SMART_SURVEY, getSmartSurvey()); return templateVars; } @@ -372,4 +378,12 @@ public void setIsDivorceAndIsDissolutionVariables(CaseData caseData, Map { + + @Autowired + private CcdAccessService ccdAccessService; + + @Autowired + private HttpServletRequest httpServletRequest; + + public static final String SYSTEM_LINK_APPLICANT_1 = "system-link-applicant1"; + + @Override + public void configure(final ConfigBuilder configBuilder) { + configBuilder + .event(SYSTEM_LINK_APPLICANT_1) + .forAllStates() + .name("Link Applicant 1 to case") + .description("Link Applicant 1 to case so they are online") + .grant(CREATE_READ_UPDATE, SYSTEMUPDATE) + .retries(120, 120) + .aboutToSubmitCallback(this::aboutToSubmit); + } + + public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails details, + CaseDetails beforeDetails) { + + CaseData data = details.getData(); + + log.info("Linking Applicant 1 to Case {}", data.getHyphenatedCaseRef()); + ccdAccessService.linkApplicant1( + httpServletRequest.getHeader(AUTHORIZATION), + details.getId(), + data.getCaseInviteApp1().applicant1UserId() + ); + + data.setCaseInviteApp1(data.getCaseInviteApp1().useAccessCode()); + data.getApplicant1().setOffline(NO); + + return AboutToStartOrSubmitResponse.builder() + .data(data) + .build(); + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 49ab11f2682..ec08c8e7006 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -55,6 +55,7 @@ uk: divorceCourtEmail: contactdivorce@justice.gov.uk dissolutionCourtEmail: contactdivorce@justice.gov.uk webformUrl: ${WEBFORM_URL:https://contact-us-about-a-divorce-application.form.service.justice.gov.uk/} + webformCyUrl: ${WEBFORM_CY_URL:https://contact-us-about-a-divorce-application-cy.form.service.justice.gov.uk/} smartSurvey: https://www.smartsurvey.co.uk/s/NFD_Feedback/?pageurl=email templates: english: @@ -184,6 +185,8 @@ uk: NOC_TO_SOLS_EMAIL_OLD_SOL: '98c2a7a4-79c4-410c-909d-10db0108fe8d' NFD_APP1_SOLICITOR_APPLIED_FOR_FINAL_ORDER: '1b1f508d-2d91-447c-836b-d9ec0da004b1' AUTOMATED_DAILY_REPORT: '641cb2b6-5895-46c6-886c-8696096e1a43' + REINVITE_CITIZEN_TO_CASE: '4b746cbd-e65d-4686-bc57-d5771c857c67' + CITIZEN_EMAIL_UPDATED: 'a2168463-ca67-4b0b-a511-a76c25576fac' welsh: SAVE_SIGN_OUT: '6e9526c5-9380-4bf1-879a-b8e401e39a6f' OUTSTANDING_ACTIONS: 'd7e14a2c-ede8-4f90-8f6f-f7f09ff920c4' @@ -309,6 +312,8 @@ uk: NOC_TO_SOLS_EMAIL_NEW_SOL: '7fd68c22-f0cc-41ce-af9e-be9f732eab3a' NOC_TO_SOLS_EMAIL_OLD_SOL: '977d2857-dcf1-4bc2-97fb-f9b1e13af907' NFD_APP1_SOLICITOR_APPLIED_FOR_FINAL_ORDER: 'cfeb62ee-7f2c-4612-bf87-35205ccb26b5' + REINVITE_CITIZEN_TO_CASE: '1c682066-7088-4a98-be05-d1ea6cf26f84' + CITIZEN_EMAIL_UPDATED: '981cdc17-4dc2-4561-a02b-df38f40970e5' s2s-authorised: services: ${S2S_AUTHORISED_SERVICES:ccd_data,bulk_scan_processor,bulk_scan_orchestrator} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java new file mode 100644 index 00000000000..46c5e287083 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java @@ -0,0 +1,205 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; +import uk.gov.hmcts.divorce.caseworker.service.IssueApplicationService; +import uk.gov.hmcts.divorce.divorcecase.model.*; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant1Email.CASEWORKER_UPDATE_APP1_EMAIL; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; + +@ExtendWith(MockitoExtension.class) +public class CaseworkerUpdateApplicant1EmailTest { + + @Mock + private EmailUpdateService emailUpdateService; + @InjectMocks + private CaseworkerUpdateApplicant1Email caseworkerUpdateApplicant1Email; + + @Test + void shouldAddConfigurationToConfigBuilder() throws Exception { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + caseworkerUpdateApplicant1Email.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CASEWORKER_UPDATE_APP1_EMAIL); + } + + @Test + void shouldReturnErrorsIfApplicant1EmailHasBeenRemovedInOnlineCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .email(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder().build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()) + .isEqualTo(singletonList("Please use the 'Update offline status' event before removing the email address.")); + } + + @Test + void shouldAllowApplicant1EmailRemovalInOfflineCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .email(TEST_USER_EMAIL) + .offline(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldAllowApplicant1EmailRemovalIfRepresentedCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .email(TEST_USER_EMAIL) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder() + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldCallUpdateEmailServiceAndReturnCaseData() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + final CaseData expectedCaseData = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .build()) + .caseInviteApp1(CaseInviteApp1.builder() + .accessCode("ABCD1234") + .applicant1InviteEmailAddress(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails expectedDetails = new CaseDetails<>(); + expectedDetails.setData(expectedCaseData); + expectedDetails.setId(TEST_CASE_ID); + + when(emailUpdateService.processUpdateForApplicant1(details)).thenReturn(expectedDetails); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, detailsBefore); + + verify(emailUpdateService).processUpdateForApplicant1(details); + verify(emailUpdateService).sendNotificationToOldEmail(detailsBefore, TEST_USER_EMAIL, true); + assertThat(response.getData()).isEqualTo(expectedCaseData); + } + + @Test + void shouldNotCallUpdateEmailServiceWhenRepresented() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, detailsBefore); + + verifyNoInteractions(emailUpdateService); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java new file mode 100644 index 00000000000..3f762bef55c --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java @@ -0,0 +1,202 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; +import uk.gov.hmcts.divorce.divorcecase.model.*; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant2Email.CASEWORKER_UPDATE_APP2_EMAIL; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; + +@ExtendWith(MockitoExtension.class) +public class CaseworkerUpdateApplicant2EmailTest { + + @Mock + private EmailUpdateService emailUpdateService; + @InjectMocks + private CaseworkerUpdateApplicant2Email caseworkerUpdateApplicant2Email; + + @Test + void shouldAddConfigurationToConfigBuilder() throws Exception { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + caseworkerUpdateApplicant2Email.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CASEWORKER_UPDATE_APP2_EMAIL); + } + + @Test + void shouldReturnErrorsIfApplicant2EmailHasBeenRemovedInOnlineCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .email(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder().build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()) + .isEqualTo(singletonList("Please use the 'Update offline status' event before removing the email address.")); + } + + @Test + void shouldAllowApplicant2EmailRemovalInOfflineCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .email(TEST_USER_EMAIL) + .offline(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldAllowApplicant2EmailRemovalIfRepresentedCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .email(TEST_USER_EMAIL) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldCallUpdateEmailServiceAndReturnCaseData() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + final CaseData expectedCaseData = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .build()) + .caseInviteApp1(CaseInviteApp1.builder() + .accessCode("ABCD1234") + .applicant1InviteEmailAddress(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails expectedDetails = new CaseDetails<>(); + expectedDetails.setData(expectedCaseData); + expectedDetails.setId(TEST_CASE_ID); + + when(emailUpdateService.processUpdateForApplicant2(details)).thenReturn(expectedDetails); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, detailsBefore); + + verify(emailUpdateService).processUpdateForApplicant2(details); + verify(emailUpdateService).sendNotificationToOldEmail(detailsBefore, TEST_USER_EMAIL, true); + assertThat(response.getData()).isEqualTo(expectedCaseData); + } + + @Test + void shouldNotCallUpdateEmailServiceWhenRepresented() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, detailsBefore); + + verifyNoInteractions(emailUpdateService); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java new file mode 100644 index 00000000000..c7e095ec7ac --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java @@ -0,0 +1,53 @@ +package uk.gov.hmcts.divorce.caseworker.service; + +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 org.springframework.beans.factory.annotation.Autowired; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.divorce.caseworker.service.task.*; +import uk.gov.hmcts.divorce.common.notification.EmailUpdatedNotification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.document.task.DivorceApplicationRemover; +import uk.gov.hmcts.divorce.systemupdate.service.task.GenerateD84Form; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.LOCAL_DATE_TIME; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; + +@ExtendWith(MockitoExtension.class) +class EmailUpdateServiceTest { + + @Mock + private SetCaseInviteApplicant1 setCaseInviteApplicant1; + @Mock + private SetCaseInviteApplicant2 setCaseInviteApplicant2; + @Mock + private SendCaseInviteToApplicant1 sendCaseInviteToApplicant1; + @Mock + private SendCaseInviteToApplicant2 sendCaseInviteToApplicant2; + @Mock + private EmailUpdatedNotification emailUpdatedNotification; + @InjectMocks + private EmailUpdateService emailUpdateService; + + @Test + void shouldRunProcessUpdateForApplicant1() { + //TBD + } + + @Test + void shouldRunProcessUpdateForApplicant2() { + //TBD + } + + @Test + void shouldSendNotificationToOldEmail() { + //TBD + } +} From 22e6a8869b8cd946095ac987a1163b5ba17dc865 Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Fri, 8 Nov 2024 08:12:37 +0000 Subject: [PATCH 02/16] NFDIV-4356 Send invite to unrepresented applicant when email address is updated. --- .../CaseworkerUpdateApplicant1Email.java | 119 ++++++++++ .../CaseworkerUpdateApplicant2Email.java | 119 ++++++++++ .../service/EmailUpdateService.java | 74 +++++++ .../EmailUpdatedNotification.java | 42 ++++ .../InviteApplicantToCaseNotification.java | 59 +++++ .../divorce/divorcecase/model/CaseData.java | 4 + .../divorcecase/model/CaseInviteApp1.java | 21 ++ .../divorce/notification/CommonContent.java | 1 + .../notification/EmailTemplateName.java | 4 +- .../solicitor/service/CcdAccessService.java | 60 +++-- .../event/SystemLinkApplicant1.java | 64 ++++++ src/main/resources/application.yaml | 4 + .../CaseworkerUpdateApplicant1EmailTest.java | 208 ++++++++++++++++++ .../CaseworkerUpdateApplicant2EmailTest.java | 208 ++++++++++++++++++ .../service/EmailUpdateServiceTest.java | 76 +++++++ .../EmailUpdatedNotificationTest.java | 84 +++++++ ...InviteApplicantToCaseNotificationTest.java | 110 +++++++++ .../service/CcdAccessServiceTest.java | 45 ++++ .../event/SystemLinkApplicant1Test.java | 75 +++++++ 19 files changed, 1355 insertions(+), 22 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotificationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1Test.java diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java new file mode 100644 index 00000000000..0b8d6671049 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java @@ -0,0 +1,119 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import static java.util.Collections.singletonList; +import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE; + +@Component +@Slf4j +public class CaseworkerUpdateApplicant1Email implements CCDConfig { + + public static final String CASEWORKER_UPDATE_APP1_EMAIL = "caseworker-update-app1-email"; + + @Autowired + private EmailUpdateService emailUpdateService; + + private static final String EMAIL_LABEL = "${%s} email address"; + private static final String APPLICANTS_OR_APPLICANT1S = "labelContentApplicantsOrApplicant1s"; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CASEWORKER_UPDATE_APP1_EMAIL) + .forStates(POST_SUBMISSION_STATES) + .name("Update applicant1 email") + .description("Update applicant/applicant1 email") + .aboutToSubmitCallback(this::aboutToSubmit) + .showSummary() + .showEventNotes() + .grant(CREATE_READ_UPDATE_DELETE, SUPER_USER, CASE_WORKER) + .grantHistoryOnly( + SOLICITOR, + LEGAL_ADVISOR)) + .page("updateApp1Email") + .pageLabel("Update applicant/applicant1 email") + .complex(CaseData::getApplicant1) + .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, APPLICANTS_OR_APPLICANT1S)) + .done(); + } + + public AboutToStartOrSubmitResponse midEvent(final CaseDetails details, + final CaseDetails detailsBefore) { + log.info("midEvent callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId()); + + CaseData caseData = details.getData(); + CaseData caseDataBefore = detailsBefore.getData(); + + if (!validApplicant1ContactDetails(caseDataBefore, caseData)) { + + return AboutToStartOrSubmitResponse.builder() + .errors(singletonList("Please use the 'Update offline status' event before removing the email address.")) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .build(); + } + + public AboutToStartOrSubmitResponse aboutToSubmit( + final CaseDetails details, + final CaseDetails beforeDetails + ) { + log.info("aboutToSubmit callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId()); + + CaseData caseData = details.getData(); + CaseData caseDataBefore = beforeDetails.getData(); + + if (!caseData.getApplicant1().isRepresented()) { + final CaseDetails result = emailUpdateService.processUpdateForApplicant1(details); + String newEmail = caseData.getApplicant1().getEmail(); + + if (caseDataBefore.getApplicant1().getEmail() != null + && !caseDataBefore.getApplicant1().getEmail().isBlank()) { + emailUpdateService.sendNotificationToOldEmail(beforeDetails, newEmail, true); + } + return AboutToStartOrSubmitResponse.builder() + .data(result.getData()) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .build(); + } + + private boolean validApplicant1ContactDetails(CaseData caseDataBefore, CaseData caseData) { + + if (caseDataBefore.getApplicant1().getEmail() != null && !caseDataBefore.getApplicant1().getEmail().isBlank()) { + if (!caseDataBefore.getApplicant1().isRepresented() + && !caseData.getApplicant1().isApplicantOffline() + && (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank())) { + return false; + } + } + return true; + } + + private String getLabel(final String label, final Object... value) { + return String.format(label, value); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java new file mode 100644 index 00000000000..9d7931218c1 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java @@ -0,0 +1,119 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import static java.util.Collections.singletonList; +import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE; + +@Component +@Slf4j +public class CaseworkerUpdateApplicant2Email implements CCDConfig { + + public static final String CASEWORKER_UPDATE_APP2_EMAIL = "caseworker-update-app2-email"; + + @Autowired + private EmailUpdateService emailUpdateService; + + private static final String EMAIL_LABEL = "${%s} email address"; + private static final String RESPONDENTS_OR_APPLICANT2S = "labelContentRespondentsOrApplicant2s"; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CASEWORKER_UPDATE_APP2_EMAIL) + .forStates(POST_SUBMISSION_STATES) + .name("Update applicant2 email") + .description("Update respondent/applicant2 email") + .aboutToSubmitCallback(this::aboutToSubmit) + .showSummary() + .showEventNotes() + .grant(CREATE_READ_UPDATE_DELETE, SUPER_USER, CASE_WORKER) + .grantHistoryOnly( + SOLICITOR, + LEGAL_ADVISOR)) + .page("updateApp2Email") + .pageLabel("Update respondent/applicant2 email") + .complex(CaseData::getApplicant2) + .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, RESPONDENTS_OR_APPLICANT2S)) + .done(); + } + + public AboutToStartOrSubmitResponse midEvent(final CaseDetails details, + final CaseDetails detailsBefore) { + log.info("midEvent callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId()); + + CaseData caseData = details.getData(); + CaseData caseDataBefore = detailsBefore.getData(); + + if (!validApplicant2ContactDetails(caseDataBefore, caseData)) { + + return AboutToStartOrSubmitResponse.builder() + .errors(singletonList("Please use the 'Update offline status' event before removing the email address.")) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .build(); + } + + public AboutToStartOrSubmitResponse aboutToSubmit( + final CaseDetails details, + final CaseDetails beforeDetails + ) { + log.info("aboutToSubmit callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId()); + + CaseData caseData = details.getData(); + CaseData caseDataBefore = beforeDetails.getData(); + + if (!caseData.getApplicant2().isRepresented()) { + final CaseDetails result = emailUpdateService.processUpdateForApplicant2(details); + String newEmail = caseData.getApplicant2().getEmail(); + + if (caseDataBefore.getApplicant2().getEmail() != null + && !caseDataBefore.getApplicant2().getEmail().isBlank()) { + emailUpdateService.sendNotificationToOldEmail(beforeDetails, newEmail, false); + } + return AboutToStartOrSubmitResponse.builder() + .data(result.getData()) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .build(); + } + + private boolean validApplicant2ContactDetails(CaseData caseDataBefore, CaseData caseData) { + + if (caseDataBefore.getApplicant2().getEmail() != null && !caseDataBefore.getApplicant2().getEmail().isBlank()) { + if (!caseDataBefore.getApplicant2().isRepresented() + && !caseData.getApplicant2().isApplicantOffline() + && (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank())) { + return false; + } + } + return true; + } + + private String getLabel(final String label, final Object... value) { + return String.format(label, value); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java new file mode 100644 index 00000000000..b778e76e010 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java @@ -0,0 +1,74 @@ +package uk.gov.hmcts.divorce.caseworker.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.divorce.common.notification.EmailUpdatedNotification; +import uk.gov.hmcts.divorce.common.notification.InviteApplicantToCaseNotification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; + +@Service +@Slf4j +public class EmailUpdateService { + + @Autowired + InviteApplicantToCaseNotification inviteApplicantToCaseNotification; + @Autowired + EmailUpdatedNotification emailUpdatedNotification; + @Autowired + NotificationDispatcher notificationDispatcher; + + public CaseDetails processUpdateForApplicant1(final CaseDetails caseDetails) { + + final CaseData data = caseDetails.getData(); + + CaseInviteApp1 invite = CaseInviteApp1.builder() + .applicant1InviteEmailAddress(data.getApplicant1().getEmail()) + .build() + .generateAccessCode(); + data.setCaseInviteApp1(invite); + + triggerNotificationToApplicant1(caseDetails); + + return caseDetails; + } + + public CaseDetails processUpdateForApplicant2(final CaseDetails caseDetails) { + + final CaseData data = caseDetails.getData(); + + CaseInvite invite = CaseInvite.builder() + .applicant2InviteEmailAddress(data.getApplicant2().getEmail()) + .build() + .generateAccessCode(); + data.setCaseInvite(invite); + + triggerNotificationToApplicant2(caseDetails); + + return caseDetails; + } + + public void triggerNotificationToApplicant1(final CaseDetails caseDetails) { + final CaseData caseData = caseDetails.getData(); + final Long caseId = caseDetails.getId(); + + inviteApplicantToCaseNotification.send(caseData, caseId, true); + } + + public void triggerNotificationToApplicant2(final CaseDetails caseDetails) { + final CaseData caseData = caseDetails.getData(); + final Long caseId = caseDetails.getId(); + + inviteApplicantToCaseNotification.send(caseData, caseId, false); + } + + public void sendNotificationToOldEmail(final CaseDetails caseDetails, + String newEmail, boolean isApplicant1) { + emailUpdatedNotification.send(caseDetails.getData(), caseDetails.getId(), newEmail, isApplicant1); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java new file mode 100644 index 00000000000..12ce5c857ba --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java @@ -0,0 +1,42 @@ +package uk.gov.hmcts.divorce.common.notification; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.util.Map; + +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.CITIZEN_EMAIL_UPDATED; + +@Component +@Slf4j +public class EmailUpdatedNotification { + + @Autowired + private CommonContent commonContent; + + @Autowired + private NotificationService notificationService; + + public void send(final CaseData caseData, final Long caseId, final String newEmail, final boolean isApplicant1) { + + Applicant applicant = isApplicant1 ? caseData.getApplicant1() : caseData.getApplicant2(); + Applicant partner = isApplicant1 ? caseData.getApplicant2() : caseData.getApplicant1(); + Map templateVars = commonContent.mainTemplateVars(caseData, caseId, applicant, partner); + templateVars.put("old email", applicant.getEmail()); + templateVars.put("new email", newEmail); + + notificationService.sendEmail( + applicant.getEmail(), + CITIZEN_EMAIL_UPDATED, + templateVars, + applicant.getLanguagePreference(), + caseId + ); + log.info("Successfully sent email updated notification for case id: {}", caseId); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java new file mode 100644 index 00000000000..14050098829 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java @@ -0,0 +1,59 @@ +package uk.gov.hmcts.divorce.common.notification; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.divorce.common.config.EmailTemplatesConfig; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.util.Map; + +import static uk.gov.hmcts.divorce.notification.CommonContent.ACCESS_CODE; +import static uk.gov.hmcts.divorce.notification.CommonContent.CREATE_ACCOUNT_LINK; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REINVITE_CITIZEN_TO_CASE; + +@Component +@Slf4j +public class InviteApplicantToCaseNotification { + + @Autowired + private CommonContent commonContent; + + @Autowired + private EmailTemplatesConfig config; + + @Autowired + private NotificationService notificationService; + + public static final String APPLICANT_2_SIGN_IN_DIVORCE_URL = "applicant2SignInDivorceUrl"; + public static final String APPLICANT_2_SIGN_IN_DISSOLUTION_URL = "applicant2SignInDissolutionUrl"; + + public void send(final CaseData caseData, final Long caseId, final boolean isApplicant1) { + + Applicant applicant = isApplicant1 ? caseData.getApplicant1() : caseData.getApplicant2(); + Applicant partner = isApplicant1 ? caseData.getApplicant2() : caseData.getApplicant1(); + + Map templateVars = templateVars(caseData,caseId,applicant,partner); + templateVars.put(ACCESS_CODE, isApplicant1 ? caseData.getCaseInviteApp1().accessCode() + : caseData.getCaseInvite().accessCode()); + + notificationService.sendEmail( + applicant.getEmail(), + REINVITE_CITIZEN_TO_CASE, + templateVars, + applicant.getLanguagePreference(), + caseId + ); + } + + private Map templateVars(CaseData caseData, Long id, Applicant applicant, Applicant partner) { + Map templateVars = commonContent.mainTemplateVars(caseData, id, applicant, partner); + templateVars.put(CREATE_ACCOUNT_LINK, + config.getTemplateVars().get(caseData.isDivorce() ? APPLICANT_2_SIGN_IN_DIVORCE_URL : APPLICANT_2_SIGN_IN_DISSOLUTION_URL)); + + return templateVars; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java index 39261e35561..23158f2e20f 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java @@ -126,6 +126,10 @@ public class CaseData { @CCD(access = {DefaultAccess.class}) private CaseInvite caseInvite; + @JsonUnwrapped() + @CCD(access = {DefaultAccess.class}) + private CaseInviteApp1 caseInviteApp1; + @JsonUnwrapped() @Builder.Default private AcknowledgementOfService acknowledgementOfService = new AcknowledgementOfService(); diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java new file mode 100644 index 00000000000..4d1c55ee577 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java @@ -0,0 +1,21 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import lombok.Builder; +import uk.gov.hmcts.divorce.divorcecase.util.AccessCodeGenerator; + +public record CaseInviteApp1( + String applicant1InviteEmailAddress, + String accessCode, + String applicant1UserId) { + + @Builder() + public CaseInviteApp1 {} + + public CaseInviteApp1 generateAccessCode() { + return new CaseInviteApp1(applicant1InviteEmailAddress, AccessCodeGenerator.generateAccessCode(), applicant1UserId); + } + + public CaseInviteApp1 useAccessCode() { + return new CaseInviteApp1(applicant1InviteEmailAddress, null, applicant1UserId); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java b/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java index f1117220e0d..8669c9a79e3 100644 --- a/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java +++ b/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java @@ -159,6 +159,7 @@ public Map mainTemplateVars(final CaseData caseData, config.getTemplateVars().get(caseData.isDivorce() ? DIVORCE_COURT_EMAIL : DISSOLUTION_COURT_EMAIL)); templateVars.put(SIGN_IN_URL, getSignInUrl(caseData)); templateVars.put(WEBFORM_URL, config.getTemplateVars().get(WEBFORM_URL)); + templateVars.put(WEB_FORM_TEXT, getContactWebFormText(applicant.getLanguagePreference())); templateVars.put(SMART_SURVEY, getSmartSurvey()); return templateVars; } diff --git a/src/main/java/uk/gov/hmcts/divorce/notification/EmailTemplateName.java b/src/main/java/uk/gov/hmcts/divorce/notification/EmailTemplateName.java index 524913a05c5..48b4ea47262 100644 --- a/src/main/java/uk/gov/hmcts/divorce/notification/EmailTemplateName.java +++ b/src/main/java/uk/gov/hmcts/divorce/notification/EmailTemplateName.java @@ -125,5 +125,7 @@ public enum EmailTemplateName { NOC_TO_SOLS_EMAIL_NEW_SOL, NOC_TO_SOLS_EMAIL_OLD_SOL, NFD_APP1_SOLICITOR_APPLIED_FOR_FINAL_ORDER, - AUTOMATED_DAILY_REPORT + AUTOMATED_DAILY_REPORT, + REINVITE_CITIZEN_TO_CASE, + CITIZEN_EMAIL_UPDATED } diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessService.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessService.java index fe228d682fb..235f572e7e9 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessService.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessService.java @@ -2,6 +2,7 @@ import feign.FeignException; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; @@ -99,6 +100,19 @@ public void linkRespondentToApplication(String caseworkerUserToken, Long caseId, log.info("Successfully linked applicant 2 to case Id {} ", caseId); } + @Retryable(value = {FeignException.class, RuntimeException.class}) + public void linkApplicant1(String caseworkerUserToken, Long caseId, String applicant1UserId) { + User systemUpdateUser = idamService.retrieveUser(caseworkerUserToken); + + caseAssignmentApi.addCaseUserRoles( + systemUpdateUser.getAuthToken(), + authTokenGenerator.generate(), + getCaseAssignmentRequest(caseId, applicant1UserId, null, CREATOR) + ); + + log.info("Successfully linked applicant 1 to case Id {} ", caseId); + } + @Retryable(value = {FeignException.class, RuntimeException.class}) public void unlinkApplicant2FromCase(Long caseId, String userToRemoveId) { User caseworkerUser = idamService.retrieveSystemUpdateUserDetails(); @@ -135,38 +149,42 @@ public void unlinkUserFromCase(Long caseId, String userToRemoveId) { @Retryable(value = {FeignException.class, RuntimeException.class}) public boolean isApplicant1(String userToken, Long caseId) { - log.info("Retrieving roles for user on case {}", caseId); - User user = idamService.retrieveUser(userToken); - List userRoles = - caseAssignmentApi.getUserRoles( - userToken, - authTokenGenerator.generate(), - List.of(String.valueOf(caseId)), - List.of(user.getUserDetails().getUid()) - ) - .getCaseAssignmentUserRoles() - .stream() - .map(CaseAssignmentUserRole::getCaseRole) - .collect(Collectors.toList()); - return userRoles.contains(CREATOR.getRole()) || userRoles.contains(APPLICANT_1_SOLICITOR.getRole()); + return hasUserRole(userToken, caseId, List.of(CREATOR, APPLICANT_1_SOLICITOR)); } @Retryable(value = {FeignException.class, RuntimeException.class}) public boolean isApplicant2(String userToken, Long caseId) { + return hasUserRole(userToken, caseId, List.of(APPLICANT_2, APPLICANT_2_SOLICITOR)); + } + + @Retryable(value = {FeignException.class, RuntimeException.class}) + public boolean hasCreatorRole(String userToken, Long caseId) { + return hasUserRole(userToken, caseId, (List.of(CREATOR))); + } + + boolean hasUserRole(String userToken, Long caseId, List roleMatches) { + List userRoles = fetchUserRoles(caseId, userToken); + List roleMatchStrings = roleMatches.stream() + .map(UserRole::getRole) + .collect(Collectors.toList()); + return CollectionUtils.isNotEmpty(userRoles) + && userRoles.stream().anyMatch(roleMatchStrings::contains); + } + + private List fetchUserRoles(Long caseId, String userToken) { log.info("Retrieving roles for user on case {}", caseId); User user = idamService.retrieveUser(userToken); - List userRoles = - caseAssignmentApi.getUserRoles( + List userRoles = caseAssignmentApi.getUserRoles( userToken, authTokenGenerator.generate(), List.of(String.valueOf(caseId)), List.of(user.getUserDetails().getUid()) ) - .getCaseAssignmentUserRoles() - .stream() - .map(CaseAssignmentUserRole::getCaseRole) - .collect(Collectors.toList()); - return userRoles.contains(APPLICANT_2.getRole()) || userRoles.contains(APPLICANT_2_SOLICITOR.getRole()); + .getCaseAssignmentUserRoles() + .stream() + .map(CaseAssignmentUserRole::getCaseRole) + .collect(Collectors.toList()); + return userRoles; } public void removeUsersWithRole(Long caseId, List roles) { diff --git a/src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java b/src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java new file mode 100644 index 00000000000..1ca5e7aaf3e --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java @@ -0,0 +1,64 @@ +package uk.gov.hmcts.divorce.systemupdate.event; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.solicitor.service.CcdAccessService; + +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SYSTEMUPDATE; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; + +@Slf4j +@Component +public class SystemLinkApplicant1 implements CCDConfig { + + @Autowired + private CcdAccessService ccdAccessService; + + @Autowired + private HttpServletRequest httpServletRequest; + + public static final String SYSTEM_LINK_APPLICANT_1 = "system-link-applicant1"; + + @Override + public void configure(final ConfigBuilder configBuilder) { + configBuilder + .event(SYSTEM_LINK_APPLICANT_1) + .forAllStates() + .name("Link Applicant 1 to case") + .description("Link Applicant 1 to case so they are online") + .grant(CREATE_READ_UPDATE, SYSTEMUPDATE) + .retries(120, 120) + .aboutToSubmitCallback(this::aboutToSubmit); + } + + public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails details, + CaseDetails beforeDetails) { + + CaseData data = details.getData(); + + log.info("Linking Applicant 1 to Case {}", data.getHyphenatedCaseRef()); + ccdAccessService.linkApplicant1( + httpServletRequest.getHeader(AUTHORIZATION), + details.getId(), + data.getCaseInviteApp1().applicant1UserId() + ); + + data.setCaseInviteApp1(data.getCaseInviteApp1().useAccessCode()); + data.getApplicant1().setOffline(NO); + + return AboutToStartOrSubmitResponse.builder() + .data(data) + .build(); + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 0167dd028a4..4e5391e3742 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -185,6 +185,8 @@ uk: NOC_TO_SOLS_EMAIL_OLD_SOL: '98c2a7a4-79c4-410c-909d-10db0108fe8d' NFD_APP1_SOLICITOR_APPLIED_FOR_FINAL_ORDER: '1b1f508d-2d91-447c-836b-d9ec0da004b1' AUTOMATED_DAILY_REPORT: '641cb2b6-5895-46c6-886c-8696096e1a43' + REINVITE_CITIZEN_TO_CASE: '4b746cbd-e65d-4686-bc57-d5771c857c67' + CITIZEN_EMAIL_UPDATED: 'a2168463-ca67-4b0b-a511-a76c25576fac' welsh: SAVE_SIGN_OUT: '6e9526c5-9380-4bf1-879a-b8e401e39a6f' OUTSTANDING_ACTIONS: 'd7e14a2c-ede8-4f90-8f6f-f7f09ff920c4' @@ -310,6 +312,8 @@ uk: NOC_TO_SOLS_EMAIL_NEW_SOL: '5fb73af1-33e6-4d0c-ab39-6c32263cbf43' NOC_TO_SOLS_EMAIL_OLD_SOL: '977d2857-dcf1-4bc2-97fb-f9b1e13af907' NFD_APP1_SOLICITOR_APPLIED_FOR_FINAL_ORDER: 'cfeb62ee-7f2c-4612-bf87-35205ccb26b5' + REINVITE_CITIZEN_TO_CASE: '1c682066-7088-4a98-be05-d1ea6cf26f84' + CITIZEN_EMAIL_UPDATED: '981cdc17-4dc2-4561-a02b-df38f40970e5' s2s-authorised: services: ${S2S_AUTHORISED_SERVICES:ccd_data,bulk_scan_processor,bulk_scan_orchestrator} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java new file mode 100644 index 00000000000..9bf34f98bef --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java @@ -0,0 +1,208 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant1Email.CASEWORKER_UPDATE_APP1_EMAIL; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; + +@ExtendWith(MockitoExtension.class) +public class CaseworkerUpdateApplicant1EmailTest { + + @Mock + private EmailUpdateService emailUpdateService; + @InjectMocks + private CaseworkerUpdateApplicant1Email caseworkerUpdateApplicant1Email; + + @Test + void shouldAddConfigurationToConfigBuilder() throws Exception { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + caseworkerUpdateApplicant1Email.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CASEWORKER_UPDATE_APP1_EMAIL); + } + + @Test + void shouldReturnErrorsIfApplicant1EmailHasBeenRemovedInOnlineCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .email(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder().build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()) + .isEqualTo(singletonList("Please use the 'Update offline status' event before removing the email address.")); + } + + @Test + void shouldAllowApplicant1EmailRemovalInOfflineCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .email(TEST_USER_EMAIL) + .offline(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldAllowApplicant1EmailRemovalIfRepresentedCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .email(TEST_USER_EMAIL) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder() + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldCallUpdateEmailServiceAndReturnCaseData() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + final CaseData expectedCaseData = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .build()) + .caseInviteApp1(CaseInviteApp1.builder() + .accessCode("ABCD1234") + .applicant1InviteEmailAddress(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails expectedDetails = new CaseDetails<>(); + expectedDetails.setData(expectedCaseData); + expectedDetails.setId(TEST_CASE_ID); + + when(emailUpdateService.processUpdateForApplicant1(details)).thenReturn(expectedDetails); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, detailsBefore); + + verify(emailUpdateService).processUpdateForApplicant1(details); + verify(emailUpdateService).sendNotificationToOldEmail(detailsBefore, TEST_USER_EMAIL, true); + assertThat(response.getData()).isEqualTo(expectedCaseData); + } + + @Test + void shouldNotCallUpdateEmailServiceWhenRepresented() { + final CaseData caseDataBefore = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, detailsBefore); + + verifyNoInteractions(emailUpdateService); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java new file mode 100644 index 00000000000..361c07ae574 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java @@ -0,0 +1,208 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant2Email.CASEWORKER_UPDATE_APP2_EMAIL; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; + +@ExtendWith(MockitoExtension.class) +public class CaseworkerUpdateApplicant2EmailTest { + + @Mock + private EmailUpdateService emailUpdateService; + @InjectMocks + private CaseworkerUpdateApplicant2Email caseworkerUpdateApplicant2Email; + + @Test + void shouldAddConfigurationToConfigBuilder() throws Exception { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + caseworkerUpdateApplicant2Email.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CASEWORKER_UPDATE_APP2_EMAIL); + } + + @Test + void shouldReturnErrorsIfApplicant2EmailHasBeenRemovedInOnlineCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .email(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder().build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()) + .isEqualTo(singletonList("Please use the 'Update offline status' event before removing the email address.")); + } + + @Test + void shouldAllowApplicant2EmailRemovalInOfflineCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .email(TEST_USER_EMAIL) + .offline(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldAllowApplicant2EmailRemovalIfRepresentedCase() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .email(TEST_USER_EMAIL) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldCallUpdateEmailServiceAndReturnCaseData() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + final CaseData expectedCaseData = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .build()) + .caseInvite(CaseInvite.builder() + .accessCode("ABCD1234") + .applicant2InviteEmailAddress(TEST_USER_EMAIL) + .build()) + .build(); + + final CaseDetails expectedDetails = new CaseDetails<>(); + expectedDetails.setData(expectedCaseData); + expectedDetails.setId(TEST_CASE_ID); + + when(emailUpdateService.processUpdateForApplicant2(details)).thenReturn(expectedDetails); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, detailsBefore); + + verify(emailUpdateService).processUpdateForApplicant2(details); + verify(emailUpdateService).sendNotificationToOldEmail(detailsBefore, TEST_USER_EMAIL, true); + assertThat(response.getData()).isEqualTo(expectedCaseData); + } + + @Test + void shouldNotCallUpdateEmailServiceWhenRepresented() { + final CaseData caseDataBefore = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails detailsBefore = new CaseDetails<>(); + detailsBefore.setId(TEST_CASE_ID); + detailsBefore.setData(caseDataBefore); + + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .offline(YES) + .email(TEST_USER_EMAIL) + .solicitorRepresented(YES) + .build()) + .build(); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, detailsBefore); + + verifyNoInteractions(emailUpdateService); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java new file mode 100644 index 00000000000..ecf88f7d8b2 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java @@ -0,0 +1,76 @@ +package uk.gov.hmcts.divorce.caseworker.service; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.divorce.common.notification.EmailUpdatedNotification; +import uk.gov.hmcts.divorce.common.notification.InviteApplicantToCaseNotification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.validApplicant1CaseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.validApplicant2CaseData; + +@ExtendWith(MockitoExtension.class) +class EmailUpdateServiceTest { + @Mock + InviteApplicantToCaseNotification inviteApplicantToCaseNotification; + @Mock + EmailUpdatedNotification emailUpdatedNotification; + @Mock + NotificationDispatcher notificationDispatcher; + @InjectMocks + private EmailUpdateService emailUpdateService; + + @Test + void shouldSetCaseInviteForApp1AndTriggerNotification() { + final CaseData caseData = validApplicant1CaseData(); + + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + final CaseDetails newDetails = emailUpdateService.processUpdateForApplicant1(details); + + verify(inviteApplicantToCaseNotification).send(caseData, TEST_CASE_ID, true); + assertThat(newDetails.getData().getCaseInviteApp1().accessCode()).isNotBlank(); + assertThat(newDetails.getData().getCaseInviteApp1().accessCode().length()).isEqualTo(8); + assertThat(newDetails.getData().getCaseInviteApp1().accessCode()).doesNotContain("I", "O", "U", "0", "1"); + } + + @Test + void shouldSetCaseInviteForApp2AndTriggerNotification() { + final CaseData caseData = validApplicant2CaseData(); + + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + final CaseDetails newDetails = emailUpdateService.processUpdateForApplicant1(details); + + verify(inviteApplicantToCaseNotification).send(caseData, TEST_CASE_ID, false); + assertThat(newDetails.getData().getCaseInvite().accessCode()).isNotBlank(); + assertThat(newDetails.getData().getCaseInvite().accessCode().length()).isEqualTo(8); + assertThat(newDetails.getData().getCaseInvite().accessCode()).doesNotContain("I", "O", "U", "0", "1"); + } + + @Test + void shouldSendNotificationToOldEmail() { + final CaseData caseData = validApplicant1CaseData(); + + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + emailUpdateService.sendNotificationToOldEmail(details,"test@test.com", true); + + verify(emailUpdatedNotification).send(caseData,TEST_CASE_ID,"test@test.com", true); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotificationTest.java b/src/test/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotificationTest.java new file mode 100644 index 00000000000..13bad1b41a1 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotificationTest.java @@ -0,0 +1,84 @@ +package uk.gov.hmcts.divorce.common.notification; + +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.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.Gender; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.collection.IsMapContaining.hasEntry; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; +import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.CITIZEN_EMAIL_UPDATED; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getApplicant2; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getMainTemplateVars; + + +@ExtendWith(MockitoExtension.class) +class EmailUpdatedNotificationTest { + + @Mock + private NotificationService notificationService; + + @Mock + private CommonContent commonContent; + + @InjectMocks + private EmailUpdatedNotification notification; + + @Test + void shouldSendEmailToApplicant1() { + CaseData data = caseData(); + data.setApplicant2(getApplicant2(Gender.MALE)); + + when(commonContent.mainTemplateVars(data, TEST_CASE_ID, data.getApplicant1(), data.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + notification.send(data, TEST_CASE_ID, "test@test.com", true); + + verify(notificationService).sendEmail( + eq(TEST_USER_EMAIL), + eq(CITIZEN_EMAIL_UPDATED), + argThat(allOf( + hasEntry("old email", TEST_USER_EMAIL), + hasEntry("new email", "test@test.com") + )), + eq(ENGLISH), + eq(TEST_CASE_ID) + ); + } + + @Test + void shouldSendEmailToApplicant2() { + CaseData data = caseData(); + data.setApplicant2(getApplicant2(Gender.MALE)); + data.getApplicant2().setEmail("oldemail@test.com"); + + when(commonContent.mainTemplateVars(data, TEST_CASE_ID, data.getApplicant2(), data.getApplicant1())) + .thenReturn(getMainTemplateVars()); + + notification.send(data, TEST_CASE_ID, "newemail@test.com", false); + + verify(notificationService).sendEmail( + eq("oldemail@test.com"), + eq(CITIZEN_EMAIL_UPDATED), + argThat(allOf( + hasEntry("old email", "oldemail@test.com"), + hasEntry("new email", "newemail@test.com") + )), + eq(ENGLISH), + eq(TEST_CASE_ID) + ); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java b/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java new file mode 100644 index 00000000000..1786fe881d6 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java @@ -0,0 +1,110 @@ +package uk.gov.hmcts.divorce.common.notification; + +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.divorce.common.config.EmailTemplatesConfig; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; +import uk.gov.hmcts.divorce.divorcecase.model.Gender; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.collection.IsMapContaining.hasEntry; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; +import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.notification.CommonContent.ACCESS_CODE; +import static uk.gov.hmcts.divorce.notification.CommonContent.CREATE_ACCOUNT_LINK; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REINVITE_CITIZEN_TO_CASE; +import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getApplicant2; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getConfigTemplateVars; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getMainTemplateVars; + + +@ExtendWith(MockitoExtension.class) +class InviteApplicantToCaseNotificationTest { + + @Mock + private NotificationService notificationService; + + @Mock + private CommonContent commonContent; + + @Mock + private EmailTemplatesConfig config; + + @InjectMocks + private InviteApplicantToCaseNotification notification; + + @Test + void shouldSendCaseInviteToApplicant1() { + CaseData data = caseData(); + data.setApplicant2(getApplicant2(Gender.MALE)); + + CaseInviteApp1 invite = CaseInviteApp1.builder() + .applicant1InviteEmailAddress(data.getApplicant1().getEmail()) + .accessCode("12345678") + .build(); + data.setCaseInviteApp1(invite); + + when(commonContent.mainTemplateVars(data, TEST_CASE_ID, data.getApplicant1(), data.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(config.getTemplateVars()).thenReturn(getConfigTemplateVars()); + + notification.send(data, TEST_CASE_ID, true); + + verify(notificationService).sendEmail( + eq(TEST_USER_EMAIL), + eq(REINVITE_CITIZEN_TO_CASE), + argThat(allOf( + hasEntry(ACCESS_CODE, "12345678"), + hasEntry(CREATE_ACCOUNT_LINK, APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL) + )), + eq(ENGLISH), + eq(TEST_CASE_ID) + ); + } + + @Test + void shouldSendCaseInviteToApplicant2() { + CaseData data = caseData(); + data.setApplicant2(getApplicant2(Gender.MALE)); + data.getApplicant2().setEmail(TEST_USER_EMAIL); + + CaseInvite invite = CaseInvite.builder() + .applicant2InviteEmailAddress(data.getApplicant2().getEmail()) + .accessCode("12345678") + .build(); + data.setCaseInvite(invite); + + when(commonContent.mainTemplateVars(data, TEST_CASE_ID, data.getApplicant2(), data.getApplicant1())) + .thenReturn(getMainTemplateVars()); + + when(config.getTemplateVars()).thenReturn(getConfigTemplateVars()); + + notification.send(data, TEST_CASE_ID, false); + + verify(notificationService).sendEmail( + eq(TEST_USER_EMAIL), + eq(REINVITE_CITIZEN_TO_CASE), + argThat(allOf( + hasEntry(ACCESS_CODE, "12345678"), + hasEntry(CREATE_ACCOUNT_LINK, APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL) + )), + eq(ENGLISH), + eq(TEST_CASE_ID) + ); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceTest.java index 005e2d02b44..a50e48c1ec2 100644 --- a/src/test/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceTest.java @@ -23,6 +23,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; @@ -31,6 +33,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_1_SOLICITOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_2; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CREATOR; import static uk.gov.hmcts.divorce.testutil.TestConstants.APP_1_SOL_AUTH_TOKEN; @@ -407,4 +410,46 @@ private User getIdamUser(String authToken, String userId, String email) { UserInfo.builder().uid(userId).sub(email).build() ); } + + @Test + void shouldReturnTrueCallingHasCreatorRole() throws NoSuchMethodException { + String userToken = "Bearer SystemUpdateAuthToken"; + User user = new User(TEST_SERVICE_AUTH_TOKEN, UserInfo.builder().uid("user-id").build()); + when(idamService.retrieveUser(SYSTEM_UPDATE_AUTH_TOKEN)).thenReturn(user); + when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN); + when(caseAssignmentApi.getUserRoles( + SYSTEM_UPDATE_AUTH_TOKEN, + TEST_SERVICE_AUTH_TOKEN, + List.of(TEST_CASE_ID.toString()), + List.of("user-id") + )).thenReturn(CaseAssignmentUserRolesResource.builder() + .caseAssignmentUserRoles(List.of( + CaseAssignmentUserRole.builder().caseRole(CREATOR.getRole()).build() + )).build() + ); + + boolean result = ccdAccessService.hasCreatorRole(userToken, TEST_CASE_ID); + assertTrue(result); + } + + @Test + void shouldReturnFalseCallingHasCreatorRole() throws NoSuchMethodException { + String userToken = "Bearer SystemUpdateAuthToken"; + User user = new User(TEST_SERVICE_AUTH_TOKEN, UserInfo.builder().uid("user-id").build()); + when(idamService.retrieveUser(SYSTEM_UPDATE_AUTH_TOKEN)).thenReturn(user); + when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN); + when(caseAssignmentApi.getUserRoles( + SYSTEM_UPDATE_AUTH_TOKEN, + TEST_SERVICE_AUTH_TOKEN, + List.of(TEST_CASE_ID.toString()), + List.of("user-id") + )).thenReturn(CaseAssignmentUserRolesResource.builder() + .caseAssignmentUserRoles(List.of( + CaseAssignmentUserRole.builder().caseRole(APPLICANT_1_SOLICITOR.getRole()).build() + )).build() + ); + + boolean result = ccdAccessService.hasCreatorRole(userToken, TEST_CASE_ID); + assertFalse(result); + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1Test.java b/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1Test.java new file mode 100644 index 00000000000..fa87c8597bc --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1Test.java @@ -0,0 +1,75 @@ +package uk.gov.hmcts.divorce.systemupdate.event; + +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.solicitor.service.CcdAccessService; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static uk.gov.hmcts.divorce.systemupdate.event.SystemLinkApplicant1.SYSTEM_LINK_APPLICANT_1; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; + +@ExtendWith(SpringExtension.class) +public class SystemLinkApplicant1Test { + + @Mock + private CcdAccessService ccdAccessService; + + @Mock + private HttpServletRequest httpServletRequest; + + @InjectMocks + private SystemLinkApplicant1 systemLinkApplicant1; + + @Test + void shouldAddConfigurationToConfigBuilder() { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + systemLinkApplicant1.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(SYSTEM_LINK_APPLICANT_1); + } + + @Test + void shouldRemoveAccessCodeAfterLinkingApplicationAndSetApplicant1OfflineNo() { + final CaseData caseData = caseData(); + caseData.setCaseInviteApp1( + CaseInviteApp1.builder() + .accessCode("D8BC9AQR") + .applicant1UserId("Applicant1Id") + .build()); + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + when(httpServletRequest.getHeader(AUTHORIZATION)) + .thenReturn("auth header"); + + final AboutToStartOrSubmitResponse response = systemLinkApplicant1.aboutToSubmit(details, details); + + assertThat(response.getData().getCaseInviteApp1().accessCode()).isNull(); + assertThat(response.getData().getApplicant1().getOffline()).isEqualTo(YesOrNo.NO); + verify(ccdAccessService).linkApplicant1(eq("auth header"), eq(TEST_CASE_ID), eq("Applicant1Id")); + } +} From 97d04ee667a0fa406107a99640acf06f8b67346b Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Fri, 8 Nov 2024 08:32:42 +0000 Subject: [PATCH 03/16] NFDIV-4356 Remove unwanted classes. --- .../task/SendCaseInviteToApplicant1.java | 34 ------------------- .../task/SendCaseInviteToApplicant2.java | 33 ------------------ .../service/task/SetCaseInviteApplicant1.java | 31 ----------------- .../service/task/SetCaseInviteApplicant2.java | 30 ---------------- 4 files changed, 128 deletions(-) delete mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant1.java delete mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant2.java delete mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant1.java delete mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant2.java diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant1.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant1.java deleted file mode 100644 index 3472c7dfdd0..00000000000 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant1.java +++ /dev/null @@ -1,34 +0,0 @@ -package uk.gov.hmcts.divorce.caseworker.service.task; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import uk.gov.hmcts.ccd.sdk.api.CaseDetails; -import uk.gov.hmcts.divorce.common.notification.ReInviteApplicant1Notification; -import uk.gov.hmcts.divorce.divorcecase.model.CaseData; -import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; -import uk.gov.hmcts.divorce.divorcecase.model.State; -import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; -import uk.gov.hmcts.divorce.notification.NotificationDispatcher; - -@Component -@Slf4j -public class SendCaseInviteToApplicant1 implements CaseTask { - - @Autowired - ReInviteApplicant1Notification reInviteApplicant1Notification; - - @Autowired - NotificationDispatcher notificationDispatcher; - - @Override - public CaseDetails apply(final CaseDetails details) { - - final CaseData caseData = details.getData(); - final Long caseId = details.getId(); - - notificationDispatcher.send(reInviteApplicant1Notification, caseData, caseId); - - return details; - } -} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant2.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant2.java deleted file mode 100644 index 0d0bdad413b..00000000000 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SendCaseInviteToApplicant2.java +++ /dev/null @@ -1,33 +0,0 @@ -package uk.gov.hmcts.divorce.caseworker.service.task; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import uk.gov.hmcts.ccd.sdk.api.CaseDetails; -import uk.gov.hmcts.divorce.common.notification.ReInviteApplicant2Notification; -import uk.gov.hmcts.divorce.divorcecase.model.CaseData; -import uk.gov.hmcts.divorce.divorcecase.model.State; -import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; -import uk.gov.hmcts.divorce.notification.NotificationDispatcher; - -@Component -@Slf4j -public class SendCaseInviteToApplicant2 implements CaseTask { - - @Autowired - ReInviteApplicant2Notification reInviteApplicant2Notification; - - @Autowired - NotificationDispatcher notificationDispatcher; - - @Override - public CaseDetails apply(final CaseDetails details) { - - final CaseData caseData = details.getData(); - final Long caseId = details.getId(); - - notificationDispatcher.send(reInviteApplicant2Notification, caseData, caseId); - - return details; - } -} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant1.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant1.java deleted file mode 100644 index c629d2b3d46..00000000000 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant1.java +++ /dev/null @@ -1,31 +0,0 @@ -package uk.gov.hmcts.divorce.caseworker.service.task; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import uk.gov.hmcts.ccd.sdk.api.CaseDetails; -import uk.gov.hmcts.divorce.divorcecase.model.CaseData; -import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; -import uk.gov.hmcts.divorce.divorcecase.model.State; -import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; - -import java.time.Clock; -import java.time.LocalDate; - -@Component -@Slf4j -public class SetCaseInviteApplicant1 implements CaseTask { - - @Override - public CaseDetails apply(final CaseDetails details) { - - final CaseData data = details.getData(); - CaseInviteApp1 invite = CaseInviteApp1.builder() - .applicant1InviteEmailAddress(data.getApplicant1().getEmail()) - .build() - .generateAccessCode(); - data.setCaseInviteApp1(invite); - - return details; - } -} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant2.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant2.java deleted file mode 100644 index 3ffbfe49499..00000000000 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/task/SetCaseInviteApplicant2.java +++ /dev/null @@ -1,30 +0,0 @@ -package uk.gov.hmcts.divorce.caseworker.service.task; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import uk.gov.hmcts.ccd.sdk.api.CaseDetails; -import uk.gov.hmcts.divorce.divorcecase.model.CaseData; -import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; -import uk.gov.hmcts.divorce.divorcecase.model.State; -import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; - -import java.time.Clock; - -@Component -@Slf4j -public class SetCaseInviteApplicant2 implements CaseTask { - - @Override - public CaseDetails apply(final CaseDetails details) { - - final CaseData data = details.getData(); - CaseInvite invite = CaseInvite.builder() - .applicant2InviteEmailAddress(data.getApplicant2().getEmail()) - .build() - .generateAccessCode(); - data.setCaseInvite(invite); - - return details; - } -} From 9e821c22b325a5c40ee8a2bbb497e318a1b18920 Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Fri, 8 Nov 2024 09:01:37 +0000 Subject: [PATCH 04/16] NFDIV-4356 Remove unwanted classes. --- .../ReInviteApplicant1Notification.java | 65 ------------------- .../ReInviteApplicant2Notification.java | 44 ------------- 2 files changed, 109 deletions(-) delete mode 100644 src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant1Notification.java delete mode 100644 src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant2Notification.java diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant1Notification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant1Notification.java deleted file mode 100644 index 2d29c72cca6..00000000000 --- a/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant1Notification.java +++ /dev/null @@ -1,65 +0,0 @@ -package uk.gov.hmcts.divorce.common.notification; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import uk.gov.hmcts.divorce.common.config.EmailTemplatesConfig; -import uk.gov.hmcts.divorce.divorcecase.model.Applicant; -import uk.gov.hmcts.divorce.divorcecase.model.CaseData; -import uk.gov.hmcts.divorce.notification.ApplicantNotification; -import uk.gov.hmcts.divorce.notification.CommonContent; -import uk.gov.hmcts.divorce.notification.NotificationService; - -import java.util.Map; - -import static uk.gov.hmcts.divorce.notification.CommonContent.*; -import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_FIRM; -import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REINVITE_CITIZEN_TO_CASE; -import static uk.gov.hmcts.divorce.notification.FormatUtil.getDateTimeFormatterForPreferredLanguage; - -@Component -@Slf4j -public class ReInviteApplicant1Notification implements ApplicantNotification { - - public static final String APPLICANT_2_SIGN_IN_DIVORCE_URL = "applicant2SignInDivorceUrl"; - public static final String APPLICANT_2_SIGN_IN_DISSOLUTION_URL = "applicant2SignInDissolutionUrl"; - - @Autowired - private CommonContent commonContent; - - @Autowired - private EmailTemplatesConfig config; - - @Autowired - private NotificationService notificationService; - - @Override - public void sendToApplicant1(final CaseData caseData, final Long id) { - - log.info("Notifying applicant 1 to invite to case: {}", id); - - final Applicant applicant1 = caseData.getApplicant1(); - - notificationService.sendEmail( - applicant1.getEmail(), - REINVITE_CITIZEN_TO_CASE, - templateVars(caseData, id, applicant1, caseData.getApplicant2()), - applicant1.getLanguagePreference(), - id - ); - } - - @Override - public void sendToApplicant1Offline(final CaseData caseData, final Long id) { - - } - - private Map templateVars(CaseData caseData, Long id, Applicant applicant, Applicant partner) { - Map templateVars = commonContent.mainTemplateVars(caseData, id, applicant, partner); - templateVars.put(ACCESS_CODE, caseData.getCaseInviteApp1().accessCode()); - templateVars.put(CREATE_ACCOUNT_LINK, - config.getTemplateVars().get(caseData.isDivorce() ? APPLICANT_2_SIGN_IN_DIVORCE_URL : APPLICANT_2_SIGN_IN_DISSOLUTION_URL)); - - return templateVars; - } -} diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant2Notification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant2Notification.java deleted file mode 100644 index a1d232003a0..00000000000 --- a/src/main/java/uk/gov/hmcts/divorce/common/notification/ReInviteApplicant2Notification.java +++ /dev/null @@ -1,44 +0,0 @@ -package uk.gov.hmcts.divorce.common.notification; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import uk.gov.hmcts.divorce.divorcecase.model.Applicant; -import uk.gov.hmcts.divorce.divorcecase.model.CaseData; -import uk.gov.hmcts.divorce.notification.ApplicantNotification; -import uk.gov.hmcts.divorce.notification.CommonContent; -import uk.gov.hmcts.divorce.notification.NotificationService; - -import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REINVITE_CITIZEN_TO_CASE; - -@Component -@Slf4j -public class ReInviteApplicant2Notification implements ApplicantNotification { - - @Autowired - private CommonContent commonContent; - - @Autowired - private NotificationService notificationService; - - @Override - public void sendToApplicant2(final CaseData caseData, final Long id) { - - log.info("Notifying applicant 2 to invite to case: {}", id); - - final Applicant applicant2 = caseData.getApplicant2(); - - notificationService.sendEmail( - applicant2.getEmail(), - REINVITE_CITIZEN_TO_CASE, - commonContent.mainTemplateVars(caseData, id, applicant2, caseData.getApplicant1()), - applicant2.getLanguagePreference(), - id - ); - } - - @Override - public void sendToApplicant2Offline(final CaseData caseData, final Long id) { - - } -} From c9a2888906a4a1c8555accf7e8efb8dfb00e22fc Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Fri, 8 Nov 2024 11:13:56 +0000 Subject: [PATCH 05/16] NFDIV-4356 Fix test issues --- .../notification/InviteApplicantToCaseNotification.java | 2 +- .../hmcts/divorce/divorcecase/model/CaseInviteApp1.java | 2 +- .../event/CaseworkerUpdateApplicant1EmailTest.java | 3 ++- .../event/CaseworkerUpdateApplicant2EmailTest.java | 3 ++- .../caseworker/service/EmailUpdateServiceTest.java | 8 ++++---- .../InviteApplicantToCaseNotificationTest.java | 2 +- .../systemupdate/event/SystemLinkApplicant1Test.java | 4 ++-- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java index 14050098829..ab966ed2dd8 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java @@ -37,7 +37,7 @@ public void send(final CaseData caseData, final Long caseId, final boolean isApp Applicant partner = isApplicant1 ? caseData.getApplicant2() : caseData.getApplicant1(); Map templateVars = templateVars(caseData,caseId,applicant,partner); - templateVars.put(ACCESS_CODE, isApplicant1 ? caseData.getCaseInviteApp1().accessCode() + templateVars.put(ACCESS_CODE, isApplicant1 ? caseData.getCaseInviteApp1().accessCodeApplicant1() : caseData.getCaseInvite().accessCode()); notificationService.sendEmail( diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java index 4d1c55ee577..dc3388a79c4 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseInviteApp1.java @@ -5,7 +5,7 @@ public record CaseInviteApp1( String applicant1InviteEmailAddress, - String accessCode, + String accessCodeApplicant1, String applicant1UserId) { @Builder() diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java index 9bf34f98bef..66b5f0d880a 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java @@ -134,6 +134,7 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { final CaseData caseDataBefore = CaseData.builder() .applicant1(Applicant.builder() .offline(YES) + .email("test@test.com") .build()) .build(); @@ -158,7 +159,7 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { .email(TEST_USER_EMAIL) .build()) .caseInviteApp1(CaseInviteApp1.builder() - .accessCode("ABCD1234") + .accessCodeApplicant1("ABCD1234") .applicant1InviteEmailAddress(TEST_USER_EMAIL) .build()) .build(); diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java index 361c07ae574..d615bad7b04 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java @@ -134,6 +134,7 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { final CaseData caseDataBefore = CaseData.builder() .applicant2(Applicant.builder() .offline(YES) + .email("test@test.com") .build()) .build(); @@ -172,7 +173,7 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, detailsBefore); verify(emailUpdateService).processUpdateForApplicant2(details); - verify(emailUpdateService).sendNotificationToOldEmail(detailsBefore, TEST_USER_EMAIL, true); + verify(emailUpdateService).sendNotificationToOldEmail(detailsBefore, TEST_USER_EMAIL, false); assertThat(response.getData()).isEqualTo(expectedCaseData); } diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java index ecf88f7d8b2..672904f4066 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java @@ -40,9 +40,9 @@ void shouldSetCaseInviteForApp1AndTriggerNotification() { final CaseDetails newDetails = emailUpdateService.processUpdateForApplicant1(details); verify(inviteApplicantToCaseNotification).send(caseData, TEST_CASE_ID, true); - assertThat(newDetails.getData().getCaseInviteApp1().accessCode()).isNotBlank(); - assertThat(newDetails.getData().getCaseInviteApp1().accessCode().length()).isEqualTo(8); - assertThat(newDetails.getData().getCaseInviteApp1().accessCode()).doesNotContain("I", "O", "U", "0", "1"); + assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1()).isNotBlank(); + assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1().length()).isEqualTo(8); + assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1()).doesNotContain("I", "O", "U", "0", "1"); } @Test @@ -53,7 +53,7 @@ void shouldSetCaseInviteForApp2AndTriggerNotification() { details.setData(caseData); details.setId(TEST_CASE_ID); - final CaseDetails newDetails = emailUpdateService.processUpdateForApplicant1(details); + final CaseDetails newDetails = emailUpdateService.processUpdateForApplicant2(details); verify(inviteApplicantToCaseNotification).send(caseData, TEST_CASE_ID, false); assertThat(newDetails.getData().getCaseInvite().accessCode()).isNotBlank(); diff --git a/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java b/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java index 1786fe881d6..f87abb20e14 100644 --- a/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java @@ -54,7 +54,7 @@ void shouldSendCaseInviteToApplicant1() { CaseInviteApp1 invite = CaseInviteApp1.builder() .applicant1InviteEmailAddress(data.getApplicant1().getEmail()) - .accessCode("12345678") + .accessCodeApplicant1("12345678") .build(); data.setCaseInviteApp1(invite); diff --git a/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1Test.java b/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1Test.java index fa87c8597bc..870eeeaf515 100644 --- a/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1Test.java +++ b/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1Test.java @@ -56,7 +56,7 @@ void shouldRemoveAccessCodeAfterLinkingApplicationAndSetApplicant1OfflineNo() { final CaseData caseData = caseData(); caseData.setCaseInviteApp1( CaseInviteApp1.builder() - .accessCode("D8BC9AQR") + .accessCodeApplicant1("D8BC9AQR") .applicant1UserId("Applicant1Id") .build()); final CaseDetails details = new CaseDetails<>(); @@ -68,7 +68,7 @@ void shouldRemoveAccessCodeAfterLinkingApplicationAndSetApplicant1OfflineNo() { final AboutToStartOrSubmitResponse response = systemLinkApplicant1.aboutToSubmit(details, details); - assertThat(response.getData().getCaseInviteApp1().accessCode()).isNull(); + assertThat(response.getData().getCaseInviteApp1().accessCodeApplicant1()).isNull(); assertThat(response.getData().getApplicant1().getOffline()).isEqualTo(YesOrNo.NO); verify(ccdAccessService).linkApplicant1(eq("auth header"), eq(TEST_CASE_ID), eq("Applicant1Id")); } From 73af960a3cb2d94483723f194359e35c08d0e694 Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Mon, 11 Nov 2024 14:50:16 +0000 Subject: [PATCH 06/16] NFDIV-4356 Remove email address update from Update Contact details page. Also remove previous roles before new assignment --- .../event/page/UpdateContactDetails.java | 30 ---- .../InviteApplicantToCaseNotification.java | 25 ++- .../solicitor/service/CcdAccessService.java | 4 + .../event/page/UpdateContactDetailsTest.java | 164 ------------------ 4 files changed, 27 insertions(+), 196 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetails.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetails.java index a5a8be96f26..a2f69de7674 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetails.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetails.java @@ -167,7 +167,6 @@ private void buildApplicant1Fields(final FieldCollectionBuilder midEvent(final CaseDetails< .build(); } - if (!validApplicantContactDetails(caseDataBefore, caseData)) { - - return AboutToStartOrSubmitResponse.builder() - .errors(singletonList("Please use the 'Update offline status' event before removing the email address.")) - .build(); - } - if (!isValidCombination(caseData)) { final String errorMessage = String.format( """ @@ -300,27 +291,6 @@ private List validateSolicitorDetailsNotRemoved(Solicitor solicitorBefor return solicitorDetailRemovedErrors; } - private boolean validApplicantContactDetails(CaseData caseDataBefore, CaseData caseData) { - - if (caseDataBefore.getApplicant1().getEmail() != null && !caseDataBefore.getApplicant1().getEmail().isBlank()) { - if (!caseDataBefore.getApplicant1().isRepresented() - && !caseData.getApplicant1().isApplicantOffline() - && (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank())) { - return false; - } - } - - if (caseDataBefore.getApplicant2().getEmail() != null && !caseDataBefore.getApplicant2().getEmail().isBlank()) { - if (!caseDataBefore.getApplicant2().isRepresented() - && !caseData.getApplicant2().isApplicantOffline() - && (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank())) { - return false; - } - } - - return true; - } - private boolean isValidCombination(final CaseData caseData) { final Gender applicant1Gender = caseData.getApplicant1().getGender(); diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java index ab966ed2dd8..da4f153f08d 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java @@ -28,8 +28,12 @@ public class InviteApplicantToCaseNotification { @Autowired private NotificationService notificationService; - public static final String APPLICANT_2_SIGN_IN_DIVORCE_URL = "applicant2SignInDivorceUrl"; - public static final String APPLICANT_2_SIGN_IN_DISSOLUTION_URL = "applicant2SignInDissolutionUrl"; + private static final String APPLICANT_2_SIGN_IN_DIVORCE_URL = "applicant2SignInDivorceUrl"; + private static final String APPLICANT_2_SIGN_IN_DISSOLUTION_URL = "applicant2SignInDissolutionUrl"; + private static final String RESPONDENT_SIGN_IN_DIVORCE_URL = "respondentSignInDivorceUrl"; + private static final String RESPONDENT_SIGN_IN_DISSOLUTION_URL = "respondentSignInDissolutionUrl"; + private static final String SIGN_IN_DIVORCE_URL = "signInDivorceUrl"; + private static final String SIGN_IN_DISSOLUTION_URL = "signInDissolutionUrl"; public void send(final CaseData caseData, final Long caseId, final boolean isApplicant1) { @@ -40,6 +44,23 @@ public void send(final CaseData caseData, final Long caseId, final boolean isApp templateVars.put(ACCESS_CODE, isApplicant1 ? caseData.getCaseInviteApp1().accessCodeApplicant1() : caseData.getCaseInvite().accessCode()); + if (isApplicant1) { + templateVars.put(CREATE_ACCOUNT_LINK, + config.getTemplateVars().get(caseData.isDivorce() ? SIGN_IN_DIVORCE_URL : SIGN_IN_DISSOLUTION_URL)); + } else { + if (caseData.getApplicationType().isSole()) { + templateVars.put(CREATE_ACCOUNT_LINK, + config.getTemplateVars().get(caseData.isDivorce() ? RESPONDENT_SIGN_IN_DIVORCE_URL + : RESPONDENT_SIGN_IN_DISSOLUTION_URL)); + } else { + templateVars.put(CREATE_ACCOUNT_LINK, + config.getTemplateVars().get(caseData.isDivorce() ? APPLICANT_2_SIGN_IN_DIVORCE_URL + : APPLICANT_2_SIGN_IN_DISSOLUTION_URL)); + } + } + + + notificationService.sendEmail( applicant.getEmail(), REINVITE_CITIZEN_TO_CASE, diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessService.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessService.java index 235f572e7e9..55afeae77e7 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessService.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessService.java @@ -91,6 +91,8 @@ public void addRoleToCase(String userId, Long caseId, String orgId, UserRole rol public void linkRespondentToApplication(String caseworkerUserToken, Long caseId, String applicant2UserId) { User caseworkerUser = idamService.retrieveUser(caseworkerUserToken); + removeUsersWithRole(caseId, List.of(APPLICANT_2.getRole())); + caseAssignmentApi.addCaseUserRoles( caseworkerUser.getAuthToken(), authTokenGenerator.generate(), @@ -104,6 +106,8 @@ public void linkRespondentToApplication(String caseworkerUserToken, Long caseId, public void linkApplicant1(String caseworkerUserToken, Long caseId, String applicant1UserId) { User systemUpdateUser = idamService.retrieveUser(caseworkerUserToken); + removeUsersWithRole(caseId, List.of(CREATOR.getRole())); + caseAssignmentApi.addCaseUserRoles( systemUpdateUser.getAuthToken(), authTokenGenerator.generate(), diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetailsTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetailsTest.java index 54378f679b6..3ed958d07a3 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetailsTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetailsTest.java @@ -246,170 +246,6 @@ void shouldReturnErrorsIfFemaleHusbandSameSexCouple() { Please ensure this is correct before submitting."""); } - @Test - void shouldReturnErrorsIfApplicant1EmailHasBeenRemovedInOnlineCase() { - final CaseData caseDataBefore = CaseData.builder() - .applicant1(Applicant.builder() - .email(TEST_USER_EMAIL) - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - - final CaseData caseData = CaseData.builder() - .applicant1(Applicant.builder().build()) - .build(); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = updateContactDetails.midEvent(details, detailsBefore); - - assertThat(response.getErrors()) - .isEqualTo(singletonList("Please use the 'Update offline status' event before removing the email address.")); - } - - @Test - void shouldReturnErrorsIfApplicant2EmailHasBeenRemovedInOnlineCase() { - final CaseData caseDataBefore = CaseData.builder() - .applicant2(Applicant.builder() - .email(TEST_USER_EMAIL) - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - - final CaseData caseData = CaseData.builder() - .applicant2(Applicant.builder().build()) - .build(); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = updateContactDetails.midEvent(details, detailsBefore); - - assertThat(response.getErrors()) - .isEqualTo(singletonList("Please use the 'Update offline status' event before removing the email address.")); - } - - @Test - void shouldAllowApplicant1EmailRemovalInOfflineCase() { - final CaseData caseDataBefore = CaseData.builder() - .applicant1(Applicant.builder() - .email(TEST_USER_EMAIL) - .offline(YES) - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - - final CaseData caseData = CaseData.builder() - .applicant1(Applicant.builder() - .offline(YES) - .build()) - .build(); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = updateContactDetails.midEvent(details, detailsBefore); - - assertThat(response.getErrors()).isNull(); - } - - @Test - void shouldAllowApplicant2EmailRemovalInOfflineCase() { - final CaseData caseDataBefore = CaseData.builder() - .applicant2(Applicant.builder() - .email(TEST_USER_EMAIL) - .offline(YES) - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - - final CaseData caseData = CaseData.builder() - .applicant2(Applicant.builder() - .offline(YES) - .build()) - .build(); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = updateContactDetails.midEvent(details, detailsBefore); - - assertThat(response.getErrors()).isNull(); - } - - @Test - void shouldAllowApplicant1EmailRemovalIfRepresentedCase() { - final CaseData caseDataBefore = CaseData.builder() - .applicant1(Applicant.builder() - .email(TEST_USER_EMAIL) - .solicitorRepresented(YES) - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - - final CaseData caseData = CaseData.builder() - .applicant1(Applicant.builder() - .solicitorRepresented(YES) - .build()) - .build(); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = updateContactDetails.midEvent(details, detailsBefore); - - assertThat(response.getErrors()).isNull(); - } - - @Test - void shouldAllowApplicant2EmailRemovalIfRepresentedCase() { - final CaseData caseDataBefore = CaseData.builder() - .applicant2(Applicant.builder() - .email(TEST_USER_EMAIL) - .solicitorRepresented(YES) - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - - final CaseData caseData = CaseData.builder() - .applicant2(Applicant.builder() - .solicitorRepresented(YES) - .build()) - .build(); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = updateContactDetails.midEvent(details, detailsBefore); - - assertThat(response.getErrors()).isNull(); - } - @Test void shouldReturnErrorsWhenApplicant1SolicitorDetailsAreRemoved() { final CaseDetails detailsBefore = new CaseDetails<>(); From 1c57597fa8b30ec6dca88f365f51e2c0f3694fd6 Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Wed, 13 Nov 2024 11:09:15 +0000 Subject: [PATCH 07/16] NFDIV-4356 Send case invite only when certain conditions are met. --- .../solicitor/service/CcdAccessServiceIT.java | 19 +++ .../CaseworkerUpdateApplicant1Email.java | 14 +- .../CaseworkerUpdateApplicant2Email.java | 18 ++- .../InviteApplicantToCaseNotification.java | 16 +-- .../divorce/divorcecase/model/State.java | 7 + .../event/SystemLinkApplicant1.java | 2 +- .../event/SystemLinkApplicant2.java | 11 +- .../CaseworkerUpdateApplicant1EmailTest.java | 24 +++- .../CaseworkerUpdateApplicant2EmailTest.java | 133 +++++++++++++++--- .../event/page/UpdateContactDetailsTest.java | 3 - ...InviteApplicantToCaseNotificationTest.java | 41 +++++- .../service/CcdAccessServiceTest.java | 68 ++++++++- .../hmcts/divorce/testutil/TestConstants.java | 2 + .../divorce/testutil/TestDataHelper.java | 30 +--- 14 files changed, 305 insertions(+), 83 deletions(-) diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceIT.java index 2774afc7d04..a983bb70cff 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceIT.java @@ -13,13 +13,17 @@ import uk.gov.hmcts.divorce.idam.User; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.ccd.client.CaseAssignmentApi; +import uk.gov.hmcts.reform.ccd.client.model.CaseAssignmentUserRole; import uk.gov.hmcts.reform.ccd.client.model.CaseAssignmentUserRoleWithOrganisation; import uk.gov.hmcts.reform.ccd.client.model.CaseAssignmentUserRolesRequest; +import uk.gov.hmcts.reform.ccd.client.model.CaseAssignmentUserRolesResource; import uk.gov.hmcts.reform.idam.client.models.UserInfo; import java.util.List; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -84,8 +88,23 @@ void shouldRetryRemovingRolesThreeTimesWhenRemovingRolesThrowsException() { @Test void shouldRetryAddCaseRolesThreeTimesWhenAddingCaseRolesThrowsException() { when(idamService.retrieveUser(CASEWORKER_AUTH_TOKEN)).thenReturn(caseworkerUser()); + when(idamService.retrieveSystemUpdateUserDetails()).thenReturn(caseworkerUser()); when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN); + var response = CaseAssignmentUserRolesResource.builder() + .caseAssignmentUserRoles(List.of( + CaseAssignmentUserRole.builder().userId("1").caseRole("NOT_THIS_ONE").build(), + CaseAssignmentUserRole.builder().userId("2").caseRole("NOT_THIS_ONE").build() + )) + .build(); + + when(caseAssignmentApi.getUserRoles( + eq(CASEWORKER_AUTH_TOKEN), + eq(TEST_SERVICE_AUTH_TOKEN), + anyList() + ) + ).thenReturn(response); + doThrow(feignException(500, "some error")) .when(caseAssignmentApi).addCaseUserRoles( CASEWORKER_AUTH_TOKEN, diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java index 0b8d6671049..9b7a2bb9313 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java @@ -78,12 +78,15 @@ public AboutToStartOrSubmitResponse aboutToSubmit( final CaseDetails details, final CaseDetails beforeDetails ) { - log.info("aboutToSubmit callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId()); + log.info("{} aboutToSubmit callback invoked for Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId()); CaseData caseData = details.getData(); CaseData caseDataBefore = beforeDetails.getData(); - if (!caseData.getApplicant1().isRepresented()) { + boolean shouldSendInviteToApp1 = shouldSendInvite(caseData); + + if (!caseData.getApplicant1().isRepresented() && shouldSendInviteToApp1) { + log.info("Sending new invite to applicant/applicant1 for Case Id: {}", details.getId()); final CaseDetails result = emailUpdateService.processUpdateForApplicant1(details); String newEmail = caseData.getApplicant1().getEmail(); @@ -113,6 +116,13 @@ private boolean validApplicant1ContactDetails(CaseData caseDataBefore, CaseData return true; } + private boolean shouldSendInvite(CaseData caseData) { + if (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank()) { + return false; + } + return true; + } + private String getLabel(final String label, final Object... value) { return String.format(label, value); } diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java index 9d7931218c1..98cde5552ed 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java @@ -78,12 +78,15 @@ public AboutToStartOrSubmitResponse aboutToSubmit( final CaseDetails details, final CaseDetails beforeDetails ) { - log.info("aboutToSubmit callback invoked for {}, Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId()); + log.info("{} aboutToSubmit callback invoked for Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId()); CaseData caseData = details.getData(); CaseData caseDataBefore = beforeDetails.getData(); - if (!caseData.getApplicant2().isRepresented()) { + boolean shouldSendInviteToApp2 = shouldSendInvite(caseData); + + if (!caseData.getApplicant2().isRepresented() && shouldSendInviteToApp2) { + log.info("Sending new invite to applicant2/respondent for Case Id: {}", details.getId()); final CaseDetails result = emailUpdateService.processUpdateForApplicant2(details); String newEmail = caseData.getApplicant2().getEmail(); @@ -113,6 +116,17 @@ private boolean validApplicant2ContactDetails(CaseData caseDataBefore, CaseData return true; } + private boolean shouldSendInvite(CaseData caseData) { + if (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank()) { + return false; + } + + if (caseData.getApplicationType().isSole() && (caseData.getApplication().getIssueDate() == null)) { + return false; + } + return true; + } + private String getLabel(final String label, final Object... value) { return String.format(label, value); } diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java index da4f153f08d..92d868c20e8 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotification.java @@ -30,8 +30,8 @@ public class InviteApplicantToCaseNotification { private static final String APPLICANT_2_SIGN_IN_DIVORCE_URL = "applicant2SignInDivorceUrl"; private static final String APPLICANT_2_SIGN_IN_DISSOLUTION_URL = "applicant2SignInDissolutionUrl"; - private static final String RESPONDENT_SIGN_IN_DIVORCE_URL = "respondentSignInDivorceUrl"; - private static final String RESPONDENT_SIGN_IN_DISSOLUTION_URL = "respondentSignInDissolutionUrl"; + public static final String RESPONDENT_SIGN_IN_DIVORCE_URL = "respondentSignInDivorceUrl"; + public static final String RESPONDENT_SIGN_IN_DISSOLUTION_URL = "respondentSignInDissolutionUrl"; private static final String SIGN_IN_DIVORCE_URL = "signInDivorceUrl"; private static final String SIGN_IN_DISSOLUTION_URL = "signInDissolutionUrl"; @@ -40,7 +40,7 @@ public void send(final CaseData caseData, final Long caseId, final boolean isApp Applicant applicant = isApplicant1 ? caseData.getApplicant1() : caseData.getApplicant2(); Applicant partner = isApplicant1 ? caseData.getApplicant2() : caseData.getApplicant1(); - Map templateVars = templateVars(caseData,caseId,applicant,partner); + Map templateVars = commonContent.mainTemplateVars(caseData, caseId, applicant, partner); templateVars.put(ACCESS_CODE, isApplicant1 ? caseData.getCaseInviteApp1().accessCodeApplicant1() : caseData.getCaseInvite().accessCode()); @@ -59,8 +59,6 @@ public void send(final CaseData caseData, final Long caseId, final boolean isApp } } - - notificationService.sendEmail( applicant.getEmail(), REINVITE_CITIZEN_TO_CASE, @@ -69,12 +67,4 @@ public void send(final CaseData caseData, final Long caseId, final boolean isApp caseId ); } - - private Map templateVars(CaseData caseData, Long id, Applicant applicant, Applicant partner) { - Map templateVars = commonContent.mainTemplateVars(caseData, id, applicant, partner); - templateVars.put(CREATE_ACCOUNT_LINK, - config.getTemplateVars().get(caseData.isDivorce() ? APPLICANT_2_SIGN_IN_DIVORCE_URL : APPLICANT_2_SIGN_IN_DISSOLUTION_URL)); - - return templateVars; - } } diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/State.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/State.java index dd07d2d94a1..a2d2df383d1 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/State.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/State.java @@ -537,5 +537,12 @@ public enum State { AwaitingBailiffService, IssuedToBailiff }; + + public static final EnumSet STATES_FOR_LINKING_APP2 = EnumSet.complementOf(EnumSet.of( + Draft, + Withdrawn, + Rejected, + Archived + )); } diff --git a/src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java b/src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java index 1ca5e7aaf3e..cceb9bd3d86 100644 --- a/src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java +++ b/src/main/java/uk/gov/hmcts/divorce/systemupdate/event/SystemLinkApplicant1.java @@ -47,7 +47,7 @@ public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails configBuilder) { configBuilder .event(SYSTEM_LINK_APPLICANT_2) - .forStates(ArrayUtils.addAll(AOS_STATES, - AwaitingApplicant2Response, AwaitingAos, AosOverdue, AwaitingService, OfflineDocumentReceived)) + .forStates(STATES_FOR_LINKING_APP2) .name("Link Applicant 2 to case") .description("Link Applicant 2 to case to enable completion of joint application") .grant(CREATE_READ_UPDATE, SYSTEMUPDATE) diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java index 66b5f0d880a..a2cc4eb0ce5 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant1Email.CASEWORKER_UPDATE_APP1_EMAIL; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; @@ -179,30 +180,39 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { @Test void shouldNotCallUpdateEmailServiceWhenRepresented() { - final CaseData caseDataBefore = CaseData.builder() + final CaseData caseData = CaseData.builder() .applicant1(Applicant.builder() .offline(YES) .solicitorRepresented(YES) .build()) .build(); - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); + caseData.getApplicant1().setEmail(TEST_USER_EMAIL); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, details); + + verifyNoInteractions(emailUpdateService); + } + + @Test + void shouldNotCallUpdateEmailServiceWhenNotRepresentedAndEmailNotPresent() { final CaseData caseData = CaseData.builder() .applicant1(Applicant.builder() .offline(YES) - .email(TEST_USER_EMAIL) - .solicitorRepresented(YES) + .solicitorRepresented(NO) .build()) .build(); + final CaseDetails details = new CaseDetails<>(); details.setId(TEST_CASE_ID); details.setData(caseData); - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, detailsBefore); + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, details); verifyNoInteractions(emailUpdateService); } diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java index d615bad7b04..4cf201d350c 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java @@ -11,11 +11,15 @@ import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.Application; +import uk.gov.hmcts.divorce.divorcecase.model.ApplicationType; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import java.time.LocalDate; + import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; @@ -138,6 +142,8 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { .build()) .build(); + caseDataBefore.setApplicationType(ApplicationType.JOINT_APPLICATION); + final CaseDetails detailsBefore = new CaseDetails<>(); detailsBefore.setId(TEST_CASE_ID); detailsBefore.setData(caseDataBefore); @@ -149,6 +155,8 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { .build()) .build(); + caseData.setApplicationType(ApplicationType.JOINT_APPLICATION); + final CaseDetails details = new CaseDetails<>(); details.setId(TEST_CASE_ID); details.setData(caseData); @@ -164,6 +172,8 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { .build()) .build(); + expectedCaseData.setApplicationType(ApplicationType.JOINT_APPLICATION); + final CaseDetails expectedDetails = new CaseDetails<>(); expectedDetails.setData(expectedCaseData); expectedDetails.setId(TEST_CASE_ID); @@ -178,32 +188,119 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { } @Test - void shouldNotCallUpdateEmailServiceWhenRepresented() { - final CaseData caseDataBefore = CaseData.builder() - .applicant2(Applicant.builder() - .offline(YES) - .solicitorRepresented(YES) - .build()) - .build(); + void shouldNotSendInviteWhenSoleApplicationAndRepresented() { + CaseData caseData = getCaseDataForTest(ApplicationType.SOLE_APPLICATION, true, true, true); - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); - final CaseData caseData = CaseData.builder() - .applicant2(Applicant.builder() - .offline(YES) - .email(TEST_USER_EMAIL) - .solicitorRepresented(YES) - .build()) - .build(); + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); + + verifyNoInteractions(emailUpdateService); + } + + @Test + void shouldNotSendInviteWhenJointApplicationAndRepresented() { + CaseData caseData = getCaseDataForTest(ApplicationType.JOINT_APPLICATION, false, true, true); final CaseDetails details = new CaseDetails<>(); details.setId(TEST_CASE_ID); details.setData(caseData); - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, detailsBefore); + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); + + verifyNoInteractions(emailUpdateService); + } + + @Test + void shouldNotSendInviteWhenJointApplicationAndNotRepresentedAndEmailNotPresent() { + CaseData caseData = getCaseDataForTest(ApplicationType.JOINT_APPLICATION, false, false, false); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); verifyNoInteractions(emailUpdateService); } + + @Test + void shouldNotSendInviteWhenSoleApplicationAndNotRepresentedAndCaseNotIssued() { + CaseData caseData = getCaseDataForTest(ApplicationType.SOLE_APPLICATION, false, false, true); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); + + verifyNoInteractions(emailUpdateService); + } + + @Test + void shouldNotSendInviteWhenSoleApplicationAndNotRepresentedAndEmailNotAvailable() { + CaseData caseData = getCaseDataForTest(ApplicationType.SOLE_APPLICATION, false, false, false); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); + + verifyNoInteractions(emailUpdateService); + } + + @Test + void shouldSendInviteWhenSoleApplicationAndNotRepresentedAndCaseIssuedAndEmailAvailable() { + CaseData caseData = getCaseDataForTest(ApplicationType.SOLE_APPLICATION, true, false, true); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + when(emailUpdateService.processUpdateForApplicant2(details)).thenReturn(details); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); + + verify(emailUpdateService).processUpdateForApplicant2(details); + } + + @Test + void shouldSendInviteWhenJointApplicationAndNotRepresentedAndEmailPresent() { + CaseData caseData = getCaseDataForTest(ApplicationType.JOINT_APPLICATION, false, false, true); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + when(emailUpdateService.processUpdateForApplicant2(details)).thenReturn(details); + + AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); + + verify(emailUpdateService).processUpdateForApplicant2(details); + } + + CaseData getCaseDataForTest(ApplicationType applicationType, boolean caseIssued, boolean isRepresented, boolean emailPresent) { + final CaseData caseData = CaseData.builder() + .applicant2(Applicant.builder() + .build()) + .applicationType(applicationType) + .application(Application.builder() + .build()) + .build(); + + if (caseIssued) { + caseData.getApplication().setIssueDate(LocalDate.of(2021, 4, 28)); + } + if (isRepresented) { + caseData.getApplicant2().setSolicitorRepresented(YES); + } + if (emailPresent) { + caseData.getApplicant2().setEmail(TEST_USER_EMAIL); + } + + return caseData; + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetailsTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetailsTest.java index 3ed958d07a3..1fb4ad0fe6c 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetailsTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/page/UpdateContactDetailsTest.java @@ -11,9 +11,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.Solicitor; import uk.gov.hmcts.divorce.divorcecase.model.State; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; -import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.caseworker.event.page.UpdateContactDetails.SOLICITOR_DETAILS_REMOVED_ERROR; import static uk.gov.hmcts.divorce.divorcecase.model.Gender.FEMALE; import static uk.gov.hmcts.divorce.divorcecase.model.Gender.MALE; @@ -22,7 +20,6 @@ import static uk.gov.hmcts.divorce.divorcecase.model.WhoDivorcing.HUSBAND; import static uk.gov.hmcts.divorce.divorcecase.model.WhoDivorcing.WIFE; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; @ExtendWith(MockitoExtension.class) public class UpdateContactDetailsTest { diff --git a/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java b/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java index f87abb20e14..419065e1afb 100644 --- a/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/common/notification/InviteApplicantToCaseNotificationTest.java @@ -6,6 +6,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.hmcts.divorce.common.config.EmailTemplatesConfig; +import uk.gov.hmcts.divorce.divorcecase.model.ApplicationType; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; @@ -24,6 +25,8 @@ import static uk.gov.hmcts.divorce.notification.CommonContent.CREATE_ACCOUNT_LINK; import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REINVITE_CITIZEN_TO_CASE; import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.RESPONDENT_SIGN_IN_DIVORCE_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.SIGN_IN_DIVORCE_TEST_URL; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; @@ -48,7 +51,7 @@ class InviteApplicantToCaseNotificationTest { private InviteApplicantToCaseNotification notification; @Test - void shouldSendCaseInviteToApplicant1() { + void shouldSendCaseInviteToApplicant1WithDivorceSignInUrl() { CaseData data = caseData(); data.setApplicant2(getApplicant2(Gender.MALE)); @@ -70,7 +73,38 @@ void shouldSendCaseInviteToApplicant1() { eq(REINVITE_CITIZEN_TO_CASE), argThat(allOf( hasEntry(ACCESS_CODE, "12345678"), - hasEntry(CREATE_ACCOUNT_LINK, APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL) + hasEntry(CREATE_ACCOUNT_LINK, SIGN_IN_DIVORCE_TEST_URL) + )), + eq(ENGLISH), + eq(TEST_CASE_ID) + ); + } + + @Test + void shouldSendCaseInviteToApplicant2WithRespondentDivorceSignInUrl() { + CaseData data = caseData(); + data.setApplicant2(getApplicant2(Gender.MALE)); + data.getApplicant2().setEmail(TEST_USER_EMAIL); + + CaseInvite invite = CaseInvite.builder() + .applicant2InviteEmailAddress(data.getApplicant2().getEmail()) + .accessCode("12345678") + .build(); + data.setCaseInvite(invite); + + when(commonContent.mainTemplateVars(data, TEST_CASE_ID, data.getApplicant2(), data.getApplicant1())) + .thenReturn(getMainTemplateVars()); + + when(config.getTemplateVars()).thenReturn(getConfigTemplateVars()); + + notification.send(data, TEST_CASE_ID, false); + + verify(notificationService).sendEmail( + eq(TEST_USER_EMAIL), + eq(REINVITE_CITIZEN_TO_CASE), + argThat(allOf( + hasEntry(ACCESS_CODE, "12345678"), + hasEntry(CREATE_ACCOUNT_LINK, RESPONDENT_SIGN_IN_DIVORCE_TEST_URL) )), eq(ENGLISH), eq(TEST_CASE_ID) @@ -78,10 +112,11 @@ void shouldSendCaseInviteToApplicant1() { } @Test - void shouldSendCaseInviteToApplicant2() { + void shouldSendCaseInviteToApplicant2WithApplicant2DivorceSignInUrl() { CaseData data = caseData(); data.setApplicant2(getApplicant2(Gender.MALE)); data.getApplicant2().setEmail(TEST_USER_EMAIL); + data.setApplicationType(ApplicationType.JOINT_APPLICATION); CaseInvite invite = CaseInvite.builder() .applicant2InviteEmailAddress(data.getApplicant2().getEmail()) diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceTest.java index a50e48c1ec2..f377d4ed7a6 100644 --- a/src/test/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/service/CcdAccessServiceTest.java @@ -205,9 +205,26 @@ public void shouldNotThrowAnyExceptionWhenLinkApplicant2ToApplicationIsInvoked() when(idamService.retrieveUser(SYSTEM_UPDATE_AUTH_TOKEN)) .thenReturn(systemUpdateUser); + when(idamService.retrieveSystemUpdateUserDetails()) + .thenReturn(systemUpdateUser); + when(authTokenGenerator.generate()) .thenReturn(TEST_SERVICE_AUTH_TOKEN); + var response = CaseAssignmentUserRolesResource.builder() + .caseAssignmentUserRoles(List.of( + CaseAssignmentUserRole.builder().userId("1").caseRole("NOT_THIS_ONE").build(), + CaseAssignmentUserRole.builder().userId("2").caseRole("[CREATOR]").build() + )) + .build(); + + when(caseAssignmentApi.getUserRoles( + eq(SYSTEM_UPDATE_AUTH_TOKEN), + eq(TEST_SERVICE_AUTH_TOKEN), + anyList() + ) + ).thenReturn(response); + when(caseAssignmentApi.addCaseUserRoles( eq(SYSTEM_UPDATE_AUTH_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), @@ -219,7 +236,56 @@ public void shouldNotThrowAnyExceptionWhenLinkApplicant2ToApplicationIsInvoked() .doesNotThrowAnyException(); verify(idamService).retrieveUser(SYSTEM_UPDATE_AUTH_TOKEN); - verify(authTokenGenerator).generate(); + verify(authTokenGenerator, times(2)).generate(); + verify(caseAssignmentApi) + .addCaseUserRoles( + eq(SYSTEM_UPDATE_AUTH_TOKEN), + eq(TEST_SERVICE_AUTH_TOKEN), + any(CaseAssignmentUserRolesRequest.class) + ); + + verifyNoMoreInteractions(idamService, authTokenGenerator, caseAssignmentApi); + } + + @Test + public void shouldNotThrowAnyExceptionWhenLinkApplicant1ToApplicationIsInvoked() { + User systemUpdateUser = getIdamUser(SYSTEM_UPDATE_AUTH_TOKEN, CASEWORKER_USER_ID, TEST_CASEWORKER_USER_EMAIL); + + when(idamService.retrieveUser(SYSTEM_UPDATE_AUTH_TOKEN)) + .thenReturn(systemUpdateUser); + + when(idamService.retrieveSystemUpdateUserDetails()) + .thenReturn(systemUpdateUser); + + when(authTokenGenerator.generate()) + .thenReturn(TEST_SERVICE_AUTH_TOKEN); + + var response = CaseAssignmentUserRolesResource.builder() + .caseAssignmentUserRoles(List.of( + CaseAssignmentUserRole.builder().userId("1").caseRole("NOT_THIS_ONE").build(), + CaseAssignmentUserRole.builder().userId("2").caseRole("NOT_THIS_ONE").build() + )) + .build(); + + when(caseAssignmentApi.getUserRoles( + eq(SYSTEM_UPDATE_AUTH_TOKEN), + eq(TEST_SERVICE_AUTH_TOKEN), + anyList() + ) + ).thenReturn(response); + + when(caseAssignmentApi.addCaseUserRoles( + eq(SYSTEM_UPDATE_AUTH_TOKEN), + eq(TEST_SERVICE_AUTH_TOKEN), + any(CaseAssignmentUserRolesRequest.class) + ) + ).thenReturn(any()); + + assertThatCode(() -> ccdAccessService.linkApplicant1(SYSTEM_UPDATE_AUTH_TOKEN, TEST_CASE_ID, APP_2_CITIZEN_USER_ID)) + .doesNotThrowAnyException(); + + verify(idamService).retrieveUser(SYSTEM_UPDATE_AUTH_TOKEN); + verify(authTokenGenerator, times(2)).generate(); verify(caseAssignmentApi) .addCaseUserRoles( eq(SYSTEM_UPDATE_AUTH_TOKEN), diff --git a/src/test/java/uk/gov/hmcts/divorce/testutil/TestConstants.java b/src/test/java/uk/gov/hmcts/divorce/testutil/TestConstants.java index 8e800b81f9c..cdd94ca8a93 100644 --- a/src/test/java/uk/gov/hmcts/divorce/testutil/TestConstants.java +++ b/src/test/java/uk/gov/hmcts/divorce/testutil/TestConstants.java @@ -89,8 +89,10 @@ public final class TestConstants { public static final String SIGN_IN_DIVORCE_TEST_URL = "divorceTestUrl"; public static final String APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL = "applicant2DivorceTestUrl"; + public static final String RESPONDENT_SIGN_IN_DIVORCE_TEST_URL = "respondentDivorceTestUrl"; public static final String SIGN_IN_DISSOLUTION_TEST_URL = "dissolutionTestUrl"; public static final String APPLICANT_2_SIGN_IN_DISSOLUTION_TEST_URL = "applicant2DissolutionTestUrl"; + public static final String RESPONDENT_SIGN_IN_DISSOLUTION_TEST_URL = "respondentDissolutionTestUrl"; public static final String EMPTY_STRING = ""; diff --git a/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java b/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java index e7b44401e7e..331175a8618 100644 --- a/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java +++ b/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java @@ -95,6 +95,8 @@ import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.citizen.notification.ApplicationRemindApplicant2Notification.APPLICANT_2_SIGN_IN_DISSOLUTION_URL; import static uk.gov.hmcts.divorce.citizen.notification.ApplicationRemindApplicant2Notification.APPLICANT_2_SIGN_IN_DIVORCE_URL; +import static uk.gov.hmcts.divorce.common.notification.InviteApplicantToCaseNotification.RESPONDENT_SIGN_IN_DISSOLUTION_URL; +import static uk.gov.hmcts.divorce.common.notification.InviteApplicantToCaseNotification.RESPONDENT_SIGN_IN_DIVORCE_URL; import static uk.gov.hmcts.divorce.divorcecase.NoFaultDivorce.getCaseType; import static uk.gov.hmcts.divorce.divorcecase.model.ApplicantPrayer.DissolveDivorce.DISSOLVE_DIVORCE; import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.JOINT_APPLICATION; @@ -156,29 +158,7 @@ import static uk.gov.hmcts.divorce.notification.FormatUtil.DATE_TIME_FORMATTER; import static uk.gov.hmcts.divorce.notification.FormatUtil.formatId; import static uk.gov.hmcts.divorce.systemupdate.service.task.GenerateCertificateOfEntitlementHelper.IS_JOINT; -import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_FIRST_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_LAST_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_SIGN_IN_DISSOLUTION_TEST_URL; -import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL; -import static uk.gov.hmcts.divorce.testutil.TestConstants.FEE_CODE; -import static uk.gov.hmcts.divorce.testutil.TestConstants.FORMATTED_TEST_CASE_ID; -import static uk.gov.hmcts.divorce.testutil.TestConstants.ISSUE_FEE; -import static uk.gov.hmcts.divorce.testutil.TestConstants.SIGN_IN_DISSOLUTION_TEST_URL; -import static uk.gov.hmcts.divorce.testutil.TestConstants.SIGN_IN_DIVORCE_TEST_URL; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APP2_FIRST_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APP2_LAST_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APP2_MIDDLE_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APPLICANT_2_USER_EMAIL; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_FIRST_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_LAST_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_MIDDLE_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_ORG_ID; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_ORG_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_EMAIL; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_FIRM_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_NAME; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.*; public class TestDataHelper { @@ -814,7 +794,9 @@ public static Map getConfigTemplateVars() { SIGN_IN_DIVORCE_URL, SIGN_IN_DIVORCE_TEST_URL, SIGN_IN_DISSOLUTION_URL, SIGN_IN_DISSOLUTION_TEST_URL, APPLICANT_2_SIGN_IN_DIVORCE_URL, APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL, - APPLICANT_2_SIGN_IN_DISSOLUTION_URL, APPLICANT_2_SIGN_IN_DISSOLUTION_TEST_URL + APPLICANT_2_SIGN_IN_DISSOLUTION_URL, APPLICANT_2_SIGN_IN_DISSOLUTION_TEST_URL, + RESPONDENT_SIGN_IN_DIVORCE_URL, RESPONDENT_SIGN_IN_DIVORCE_TEST_URL, + RESPONDENT_SIGN_IN_DISSOLUTION_URL, RESPONDENT_SIGN_IN_DISSOLUTION_URL ); } From 71aab0c863c3f70202c7623d2c73720a8c5a1dfa Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Wed, 13 Nov 2024 11:25:26 +0000 Subject: [PATCH 08/16] NFDIV-4356 Fix checkstyle --- .../divorce/testutil/TestDataHelper.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java b/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java index 331175a8618..db32cbb5bd1 100644 --- a/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java +++ b/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java @@ -158,7 +158,31 @@ import static uk.gov.hmcts.divorce.notification.FormatUtil.DATE_TIME_FORMATTER; import static uk.gov.hmcts.divorce.notification.FormatUtil.formatId; import static uk.gov.hmcts.divorce.systemupdate.service.task.GenerateCertificateOfEntitlementHelper.IS_JOINT; -import static uk.gov.hmcts.divorce.testutil.TestConstants.*; +import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_FIRST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_LAST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_SIGN_IN_DISSOLUTION_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.FEE_CODE; +import static uk.gov.hmcts.divorce.testutil.TestConstants.FORMATTED_TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.ISSUE_FEE; +import static uk.gov.hmcts.divorce.testutil.TestConstants.RESPONDENT_SIGN_IN_DISSOLUTION_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.RESPONDENT_SIGN_IN_DIVORCE_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.SIGN_IN_DISSOLUTION_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.SIGN_IN_DIVORCE_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APP2_FIRST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APP2_LAST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APP2_MIDDLE_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APPLICANT_2_USER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_FIRST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_LAST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_MIDDLE_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_ORG_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_ORG_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_FIRM_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; public class TestDataHelper { @@ -796,7 +820,7 @@ public static Map getConfigTemplateVars() { APPLICANT_2_SIGN_IN_DIVORCE_URL, APPLICANT_2_SIGN_IN_DIVORCE_TEST_URL, APPLICANT_2_SIGN_IN_DISSOLUTION_URL, APPLICANT_2_SIGN_IN_DISSOLUTION_TEST_URL, RESPONDENT_SIGN_IN_DIVORCE_URL, RESPONDENT_SIGN_IN_DIVORCE_TEST_URL, - RESPONDENT_SIGN_IN_DISSOLUTION_URL, RESPONDENT_SIGN_IN_DISSOLUTION_URL + RESPONDENT_SIGN_IN_DISSOLUTION_URL, RESPONDENT_SIGN_IN_DISSOLUTION_TEST_URL ); } From 3d685c7501e065bc5fd19f0e662f71ef0a518ec8 Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Fri, 15 Nov 2024 14:03:32 +0000 Subject: [PATCH 09/16] NFDIV-4356 Incorporate review comments and additional changes requested in ticket. --- .../CaseworkerUpdateApplicant1Email.java | 41 +--- .../CaseworkerUpdateApplicant2Email.java | 45 +--- .../service/EmailUpdateService.java | 61 +++--- .../EmailUpdatedNotification.java | 26 +-- .../CaseworkerUpdateApplicant1EmailTest.java | 113 +--------- .../CaseworkerUpdateApplicant2EmailTest.java | 200 +----------------- .../service/EmailUpdateServiceTest.java | 106 ++++++++-- 7 files changed, 161 insertions(+), 431 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java index 9b7a2bb9313..18784cf081a 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java @@ -62,10 +62,10 @@ public AboutToStartOrSubmitResponse midEvent(final CaseDetails< CaseData caseData = details.getData(); CaseData caseDataBefore = detailsBefore.getData(); - if (!validApplicant1ContactDetails(caseDataBefore, caseData)) { + if (!validApplicant1Update(caseDataBefore, caseData)) { return AboutToStartOrSubmitResponse.builder() - .errors(singletonList("Please use the 'Update offline status' event before removing the email address.")) + .errors(singletonList("Email address should not be removed or blanked out.")) .build(); } @@ -80,44 +80,17 @@ public AboutToStartOrSubmitResponse aboutToSubmit( ) { log.info("{} aboutToSubmit callback invoked for Case Id: {}", CASEWORKER_UPDATE_APP1_EMAIL, details.getId()); - CaseData caseData = details.getData(); - CaseData caseDataBefore = beforeDetails.getData(); - - boolean shouldSendInviteToApp1 = shouldSendInvite(caseData); - - if (!caseData.getApplicant1().isRepresented() && shouldSendInviteToApp1) { - log.info("Sending new invite to applicant/applicant1 for Case Id: {}", details.getId()); - final CaseDetails result = emailUpdateService.processUpdateForApplicant1(details); - String newEmail = caseData.getApplicant1().getEmail(); - - if (caseDataBefore.getApplicant1().getEmail() != null - && !caseDataBefore.getApplicant1().getEmail().isBlank()) { - emailUpdateService.sendNotificationToOldEmail(beforeDetails, newEmail, true); - } - return AboutToStartOrSubmitResponse.builder() - .data(result.getData()) - .build(); - } + final CaseDetails result = emailUpdateService.processEmailUpdate(details, beforeDetails, true); return AboutToStartOrSubmitResponse.builder() - .data(caseData) + .data(result.getData()) .build(); } - private boolean validApplicant1ContactDetails(CaseData caseDataBefore, CaseData caseData) { - - if (caseDataBefore.getApplicant1().getEmail() != null && !caseDataBefore.getApplicant1().getEmail().isBlank()) { - if (!caseDataBefore.getApplicant1().isRepresented() - && !caseData.getApplicant1().isApplicantOffline() - && (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank())) { - return false; - } - } - return true; - } + private boolean validApplicant1Update(CaseData caseDataBefore, CaseData caseData) { - private boolean shouldSendInvite(CaseData caseData) { - if (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank()) { + if (caseDataBefore.getApplicant1().getEmail() != null && !caseDataBefore.getApplicant1().getEmail().isBlank() + && (caseData.getApplicant1().getEmail() == null || caseData.getApplicant1().getEmail().isBlank())) { return false; } return true; diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java index 98cde5552ed..7dc25c74619 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java @@ -62,10 +62,10 @@ public AboutToStartOrSubmitResponse midEvent(final CaseDetails< CaseData caseData = details.getData(); CaseData caseDataBefore = detailsBefore.getData(); - if (!validApplicant2ContactDetails(caseDataBefore, caseData)) { + if (!validApplicant2Update(caseDataBefore, caseData)) { return AboutToStartOrSubmitResponse.builder() - .errors(singletonList("Please use the 'Update offline status' event before removing the email address.")) + .errors(singletonList("Email address should not be removed or blanked out.")) .build(); } @@ -80,48 +80,17 @@ public AboutToStartOrSubmitResponse aboutToSubmit( ) { log.info("{} aboutToSubmit callback invoked for Case Id: {}", CASEWORKER_UPDATE_APP2_EMAIL, details.getId()); - CaseData caseData = details.getData(); - CaseData caseDataBefore = beforeDetails.getData(); - - boolean shouldSendInviteToApp2 = shouldSendInvite(caseData); - - if (!caseData.getApplicant2().isRepresented() && shouldSendInviteToApp2) { - log.info("Sending new invite to applicant2/respondent for Case Id: {}", details.getId()); - final CaseDetails result = emailUpdateService.processUpdateForApplicant2(details); - String newEmail = caseData.getApplicant2().getEmail(); - - if (caseDataBefore.getApplicant2().getEmail() != null - && !caseDataBefore.getApplicant2().getEmail().isBlank()) { - emailUpdateService.sendNotificationToOldEmail(beforeDetails, newEmail, false); - } - return AboutToStartOrSubmitResponse.builder() - .data(result.getData()) - .build(); - } + final CaseDetails result = emailUpdateService.processEmailUpdate(details, beforeDetails, false); return AboutToStartOrSubmitResponse.builder() - .data(caseData) + .data(result.getData()) .build(); } - private boolean validApplicant2ContactDetails(CaseData caseDataBefore, CaseData caseData) { - - if (caseDataBefore.getApplicant2().getEmail() != null && !caseDataBefore.getApplicant2().getEmail().isBlank()) { - if (!caseDataBefore.getApplicant2().isRepresented() - && !caseData.getApplicant2().isApplicantOffline() - && (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank())) { - return false; - } - } - return true; - } - - private boolean shouldSendInvite(CaseData caseData) { - if (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank()) { - return false; - } + private boolean validApplicant2Update(CaseData caseDataBefore, CaseData caseData) { - if (caseData.getApplicationType().isSole() && (caseData.getApplication().getIssueDate() == null)) { + if (caseDataBefore.getApplicant2().getEmail() != null && !caseDataBefore.getApplicant2().getEmail().isBlank() + && (caseData.getApplicant2().getEmail() == null || caseData.getApplicant2().getEmail().isBlank())) { return false; } return true; diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java index b778e76e010..e62bdb5544b 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java @@ -6,6 +6,7 @@ import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.divorce.common.notification.EmailUpdatedNotification; import uk.gov.hmcts.divorce.common.notification.InviteApplicantToCaseNotification; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; @@ -23,48 +24,52 @@ public class EmailUpdateService { @Autowired NotificationDispatcher notificationDispatcher; - public CaseDetails processUpdateForApplicant1(final CaseDetails caseDetails) { + public CaseDetails processEmailUpdate(final CaseDetails caseDetails, + final CaseDetails beforeCaseDetails, + boolean isApplicant1) { final CaseData data = caseDetails.getData(); - CaseInviteApp1 invite = CaseInviteApp1.builder() - .applicant1InviteEmailAddress(data.getApplicant1().getEmail()) - .build() - .generateAccessCode(); - data.setCaseInviteApp1(invite); + Applicant applicant = isApplicant1 ? data.getApplicant1() : data.getApplicant2(); - triggerNotificationToApplicant1(caseDetails); + //Do not sent invite if applicant offline or new email is null + if (applicant.getEmail() == null || applicant.getEmail().isBlank() || applicant.isApplicantOffline()) { + return caseDetails; + } - return caseDetails; - } - - public CaseDetails processUpdateForApplicant2(final CaseDetails caseDetails) { + //Do not send invite to respondent if sole application and case hasn't been issued + if (data.getApplicationType().isSole() && (data.getApplication().getIssueDate() == null) && !isApplicant1) { + return caseDetails; + } - final CaseData data = caseDetails.getData(); + createCaseInvite(data, isApplicant1); - CaseInvite invite = CaseInvite.builder() - .applicant2InviteEmailAddress(data.getApplicant2().getEmail()) - .build() - .generateAccessCode(); - data.setCaseInvite(invite); + sendInviteToApplicantEmail(data, caseDetails.getId(), isApplicant1); - triggerNotificationToApplicant2(caseDetails); + sendNotificationToOldEmail(beforeCaseDetails, applicant.getEmail(), isApplicant1); return caseDetails; } - public void triggerNotificationToApplicant1(final CaseDetails caseDetails) { - final CaseData caseData = caseDetails.getData(); - final Long caseId = caseDetails.getId(); - - inviteApplicantToCaseNotification.send(caseData, caseId, true); + public void createCaseInvite(final CaseData data, boolean isApplicant1) { + Applicant applicant = isApplicant1 ? data.getApplicant1() : data.getApplicant2(); + if (isApplicant1) { + CaseInviteApp1 invite = CaseInviteApp1.builder() + .applicant1InviteEmailAddress(applicant.getEmail()) + .build() + .generateAccessCode(); + data.setCaseInviteApp1(invite); + } else { + CaseInvite invite = CaseInvite.builder() + .applicant2InviteEmailAddress(applicant.getEmail()) + .build() + .generateAccessCode(); + data.setCaseInvite(invite); + } } - public void triggerNotificationToApplicant2(final CaseDetails caseDetails) { - final CaseData caseData = caseDetails.getData(); - final Long caseId = caseDetails.getId(); - - inviteApplicantToCaseNotification.send(caseData, caseId, false); + public void sendInviteToApplicantEmail(final CaseData caseData, Long id, boolean isApplicant1) { + inviteApplicantToCaseNotification.send(caseData, id, isApplicant1); } public void sendNotificationToOldEmail(final CaseDetails caseDetails, diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java index 12ce5c857ba..d528d365eb7 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/EmailUpdatedNotification.java @@ -26,17 +26,19 @@ public void send(final CaseData caseData, final Long caseId, final String newEma Applicant applicant = isApplicant1 ? caseData.getApplicant1() : caseData.getApplicant2(); Applicant partner = isApplicant1 ? caseData.getApplicant2() : caseData.getApplicant1(); - Map templateVars = commonContent.mainTemplateVars(caseData, caseId, applicant, partner); - templateVars.put("old email", applicant.getEmail()); - templateVars.put("new email", newEmail); - - notificationService.sendEmail( - applicant.getEmail(), - CITIZEN_EMAIL_UPDATED, - templateVars, - applicant.getLanguagePreference(), - caseId - ); - log.info("Successfully sent email updated notification for case id: {}", caseId); + if (applicant.getEmail() != null && !applicant.getEmail().isBlank()) { + Map templateVars = commonContent.mainTemplateVars(caseData, caseId, applicant, partner); + templateVars.put("old email", applicant.getEmail()); + templateVars.put("new email", newEmail); + + notificationService.sendEmail( + applicant.getEmail(), + CITIZEN_EMAIL_UPDATED, + templateVars, + applicant.getLanguagePreference(), + caseId + ); + log.info("Successfully sent email updated notification for case id: {}", caseId); + } } } diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java index a2cc4eb0ce5..0248a82fe37 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java @@ -12,16 +12,13 @@ import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; import uk.gov.hmcts.divorce.divorcecase.model.Applicant; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; -import uk.gov.hmcts.divorce.divorcecase.model.CaseInviteApp1; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant1Email.CASEWORKER_UPDATE_APP1_EMAIL; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; @@ -49,7 +46,7 @@ void shouldAddConfigurationToConfigBuilder() throws Exception { } @Test - void shouldReturnErrorsIfApplicant1EmailHasBeenRemovedInOnlineCase() { + void shouldReturnErrorsIfApplicant1EmailHasBeenRemoved() { final CaseData caseDataBefore = CaseData.builder() .applicant1(Applicant.builder() .email(TEST_USER_EMAIL) @@ -71,15 +68,14 @@ void shouldReturnErrorsIfApplicant1EmailHasBeenRemovedInOnlineCase() { AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); assertThat(response.getErrors()) - .isEqualTo(singletonList("Please use the 'Update offline status' event before removing the email address.")); + .isEqualTo(singletonList("Email address should not be removed or blanked out.")); } @Test - void shouldAllowApplicant1EmailRemovalInOfflineCase() { + void shouldNotReturnErrorsIfApplicant1EmailUnchanged() { final CaseData caseDataBefore = CaseData.builder() .applicant1(Applicant.builder() .email(TEST_USER_EMAIL) - .offline(YES) .build()) .build(); @@ -88,37 +84,7 @@ void shouldAllowApplicant1EmailRemovalInOfflineCase() { detailsBefore.setData(caseDataBefore); final CaseData caseData = CaseData.builder() - .applicant1(Applicant.builder() - .offline(YES) - .build()) - .build(); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); - - assertThat(response.getErrors()).isNull(); - } - - @Test - void shouldAllowApplicant1EmailRemovalIfRepresentedCase() { - final CaseData caseDataBefore = CaseData.builder() - .applicant1(Applicant.builder() - .email(TEST_USER_EMAIL) - .solicitorRepresented(YES) - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - - final CaseData caseData = CaseData.builder() - .applicant1(Applicant.builder() - .solicitorRepresented(YES) - .build()) + .applicant1(Applicant.builder().email(TEST_USER_EMAIL).build()) .build(); final CaseDetails details = new CaseDetails<>(); @@ -132,17 +98,6 @@ void shouldAllowApplicant1EmailRemovalIfRepresentedCase() { @Test void shouldCallUpdateEmailServiceAndReturnCaseData() { - final CaseData caseDataBefore = CaseData.builder() - .applicant1(Applicant.builder() - .offline(YES) - .email("test@test.com") - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - final CaseData caseData = CaseData.builder() .applicant1(Applicant.builder() .offline(YES) @@ -154,66 +109,10 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { details.setId(TEST_CASE_ID); details.setData(caseData); - final CaseData expectedCaseData = CaseData.builder() - .applicant1(Applicant.builder() - .offline(YES) - .email(TEST_USER_EMAIL) - .build()) - .caseInviteApp1(CaseInviteApp1.builder() - .accessCodeApplicant1("ABCD1234") - .applicant1InviteEmailAddress(TEST_USER_EMAIL) - .build()) - .build(); - - final CaseDetails expectedDetails = new CaseDetails<>(); - expectedDetails.setData(expectedCaseData); - expectedDetails.setId(TEST_CASE_ID); - - when(emailUpdateService.processUpdateForApplicant1(details)).thenReturn(expectedDetails); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, detailsBefore); - - verify(emailUpdateService).processUpdateForApplicant1(details); - verify(emailUpdateService).sendNotificationToOldEmail(detailsBefore, TEST_USER_EMAIL, true); - assertThat(response.getData()).isEqualTo(expectedCaseData); - } - - @Test - void shouldNotCallUpdateEmailServiceWhenRepresented() { - final CaseData caseData = CaseData.builder() - .applicant1(Applicant.builder() - .offline(YES) - .solicitorRepresented(YES) - .build()) - .build(); - - caseData.getApplicant1().setEmail(TEST_USER_EMAIL); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, details); - - verifyNoInteractions(emailUpdateService); - } - - @Test - void shouldNotCallUpdateEmailServiceWhenNotRepresentedAndEmailNotPresent() { - final CaseData caseData = CaseData.builder() - .applicant1(Applicant.builder() - .offline(YES) - .solicitorRepresented(NO) - .build()) - .build(); - - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); + when(emailUpdateService.processEmailUpdate(details, details, true)).thenReturn(details); AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.aboutToSubmit(details, details); - verifyNoInteractions(emailUpdateService); + verify(emailUpdateService).processEmailUpdate(details, details, true); } } diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java index 4cf201d350c..d389b5f8a7d 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java @@ -11,19 +11,13 @@ import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; import uk.gov.hmcts.divorce.caseworker.service.EmailUpdateService; import uk.gov.hmcts.divorce.divorcecase.model.Applicant; -import uk.gov.hmcts.divorce.divorcecase.model.Application; -import uk.gov.hmcts.divorce.divorcecase.model.ApplicationType; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; -import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; -import java.time.LocalDate; - import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant2Email.CASEWORKER_UPDATE_APP2_EMAIL; @@ -52,7 +46,7 @@ void shouldAddConfigurationToConfigBuilder() throws Exception { } @Test - void shouldReturnErrorsIfApplicant2EmailHasBeenRemovedInOnlineCase() { + void shouldReturnErrorsIfApplicant2EmailHasBeenRemoved() { final CaseData caseDataBefore = CaseData.builder() .applicant2(Applicant.builder() .email(TEST_USER_EMAIL) @@ -74,15 +68,14 @@ void shouldReturnErrorsIfApplicant2EmailHasBeenRemovedInOnlineCase() { AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); assertThat(response.getErrors()) - .isEqualTo(singletonList("Please use the 'Update offline status' event before removing the email address.")); + .isEqualTo(singletonList("Email address should not be removed or blanked out.")); } @Test - void shouldAllowApplicant2EmailRemovalInOfflineCase() { + void shouldNotReturnErrorsIfApplicant2EmailUnchanged() { final CaseData caseDataBefore = CaseData.builder() .applicant2(Applicant.builder() .email(TEST_USER_EMAIL) - .offline(YES) .build()) .build(); @@ -91,37 +84,7 @@ void shouldAllowApplicant2EmailRemovalInOfflineCase() { detailsBefore.setData(caseDataBefore); final CaseData caseData = CaseData.builder() - .applicant2(Applicant.builder() - .offline(YES) - .build()) - .build(); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); - - assertThat(response.getErrors()).isNull(); - } - - @Test - void shouldAllowApplicant2EmailRemovalIfRepresentedCase() { - final CaseData caseDataBefore = CaseData.builder() - .applicant2(Applicant.builder() - .email(TEST_USER_EMAIL) - .solicitorRepresented(YES) - .build()) - .build(); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - - final CaseData caseData = CaseData.builder() - .applicant2(Applicant.builder() - .solicitorRepresented(YES) - .build()) + .applicant2(Applicant.builder().email(TEST_USER_EMAIL).build()) .build(); final CaseDetails details = new CaseDetails<>(); @@ -135,19 +98,6 @@ void shouldAllowApplicant2EmailRemovalIfRepresentedCase() { @Test void shouldCallUpdateEmailServiceAndReturnCaseData() { - final CaseData caseDataBefore = CaseData.builder() - .applicant2(Applicant.builder() - .offline(YES) - .email("test@test.com") - .build()) - .build(); - - caseDataBefore.setApplicationType(ApplicationType.JOINT_APPLICATION); - - final CaseDetails detailsBefore = new CaseDetails<>(); - detailsBefore.setId(TEST_CASE_ID); - detailsBefore.setData(caseDataBefore); - final CaseData caseData = CaseData.builder() .applicant2(Applicant.builder() .offline(YES) @@ -155,152 +105,14 @@ void shouldCallUpdateEmailServiceAndReturnCaseData() { .build()) .build(); - caseData.setApplicationType(ApplicationType.JOINT_APPLICATION); - final CaseDetails details = new CaseDetails<>(); details.setId(TEST_CASE_ID); details.setData(caseData); - final CaseData expectedCaseData = CaseData.builder() - .applicant2(Applicant.builder() - .offline(YES) - .email(TEST_USER_EMAIL) - .build()) - .caseInvite(CaseInvite.builder() - .accessCode("ABCD1234") - .applicant2InviteEmailAddress(TEST_USER_EMAIL) - .build()) - .build(); - - expectedCaseData.setApplicationType(ApplicationType.JOINT_APPLICATION); - - final CaseDetails expectedDetails = new CaseDetails<>(); - expectedDetails.setData(expectedCaseData); - expectedDetails.setId(TEST_CASE_ID); - - when(emailUpdateService.processUpdateForApplicant2(details)).thenReturn(expectedDetails); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, detailsBefore); - - verify(emailUpdateService).processUpdateForApplicant2(details); - verify(emailUpdateService).sendNotificationToOldEmail(detailsBefore, TEST_USER_EMAIL, false); - assertThat(response.getData()).isEqualTo(expectedCaseData); - } - - @Test - void shouldNotSendInviteWhenSoleApplicationAndRepresented() { - CaseData caseData = getCaseDataForTest(ApplicationType.SOLE_APPLICATION, true, true, true); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); + when(emailUpdateService.processEmailUpdate(details, details, false)).thenReturn(details); AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); - verifyNoInteractions(emailUpdateService); - } - - @Test - void shouldNotSendInviteWhenJointApplicationAndRepresented() { - CaseData caseData = getCaseDataForTest(ApplicationType.JOINT_APPLICATION, false, true, true); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); - - verifyNoInteractions(emailUpdateService); - } - - @Test - void shouldNotSendInviteWhenJointApplicationAndNotRepresentedAndEmailNotPresent() { - CaseData caseData = getCaseDataForTest(ApplicationType.JOINT_APPLICATION, false, false, false); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); - - verifyNoInteractions(emailUpdateService); - } - - @Test - void shouldNotSendInviteWhenSoleApplicationAndNotRepresentedAndCaseNotIssued() { - CaseData caseData = getCaseDataForTest(ApplicationType.SOLE_APPLICATION, false, false, true); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); - - verifyNoInteractions(emailUpdateService); - } - - @Test - void shouldNotSendInviteWhenSoleApplicationAndNotRepresentedAndEmailNotAvailable() { - CaseData caseData = getCaseDataForTest(ApplicationType.SOLE_APPLICATION, false, false, false); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); - - verifyNoInteractions(emailUpdateService); - } - - @Test - void shouldSendInviteWhenSoleApplicationAndNotRepresentedAndCaseIssuedAndEmailAvailable() { - CaseData caseData = getCaseDataForTest(ApplicationType.SOLE_APPLICATION, true, false, true); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - when(emailUpdateService.processUpdateForApplicant2(details)).thenReturn(details); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); - - verify(emailUpdateService).processUpdateForApplicant2(details); - } - - @Test - void shouldSendInviteWhenJointApplicationAndNotRepresentedAndEmailPresent() { - CaseData caseData = getCaseDataForTest(ApplicationType.JOINT_APPLICATION, false, false, true); - - final CaseDetails details = new CaseDetails<>(); - details.setId(TEST_CASE_ID); - details.setData(caseData); - - when(emailUpdateService.processUpdateForApplicant2(details)).thenReturn(details); - - AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.aboutToSubmit(details, details); - - verify(emailUpdateService).processUpdateForApplicant2(details); - } - - CaseData getCaseDataForTest(ApplicationType applicationType, boolean caseIssued, boolean isRepresented, boolean emailPresent) { - final CaseData caseData = CaseData.builder() - .applicant2(Applicant.builder() - .build()) - .applicationType(applicationType) - .application(Application.builder() - .build()) - .build(); - - if (caseIssued) { - caseData.getApplication().setIssueDate(LocalDate.of(2021, 4, 28)); - } - if (isRepresented) { - caseData.getApplicant2().setSolicitorRepresented(YES); - } - if (emailPresent) { - caseData.getApplicant2().setEmail(TEST_USER_EMAIL); - } - - return caseData; + verify(emailUpdateService).processEmailUpdate(details, details, false); } } diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java index 672904f4066..c042bf36f14 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateServiceTest.java @@ -8,13 +8,17 @@ import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.divorce.common.notification.EmailUpdatedNotification; import uk.gov.hmcts.divorce.common.notification.InviteApplicantToCaseNotification; +import uk.gov.hmcts.divorce.divorcecase.model.ApplicationType; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.State; -import uk.gov.hmcts.divorce.notification.NotificationDispatcher; + +import java.time.LocalDate; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.validApplicant1CaseData; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.validApplicant2CaseData; @@ -24,53 +28,119 @@ class EmailUpdateServiceTest { InviteApplicantToCaseNotification inviteApplicantToCaseNotification; @Mock EmailUpdatedNotification emailUpdatedNotification; - @Mock - NotificationDispatcher notificationDispatcher; @InjectMocks private EmailUpdateService emailUpdateService; @Test - void shouldSetCaseInviteForApp1AndTriggerNotification() { + void shouldNotProgressWhenEmailForApplicantIsNull() { final CaseData caseData = validApplicant1CaseData(); + caseData.getApplicant1().setEmail(null); final CaseDetails details = new CaseDetails<>(); details.setData(caseData); details.setId(TEST_CASE_ID); - final CaseDetails newDetails = emailUpdateService.processUpdateForApplicant1(details); + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, true); - verify(inviteApplicantToCaseNotification).send(caseData, TEST_CASE_ID, true); - assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1()).isNotBlank(); - assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1().length()).isEqualTo(8); - assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1()).doesNotContain("I", "O", "U", "0", "1"); + verifyNoInteractions(inviteApplicantToCaseNotification); + verifyNoInteractions(emailUpdatedNotification); + } + + @Test + void shouldNotProgressWhenEmailForApplicantIsBlank() { + final CaseData caseData = validApplicant1CaseData(); + caseData.getApplicant1().setEmail(""); + + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, true); + + verifyNoInteractions(inviteApplicantToCaseNotification); + verifyNoInteractions(emailUpdatedNotification); } @Test - void shouldSetCaseInviteForApp2AndTriggerNotification() { + void shouldNotProgressWhenApplicant2AndSoleApplicationNotIssuedYet() { final CaseData caseData = validApplicant2CaseData(); + caseData.setApplicationType(ApplicationType.SOLE_APPLICATION); + caseData.getApplication().setIssueDate(null); final CaseDetails details = new CaseDetails<>(); details.setData(caseData); details.setId(TEST_CASE_ID); - final CaseDetails newDetails = emailUpdateService.processUpdateForApplicant2(details); + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, false); - verify(inviteApplicantToCaseNotification).send(caseData, TEST_CASE_ID, false); - assertThat(newDetails.getData().getCaseInvite().accessCode()).isNotBlank(); - assertThat(newDetails.getData().getCaseInvite().accessCode().length()).isEqualTo(8); - assertThat(newDetails.getData().getCaseInvite().accessCode()).doesNotContain("I", "O", "U", "0", "1"); + verifyNoInteractions(inviteApplicantToCaseNotification); + verifyNoInteractions(emailUpdatedNotification); + } + + @Test + void shouldProgressWhenApplicant2AndSoleApplicationIsIssued() { + final CaseData caseData = validApplicant2CaseData(); + caseData.setApplicationType(ApplicationType.SOLE_APPLICATION); + caseData.getApplication().setIssueDate(LocalDate.of(2021, 4, 28)); + + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, false); + + verify(inviteApplicantToCaseNotification).send(caseData,TEST_CASE_ID,false); + verify(emailUpdatedNotification).send(caseData,TEST_CASE_ID,TEST_USER_EMAIL,false); } @Test - void shouldSendNotificationToOldEmail() { + void shouldProgressWhenApplicant2AndJointApplicationNotIssuedYet() { + final CaseData caseData = validApplicant2CaseData(); + caseData.getApplication().setIssueDate(null); + caseData.getApplicant2().setEmail(TEST_USER_EMAIL); + + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, false); + + verify(inviteApplicantToCaseNotification).send(caseData,TEST_CASE_ID,false); + verify(emailUpdatedNotification).send(caseData,TEST_CASE_ID,TEST_USER_EMAIL,false); + } + + @Test + void shouldSetCaseInviteForApplicant1() { final CaseData caseData = validApplicant1CaseData(); + caseData.setCaseInviteApp1(null); + + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, true); + + assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1()).isNotBlank(); + assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1().length()).isEqualTo(8); + assertThat(newDetails.getData().getCaseInviteApp1().accessCodeApplicant1()).doesNotContain("I", "O", "U", "0", "1"); + } + + @Test + void shouldSetCaseInviteForApplicant2() { + final CaseData caseData = validApplicant2CaseData(); + + caseData.setCaseInvite(null); + caseData.getApplicant2().setEmail(TEST_USER_EMAIL); + final CaseDetails details = new CaseDetails<>(); details.setData(caseData); details.setId(TEST_CASE_ID); - emailUpdateService.sendNotificationToOldEmail(details,"test@test.com", true); + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, false); - verify(emailUpdatedNotification).send(caseData,TEST_CASE_ID,"test@test.com", true); + assertThat(newDetails.getData().getCaseInvite().accessCode()).isNotBlank(); + assertThat(newDetails.getData().getCaseInvite().accessCode().length()).isEqualTo(8); + assertThat(newDetails.getData().getCaseInvite().accessCode()).doesNotContain("I", "O", "U", "0", "1"); } } From 86bdff05c002ba6bc43756ad13de938de4b600a6 Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Fri, 15 Nov 2024 14:45:03 +0000 Subject: [PATCH 10/16] NFDIV-4356 Check for applicant represented --- .../service/EmailUpdateService.java | 6 ++-- .../service/EmailUpdateServiceTest.java | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java index e62bdb5544b..094a76c7e44 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java @@ -32,8 +32,10 @@ public CaseDetails processEmailUpdate(final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, true); + + verifyNoInteractions(inviteApplicantToCaseNotification); + verifyNoInteractions(emailUpdatedNotification); + } + + @Test + void shouldNotProgressWhenApplicantIsRepresented() { + final CaseData caseData = validApplicant1CaseData(); + caseData.getApplicant1().setSolicitorRepresented(YesOrNo.YES); + + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, true); + + verifyNoInteractions(inviteApplicantToCaseNotification); + verifyNoInteractions(emailUpdatedNotification); + } + @Test void shouldNotProgressWhenApplicant2AndSoleApplicationNotIssuedYet() { final CaseData caseData = validApplicant2CaseData(); From 912f24d722e8b0ab6aaf88890433f548ffbae78a Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Mon, 18 Nov 2024 16:58:39 +0000 Subject: [PATCH 11/16] NFDIV-4521 - Change message when removing email. Also add hint to screens when applicant offline. --- .../event/CaseworkerUpdateApplicant1Email.java | 13 +++++++++++-- .../event/CaseworkerUpdateApplicant2Email.java | 13 +++++++++++-- .../event/CaseworkerUpdateApplicant1EmailTest.java | 3 ++- .../event/CaseworkerUpdateApplicant2EmailTest.java | 3 ++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java index 18784cf081a..549c78b0158 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java @@ -33,6 +33,7 @@ public class CaseworkerUpdateApplicant1Email implements CCDConfig configBuilder) { @@ -51,7 +52,9 @@ public void configure(final ConfigBuilder configBuild .page("updateApp1Email") .pageLabel("Update applicant/applicant1 email") .complex(CaseData::getApplicant1) - .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, APPLICANTS_OR_APPLICANT1S)) + .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, APPLICANTS_OR_APPLICANT1S)) + .readonlyNoSummary(Applicant::getOffline,NEVER_SHOW) + .label("willNotReceiveInvite", getInviteNotSentLabel(),"applicant1Offline = \"Yes\"") .done(); } @@ -65,7 +68,8 @@ public AboutToStartOrSubmitResponse midEvent(final CaseDetails< if (!validApplicant1Update(caseDataBefore, caseData)) { return AboutToStartOrSubmitResponse.builder() - .errors(singletonList("Email address should not be removed or blanked out.")) + .errors(singletonList("You cannot leave the email field blank. " + + "You can only use this event to update the email of the party.")) .build(); } @@ -99,4 +103,9 @@ private boolean validApplicant1Update(CaseData caseDataBefore, CaseData caseData private String getLabel(final String label, final Object... value) { return String.format(label, value); } + + private String getInviteNotSentLabel() { + return "*The party is offline. You can update their email but they will not be invited to the case. " + + "Please use Notice of Change to invite them to gain access to the case online.*"; + } } diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java index 7dc25c74619..05e43dd28b7 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java @@ -33,6 +33,7 @@ public class CaseworkerUpdateApplicant2Email implements CCDConfig configBuilder) { @@ -51,7 +52,9 @@ public void configure(final ConfigBuilder configBuild .page("updateApp2Email") .pageLabel("Update respondent/applicant2 email") .complex(CaseData::getApplicant2) - .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, RESPONDENTS_OR_APPLICANT2S)) + .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, RESPONDENTS_OR_APPLICANT2S)) + .readonlyNoSummary(Applicant::getOffline,NEVER_SHOW) + .label("willNotReceiveInvite", getInviteNotSentLabel(),"applicant2Offline = \"Yes\"") .done(); } @@ -65,7 +68,8 @@ public AboutToStartOrSubmitResponse midEvent(final CaseDetails< if (!validApplicant2Update(caseDataBefore, caseData)) { return AboutToStartOrSubmitResponse.builder() - .errors(singletonList("Email address should not be removed or blanked out.")) + .errors(singletonList("You cannot leave the email field blank. " + + "You can only use this event to update the email of the party.")) .build(); } @@ -99,4 +103,9 @@ private boolean validApplicant2Update(CaseData caseDataBefore, CaseData caseData private String getLabel(final String label, final Object... value) { return String.format(label, value); } + + private String getInviteNotSentLabel() { + return "*The party is offline. You can update their email but they will not be invited to the case. " + + "Please use Notice of Change to invite them to gain access to the case online.*"; + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java index 0248a82fe37..c736588b197 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1EmailTest.java @@ -68,7 +68,8 @@ void shouldReturnErrorsIfApplicant1EmailHasBeenRemoved() { AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant1Email.midEvent(details, detailsBefore); assertThat(response.getErrors()) - .isEqualTo(singletonList("Email address should not be removed or blanked out.")); + .isEqualTo(singletonList("You cannot leave the email field blank. " + + "You can only use this event to update the email of the party.")); } @Test diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java index d389b5f8a7d..6b0863ab20f 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2EmailTest.java @@ -68,7 +68,8 @@ void shouldReturnErrorsIfApplicant2EmailHasBeenRemoved() { AboutToStartOrSubmitResponse response = caseworkerUpdateApplicant2Email.midEvent(details, detailsBefore); assertThat(response.getErrors()) - .isEqualTo(singletonList("Email address should not be removed or blanked out.")); + .isEqualTo(singletonList("You cannot leave the email field blank. " + + "You can only use this event to update the email of the party.")); } @Test From f65fb0371a83cf851e71bc50ccc6b04708ef7aaa Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Tue, 19 Nov 2024 13:23:10 +0000 Subject: [PATCH 12/16] NFDIV-4356 - Moving message to a constant --- .../event/CaseworkerUpdateApplicant1Email.java | 10 ++++------ .../event/CaseworkerUpdateApplicant2Email.java | 8 ++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java index 549c78b0158..58a20a8e254 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java @@ -28,6 +28,9 @@ public class CaseworkerUpdateApplicant1Email implements CCDConfig configBuild .complex(CaseData::getApplicant1) .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, APPLICANTS_OR_APPLICANT1S)) .readonlyNoSummary(Applicant::getOffline,NEVER_SHOW) - .label("willNotReceiveInvite", getInviteNotSentLabel(),"applicant1Offline = \"Yes\"") + .label("willNotReceiveInvite", WILL_NOT_SEND_INVITE,"applicant1Offline = \"Yes\"") .done(); } @@ -103,9 +106,4 @@ private boolean validApplicant1Update(CaseData caseDataBefore, CaseData caseData private String getLabel(final String label, final Object... value) { return String.format(label, value); } - - private String getInviteNotSentLabel() { - return "*The party is offline. You can update their email but they will not be invited to the case. " - + "Please use Notice of Change to invite them to gain access to the case online.*"; - } } diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java index 05e43dd28b7..cc053b82c8f 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java @@ -15,6 +15,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import static java.util.Collections.singletonList; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant1Email.WILL_NOT_SEND_INVITE; import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; @@ -54,7 +55,7 @@ public void configure(final ConfigBuilder configBuild .complex(CaseData::getApplicant2) .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, RESPONDENTS_OR_APPLICANT2S)) .readonlyNoSummary(Applicant::getOffline,NEVER_SHOW) - .label("willNotReceiveInvite", getInviteNotSentLabel(),"applicant2Offline = \"Yes\"") + .label("willNotReceiveInvite", WILL_NOT_SEND_INVITE,"applicant2Offline = \"Yes\"") .done(); } @@ -103,9 +104,4 @@ private boolean validApplicant2Update(CaseData caseDataBefore, CaseData caseData private String getLabel(final String label, final Object... value) { return String.format(label, value); } - - private String getInviteNotSentLabel() { - return "*The party is offline. You can update their email but they will not be invited to the case. " - + "Please use Notice of Change to invite them to gain access to the case online.*"; - } } From bf0ee4a1d41c5342d3e8f63d6d66a7284f00ba0c Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Tue, 19 Nov 2024 17:13:52 +0000 Subject: [PATCH 13/16] NFDIV-4356 - Send invite even when citizen marked as offline. --- .../event/CaseworkerUpdateApplicant1Email.java | 8 +------- .../event/CaseworkerUpdateApplicant2Email.java | 5 +---- .../caseworker/service/EmailUpdateService.java | 1 - .../service/EmailUpdateServiceTest.java | 15 --------------- 4 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java index 58a20a8e254..44f82479096 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant1Email.java @@ -28,22 +28,18 @@ public class CaseworkerUpdateApplicant1Email implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_UPDATE_APP1_EMAIL) .forStates(POST_SUBMISSION_STATES) - .name("Update applicant1 email") + .name("Update App or App1 Email") .description("Update applicant/applicant1 email") .aboutToSubmitCallback(this::aboutToSubmit) .showSummary() @@ -56,8 +52,6 @@ public void configure(final ConfigBuilder configBuild .pageLabel("Update applicant/applicant1 email") .complex(CaseData::getApplicant1) .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, APPLICANTS_OR_APPLICANT1S)) - .readonlyNoSummary(Applicant::getOffline,NEVER_SHOW) - .label("willNotReceiveInvite", WILL_NOT_SEND_INVITE,"applicant1Offline = \"Yes\"") .done(); } diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java index cc053b82c8f..2994fa02739 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUpdateApplicant2Email.java @@ -15,7 +15,6 @@ import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import static java.util.Collections.singletonList; -import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerUpdateApplicant1Email.WILL_NOT_SEND_INVITE; import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; @@ -41,7 +40,7 @@ public void configure(final ConfigBuilder configBuild new PageBuilder(configBuilder .event(CASEWORKER_UPDATE_APP2_EMAIL) .forStates(POST_SUBMISSION_STATES) - .name("Update applicant2 email") + .name("Update Resp or App 2 Email") .description("Update respondent/applicant2 email") .aboutToSubmitCallback(this::aboutToSubmit) .showSummary() @@ -54,8 +53,6 @@ public void configure(final ConfigBuilder configBuild .pageLabel("Update respondent/applicant2 email") .complex(CaseData::getApplicant2) .optionalWithLabel(Applicant::getEmail, getLabel(EMAIL_LABEL, RESPONDENTS_OR_APPLICANT2S)) - .readonlyNoSummary(Applicant::getOffline,NEVER_SHOW) - .label("willNotReceiveInvite", WILL_NOT_SEND_INVITE,"applicant2Offline = \"Yes\"") .done(); } diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java index 094a76c7e44..941260d4096 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java @@ -34,7 +34,6 @@ public CaseDetails processEmailUpdate(final CaseDetails details = new CaseDetails<>(); - details.setData(caseData); - details.setId(TEST_CASE_ID); - - final CaseDetails newDetails = emailUpdateService.processEmailUpdate(details, details, true); - - verifyNoInteractions(inviteApplicantToCaseNotification); - verifyNoInteractions(emailUpdatedNotification); - } - @Test void shouldNotProgressWhenApplicantIsRepresented() { final CaseData caseData = validApplicant1CaseData(); From 80785245ae305750813e538b7e6e200a6b14aad5 Mon Sep 17 00:00:00 2001 From: pallavijustice <159147694+pallavijustice@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:25:38 +0000 Subject: [PATCH 14/16] Update EmailUpdateService.java Removed comment --- .../gov/hmcts/divorce/caseworker/service/EmailUpdateService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java index 941260d4096..d2cf70686a5 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/EmailUpdateService.java @@ -32,7 +32,6 @@ public CaseDetails processEmailUpdate(final CaseDetails Date: Mon, 30 Dec 2024 18:29:49 +0000 Subject: [PATCH 15/16] NFDIV-4356 - Merge from master --- .nvmrc | 2 +- Jenkinsfile_CNP | 4 +- build.gradle | 41 +- charts/nfdiv-case-api/Chart.yaml | 2 +- .../values.preview.template.yaml | 4 + charts/nfdiv-case-api/values.yaml | 4 + gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 61624 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 34 +- gradlew.bat | 22 +- infrastructure/.terraform-version | 2 +- .../hmcts/divorce/cftlib/CftLibConfig.java | 10 + ...stemNotifyRespondentApplyFinalOrderFT.java | 26 +- src/functionalTest/resources/application.yaml | 3 + ...sedata-app2-sol-apply-for-final-order.json | 15 + ...ss-case-to-awaiting-final-order-welsh.json | 3 +- ...progress-case-to-awaiting-final-order.json | 3 +- ...pplicants-apply-for-final-order-welsh.json | 3 +- ...mind-applicants-apply-for-final-order.json | 3 +- .../casedata/ccd-callback-casedata.json | 17 +- ...licitor-final-order-payment-mid-event.json | 14 + .../response-solicitor-payment-mid-event.json | 14 + .../citizen/event/CitizenSaveAndCloseIT.java | 22 + .../event/CitizenSubmitApplicationIT.java | 2 - ...plicant2SolicitorApplyForFinalOrderIT.java | 101 +++++ .../RespondentApplyForFinalOrderIT.java | 2 + .../SolicitorCreateApplicationIT.java | 21 +- .../SolicitorGeneralApplicationIT.java | 33 +- .../SolicitorSubmitApplicationIT.java | 84 +++- ...temProgressCaseToAwaitingFinalOrderIT.java | 6 +- .../divorce/testutil/PaymentWireMock.java | 39 +- .../resources/application.yaml | 4 + ...-general-application-payment-response.json | 3 + .../gov/hmcts/divorce/CaseApiApplication.java | 2 +- .../event/CaseworkerAddBailiffReturn.java | 6 + ...seworkerAlternativeServiceApplication.java | 15 +- .../event/CaseworkerApplicantResponded.java | 15 +- .../event/CaseworkerChangeServiceRequest.java | 12 +- .../event/CaseworkerFindMatches.java | 149 +++++-- .../event/CaseworkerHWFPartPaymentMade.java | 15 +- .../CaseworkerHwfApplicationAccepted.java | 14 +- .../event/CaseworkerHwfEvidenceRequested.java | 15 +- .../CaseworkerHwfPartPaymentRequired.java | 15 +- .../event/CaseworkerHwfRefused.java | 13 +- .../event/CaseworkerIssueApplication.java | 4 +- .../CaseworkerOfflineDocumentVerified.java | 216 ++++++++-- .../event/CaseworkerPaymentMade.java | 14 +- .../event/CaseworkerReissueApplication.java | 18 +- .../CaseworkerUploadConfidentialDocument.java | 3 +- .../event/CaseworkerUploadDocument.java | 3 +- .../CaseworkerUploadDocumentsAndSubmit.java | 5 +- .../divorce/caseworker/model/CaseNote.java | 2 + .../CitizenApplicant2UpdateApplication.java | 6 +- .../event/CitizenCreateServiceRequest.java | 31 +- .../citizen/event/CitizenSaveAndClose.java | 7 +- .../event/CitizenSubmitApplication.java | 31 +- .../event/CitizenUpdateApplication.java | 6 +- .../event/RespondentApplyForFinalOrder.java | 32 +- .../SaveAndSignOutNotificationHandler.java | 13 +- .../citizen/service/SwitchToSoleService.java | 10 + .../common/ccd/HighLevelDataSetupApp.java | 1 + ...Applicant2SolicitorApplyForFinalOrder.java | 38 +- .../common/event/SwitchedToSoleCo.java | 4 +- .../SwitchedToSoleFinalOrderOffline.java | 2 +- .../AwaitingFinalOrderNotification.java | 4 +- ...waitingFinalOrderReminderNotification.java | 2 +- .../common/service/task/SetDateSubmitted.java | 13 +- .../divorcecase/model/Application.java | 5 +- .../divorce/divorcecase/model/CaseData.java | 16 +- .../divorcecase/model/CaseDocuments.java | 8 +- .../divorce/divorcecase/model/FeeDetails.java | 8 +- .../divorce/divorcecase/model/FinalOrder.java | 8 +- .../divorcecase/model/GeneralEmail.java | 6 +- .../divorcecase/model/SolicitorService.java | 19 + .../divorce/divorcecase/model/State.java | 30 +- .../divorce/divorcecase/model/UserRole.java | 1 + .../access/CaseworkerAndSuperUserAccess.java | 2 - .../divorcecase/tab/ApplicationTab.java | 4 + .../divorce/divorcecase/tab/CaseTypeTab.java | 26 +- .../content/DocmosisTemplateConstants.java | 1 + .../FinalOrderCanApplyTemplateContent.java | 2 +- .../divorce/document/model/DocumentType.java | 5 +- .../gov/hmcts/divorce/idam/IdamService.java | 20 + .../event/SystemApplyNoticeOfChange.java | 15 +- .../event/SystemRequestNoticeOfChange.java | 4 +- .../notification/ApplicantNotification.java | 4 + .../divorce/notification/CommonContent.java | 31 +- .../notification/EmailTemplateName.java | 10 + .../notification/NotificationDispatcher.java | 92 +++++ .../divorce/payment/PaymentPbaClient.java | 4 +- .../hmcts/divorce/payment/PaymentService.java | 71 +--- .../model/CreditAccountPaymentRequest.java | 30 +- .../model/CreditAccountPaymentResponse.java | 64 +-- .../event/SolicitorConfirmService.java | 21 + .../event/SolicitorGeneralApplication.java | 9 +- .../event/SolicitorSubmitApplication.java | 38 +- .../GeneralApplicationPaymentSummary.java | 1 + .../page/GeneralApplicationSelectFee.java | 71 +++- .../event/page/SolFinalOrderPayment.java | 28 +- .../page/SolFinalOrderPaymentSummary.java | 1 + .../solicitor/event/page/SolPayment.java | 24 +- .../event/page/SolPaymentSummary.java | 3 +- .../SolicitorCreateApplicationService.java | 7 +- ...SystemNotifyRespondentApplyFinalOrder.java | 37 +- .../service/CcdSearchService.java | 50 ++- .../GenerateApplyForFinalOrderDocument.java | 2 +- src/main/resources/application.yaml | 215 +++++----- .../event/CaseworkerFindMatchesTest.java | 183 +++++++- ...CaseworkerOfflineDocumentVerifiedTest.java | 310 +++++++++++++- .../CitizenCreateServiceRequestTest.java | 34 +- .../event/CitizenSaveAndCloseTest.java | 2 +- .../event/CitizenSubmitApplicationTest.java | 26 +- .../RespondentApplyForFinalOrderTest.java | 16 +- ...SaveAndSignOutNotificationHandlerTest.java | 46 ++- .../service/SwitchToSoleServiceTest.java | 65 +++ ...icant2SolicitorApplyForFinalOrderTest.java | 16 +- .../AwaitingFinalOrderNotificationTest.java | 7 +- .../CaseworkerAndSuperUserAccessTest.java | 4 +- ...FinalOrderCanApplyTemplateContentTest.java | 4 +- .../hmcts/divorce/idam/IdamServiceTest.java | 33 +- .../event/SystemApplyNoticeOfChangeTest.java | 35 ++ .../notification/CommonContentTest.java | 98 +++++ .../NotificationDispatcherTest.java | 389 ++++++++++++++++++ .../divorce/payment/PaymentServiceTest.java | 35 +- .../event/SolicitorConfirmServiceTest.java | 75 ++++ .../SolicitorGeneralApplicationTest.java | 7 +- .../event/SolicitorSubmitApplicationTest.java | 46 +-- .../page/GeneralApplicationSelectFeeTest.java | 40 +- .../solicitor/event/page/SolPaymentTest.java | 11 + ...eateApplicationApplicationServiceTest.java | 6 + ...emNotifyRespondentApplyFinalOrderTest.java | 39 ++ .../service/CcdSearchServiceTest.java | 76 +++- ...enerateApplyForFinalOrderDocumentTest.java | 6 +- .../hmcts/divorce/testutil/TestConstants.java | 7 + .../divorce/testutil/TestDataHelper.java | 343 ++++++++++++++- 135 files changed, 3549 insertions(+), 684 deletions(-) diff --git a/.nvmrc b/.nvmrc index fdb2eaaff0c..d135defb282 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.11.0 \ No newline at end of file +22.12.0 \ No newline at end of file diff --git a/Jenkinsfile_CNP b/Jenkinsfile_CNP index 9e7b9fb4413..28023fa77fc 100644 --- a/Jenkinsfile_CNP +++ b/Jenkinsfile_CNP @@ -47,7 +47,9 @@ def secrets = [ secret('idam-solicitor-password', 'IDAM_SOLICITOR_PASSWORD'), secret('idam-systemupdate-username', 'IDAM_SYSTEM_UPDATE_USERNAME'), secret('idam-systemupdate-password', 'IDAM_SYSTEM_UPDATE_PASSWORD'), - secret('report-email', 'REPORT_EMAIL') + secret('report-email', 'REPORT_EMAIL'), + secret('idam-old-divorce-user', 'IDAM_OLD_DIVORCE_USERNAME'), + secret('idam-old-divorce-password', 'IDAM_OLD_DIVORCE_PASSWORD') ] ] diff --git a/build.gradle b/build.gradle index dab95ff707c..21a18beb1c9 100644 --- a/build.gradle +++ b/build.gradle @@ -20,12 +20,12 @@ plugins { id 'idea' id 'pmd' id 'jacoco' - id 'org.owasp.dependencycheck' version '11.1.0' - id 'org.sonarqube' version '5.1.0.4882' + id 'org.owasp.dependencycheck' version '11.1.1' + id 'org.sonarqube' version '6.0.1.5171' id 'org.springframework.boot' version '3.3.5' id 'com.github.ben-manes.versions' version '0.51.0' - id 'hmcts.ccd.sdk' version '5.5.11' - id 'com.github.hmcts.rse-cft-lib' version '0.19.1460' + id 'hmcts.ccd.sdk' version '5.5.16' + id 'com.github.hmcts.rse-cft-lib' version '0.19.1511' } apply plugin: 'cz.habarta.typescript-generator' @@ -218,7 +218,7 @@ dependencyManagement { dependencies { dependency group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.70' //CVE-2020-23064 - dependency group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '10.1.33' + dependency group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '10.1.34' //CVE-2023-5072 dependency group: 'org.json', name: 'json', version: '20240303' } @@ -231,9 +231,9 @@ repositories { } def versions = [ - jackson : '2.18.1', - junit : '5.11.3', - junitPlatform: '1.11.3', + jackson : '2.18.2', + junit : '5.11.4', + junitPlatform: '1.11.4', lombok : '1.18.36', springBoot : springBoot.class.package.implementationVersion ] @@ -250,12 +250,12 @@ ext.libraries = [ dependencies { // uncomment for local version -// implementation group: 'com.github.hmcts', name: 'ccd-config-generator', version: 'DEV-SNAPSHOT' +// implementation group: 'com.github.hmcts', name: 'ccd-config-generator', version: 'DEV-SNAPSHOT' implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version: '8.0' - implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.24.1' - implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.24.1' - implementation group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: '2.24.1' - implementation group: 'com.google.guava', name: 'guava', version: '33.3.1-jre' + implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.24.3' + implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.24.3' + implementation group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: '2.24.3' + implementation group: 'com.google.guava', name: 'guava', version: '33.4.0-jre' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: versions.jackson implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: versions.jackson @@ -283,15 +283,16 @@ dependencies { implementation group: 'org.springframework.boot', name: 'spring-boot-starter-json' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation' implementation group: 'org.springframework.retry', name: 'spring-retry' - implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-bootstrap', version: '4.1.4' + implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-bootstrap', version: '4.1.5' - implementation group: 'org.elasticsearch', name: 'elasticsearch', version: '7.17.25' + implementation group: 'org.elasticsearch', name: 'elasticsearch', version: '7.17.26' implementation group: 'uk.gov.service.notify', name: 'notifications-java-client', version: '5.2.1-RELEASE' implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.4' implementation group: 'commons-validator', name: 'commons-validator', version: '1.9.0' implementation group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '3.1.8' implementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok + implementation group: 'org.apache.commons', name: 'commons-jcs3-core', version: '3.2.1' annotationProcessor group: 'org.projectlombok', name: 'lombok', version: versions.lombok testImplementation libraries.junit5 @@ -304,10 +305,10 @@ dependencies { testImplementation group: 'com.jayway.awaitility', name: 'awaitility', version: '1.7.0' testImplementation group: 'com.github.stefanbirkner', name: 'system-lambda', version: '1.2.1' - testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.26.3' - testImplementation group: 'org.assertj', name: 'assertj-guava', version: '3.26.3' - testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-contract-stub-runner', version: '4.1.4' - testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '4.1.3' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.27.0' + testImplementation group: 'org.assertj', name: 'assertj-guava', version: '3.27.0' + testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-contract-stub-runner', version: '4.1.5' + testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '4.1.4' testImplementation group: 'org.mockito', name: 'mockito-inline', version: '5.2.0' testImplementation 'org.powermock:powermock-module-junit4:2.0.9' testImplementation 'org.powermock:powermock-api-mockito2:2.0.9' @@ -326,7 +327,7 @@ dependencies { // Provides fast-reload of just the NFDIV service. cftlibImplementation 'org.springframework.boot:spring-boot-devtools' - cftlibTestImplementation 'com.microsoft.playwright:playwright:1.48.0' + cftlibTestImplementation 'com.microsoft.playwright:playwright:1.49.0' cftlibTestImplementation 'org.junit-pioneer:junit-pioneer:2.3.0' } diff --git a/charts/nfdiv-case-api/Chart.yaml b/charts/nfdiv-case-api/Chart.yaml index 8ebd93c09b9..f806b0be264 100644 --- a/charts/nfdiv-case-api/Chart.yaml +++ b/charts/nfdiv-case-api/Chart.yaml @@ -3,7 +3,7 @@ appVersion: "1.0" description: A Helm chart for nfdiv-case-api App name: nfdiv-case-api home: https://github.com/hmcts/nfdiv-case-api -version: 0.0.95 +version: 0.0.97 maintainers: - name: HMCTS nfdiv team dependencies: diff --git a/charts/nfdiv-case-api/values.preview.template.yaml b/charts/nfdiv-case-api/values.preview.template.yaml index 62161854f1b..bff9ab2f594 100644 --- a/charts/nfdiv-case-api/values.preview.template.yaml +++ b/charts/nfdiv-case-api/values.preview.template.yaml @@ -22,6 +22,10 @@ java: alias: IDAM_SYSTEM_UPDATE_PASSWORD - name: report-email alias: REPORT_EMAIL + - name: idam-old-divorce-user + alias: IDAM_OLD_DIVORCE_USERNAME + - name: idam-old-divorce-password + alias: IDAM_OLD_DIVORCE_PASSWORD nfdiv-frontend: enabled: true nodejs: diff --git a/charts/nfdiv-case-api/values.yaml b/charts/nfdiv-case-api/values.yaml index b555b968a54..4b953fe8bfc 100644 --- a/charts/nfdiv-case-api/values.yaml +++ b/charts/nfdiv-case-api/values.yaml @@ -36,6 +36,10 @@ java: alias: app-insights-connection-string - name: report-email alias: REPORT_EMAIL + - name: idam-old-divorce-user + alias: IDAM_OLD_DIVORCE_USERNAME + - name: idam-old-divorce-password + alias: IDAM_OLD_DIVORCE_PASSWORD environment: NOTIFY_TEMPLATE_SIGN_IN_DIVORCE_URL: https://nfdiv-apply-for-divorce.{{ .Values.global.environment }}.platform.hmcts.net/ NOTIFY_TEMPLATE_SIGN_IN_DISSOLUTION_URL: https://nfdiv-end-civil-partnership.{{ .Values.global.environment }}.platform.hmcts.net/ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..afba109285af78dbd2a1d187e33ac4f87c76e392 100644 GIT binary patch literal 61624 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�Zy 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 literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 94113f200e6..e2847c82004 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d6b1..65dcd68d65c 100755 --- a/gradlew +++ b/gradlew @@ -15,8 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# SPDX-License-Identifier: Apache-2.0 -# ############################################################################## # @@ -57,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -85,9 +83,10 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -134,13 +133,10 @@ location of your Java installation." fi else JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." - fi fi # Increase the maximum file descriptors if we can. @@ -148,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -156,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -201,15 +197,11 @@ if "$cygwin" || "$msys" ; then done fi - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 9b42019c791..6689b85beec 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,6 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -45,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. goto fail @@ -59,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. goto fail diff --git a/infrastructure/.terraform-version b/infrastructure/.terraform-version index 66beabb5795..587c5f0c730 100644 --- a/infrastructure/.terraform-version +++ b/infrastructure/.terraform-version @@ -1 +1 @@ -1.9.8 +1.10.3 diff --git a/src/cftlib/java/uk/gov/hmcts/divorce/cftlib/CftLibConfig.java b/src/cftlib/java/uk/gov/hmcts/divorce/cftlib/CftLibConfig.java index 7839abcae14..2e7475dfed3 100644 --- a/src/cftlib/java/uk/gov/hmcts/divorce/cftlib/CftLibConfig.java +++ b/src/cftlib/java/uk/gov/hmcts/divorce/cftlib/CftLibConfig.java @@ -13,6 +13,9 @@ import java.io.File; import java.io.IOException; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.Map; @@ -26,6 +29,8 @@ public class CftLibConfig implements CFTLibConfigurer { public void configure(CFTLib lib) throws Exception { var users = Map.of( "DivCaseWorkerUser@AAT.com", List.of("caseworker", "caseworker-divorce", "caseworker-divorce-courtadmin_beta"), + "DivCaseSuperUser@AAT.com", List.of( + "caseworker", "caseworker-divorce", "caseworker-divorce-superuser", "caseworker-divorce-courtadmin_beta"), "TEST_CASE_WORKER_USER@mailinator.com", List.of("caseworker", "caseworker-divorce", "caseworker-divorce-courtadmin_beta"), "TEST_SOLICITOR@mailinator.com", List.of("caseworker", "caseworker-divorce", "caseworker-divorce-solicitor"), "TEST_JUDGE@mailinator.com", List.of("caseworker", "caseworker-divorce", "caseworker-divorce-judge"), @@ -55,6 +60,7 @@ public void configure(CFTLib lib) throws Exception { "citizen", "caseworker-divorce", "caseworker", + "caseworker-divorce-rparobot", "payments", "pui-case-manager", "pui-finance-manager", @@ -81,5 +87,9 @@ public void configure(CFTLib lib) throws Exception { // Import CCD definitions lib.importJsonDefinition(new File("build/definitions/NFD")); lib.importJsonDefinition(new File("build/definitions/NO_FAULT_DIVORCE_BulkAction")); + + Path filePath = Paths.get("resources/ccd-OLD-DIVORCE.xlsx"); + byte[] defDivorce = Files.readAllBytes(filePath); + lib.importDefinition(defDivorce); } } diff --git a/src/functionalTest/java/uk/gov/hmcts/divorce/systemupdate/event/SystemNotifyRespondentApplyFinalOrderFT.java b/src/functionalTest/java/uk/gov/hmcts/divorce/systemupdate/event/SystemNotifyRespondentApplyFinalOrderFT.java index 7ef1a2e5ef0..21d61edb770 100644 --- a/src/functionalTest/java/uk/gov/hmcts/divorce/systemupdate/event/SystemNotifyRespondentApplyFinalOrderFT.java +++ b/src/functionalTest/java/uk/gov/hmcts/divorce/systemupdate/event/SystemNotifyRespondentApplyFinalOrderFT.java @@ -33,6 +33,8 @@ import static uk.gov.hmcts.divorce.systemupdate.service.CcdSearchService.STATE; import static uk.gov.hmcts.divorce.testutil.CaseDataUtil.caseData; import static uk.gov.hmcts.divorce.testutil.TestConstants.ABOUT_TO_SUBMIT_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOL_USER_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestResourceUtil.expectedResponse; @SpringBootTest @@ -40,8 +42,10 @@ public class SystemNotifyRespondentApplyFinalOrderFT extends FunctionalTestSuite private static final String REQUEST = "classpath:request/casedata/ccd-callback-casedata-system-notify-respondent-apply-final-order.json"; - private static final String RESPONSE = "classpath:responses/response-system-notify-respondent-apply-final-order.json"; - + private static final String UNREPRESENTED_RESPONSE = + "classpath:responses/response-system-notify-respondent-apply-final-order.json"; + private static final String REPRESENTED_RESPONSE = + "classpath:responses/response-system-notify-respondent-solicitor-apply-final-order.json"; @Test public void shouldPassValidationAndSendEmailsToRespondent() throws IOException { @@ -53,7 +57,23 @@ public void shouldPassValidationAndSendEmailsToRespondent() throws IOException { assertThatJson(response.asString()) .when(IGNORING_ARRAY_ORDER) - .isEqualTo(json(expectedResponse(RESPONSE))); + .isEqualTo(json(expectedResponse(UNREPRESENTED_RESPONSE))); + } + + @Test + public void shouldPrepareCaseForPbaPaymentIfRespondentIsRepresented() throws IOException { + Map request = caseData(REQUEST); + request.put("applicant2SolicitorRepresented", YesOrNo.YES); + request.put("applicant2SolicitorEmail", TEST_SOL_USER_EMAIL); + request.put("applicant2SolicitorName", TEST_SOLICITOR_NAME); + + Response response = triggerCallback(request, SYSTEM_NOTIFY_RESPONDENT_APPLY_FINAL_ORDER, ABOUT_TO_SUBMIT_URL); + + assertThat(response.getStatusCode()).isEqualTo(OK.value()); + + assertThatJson(response.asString()) + .when(IGNORING_ARRAY_ORDER) + .isEqualTo(json(expectedResponse(REPRESENTED_RESPONSE))); } @Test diff --git a/src/functionalTest/resources/application.yaml b/src/functionalTest/resources/application.yaml index 718da93678d..1df61af440f 100644 --- a/src/functionalTest/resources/application.yaml +++ b/src/functionalTest/resources/application.yaml @@ -44,6 +44,9 @@ idam: id: 'divorce' secret: ${OAUTH2_CLIENT_SECRET:dummy} redirect_uri: ${IDAM_API_REDIRECT_URL:http://localhost:3001/oauth2/callback} + divorce: + username: ${IDAM_OLD_DIVORCE_USERNAME:dummysystemupdate@test.com} + password: ${IDAM_OLD_DIVORCE_PASSWORD:dummy} s2s-authorised: services: ccd_data diff --git a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-app2-sol-apply-for-final-order.json b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-app2-sol-apply-for-final-order.json index 77942813c0d..816bfdc42b5 100644 --- a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-app2-sol-apply-for-final-order.json +++ b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-app2-sol-apply-for-final-order.json @@ -14,6 +14,21 @@ "applicant1SolicitorName": "Applicant 1 Solicitor", "applicant1SolicitorEmail": "applicant1.solicitor@example.com", "applicant2SolicitorRepresented": "Yes", + "applicant2SolFinalOrderFeeOrderSummary": { + "PaymentTotal": "55000", + "PaymentReference": null, + "Fees": [ + { + "id": null, + "value": { + "FeeDescription": "Filing an application for a divorce, nullity or civil partnership dissolution", + "FeeVersion": "5", + "FeeCode": "FEE0002", + "FeeAmount": "55000" + } + } + ] + }, "applicant2SolicitorName": "Applicant 2 Solicitor", "applicant2SolicitorEmail": "applicant2.solicitor@example.com", "divorceWho": "wife", diff --git a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-progress-case-to-awaiting-final-order-welsh.json b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-progress-case-to-awaiting-final-order-welsh.json index 9e92954ee15..74f47d43fe4 100644 --- a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-progress-case-to-awaiting-final-order-welsh.json +++ b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-progress-case-to-awaiting-final-order-welsh.json @@ -51,5 +51,6 @@ "applicant2StatementOfTruth": "Yes", "documentUploadComplete": "Yes", "dateFinalOrderEligibleToRespondent": "2022-06-10", - "dateFinalOrderEligibleFrom": "2022-03-10" + "dateFinalOrderEligibleFrom": "2022-03-10", + "coGrantedDate": "2022-02-10" } diff --git a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-progress-case-to-awaiting-final-order.json b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-progress-case-to-awaiting-final-order.json index 3ed8353bc27..28dacc3d170 100644 --- a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-progress-case-to-awaiting-final-order.json +++ b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-progress-case-to-awaiting-final-order.json @@ -51,5 +51,6 @@ "applicant2StatementOfTruth": "Yes", "documentUploadComplete": "Yes", "dateFinalOrderEligibleToRespondent": "2022-06-10", - "dateFinalOrderEligibleFrom": "2022-03-10" + "dateFinalOrderEligibleFrom": "2022-03-10", + "coGrantedDate": "2022-02-10" } diff --git a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-remind-applicants-apply-for-final-order-welsh.json b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-remind-applicants-apply-for-final-order-welsh.json index fced1b3c6b4..f5584fe192d 100644 --- a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-remind-applicants-apply-for-final-order-welsh.json +++ b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-remind-applicants-apply-for-final-order-welsh.json @@ -70,5 +70,6 @@ ], "documentUploadComplete": "Yes", "dateFinalOrderEligibleToRespondent": "2022-07-11", - "dateFinalOrderEligibleFrom": "2022-07-11" + "dateFinalOrderEligibleFrom": "2022-07-11", + "coGrantedDate": "2022-02-10" } diff --git a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-remind-applicants-apply-for-final-order.json b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-remind-applicants-apply-for-final-order.json index 1866bbc5103..b5aa1c55f9a 100644 --- a/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-remind-applicants-apply-for-final-order.json +++ b/src/functionalTest/resources/request/casedata/ccd-callback-casedata-system-remind-applicants-apply-for-final-order.json @@ -70,5 +70,6 @@ ], "documentUploadComplete": "Yes", "dateFinalOrderEligibleToRespondent": "2022-07-11", - "dateFinalOrderEligibleFrom": "2022-07-11" + "dateFinalOrderEligibleFrom": "2022-07-11", + "coGrantedDate": "2022-02-10" } diff --git a/src/functionalTest/resources/request/casedata/ccd-callback-casedata.json b/src/functionalTest/resources/request/casedata/ccd-callback-casedata.json index cdf7f50df66..6df98013444 100644 --- a/src/functionalTest/resources/request/casedata/ccd-callback-casedata.json +++ b/src/functionalTest/resources/request/casedata/ccd-callback-casedata.json @@ -7,5 +7,20 @@ "divorceWho": "wife", "marriageFormationType": "oppositeSexCouple", "solPaymentHowToPay": "feePayByAccount", - "marriageDate": "2009-04-20" + "marriageDate": "2009-04-20", + "applicationFeeOrderSummary": { + "PaymentTotal": "55000", + "PaymentReference": null, + "Fees": [ + { + "id": null, + "value": { + "FeeDescription": "Filing an application for a divorce, nullity or civil partnership dissolution", + "FeeVersion": "5", + "FeeCode": "FEE0002", + "FeeAmount": "55000" + } + } + ] + } } diff --git a/src/functionalTest/resources/responses/response-solicitor-final-order-payment-mid-event.json b/src/functionalTest/resources/responses/response-solicitor-final-order-payment-mid-event.json index ab581f4c268..6aa19e91600 100644 --- a/src/functionalTest/resources/responses/response-solicitor-final-order-payment-mid-event.json +++ b/src/functionalTest/resources/responses/response-solicitor-final-order-payment-mid-event.json @@ -26,6 +26,20 @@ "marriageFormationType": "oppositeSexCouple", "solServiceTruthStatement": "I believe that the facts stated in the application are true.", "applicant2SolPaymentHowToPay": "feePayByAccount", + "applicant2FinalOrderFeeServiceRequestReference": "${json-unit.any-string}", + "applicant2SolFinalOrderFeeOrderSummary": { + "PaymentTotal": "55000", + "Fees": [ + { + "value": { + "FeeDescription": "Filing an application for a divorce, nullity or civil partnership dissolution", + "FeeVersion": "5", + "FeeCode": "FEE0002", + "FeeAmount": "55000" + } + } + ] + }, "finalOrderPbaNumbers": { "list_items": [ { diff --git a/src/functionalTest/resources/responses/response-solicitor-payment-mid-event.json b/src/functionalTest/resources/responses/response-solicitor-payment-mid-event.json index 54e48919e2b..2de43428e7e 100644 --- a/src/functionalTest/resources/responses/response-solicitor-payment-mid-event.json +++ b/src/functionalTest/resources/responses/response-solicitor-payment-mid-event.json @@ -14,6 +14,20 @@ "marriageFormationType": "oppositeSexCouple", "solServiceTruthStatement": "I believe that the facts stated in the application are true.", "solPaymentHowToPay": "feePayByAccount", + "applicationFeeOrderSummary": { + "PaymentTotal": "55000", + "Fees": [ + { + "value": { + "FeeDescription": "Filing an application for a divorce, nullity or civil partnership dissolution", + "FeeVersion": "5", + "FeeCode": "FEE0002", + "FeeAmount": "55000" + } + } + ] + }, + "applicationFeeServiceRequestReference": "${json-unit.any-string}", "pbaNumbers": { "list_items": [ { diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndCloseIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndCloseIT.java index 6ae90e4c5f8..512023d6d05 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndCloseIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndCloseIT.java @@ -35,6 +35,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static uk.gov.hmcts.divorce.citizen.event.CitizenSaveAndClose.CITIZEN_SAVE_AND_CLOSE; import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT; import static uk.gov.hmcts.divorce.notification.EmailTemplateName.SAVE_SIGN_OUT; import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTH_HEADER_VALUE; import static uk.gov.hmcts.divorce.testutil.TestConstants.SERVICE_AUTHORIZATION; @@ -99,6 +101,26 @@ public void givenValidCaseDataWhenCallbackIsInvokedThenSendEmail() throws Except verifyNoMoreInteractions(notificationService); } + @Test + public void givenValidCaseDataWhenCallbackIsInvokedThenSendEmailWhenInformationRequested() throws Exception { + mockMvc.perform(post(SUBMITTED_URL) + .contentType(APPLICATION_JSON) + .header(SERVICE_AUTHORIZATION, AUTH_HEADER_VALUE) + .header(AUTHORIZATION, AUTH_HEADER_VALUE) + .content(objectMapper + .writeValueAsString( + callbackRequest(caseDataWithOrderSummary(), CITIZEN_SAVE_AND_CLOSE, InformationRequested.toString()) + ) + ) + .accept(APPLICATION_JSON)) + .andExpect(status().isOk()); + + verify(notificationService) + .sendEmail(eq(TEST_USER_EMAIL), eq(REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT), anyMap(), eq(ENGLISH), anyLong()); + + verifyNoMoreInteractions(notificationService); + } + @Test public void givenRequestBodyIsNullWhenEndpointInvokedThenReturnBadRequest() throws Exception { mockMvc.perform(post(SUBMITTED_URL) diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplicationIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplicationIT.java index e413d529815..ce26eff07ba 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplicationIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplicationIT.java @@ -13,7 +13,6 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; -import uk.gov.hmcts.ccd.sdk.type.OrderSummary; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; import uk.gov.hmcts.divorce.common.config.WebMvcConfig; import uk.gov.hmcts.divorce.common.config.interceptors.RequestInterceptor; @@ -167,7 +166,6 @@ public void givenFeeEventIsNotAvailableWhenAboutToStartCallbackIsInvokedThenRetu stubForFeesNotFound(); CaseData caseData = validApplicant1CaseData(); - caseData.getApplication().setApplicationFeeOrderSummary(OrderSummary.builder().paymentTotal("55000").build()); mockMvc.perform(post(ABOUT_TO_SUBMIT_URL) .contentType(APPLICATION_JSON) diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/common/Applicant2SolicitorApplyForFinalOrderIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/common/Applicant2SolicitorApplyForFinalOrderIT.java index 2eeebdf75aa..79b4325d9ef 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/common/Applicant2SolicitorApplyForFinalOrderIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/common/Applicant2SolicitorApplyForFinalOrderIT.java @@ -1,26 +1,37 @@ package uk.gov.hmcts.divorce.common; import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.type.DynamicList; import uk.gov.hmcts.divorce.common.config.WebMvcConfig; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.Solicitor; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.notification.NotificationService; +import uk.gov.hmcts.divorce.payment.model.PaymentItem; +import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; +import uk.gov.hmcts.divorce.testutil.FeesWireMock; +import uk.gov.hmcts.divorce.testutil.PaymentWireMock; +import uk.gov.hmcts.divorce.testutil.TestDataHelper; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import java.time.Clock; import java.time.LocalDate; +import java.util.List; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyMap; @@ -28,19 +39,31 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.common.event.Applicant2SolicitorApplyForFinalOrder.FINAL_ORDER_REQUESTED_APP2_SOL; import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.SOLE_APPLICATION; import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.divorcecase.model.SolicitorPaymentMethod.FEE_PAY_BY_ACCOUNT; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingFinalOrder; import static uk.gov.hmcts.divorce.notification.EmailTemplateName.APPLICANT_2_SOLICITOR_APPLIED_FOR_FINAL_ORDER; +import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; +import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; +import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; import static uk.gov.hmcts.divorce.testutil.ClockTestUtil.getExpectedLocalDate; import static uk.gov.hmcts.divorce.testutil.ClockTestUtil.setMockClock; +import static uk.gov.hmcts.divorce.testutil.FeesWireMock.stubForFeesLookup; import static uk.gov.hmcts.divorce.testutil.IdamWireMock.SYSTEM_USER_ROLE; import static uk.gov.hmcts.divorce.testutil.IdamWireMock.stubForIdamDetails; import static uk.gov.hmcts.divorce.testutil.IdamWireMock.stubForIdamToken; +import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.buildServiceReferenceRequest; +import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.stubCreateServiceRequest; +import static uk.gov.hmcts.divorce.testutil.TestConstants.ABOUT_TO_START_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.APP2_SOL_FO_PAYMENT_MID_EVENT_URL; import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTHORIZATION; import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTH_HEADER_VALUE; import static uk.gov.hmcts.divorce.testutil.TestConstants.SERVICE_AUTHORIZATION; @@ -48,18 +71,26 @@ import static uk.gov.hmcts.divorce.testutil.TestConstants.SYSTEM_USER_USER_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_APPLICANT_2_USER_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_AUTHORIZATION_TOKEN; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_AUTH_TOKEN; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_NAME; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SYSTEM_AUTHORISATION_TOKEN; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.callbackRequest; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseDataWithOrderSummary; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.organisationPolicy; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.validCaseDataForAwaitingFinalOrder; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc +@DirtiesContext +@ContextConfiguration(initializers = { + PaymentWireMock.PropertiesInitializer.class, + FeesWireMock.PropertiesInitializer.class +}) public class Applicant2SolicitorApplyForFinalOrderIT { @Autowired @@ -71,6 +102,9 @@ public class Applicant2SolicitorApplyForFinalOrderIT { @MockBean private AuthTokenGenerator serviceTokenGenerator; + @MockBean + private PbaService pbaService; + @MockBean private WebMvcConfig webMvcConfig; @@ -80,6 +114,73 @@ public class Applicant2SolicitorApplyForFinalOrderIT { @MockBean private NotificationService notificationService; + @BeforeAll + static void setUp() { + PaymentWireMock.start(); + FeesWireMock.start(); + } + + @AfterAll + static void tearDown() { + PaymentWireMock.stopAndReset(); + FeesWireMock.stopAndReset(); + } + + @Test + public void createsOrderSummaryToPrepareCaseForPayment() throws Exception { + var data = validCaseDataForAwaitingFinalOrder(); + + stubForFeesLookup(TestDataHelper.getFeeResponseAsJson(), EVENT_GENERAL, SERVICE_OTHER, KEYWORD_NOTICE); + + mockMvc.perform(post(ABOUT_TO_START_URL) + .contentType(APPLICATION_JSON) + .header(SERVICE_AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .content(objectMapper.writeValueAsString(callbackRequest(data, FINAL_ORDER_REQUESTED_APP2_SOL, "AwaitingFinalOrder"))) + .accept(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.applicant2SolFinalOrderFeeOrderSummary.PaymentTotal") + .value("1000") + ) + .andReturn() + .getResponse() + .getContentAsString(); + } + + @Test + public void createsServiceRequestToPrepareCaseForPayment() throws Exception { + var data = caseDataWithOrderSummary(); + data.getFinalOrder().setApplicant2SolFinalOrderFeeOrderSummary( + data.getApplication().getApplicationFeeOrderSummary() + ); + data.getFinalOrder().setApplicant2SolPaymentHowToPay(FEE_PAY_BY_ACCOUNT); + var serviceRequestBody = buildServiceReferenceRequest(data, data.getApplicant2()); + serviceRequestBody.setFees(List.of( + PaymentItem.builder() + .ccdCaseNumber(TEST_CASE_ID.toString()) + .calculatedAmount("550") + .code("FEE002") + .build() + )); + + when(pbaService.populatePbaDynamicList()).thenReturn(new DynamicList()); + stubCreateServiceRequest(OK, serviceRequestBody); + + mockMvc.perform(post(APP2_SOL_FO_PAYMENT_MID_EVENT_URL) + .contentType(APPLICATION_JSON) + .header(SERVICE_AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .content(objectMapper.writeValueAsString(callbackRequest(data, FINAL_ORDER_REQUESTED_APP2_SOL, "AwaitingFinalOrder"))) + .accept(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.applicant2FinalOrderFeeServiceRequestReference") + .value(TEST_SERVICE_REFERENCE) + ) + .andReturn() + .getResponse() + .getContentAsString(); + } + @Test void shouldSendEmailToApplicant2InAwaitingFinalOrderStateInSoleApplication() throws Exception { diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/common/RespondentApplyForFinalOrderIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/common/RespondentApplyForFinalOrderIT.java index 3f524a08740..29fb8d9158c 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/common/RespondentApplyForFinalOrderIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/common/RespondentApplyForFinalOrderIT.java @@ -9,6 +9,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; @@ -66,6 +67,7 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc +@DirtiesContext @ContextConfiguration(initializers = { PaymentWireMock.PropertiesInitializer.class, FeesWireMock.PropertiesInitializer.class diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorCreateApplicationIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorCreateApplicationIT.java index f516dcd6e25..644dae6de2f 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorCreateApplicationIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorCreateApplicationIT.java @@ -26,8 +26,11 @@ import uk.gov.hmcts.divorce.solicitor.client.organisation.OrganisationsResponse; import uk.gov.hmcts.divorce.testutil.CaseDataWireMock; import uk.gov.hmcts.divorce.testutil.DocAssemblyWireMock; +import uk.gov.hmcts.divorce.testutil.FeesWireMock; import uk.gov.hmcts.divorce.testutil.IdamWireMock; +import uk.gov.hmcts.divorce.testutil.PaymentWireMock; import uk.gov.hmcts.divorce.testutil.PrdOrganisationWireMock; +import uk.gov.hmcts.divorce.testutil.TestDataHelper; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import java.time.LocalDate; @@ -40,6 +43,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -51,10 +55,13 @@ import static uk.gov.hmcts.divorce.testutil.CaseDataWireMock.stubForCaseAssignmentRoles; import static uk.gov.hmcts.divorce.testutil.CaseDataWireMock.stubForCcdCaseRolesUpdateFailure; import static uk.gov.hmcts.divorce.testutil.DocAssemblyWireMock.stubForDocAssembly; +import static uk.gov.hmcts.divorce.testutil.FeesWireMock.stubForFeesLookup; import static uk.gov.hmcts.divorce.testutil.IdamWireMock.SOLICITOR_ROLE; import static uk.gov.hmcts.divorce.testutil.IdamWireMock.SYSTEM_USER_ROLE; import static uk.gov.hmcts.divorce.testutil.IdamWireMock.stubForIdamDetails; import static uk.gov.hmcts.divorce.testutil.IdamWireMock.stubForIdamToken; +import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.buildServiceReferenceRequest; +import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.stubCreateServiceRequest; import static uk.gov.hmcts.divorce.testutil.PrdOrganisationWireMock.stubGetOrganisationEndpoint; import static uk.gov.hmcts.divorce.testutil.PrdOrganisationWireMock.stubGetOrganisationEndpointForFailure; import static uk.gov.hmcts.divorce.testutil.TestConstants.ABOUT_THE_SOL_MID_EVENT_URL; @@ -88,7 +95,10 @@ PrdOrganisationWireMock.PropertiesInitializer.class, CaseDataWireMock.PropertiesInitializer.class, DocAssemblyWireMock.PropertiesInitializer.class, - IdamWireMock.PropertiesInitializer.class}) + IdamWireMock.PropertiesInitializer.class, + PaymentWireMock.PropertiesInitializer.class, + FeesWireMock.PropertiesInitializer.class +}) class SolicitorCreateApplicationIT { private static final String SOLICITOR_CREATE_ABOUT_TO_SUBMIT = "classpath:solicitor-create-about-to-submit-response.json"; @@ -114,6 +124,8 @@ static void setUp() { PrdOrganisationWireMock.start(); IdamWireMock.start(); CaseDataWireMock.start(); + PaymentWireMock.start(); + FeesWireMock.start(); } @AfterAll @@ -122,10 +134,13 @@ static void tearDown() { PrdOrganisationWireMock.stopAndReset(); IdamWireMock.stopAndReset(); CaseDataWireMock.stopAndReset(); + PaymentWireMock.stopAndReset(); + FeesWireMock.stopAndReset(); } @Test void givenValidCaseDataWhenAboutToSubmitCallbackIsInvokedCaseDataIsSetCorrectly() throws Exception { + final CaseData caseData = caseDataWithApplicant1AndApplicant2Org(); when(serviceTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN); when(documentIdProvider.documentId()).thenReturn("Divorce application"); @@ -133,13 +148,15 @@ void givenValidCaseDataWhenAboutToSubmitCallbackIsInvokedCaseDataIsSetCorrectly( stubGetOrganisationEndpoint(getOrganisationResponseWith(TEST_ORG_ID)); stubForIdamDetails(TEST_SYSTEM_AUTHORISATION_TOKEN, SYSTEM_USER_USER_ID, SYSTEM_USER_ROLE); stubForIdamToken(TEST_SYSTEM_AUTHORISATION_TOKEN); + stubForFeesLookup(TestDataHelper.getFeeResponseAsJson()); + stubCreateServiceRequest(OK, buildServiceReferenceRequest(caseData, caseData.getApplicant1())); stubForDocAssembly(); final var jsonStringResponse = mockMvc.perform(MockMvcRequestBuilders.post(ABOUT_TO_SUBMIT_URL) .contentType(APPLICATION_JSON) .header(SERVICE_AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) .header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) - .content(objectMapper.writeValueAsString(callbackRequest(caseDataWithApplicant1AndApplicant2Org(), SOLICITOR_CREATE))) + .content(objectMapper.writeValueAsString(callbackRequest(caseData, SOLICITOR_CREATE))) .accept(APPLICATION_JSON)) .andExpect( status().isOk() diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorGeneralApplicationIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorGeneralApplicationIT.java index 86b6a8ba733..c8bad64cf33 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorGeneralApplicationIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorGeneralApplicationIT.java @@ -61,9 +61,9 @@ import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTH_HEADER_VALUE; import static uk.gov.hmcts.divorce.testutil.TestConstants.SERVICE_AUTHORIZATION; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_AUTHORIZATION_TOKEN; -import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_ORG_NAME; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_AUTH_TOKEN; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.LOCAL_DATE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.callbackRequest; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getListOfDivorceDocumentListValue; @@ -155,25 +155,26 @@ public void shouldAddPaymentWhenGeneralApplicationSubmitted() throws Exception { docs.get(0).getValue().setDocumentFileName("Testfile"); docs.get(0).getValue().setDocumentDateAdded(LOCAL_DATE); + OrderSummary orderSummary = OrderSummary.builder() + .paymentTotal("55000") + .fees(List.of(ListValue + .builder() + .id("1") + .value(Fee.builder() + .code("FEE002") + .description("fees for divorce") + .build()) + .build()) + ).build(); + final CaseData caseData = CaseData.builder() .documents(CaseDocuments.builder().documentsUploaded(new ArrayList<>()).build()) .generalApplication(GeneralApplication.builder() .generalApplicationDocuments(docs) .generalApplicationFee( FeeDetails.builder() - .orderSummary( - OrderSummary.builder() - .paymentTotal("55000") - .fees(List.of(ListValue - .builder() - .id("1") - .value(Fee.builder() - .code("FEE002") - .description("fees for divorce") - .build()) - .build()) - ) - .build()) + .serviceRequestReference(TEST_SERVICE_REFERENCE) + .orderSummary(orderSummary) .pbaNumbers( DynamicList.builder() .value( @@ -220,8 +221,8 @@ public void shouldAddPaymentWhenGeneralApplicationSubmitted() throws Exception { CreditAccountPaymentResponse .builder() .status(SUCCESS.toString()) - .caseReference(TEST_CASE_ID.toString()) - .build() + .build(), + orderSummary ); String actualResponse = mockMvc.perform(post(ABOUT_TO_SUBMIT_URL) diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorSubmitApplicationIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorSubmitApplicationIT.java index c9abb980938..dc1e0e45e87 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorSubmitApplicationIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/SolicitorSubmitApplicationIT.java @@ -14,16 +14,22 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import uk.gov.hmcts.ccd.sdk.type.DynamicList; import uk.gov.hmcts.divorce.common.config.WebMvcConfig; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.notification.NotificationService; import uk.gov.hmcts.divorce.payment.model.CreditAccountPaymentResponse; +import uk.gov.hmcts.divorce.payment.model.PaymentItem; +import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; import uk.gov.hmcts.divorce.testutil.CaseDataWireMock; import uk.gov.hmcts.divorce.testutil.FeesWireMock; import uk.gov.hmcts.divorce.testutil.IdamWireMock; import uk.gov.hmcts.divorce.testutil.PaymentWireMock; +import uk.gov.hmcts.divorce.testutil.TestDataHelper; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import java.io.IOException; +import java.util.List; import static java.util.Objects.requireNonNull; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; @@ -32,25 +38,33 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static uk.gov.hmcts.divorce.divorcecase.model.PaymentStatus.SUCCESS; import static uk.gov.hmcts.divorce.divorcecase.model.SolicitorPaymentMethod.FEE_PAY_BY_ACCOUNT; import static uk.gov.hmcts.divorce.divorcecase.model.State.Draft; import static uk.gov.hmcts.divorce.solicitor.event.SolicitorSubmitApplication.SOLICITOR_SUBMIT; +import static uk.gov.hmcts.divorce.testutil.FeesWireMock.stubForFeesLookup; import static uk.gov.hmcts.divorce.testutil.FeesWireMock.stubForFeesNotFound; +import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.buildServiceReferenceRequest; +import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.stubCreateServiceRequest; import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.stubCreditAccountPayment; import static uk.gov.hmcts.divorce.testutil.TestConstants.ABOUT_TO_START_URL; import static uk.gov.hmcts.divorce.testutil.TestConstants.ABOUT_TO_SUBMIT_URL; import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTHORIZATION; import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTH_HEADER_VALUE; import static uk.gov.hmcts.divorce.testutil.TestConstants.SERVICE_AUTHORIZATION; +import static uk.gov.hmcts.divorce.testutil.TestConstants.SOL_PAYMENT_MID_EVENT_URL; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_AUTHORIZATION_TOKEN; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_AUTH_TOKEN; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.callbackRequest; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseDataWithOrderSummary; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseDataWithStatementOfTruth; @@ -81,6 +95,9 @@ public class SolicitorSubmitApplicationIT { @MockBean private AuthTokenGenerator serviceTokenGenerator; + @MockBean + private PbaService pbaService; + @MockBean private NotificationService notificationService; @@ -100,16 +117,76 @@ static void tearDown() { PaymentWireMock.stopAndReset(); } + @Test + public void createsOrderSummaryToPrepareCaseForPayment() throws Exception { + var data = caseDataWithStatementOfTruth(); + data.getApplication().setApplicationFeeOrderSummary(null); + + when(serviceTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN); + + stubForFeesLookup(TestDataHelper.getFeeResponseAsJson()); + stubCreateServiceRequest(OK, buildServiceReferenceRequest(data, data.getApplicant1())); + + mockMvc.perform(post(ABOUT_TO_START_URL) + .contentType(APPLICATION_JSON) + .header(SERVICE_AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .content(objectMapper.writeValueAsString(callbackRequest(data, SOLICITOR_SUBMIT))) + .accept(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.applicationFeeOrderSummary.PaymentTotal") + .value("1000") + ) + .andReturn() + .getResponse() + .getContentAsString(); + } + + @Test + public void createsServiceRequestToPrepareCaseForPayment() throws Exception { + var data = caseDataWithOrderSummary(); + data.getApplication().setSolPaymentHowToPay(FEE_PAY_BY_ACCOUNT); + var serviceRequestBody = buildServiceReferenceRequest(data, data.getApplicant1()); + serviceRequestBody.setFees(List.of( + PaymentItem.builder() + .ccdCaseNumber(TEST_CASE_ID.toString()) + .calculatedAmount("550") + .code("FEE002") + .build() + )); + + when(serviceTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN); + when(pbaService.populatePbaDynamicList()).thenReturn(new DynamicList()); + stubCreateServiceRequest(OK, serviceRequestBody); + + mockMvc.perform(post(SOL_PAYMENT_MID_EVENT_URL) + .contentType(APPLICATION_JSON) + .header(SERVICE_AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .content(objectMapper.writeValueAsString(callbackRequest(data, SOLICITOR_SUBMIT))) + .accept(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.applicationFeeServiceRequestReference") + .value(TEST_SERVICE_REFERENCE) + ) + .andReturn() + .getResponse() + .getContentAsString(); + } + @Test public void givenFeeEventIsNotAvailableWhenCallbackIsInvokedThenReturn404FeeEventNotFound() throws Exception { stubForFeesNotFound(); + CaseData data = new CaseData(); + data.getApplication().setApplicationFeeServiceRequestReference(TEST_SERVICE_REFERENCE); + mockMvc.perform(post(ABOUT_TO_START_URL) .contentType(APPLICATION_JSON) .header(SERVICE_AUTHORIZATION, AUTH_HEADER_VALUE) .header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) - .content(objectMapper.writeValueAsString(callbackRequest(caseDataWithOrderSummary(), SOLICITOR_SUBMIT))) + .content(objectMapper.writeValueAsString(callbackRequest(data, SOLICITOR_SUBMIT))) .accept(APPLICATION_JSON)) .andExpect( status().isNotFound() @@ -132,12 +209,13 @@ void givenValidCaseDataWhenAboutToSubmitCallbackIsInvokedThenStateIsChangedAndEm CreditAccountPaymentResponse .builder() .status(SUCCESS.toString()) - .caseReference(TEST_CASE_ID.toString()) - .build() + .build(), + orderSummaryWithFee() ); var data = caseDataWithStatementOfTruth(); data.getApplication().setApplicationPayments(null); + data.getApplication().setApplicationFeeServiceRequestReference(TEST_SERVICE_REFERENCE); data.getApplication().setSolPaymentHowToPay(FEE_PAY_BY_ACCOUNT); data.getApplication().setPbaNumbers(getPbaNumbersForAccount("PBA0012345")); data.getApplication().setApplicationFeeOrderSummary(orderSummaryWithFee()); diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/systemupdate/event/SystemProgressCaseToAwaitingFinalOrderIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/systemupdate/event/SystemProgressCaseToAwaitingFinalOrderIT.java index 11e00b4b048..45df311d5b6 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/systemupdate/event/SystemProgressCaseToAwaitingFinalOrderIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/systemupdate/event/SystemProgressCaseToAwaitingFinalOrderIT.java @@ -143,6 +143,7 @@ void shouldSendLettersToOfflineApplicantsInJointCase() throws Exception { caseData.getApplicant2().setOffline(YES); caseData.getApplicant1().setAddress(APPLICANT_ADDRESS); caseData.getApplicant2().setAddress(APPLICANT_ADDRESS); + caseData.getConditionalOrder().setGrantedDate(LocalDate.now()); caseData.setFinalOrder(FinalOrder.builder() .dateFinalOrderEligibleFrom(LocalDate.now()) .build()); @@ -199,10 +200,11 @@ void shouldSendLettersToOfflineApplicantsInJointCase() throws Exception { } @Test - public void shouldSendEmailsToOnlineApplicantsInJointCase() throws Exception { + void shouldSendEmailsToOnlineApplicantsInJointCase() throws Exception { final CaseData caseData = validJointApplicant1CaseData(); caseData.getApplicant1().setEmail(TEST_USER_EMAIL); caseData.getApplicant2().setEmail(TEST_APPLICANT_2_USER_EMAIL); + caseData.getConditionalOrder().setGrantedDate(LocalDate.now()); caseData.setFinalOrder(FinalOrder.builder() .dateFinalOrderEligibleFrom(LocalDate.now()) .dateFinalOrderEligibleToRespondent(LocalDate.now()) @@ -228,7 +230,7 @@ public void shouldSendEmailsToOnlineApplicantsInJointCase() throws Exception { } @Test - public void shouldSendEmailsToSolicitorsInJointCaseWhenBothApplicantsAreRepresented() throws Exception { + void shouldSendEmailsToSolicitorsInJointCaseWhenBothApplicantsAreRepresented() throws Exception { final CaseData caseData = validJointApplicant1CaseData(); caseData.getApplicant1().setSolicitorRepresented(YES); caseData.getApplicant2().setSolicitorRepresented(YES); diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/testutil/PaymentWireMock.java b/src/integrationTest/java/uk/gov/hmcts/divorce/testutil/PaymentWireMock.java index 4bcbc5c9276..4ce23f30e2e 100644 --- a/src/integrationTest/java/uk/gov/hmcts/divorce/testutil/PaymentWireMock.java +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/testutil/PaymentWireMock.java @@ -11,7 +11,6 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpStatus; import uk.gov.hmcts.ccd.sdk.type.Fee; -import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.ccd.sdk.type.OrderSummary; import uk.gov.hmcts.divorce.divorcecase.model.Applicant; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; @@ -27,16 +26,14 @@ import java.util.List; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static java.util.Collections.singletonList; import static org.springframework.http.HttpHeaders.AUTHORIZATION; import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import static uk.gov.hmcts.divorce.divorcecase.NoFaultDivorce.getCaseType; +import static uk.gov.hmcts.divorce.payment.FeesAndPaymentsUtil.penceToPounds; import static uk.gov.hmcts.divorce.payment.PaymentService.HMCTS_ORG_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTH_HEADER_VALUE; import static uk.gov.hmcts.divorce.testutil.TestConstants.FEE_CODE; @@ -45,7 +42,6 @@ import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; -import static uk.gov.hmcts.divorce.testutil.TestDataHelper.orderSummaryWithFee; public final class PaymentWireMock { @@ -84,15 +80,15 @@ public static void stubCreateServiceRequest(HttpStatus status, CreateServiceRequ ); } - public static void stubCreditAccountPayment(HttpStatus status, CreditAccountPaymentResponse response) throws JsonProcessingException { + public static void stubCreditAccountPayment( + HttpStatus status, CreditAccountPaymentResponse response, OrderSummary orderSummary + ) throws JsonProcessingException { OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); - var orderSummary = orderSummaryWithFee(); CreditAccountPaymentRequest request = getCreditAccountPaymentRequest(orderSummary); - PAYMENTS_SERVER.stubFor(post("/credit-account-payments") + PAYMENTS_SERVER.stubFor(post("/service-request/test-service-reference/pba-payments") .withHeader(AUTHORIZATION, new EqualToPattern(TEST_AUTHORIZATION_TOKEN)) - // .withHeader(SERVICE_AUTHORIZATION, new EqualToPattern(TEST_SERVICE_AUTH_TOKEN)) - .withRequestBody(equalToJson(OBJECT_MAPPER.writeValueAsString(request))) + .withRequestBody(new EqualToJsonPattern(OBJECT_MAPPER.writeValueAsString(request), true, true)) .willReturn(aResponse() .withStatus(status.value()) .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) @@ -152,22 +148,13 @@ public static CreateServiceRequestBody buildServiceReferenceRequest(CaseData dat } private static CreditAccountPaymentRequest getCreditAccountPaymentRequest(OrderSummary orderSummary) { - var request = new CreditAccountPaymentRequest(); - request.setService("DIVORCE"); - request.setCurrency("GBP"); - request.setCaseType(getCaseType()); - request.setAmount(orderSummary.getPaymentTotal()); - request.setCcdCaseNumber(TEST_CASE_ID.toString()); - request.setAccountNumber("PBA0012345"); - request.setOrganisationName("Test Organisation"); - request.setDescription("fees for divorce"); - - ListValue feeItem = orderSummary.getFees().get(0); - Fee fee = feeItem.getValue(); - - PaymentItem paymentItem = getPaymentItem(fee); - - request.setFees(singletonList(paymentItem)); + var request = CreditAccountPaymentRequest.builder() + .currency("GBP") + .amount(penceToPounds(orderSummary.getPaymentTotal())) + .accountNumber("PBA0012345") + .organisationName("Test Organisation") + .customerReference(null) + .build(); return request; } diff --git a/src/integrationTest/resources/application.yaml b/src/integrationTest/resources/application.yaml index a4c16301d33..83f0a68cb1d 100644 --- a/src/integrationTest/resources/application.yaml +++ b/src/integrationTest/resources/application.yaml @@ -27,6 +27,7 @@ uk: divorceCourtEmail: contactdivorce@justice.gov.uk dissolutionCourtEmail: contactdivorce@justice.gov.uk webformUrl: ${WEBFORM_URL:https://contact-us-about-a-divorce-application.form.service.justice.gov.uk/} + webformCyUrl: ${WEBFORM_CY_URL:https://contact-us-about-a-divorce-application-cy.form.service.justice.gov.uk/} templates: english: SAVE_SIGN_OUT: '70dd0a1e-047f-4baa-993a-e722db17d8ac' @@ -47,6 +48,9 @@ idam: id: 'divorce' secret: BBBBBBBBBBBBBBBB redirect_uri: http://localhost:3001/oauth2/callback + divorce: + username: ${IDAM_OLD_DIVORCE_USERNAME:dummysystemupdate@test.com} + password: ${IDAM_OLD_DIVORCE_PASSWORD:dummy} s2s-authorised: services: ccd_data,bulk_scan_processor,bulk_scan_orchestrator diff --git a/src/integrationTest/resources/solicitor-general-application-payment-response.json b/src/integrationTest/resources/solicitor-general-application-payment-response.json index 9439f03702b..5870d1c8fb3 100644 --- a/src/integrationTest/resources/solicitor-general-application-payment-response.json +++ b/src/integrationTest/resources/solicitor-general-application-payment-response.json @@ -21,6 +21,7 @@ "id": "${json-unit.any-string}", "value": { "feeCode": "FEE002", + "serviceRequestReference":"test-service-reference", "amount": 55000, "status": "success", "channel": "online" @@ -55,6 +56,7 @@ "PaymentTotal": "55000" }, "generalApplicationFeePaymentMethod": "feePayByAccount", + "generalApplicationFeeServiceRequestReference": "test-service-reference", "generalApplicationFeePbaNumbers": { "value": { "label": "PBA0012345" @@ -80,6 +82,7 @@ } } ], + "generalApplicationFeeServiceRequestReference": "test-service-reference", "generalApplicationFeeOrderSummary": { "Fees": [ { diff --git a/src/main/java/uk/gov/hmcts/divorce/CaseApiApplication.java b/src/main/java/uk/gov/hmcts/divorce/CaseApiApplication.java index e77d28b431e..b6aec6f9dff 100644 --- a/src/main/java/uk/gov/hmcts/divorce/CaseApiApplication.java +++ b/src/main/java/uk/gov/hmcts/divorce/CaseApiApplication.java @@ -30,7 +30,7 @@ @SpringBootApplication( scanBasePackages = {"uk.gov.hmcts.ccd.sdk", "uk.gov.hmcts.divorce", "uk.gov.hmcts.reform.idam.client", - "uk.gov.hmcts.reform.sendletter", "uk.gov.hmcts.reform.ccd.document.am.feign"} + "uk.gov.hmcts.reform.sendletter", "uk.gov.hmcts.reform.ccd.document.am.feign","uk.gov.hmcts.divorce.idam"} ) @EnableFeignClients( clients = { diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerAddBailiffReturn.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerAddBailiffReturn.java index f7ca705bb14..c1f66c7fbfc 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerAddBailiffReturn.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerAddBailiffReturn.java @@ -25,8 +25,11 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingJsNullity; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; import static uk.gov.hmcts.divorce.divorcecase.model.State.Holding; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; import static uk.gov.hmcts.divorce.divorcecase.model.State.IssuedToBailiff; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CITIZEN; @@ -67,6 +70,9 @@ public void configure(final ConfigBuilder configBuild Holding, Submitted, AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, AwaitingPayment) .name("Add bailiff return") .description("Add bailiff return") diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerAlternativeServiceApplication.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerAlternativeServiceApplication.java index dbee4b0cae9..a3e1e2a682a 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerAlternativeServiceApplication.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerAlternativeServiceApplication.java @@ -25,9 +25,12 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingAos; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingBailiffReferral; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingServiceConsideration; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingServicePayment; import static uk.gov.hmcts.divorce.divorcecase.model.State.GeneralApplicationReceived; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CITIZEN; @@ -55,7 +58,17 @@ public class CaseworkerAlternativeServiceApplication implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_SERVICE_RECEIVED) - .forStates(AosOverdue, AwaitingAos, AosDrafted, Submitted, AwaitingDocuments, GeneralApplicationReceived) + .forStates( + AosOverdue, + AwaitingAos, + AosDrafted, + Submitted, + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + GeneralApplicationReceived + ) .name("Service application received") .description("Service application received") .showSummary() diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerApplicantResponded.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerApplicantResponded.java index 02db482a941..a0b502f37c8 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerApplicantResponded.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerApplicantResponded.java @@ -8,7 +8,12 @@ import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import java.util.EnumSet; + import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; @@ -25,7 +30,15 @@ public class CaseworkerApplicantResponded implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_APPLICANT_RESPONDED) - .forStateTransition(AwaitingDocuments, Submitted) + .forStateTransition( + EnumSet.of( + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted + ), + Submitted + ) .showEventNotes() .name("Applicant responded") .description("Applicant responded") diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerChangeServiceRequest.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerChangeServiceRequest.java index c08ae8a65c1..3aa99b7bb37 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerChangeServiceRequest.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerChangeServiceRequest.java @@ -22,7 +22,10 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingAos; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFDecision; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingService; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; @@ -52,7 +55,14 @@ public class CaseworkerChangeServiceRequest implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_CHANGE_SERVICE_REQUEST) - .forStates(Submitted, AwaitingDocuments, AwaitingHWFDecision) + .forStates( + Submitted, + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingHWFDecision + ) .name("Change service request") .description("Change service request") .showSummary() diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerFindMatches.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerFindMatches.java index 6d8e67c1760..e99d69bda57 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerFindMatches.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerFindMatches.java @@ -15,6 +15,7 @@ import uk.gov.hmcts.divorce.common.ccd.PageBuilder; import uk.gov.hmcts.divorce.divorcecase.model.Application; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseDataOldDivorce; import uk.gov.hmcts.divorce.divorcecase.model.CaseMatch; import uk.gov.hmcts.divorce.divorcecase.model.MarriageDetails; import uk.gov.hmcts.divorce.divorcecase.model.State; @@ -24,8 +25,13 @@ import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; @@ -43,6 +49,7 @@ public class CaseworkerFindMatches implements CCDConfig { public static final String FIND_MATCHES = "caseworker-find-matches"; + public static final String WILDCARD_SEARCH = ".*"; private final CcdSearchService ccdSearchService; private final IdamService idamService; private final AuthTokenGenerator authTokenGenerator; @@ -73,9 +80,15 @@ public AboutToStartOrSubmitResponse aboutToStart(final CaseDeta MarriageDetails marriageDetails = caseData.getApplication().getMarriageDetails(); List caseMatchDetails = getFreshMatches(details, marriageDetails); - log.info("Case ID: " + details.getId() + " case matching search result: " + caseMatchDetails.size()); - List newMatches = transformToMatchingCasesList(caseMatchDetails); + log.info("Case ID: " + details.getId() + " nfdiv case matching search result: " + caseMatchDetails.size()); + List oldcaseMatchDetails = getOldDivorceFreshMatches(marriageDetails); + log.info("Case ID: " + details.getId() + " old divorce case matching search result: " + oldcaseMatchDetails.size()); + + List newMatches = new ArrayList<>(); + newMatches.addAll(transformToMatchingCasesList(caseMatchDetails)); + newMatches.addAll(transformOldCaseToMatchingCasesList(oldcaseMatchDetails)); + setToNewMatches(caseData, newMatches); return AboutToStartOrSubmitResponse.builder() .data(caseData) @@ -83,39 +96,46 @@ public AboutToStartOrSubmitResponse aboutToStart(final CaseDeta } - // Helper method to create term queries with variations for spaces - private BoolQueryBuilder createNameMatchQuery(String field, String name) { - return QueryBuilders.boolQuery() - .should(QueryBuilders.termQuery(field, name)) // Exact match - .should(QueryBuilders.termQuery(field, " " + name)) // Prepend space - .should(QueryBuilders.termQuery(field, name + " ")) // Append space - .should(QueryBuilders.termQuery(field, " " + name + " ")); // Prepend and append space + public String[] normalizeAndSplit(String name) { + // remove "name changed by Deed Poll" ignoring case + String nameWithoutDeedPollStatement = name.replaceAll("(?i)name changed by deed poll", "").trim(); + + // Split to separate names for search wherever there are non-alphanumeric characters (excluding accented characters) + return Arrays.stream(nameWithoutDeedPollStatement.split("[^\\p{L}\\p{N}]+")) + .map(String::trim) + .filter(part -> !part.isEmpty()) // Ignore empty parts + .flatMap(part -> part.contains(" ") ? Arrays.stream(part.split("\\s+")) : Stream.of(part)) // Split on spaces + .toArray(String[]::new); } - List getFreshMatches(CaseDetails details, - MarriageDetails marriageDetails) { - //NFDIV-4512 adding extra searches to cope with prepended and trailing space on the names - String applicant1Name = marriageDetails.getApplicant1Name().trim(); - String applicant2Name = marriageDetails.getApplicant2Name().trim(); + public List getFreshMatches(CaseDetails details, + MarriageDetails marriageDetails) { + // clean the names + String[] applicant1Names = normalizeAndSplit(marriageDetails.getApplicant1Name()); + String[] applicant2Names = normalizeAndSplit(marriageDetails.getApplicant2Name()); - BoolQueryBuilder nameMatchQuery1 = QueryBuilders.boolQuery() - .filter(createNameMatchQuery("data.marriageApplicant1Name.keyword", applicant1Name)) - .filter(createNameMatchQuery("data.marriageApplicant2Name.keyword", applicant2Name)); + BoolQueryBuilder nameMatching = QueryBuilders.boolQuery(); - BoolQueryBuilder nameMatchQuery2 = QueryBuilders.boolQuery() - .filter(createNameMatchQuery("data.marriageApplicant1Name.keyword", applicant2Name)) - .filter(createNameMatchQuery("data.marriageApplicant2Name.keyword", applicant1Name)); + // handle all combinations of name1 and name2 + for (String name1 : applicant1Names) { + for (String name2 : applicant2Names) { + // applicant1 might be applicant2 on another case and vice versa + BoolQueryBuilder sameOrderCombo = QueryBuilders.boolQuery() + .filter(createRegexQuery("data.marriageApplicant1Name.keyword", name1)) + .filter(createRegexQuery("data.marriageApplicant2Name.keyword", name2)); - BoolQueryBuilder nameMatching = QueryBuilders.boolQuery() - .should(nameMatchQuery1) - .should(nameMatchQuery2); + BoolQueryBuilder oppOrderCombo = QueryBuilders.boolQuery() + .filter(createRegexQuery("data.marriageApplicant1Name.keyword", name2)) + .filter(createRegexQuery("data.marriageApplicant2Name.keyword", name1)); + nameMatching.should(sameOrderCombo).should(oppOrderCombo); + } + } LocalDate marriageDate = marriageDetails.getDate(); - List stateValues = POST_SUBMISSION_STATES.stream().map(State::name).toList(); BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() - .filter(QueryBuilders.termsQuery(STATE_KEY,stateValues)) + .filter(QueryBuilders.termsQuery(STATE_KEY, stateValues)) .mustNot(QueryBuilders.termQuery(REFERENCE_KEY, String.valueOf(details.getId()))) .filter(QueryBuilders.termQuery("data.marriageDate", marriageDate.format(ES_DATE_FORMATTER))) .filter(nameMatching); @@ -125,6 +145,17 @@ List getFreshMatches(CaseDetai return ccdSearchService.searchForAllCasesWithQuery(boolQuery, user, serviceAuth); } + // Helper method to create name match query + private BoolQueryBuilder createRegexQuery(String field, String cleanedName) { + return QueryBuilders.boolQuery() + .should(QueryBuilders.regexpQuery(field, generateRegexPattern(cleanedName))); + } + + String generateRegexPattern(String name) { + // Join the parts into a regex pattern with .* between tokens + return WILDCARD_SEARCH + name + WILDCARD_SEARCH; + } + public List transformToMatchingCasesList( List caseMatchDetails) { @@ -156,10 +187,44 @@ public List transformToMatchingCasesList( }).toList(); } + public List transformOldCaseToMatchingCasesList( + List caseMatchDetails) { + + if (caseMatchDetails == null || caseMatchDetails.isEmpty()) { + return Collections.emptyList(); + } + + return caseMatchDetails.stream() + .map(caseDetail -> { + CaseDataOldDivorce caseData = getCaseDataOldDivorce(caseDetail.getData()); + + // Handle potential null values in fields by using Optional + return CaseMatch.builder() + .applicant1Name(Optional.ofNullable(caseData.getD8MarriagePetitionerName()).orElse("")) + .applicant2Name(Optional.ofNullable(caseData.getD8MarriageRespondentName()).orElse("")) + .date(Optional.ofNullable(caseData.getD8MarriageDate()) + .map(LocalDate::parse) + .orElse(null)) + .applicant1Postcode(Optional.ofNullable(caseData.getD8PetitionerPostCode()).orElse("")) + .applicant2Postcode(Optional.ofNullable(caseData.getD8RespondentPostCode()).orElse("")) + .applicant1Town(Optional.ofNullable(caseData.getD8PetitionerPostTown()).orElse("")) + .applicant2Town(Optional.ofNullable(caseData.getD8RespondentPostTown()).orElse("")) + .caseLink(CaseLink.builder() + .caseReference(String.valueOf(caseDetail.getId())) + .build()) + .build(); + }) + .toList(); + } + private CaseData getCaseData(Map data) { return objectMapper.convertValue(data, CaseData.class); } + private CaseDataOldDivorce getCaseDataOldDivorce(Map data) { + return objectMapper.convertValue(data, CaseDataOldDivorce.class); + } + public void setToNewMatches(CaseData data, List newMatches) { List> storedMatches = data.getCaseMatches(); storedMatches.clear(); @@ -173,4 +238,38 @@ public void setToNewMatches(CaseData data, List newMatches) { data.setCaseMatches(null); } } + + List getOldDivorceFreshMatches(MarriageDetails marriageDetails) { + + // clean the names + String[] applicant1Names = normalizeAndSplit(marriageDetails.getApplicant1Name()); + String[] applicant2Names = normalizeAndSplit(marriageDetails.getApplicant2Name()); + + BoolQueryBuilder nameMatching = QueryBuilders.boolQuery(); + + // handle all combinations of name1 and name2 + for (String name1 : applicant1Names) { + for (String name2 : applicant2Names) { + // applicant1 might be applicant2 on another case and vice versa + BoolQueryBuilder sameOrderCombo = QueryBuilders.boolQuery() + .filter(createRegexQuery("data.D8MarriageRespondentName.keyword", name1)) + .filter(createRegexQuery("data.D8MarriagePetitionerName.keyword", name2)); + + BoolQueryBuilder oppOrderCombo = QueryBuilders.boolQuery() + .filter(createRegexQuery("data.D8MarriageRespondentName.keyword", name2)) + .filter(createRegexQuery("data.D8MarriagePetitionerName.keyword", name1)); + + nameMatching.should(sameOrderCombo).should(oppOrderCombo); + } + } + LocalDate marriageDate = marriageDetails.getDate(); + BoolQueryBuilder oldDivorceQuery = QueryBuilders.boolQuery() + .filter(QueryBuilders.termQuery("data.D8MarriageDate", marriageDate.format(ES_DATE_FORMATTER))) + .filter(nameMatching); + + final var user = idamService.retrieveOldSystemUpdateUserDetails(); + final var serviceAuth = authTokenGenerator.generate(); + + return ccdSearchService.searchForOldDivorceCasesWithQuery(oldDivorceQuery, user, serviceAuth); + } } diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHWFPartPaymentMade.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHWFPartPaymentMade.java index 79787db86d3..679afc32ea7 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHWFPartPaymentMade.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHWFPartPaymentMade.java @@ -17,6 +17,9 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFEvidence; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFPartPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CITIZEN; @@ -39,7 +42,17 @@ public class CaseworkerHWFPartPaymentMade implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_HWF_PART_PAYMENT_MADE) - .forStateTransition(EnumSet.of(AwaitingDocuments, AwaitingHWFPartPayment, AwaitingHWFEvidence), Submitted) + .forStateTransition( + EnumSet.of( + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingHWFPartPayment, + AwaitingHWFEvidence + ), + Submitted + ) .name(EVENT_NAME_AND_DESCRIPTION) .description(EVENT_NAME_AND_DESCRIPTION) .aboutToSubmitCallback(this::aboutToSubmit) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfApplicationAccepted.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfApplicationAccepted.java index d0ffb9aea42..585e67467bd 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfApplicationAccepted.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfApplicationAccepted.java @@ -17,6 +17,9 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFEvidence; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFPartPayment; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; @@ -36,7 +39,16 @@ public class CaseworkerHwfApplicationAccepted implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_HWF_APPLICATION_ACCEPTED) - .forStates(AwaitingHWFPartPayment, AwaitingHWFDecision, AwaitingPayment, AwaitingDocuments, AwaitingHWFEvidence) + .forStates( + AwaitingHWFPartPayment, + AwaitingHWFDecision, + AwaitingPayment, + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingHWFEvidence + ) .name("HWF application accepted") .description("HWF application accepted") .aboutToSubmitCallback(this::aboutToSubmit) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfEvidenceRequested.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfEvidenceRequested.java index 14202fa2650..15c4d3851fc 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfEvidenceRequested.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfEvidenceRequested.java @@ -14,6 +14,9 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFDecision; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFEvidence; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFPartPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; @@ -31,7 +34,17 @@ public class CaseworkerHwfEvidenceRequested implements CCDConfig configBuilder) { configBuilder .event(CASEWORKER_HWF_EVIDENCE_REQUESTED) - .forStateTransition(EnumSet.of(AwaitingDocuments, AwaitingHWFPartPayment, AwaitingHWFDecision), AwaitingHWFEvidence) + .forStateTransition( + EnumSet.of( + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingHWFPartPayment, + AwaitingHWFDecision + ), + AwaitingHWFEvidence + ) .name("HWF evidence required") .description("HWF evidence required") .showSummary() diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfPartPaymentRequired.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfPartPaymentRequired.java index 024b0ce0e2e..21fb4aeace4 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfPartPaymentRequired.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfPartPaymentRequired.java @@ -14,6 +14,9 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFDecision; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFEvidence; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFPartPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; @@ -28,7 +31,17 @@ public class CaseworkerHwfPartPaymentRequired implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_HWF_PART_PAYMENT_REQUIRED) - .forStateTransition(EnumSet.of(AwaitingDocuments, AwaitingHWFEvidence, AwaitingHWFDecision), AwaitingHWFPartPayment) + .forStateTransition( + EnumSet.of( + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingHWFEvidence, + AwaitingHWFDecision + ), + AwaitingHWFPartPayment + ) .name("HWF part payment required") .description("HWF part payment required") .showSummary() diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfRefused.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfRefused.java index ff224747171..5cd1414ebaa 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfRefused.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerHwfRefused.java @@ -14,6 +14,9 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFDecision; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFEvidence; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFPartPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; @@ -31,7 +34,15 @@ public void configure(final ConfigBuilder configBuild new PageBuilder(configBuilder .event(CASEWORKER_HWF_REFUSED) .forStateTransition( - EnumSet.of(AwaitingDocuments, AwaitingHWFDecision, AwaitingHWFEvidence, AwaitingHWFPartPayment), + EnumSet.of( + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingHWFDecision, + AwaitingHWFEvidence, + AwaitingHWFPartPayment + ), AwaitingDocuments ) .name("HWF refused") diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerIssueApplication.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerIssueApplication.java index c34c6c1c795..a8ef0e4c76b 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerIssueApplication.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerIssueApplication.java @@ -26,6 +26,8 @@ import static org.springframework.util.CollectionUtils.isEmpty; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDwpResponse; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; @@ -60,7 +62,7 @@ public class CaseworkerIssueApplication implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_ISSUE_APPLICATION) - .forStates(Submitted, AwaitingDocuments, AwaitingDwpResponse) + .forStates(Submitted, AwaitingDocuments, InformationRequested, RequestedInformationSubmitted, AwaitingDwpResponse) .name("Application issue") .description("Application issued") .showSummary() diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerOfflineDocumentVerified.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerOfflineDocumentVerified.java index 553a1d8befc..5448463e4b8 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerOfflineDocumentVerified.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerOfflineDocumentVerified.java @@ -9,6 +9,8 @@ import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; import uk.gov.hmcts.ccd.sdk.type.DynamicList; import uk.gov.hmcts.ccd.sdk.type.DynamicListElement; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponseNotification; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; import uk.gov.hmcts.divorce.citizen.notification.conditionalorder.Applicant1AppliedForConditionalOrderNotification; import uk.gov.hmcts.divorce.common.ccd.PageBuilder; import uk.gov.hmcts.divorce.common.service.GeneralReferralService; @@ -21,38 +23,53 @@ import uk.gov.hmcts.divorce.divorcecase.model.ConditionalOrder; import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.OfflineWhoApplying; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationList; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.document.model.DocumentType; import uk.gov.hmcts.divorce.idam.IdamService; import uk.gov.hmcts.divorce.idam.User; import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; import uk.gov.hmcts.divorce.systemupdate.service.CcdUpdateService; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.ccd.client.model.SubmittedCallbackResponse; import java.time.Clock; +import java.util.Collections; import java.util.List; import java.util.UUID; import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; import static org.apache.commons.lang3.ObjectUtils.isEmpty; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.common.event.SwitchedToSoleCo.SWITCH_TO_SOLE_CO; import static uk.gov.hmcts.divorce.common.event.SwitchedToSoleFinalOrderOffline.SWITCH_TO_SOLE_FO_OFFLINE; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.AOS_D10; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.CO_D84; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.FO_D36; +import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.RFI_RESPONSE; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.ScannedDocumentSubtypes.D10; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.ScannedDocumentSubtypes.D36; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.ScannedDocumentSubtypes.D84; +import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.ScannedDocumentSubtypes.RFIR; import static uk.gov.hmcts.divorce.divorcecase.model.OfflineApplicationType.SWITCH_TO_SOLE; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.OTHER; import static uk.gov.hmcts.divorce.divorcecase.model.State.AosDrafted; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingLegalAdvisorReferral; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; import static uk.gov.hmcts.divorce.divorcecase.model.State.FinalOrderRequested; import static uk.gov.hmcts.divorce.divorcecase.model.State.Holding; import static uk.gov.hmcts.divorce.divorcecase.model.State.JSAwaitingLA; import static uk.gov.hmcts.divorce.divorcecase.model.State.OfflineDocumentReceived; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.RespondentFinalOrderRequested; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER_BULK_SCAN; @@ -63,6 +80,7 @@ import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; import static uk.gov.hmcts.divorce.document.model.DocumentType.CONDITIONAL_ORDER_APPLICATION; import static uk.gov.hmcts.divorce.document.model.DocumentType.FINAL_ORDER_APPLICATION; +import static uk.gov.hmcts.divorce.document.model.DocumentType.REQUEST_FOR_INFORMATION_RESPONSE_DOC; import static uk.gov.hmcts.divorce.document.model.DocumentType.RESPONDENT_ANSWERS; @Component @@ -74,6 +92,8 @@ public class CaseworkerOfflineDocumentVerified implements CCDConfig configBuilder) { @@ -97,48 +125,74 @@ public void configure(final ConfigBuilder configBuild .showSummary() .grant(CREATE_READ_UPDATE, CASE_WORKER_BULK_SCAN, CASE_WORKER, SUPER_USER) .grantHistoryOnly(LEGAL_ADVISOR, SOLICITOR, JUDGE)) - .page("documentTypeReceived") + .page("documentTypeReceived", this::midEvent) .readonlyNoSummary(CaseData::getApplicationType, ALWAYS_HIDE) .complex(CaseData::getDocuments) - .readonlyNoSummary(CaseDocuments::getScannedSubtypeReceived, ALWAYS_HIDE) + .readonlyNoSummary(CaseDocuments::getScannedSubtypeReceived, ALWAYS_HIDE) .mandatory(CaseDocuments::getTypeOfDocumentAttached, "scannedSubtypeReceived!=\"*\"", true) .done() .complex(CaseData::getAcknowledgementOfService) - .label("scannedAosLabel", "Acknowledgement Of Service", "scannedSubtypeReceived=\"D10\"") - .mandatory(AcknowledgementOfService::getHowToRespondApplication, - "typeOfDocumentAttached=\"D10\" OR scannedSubtypeReceived=\"D10\"") + .label("scannedAosLabel", "Acknowledgement Of Service", "scannedSubtypeReceived=\"D10\"") + .mandatory(AcknowledgementOfService::getHowToRespondApplication, + "typeOfDocumentAttached=\"D10\" OR scannedSubtypeReceived=\"D10\"") .done() .complex(CaseData::getDocuments) - .mandatory(CaseDocuments::getScannedDocumentNames, - "scannedSubtypeReceived!=\"*\" " - + "AND (typeOfDocumentAttached=\"D10\" OR typeOfDocumentAttached=\"D84\" OR typeOfDocumentAttached=\"D36\")") + .mandatory(CaseDocuments::getScannedDocumentNames, + "scannedSubtypeReceived!=\"*\" " + + "AND (typeOfDocumentAttached=\"D10\" OR typeOfDocumentAttached=\"D84\" OR typeOfDocumentAttached=\"D36\") " + + "OR typeOfDocumentAttached=\"RFIR\"") .done() .complex(CaseData::getConditionalOrder) - .label("scannedCoLabel", "Conditional Order", "scannedSubtypeReceived=\"D84\"") - .mandatory(ConditionalOrder::getD84ApplicationType, - "typeOfDocumentAttached=\"D84\" OR scannedSubtypeReceived=\"D84\"") - .mandatory(ConditionalOrder::getD84WhoApplying, "coD84ApplicationType=\"switchToSole\"") + .label("scannedCoLabel", "Conditional Order", "scannedSubtypeReceived=\"D84\"") + .mandatory(ConditionalOrder::getD84ApplicationType, + "typeOfDocumentAttached=\"D84\" OR scannedSubtypeReceived=\"D84\"") + .mandatory(ConditionalOrder::getD84WhoApplying, "coD84ApplicationType=\"switchToSole\"") .done() .complex(CaseData::getFinalOrder) - .readonlyNoSummary(FinalOrder::getFinalOrderReminderSentApplicant2, ALWAYS_HIDE) - .label("scannedFoLabel", "Final Order", "scannedSubtypeReceived=\"D36\"") - .mandatory(FinalOrder::getD36ApplicationType, - "typeOfDocumentAttached=\"D36\" OR scannedSubtypeReceived=\"D36\"") - .mandatory(FinalOrder::getD36WhoApplying, "d36ApplicationType=\"switchToSole\" " - + "OR (d36ApplicationType=\"sole\" AND finalOrderReminderSentApplicant2=\"Yes\")") + .readonlyNoSummary(FinalOrder::getFinalOrderReminderSentApplicant2, ALWAYS_HIDE) + .label("scannedFoLabel", "Final Order", "scannedSubtypeReceived=\"D36\"") + .mandatory(FinalOrder::getD36ApplicationType, + "typeOfDocumentAttached=\"D36\" OR scannedSubtypeReceived=\"D36\"") + .mandatory(FinalOrder::getD36WhoApplying, "d36ApplicationType=\"switchToSole\" " + + "OR (d36ApplicationType=\"sole\" AND finalOrderReminderSentApplicant2=\"Yes\")") + .done() + .page("selectSenderOfDocument") + .showCondition("typeOfDocumentAttached=\"RFIR\"") + .complex(CaseData::getRequestForInformationList) + .complex(RequestForInformationList::getRequestForInformationOfflineResponseDraft) + .mandatory( + RequestForInformationOfflineResponseDraft::getRfiOfflineSoleResponseParties, + "applicationType=\"soleApplication\"" + ) + .mandatory( + RequestForInformationOfflineResponseDraft::getRfiOfflineJointResponseParties, + "applicationType=\"jointApplication\"" + ) + .mandatory( + RequestForInformationOfflineResponseDraft::getRfiOfflineResponseOtherName, + "rfiOfflineSoleResponseParties=\"other\" OR rfiOfflineJointResponseParties=\"other\"" + ) + .optional( + RequestForInformationOfflineResponseDraft::getRfiOfflineResponseOtherEmail, + "rfiOfflineSoleResponseParties=\"other\" OR rfiOfflineJointResponseParties=\"other\"" + ) + .mandatory(RequestForInformationOfflineResponseDraft::getRfiOfflineAllDocumentsUploaded) + .optionalWithLabel(RequestForInformationOfflineResponseDraft::getRfiOfflineDraftResponseDetails, "Add Notes") + .mandatoryWithLabel( + RequestForInformationOfflineResponseDraft::getRfiOfflineResponseSendNotifications, "Send notifications?" + ) + .done() .done() .page("stateToTransitionToOtherDoc") .showCondition("applicationType=\"soleApplication\" AND typeOfDocumentAttached=\"Other\"") .complex(CaseData::getApplication) - .mandatory(Application::getStateToTransitionApplicationTo) + .mandatory(Application::getStateToTransitionApplicationTo) .done() - - .page("stateToTransitionToJoint") .showCondition("applicationType=\"jointApplication\" AND typeOfDocumentAttached!=\"D84\" OR scannedSubtypeReceived!=\"D84\"") .complex(CaseData::getApplication) - .mandatory(Application::getStateToTransitionApplicationTo) + .mandatory(Application::getStateToTransitionApplicationTo) .done(); } @@ -153,6 +207,8 @@ public AboutToStartOrSubmitResponse aboutToStart(CaseDetails aboutToStart(CaseDetails midEvent(CaseDetails details, + CaseDetails beforeDetails) { + log.info("{} midEvent callback invoked for Case Id: {}", CASEWORKER_OFFLINE_DOCUMENT_VERIFIED, details.getId()); + final CaseData data = details.getData(); + if (RFI_RESPONSE.equals(data.getDocuments().getTypeOfDocumentAttached())) { + if (data.getRequestForInformationList().getRequestsForInformation() == null + || data.getRequestForInformationList().getRequestsForInformation().isEmpty() + ) { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(NO_REQUEST_FOR_INFORMATION_ERROR)) + .build(); + } + + if (data.getApplication().getIssueDate() != null) { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(NO_REQUEST_FOR_INFORMATION_POST_ISSUE_ERROR)) + .build(); + } + } + + return AboutToStartOrSubmitResponse.builder() + .data(data) + .build(); + } + public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails details, CaseDetails beforeDetails) { @@ -189,13 +270,12 @@ public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails processD10AndSendNotificat .build(); } + private AboutToStartOrSubmitResponse processRfiResponse(CaseDetails details) { + log.info("Verifying RFI Response for case {}", details.getId()); + final CaseData caseData = details.getData(); + final boolean allDocumentsUploaded = YES.equals( + caseData.getRequestForInformationList().getRequestForInformationOfflineResponseDraft().getRfiOfflineAllDocumentsUploaded() + ); + final State state = allDocumentsUploaded ? RequestedInformationSubmitted : AwaitingRequestedInformation; + + reclassifyScannedDocumentToChosenDocumentType(caseData, REQUEST_FOR_INFORMATION_RESPONSE_DOC); + + caseData.getRequestForInformationList().setRequestForInformationOfflineResponseDraft( + new RequestForInformationOfflineResponseDraft() + ); + caseData.getDocuments().setScannedSubtypeReceived(null); + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .state(state) + .build(); + } + private void reclassifyScannedDocumentToChosenDocumentType(CaseData caseData, DocumentType documentType) { if (isEmpty(caseData.getDocuments().getScannedSubtypeReceived())) { String filename = caseData.getDocuments().getScannedDocumentNames().getValueLabel(); @@ -329,6 +430,14 @@ private void reclassifyScannedDocumentToChosenDocumentType(CaseData caseData, Do caseData.reclassifyScannedDocumentToChosenDocumentType(documentType, clock, filename); } + + if (REQUEST_FOR_INFORMATION_RESPONSE_DOC.equals(documentType)) { + final RequestForInformationResponse response = new RequestForInformationResponse(); + final RequestForInformationOfflineResponseDraft offlineDraft = + caseData.getRequestForInformationList().getRequestForInformationOfflineResponseDraft(); + response.setValues(caseData, offlineDraft); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(response); + } } public SubmittedCallbackResponse submitted(CaseDetails details, CaseDetails beforeDetails) { @@ -362,6 +471,63 @@ public SubmittedCallbackResponse submitted(CaseDetails details, } else { log.info("CaseID {} is Sole and Respondent Requested FO. Skipping general referral check.", details.getId()); } + } else if (RFI_RESPONSE.equals(caseData.getDocuments().getTypeOfDocumentAttached())) { + final RequestForInformation latestRequest = caseData.getRequestForInformationList().getLatestRequest(); + final RequestForInformationSoleParties soleParties = latestRequest.getRequestForInformationSoleParties(); + final RequestForInformationJointParties jointParties = latestRequest.getRequestForInformationJointParties(); + + if (NO.equals(latestRequest.getLatestResponse().getRfiOfflineResponseNotificationsRequested())) { + log.info( + "CaseworkerOfflineDocumentVerified submitted callback not sending RFI Response notifications, " + + "rfiOfflineResponseNotificationsRequested is NO for case: {}", + details.getId() + ); + } else if ((caseData.getApplicationType().isSole() && RequestForInformationSoleParties.OTHER.equals(soleParties)) + || (!caseData.getApplicationType().isSole() && OTHER.equals(jointParties)) + ) { + log.info( + "CaseworkerOfflineDocumentVerified submitted callback not sending RFI Response notifications, " + + "rfi party was OTHER for case: {}", + details.getId() + ); + } else { + log.info( + "CaseworkerOfflineDocumentVerified submitted callback sending RFI Response notifications for case: {}", + details.getId() + ); + try { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseData, + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error( + REQUEST_FOR_INFORMATION_RESPONSE_NOTIFICATION_FAILED_ERROR, + details.getId(), + e.getMessage(), + e + ); + } + + if (!caseData.getApplicationType().isSole() + && BOTH.equals(caseData.getRequestForInformationList().getLatestRequest().getRequestForInformationJointParties())) { + try { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseData, + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error( + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR, + details.getId(), + e.getMessage(), + e + ); + } + } + } } return SubmittedCallbackResponse.builder().build(); diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerPaymentMade.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerPaymentMade.java index 8addf76d0c4..2eef4c860ed 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerPaymentMade.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerPaymentMade.java @@ -17,6 +17,9 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFEvidence; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFPartPayment; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; @@ -36,7 +39,16 @@ public class CaseworkerPaymentMade implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_PAYMENT_MADE) - .forStates(AwaitingHWFPartPayment, AwaitingPayment, AwaitingHWFDecision, AwaitingDocuments, AwaitingHWFEvidence) + .forStates( + AwaitingHWFPartPayment, + AwaitingPayment, + AwaitingHWFDecision, + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingHWFEvidence + ) .name("Payment made") .description("Payment made") .aboutToSubmitCallback(this::aboutToSubmit) diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerReissueApplication.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerReissueApplication.java index 3f7c2e3e59b..abf043e529d 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerReissueApplication.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerReissueApplication.java @@ -27,9 +27,12 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDwpResponse; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingJsNullity; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingService; import static uk.gov.hmcts.divorce.divorcecase.model.State.ConditionalOrderRefused; import static uk.gov.hmcts.divorce.divorcecase.model.State.Holding; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; @@ -57,8 +60,19 @@ public void configure(final ConfigBuilder configBuild new PageBuilder(configBuilder .event(CASEWORKER_REISSUE_APPLICATION) .forStates( - AwaitingAos, AosDrafted, AosOverdue, ConditionalOrderRefused, - Holding, AwaitingDocuments, AwaitingService, AwaitingDwpResponse, AwaitingJsNullity) + AwaitingAos, + AosDrafted, + AosOverdue, + ConditionalOrderRefused, + Holding, + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingService, + AwaitingDwpResponse, + AwaitingJsNullity + ) .name("Reissue") .description("Application reissued") .showSummary() diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadConfidentialDocument.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadConfidentialDocument.java index 1cafa41bfed..f42c544efb6 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadConfidentialDocument.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadConfidentialDocument.java @@ -17,6 +17,7 @@ import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.RPA_ROBOT; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE; @@ -35,7 +36,7 @@ public void configure(final ConfigBuilder configBuild .aboutToSubmitCallback(this::aboutToSubmit) .showSummary(false) .showEventNotes() - .grant(CREATE_READ_UPDATE_DELETE, CASE_WORKER) + .grant(CREATE_READ_UPDATE_DELETE, CASE_WORKER, RPA_ROBOT) .grantHistoryOnly(SUPER_USER, LEGAL_ADVISOR, JUDGE)) .page("uploadConfidentialDocuments") .pageLabel("Upload Confidential Documents") diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadDocument.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadDocument.java index d78ed95e691..9136336f5bf 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadDocument.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadDocument.java @@ -22,6 +22,7 @@ import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.RPA_ROBOT; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE; @@ -43,7 +44,7 @@ public void configure(final ConfigBuilder configBuild .aboutToSubmitCallback(this::aboutToSubmit) .showSummary(false) .showEventNotes() - .grant(CREATE_READ_UPDATE_DELETE, CASE_WORKER) + .grant(CREATE_READ_UPDATE_DELETE, CASE_WORKER, RPA_ROBOT) .grantHistoryOnly(SOLICITOR, SUPER_USER, LEGAL_ADVISOR, JUDGE)) .page("uploadDocument") .pageLabel("Upload document") diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadDocumentsAndSubmit.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadDocumentsAndSubmit.java index 445526248dd..5f2e5ab200c 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadDocumentsAndSubmit.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerUploadDocumentsAndSubmit.java @@ -21,6 +21,9 @@ import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.sortByNewest; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; @@ -43,7 +46,7 @@ public class CaseworkerUploadDocumentsAndSubmit implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(CASEWORKER_UPLOAD_DOCUMENTS_AND_SUBMIT) - .forState(AwaitingDocuments) + .forStates(AwaitingDocuments, AwaitingRequestedInformation, InformationRequested, RequestedInformationSubmitted) .name("Upload documents and submit") .description("Upload documents from the applicant and submit") .showEventNotes() diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/model/CaseNote.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/model/CaseNote.java index 4a0cb8aa5e0..60a89ac55fa 100644 --- a/src/main/java/uk/gov/hmcts/divorce/caseworker/model/CaseNote.java +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/model/CaseNote.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import uk.gov.hmcts.ccd.sdk.api.CCD; @@ -13,6 +14,7 @@ @Data @NoArgsConstructor @AllArgsConstructor +@Builder public class CaseNote { @CCD( diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenApplicant2UpdateApplication.java b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenApplicant2UpdateApplication.java index b3b6afcbc04..e1616754238 100644 --- a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenApplicant2UpdateApplication.java +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenApplicant2UpdateApplication.java @@ -21,10 +21,13 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingFinalOrder; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingFinalOrderPayment; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingJointFinalOrder; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingService; import static uk.gov.hmcts.divorce.divorcecase.model.State.ConditionalOrderDrafted; import static uk.gov.hmcts.divorce.divorcecase.model.State.ConditionalOrderPending; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; import static uk.gov.hmcts.divorce.divorcecase.model.State.OfflineDocumentReceived; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_2; import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; @@ -41,7 +44,8 @@ public void configure(final ConfigBuilder configBuild .event(CITIZEN_APPLICANT2_UPDATE) .forStates(ArrayUtils.addAll(AOS_STATES, AwaitingApplicant2Response, AosDrafted, AosOverdue, ConditionalOrderDrafted, ConditionalOrderPending, AwaitingClarification, AwaitingService, - AwaitingFinalOrder, AwaitingFinalOrderPayment, AwaitingJointFinalOrder, OfflineDocumentReceived)) + AwaitingFinalOrder, AwaitingFinalOrderPayment, AwaitingJointFinalOrder, OfflineDocumentReceived, + AwaitingRequestedInformation, InformationRequested, RequestedInformationSubmitted)) .name("Patch a joint case") .showCondition(NEVER_SHOW) .description("Patch a joint divorce or dissolution as applicant 2") diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenCreateServiceRequest.java b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenCreateServiceRequest.java index 96ce2b512ec..708ae67f44f 100644 --- a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenCreateServiceRequest.java +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenCreateServiceRequest.java @@ -7,9 +7,12 @@ import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.divorcecase.model.Application; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import static uk.gov.hmcts.divorce.common.ccd.CcdPageConfiguration.NEVER_SHOW; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingFinalOrderPayment; @@ -24,9 +27,7 @@ public class CitizenCreateServiceRequest implements CCDConfig configBuilder) { @@ -47,9 +48,9 @@ public AboutToStartOrSubmitResponse aboutToSubmit(final CaseDet final State state = details.getState(); if (AwaitingPayment.equals(state)) { - citizenSubmit.setServiceRequestReferenceForApplicationPayment(details.getData(), details.getId()); + prepareServiceRequestForApplicationPayment(details.getData(), details.getId()); } else if (AwaitingFinalOrderPayment.equals(state)) { - respondentApplyForFinalOrder.setServiceRequestReferenceForFinalOrderPayment(details.getData(), details.getId()); + prepareServiceRequestForFinalOrderPayment(details.getData(), details.getId()); } return AboutToStartOrSubmitResponse.builder() @@ -57,5 +58,25 @@ public AboutToStartOrSubmitResponse aboutToSubmit(final CaseDet .state(state) .build(); } + + private void prepareServiceRequestForApplicationPayment(CaseData data, long caseId) { + Application application = data.getApplication(); + + String serviceRequest = paymentSetupService.createApplicationFeeServiceRequest( + data, caseId, data.getCitizenPaymentCallbackUrl() + ); + + application.setApplicationFeeServiceRequestReference(serviceRequest); + } + + private void prepareServiceRequestForFinalOrderPayment(CaseData data, long caseId) { + FinalOrder finalOrder = data.getFinalOrder(); + + String serviceRequest = paymentSetupService.createFinalOrderFeeServiceRequest( + data, caseId, data.getCitizenPaymentCallbackUrl(), finalOrder.getApplicant2FinalOrderFeeOrderSummary() + ); + + finalOrder.setApplicant2FinalOrderFeeServiceRequestReference(serviceRequest); + } } diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndClose.java b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndClose.java index 9053a89b5ed..e6140950155 100644 --- a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndClose.java +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndClose.java @@ -49,7 +49,12 @@ public SubmittedCallbackResponse submitted(CaseDetails details, CaseDetails beforeDetails) { log.info("CitizenSaveAndClose submitted callback invoked for case id: {}", details.getId()); - saveAndSignOutNotificationHandler.notifyApplicant(details.getData(), details.getId(), request.getHeader(AUTHORIZATION)); + saveAndSignOutNotificationHandler.notifyApplicant( + details.getState(), + details.getData(), + details.getId(), + request.getHeader(AUTHORIZATION) + ); return SubmittedCallbackResponse.builder().build(); } diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplication.java b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplication.java index 4770854c41c..496aa9a49ab 100644 --- a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplication.java +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplication.java @@ -13,7 +13,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; -import uk.gov.hmcts.divorce.payment.PaymentService; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import java.util.List; @@ -28,9 +28,6 @@ import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; import static uk.gov.hmcts.divorce.divorcecase.validation.ApplicationValidation.validateReadyForPayment; -import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_ISSUE; -import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_DIVORCE; -import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_DIVORCE; @Slf4j @Component @@ -39,7 +36,7 @@ public class CitizenSubmitApplication implements CCDConfig aboutToSubmit(CaseDetails aboutToSubmit(CaseDetails configBuild configBuilder .event(CITIZEN_UPDATE) .forStates(Draft, AwaitingApplicant1Response, ConditionalOrderDrafted, ConditionalOrderPending, - AwaitingClarification, AwaitingFinalOrder, AwaitingFinalOrderPayment, AwaitingJointFinalOrder) + AwaitingClarification, AwaitingFinalOrder, AwaitingFinalOrderPayment, AwaitingJointFinalOrder, + AwaitingRequestedInformation, InformationRequested, RequestedInformationSubmitted) .showCondition(NEVER_SHOW) .name("Patch case") .description("Patch a divorce or dissolution") diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/event/RespondentApplyForFinalOrder.java b/src/main/java/uk/gov/hmcts/divorce/citizen/event/RespondentApplyForFinalOrder.java index 435f3cce469..744530b34d1 100644 --- a/src/main/java/uk/gov/hmcts/divorce/citizen/event/RespondentApplyForFinalOrder.java +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/event/RespondentApplyForFinalOrder.java @@ -13,7 +13,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; -import uk.gov.hmcts.divorce.payment.PaymentService; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.reform.ccd.client.model.SubmittedCallbackResponse; import java.math.BigDecimal; @@ -29,9 +29,6 @@ import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; -import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; -import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; -import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; @Component @Slf4j @@ -40,7 +37,7 @@ public class RespondentApplyForFinalOrder implements CCDConfig setOrderSummaryAndAwaitingPaymentState( FinalOrder finalOrder ) { log.info("Setting Order Summary for Respondent Final Order for Case Id: {}", details.getId()); + final CaseData data = details.getData(); + final long caseId = details.getId(); - final OrderSummary orderSummary = paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE); + final OrderSummary orderSummary = paymentSetupService.createFinalOrderFeeOrderSummary(data, caseId); finalOrder.setApplicant2FinalOrderFeeOrderSummary(orderSummary); - finalOrder.setApplicant2FinalOrderFeeInPounds( NumberFormat.getNumberInstance().format(new BigDecimal( orderSummary.getPaymentTotal()).movePointLeft(2) ) ); - details.setState(AwaitingFinalOrderPayment); + String serviceRequest = paymentSetupService.createFinalOrderFeeServiceRequest( + data, caseId, data.getCitizenPaymentCallbackUrl(), orderSummary + ); + finalOrder.setApplicant2FinalOrderFeeServiceRequestReference(serviceRequest); - setServiceRequestReferenceForFinalOrderPayment(details.getData(), details.getId()); + details.setState(AwaitingFinalOrderPayment); return details; } - - public void setServiceRequestReferenceForFinalOrderPayment(CaseData data, long caseId) { - final FinalOrder finalOrder = data.getFinalOrder(); - - final String serviceRequestReference = paymentService.createServiceRequestReference( - data.getCitizenPaymentCallbackUrl(), - caseId, - data.getApplicant2().getFullName(), - finalOrder.getApplicant2FinalOrderFeeOrderSummary() - ); - - finalOrder.setApplicant2FinalOrderFeeServiceRequestReference(serviceRequestReference); - } } diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/notification/SaveAndSignOutNotificationHandler.java b/src/main/java/uk/gov/hmcts/divorce/citizen/notification/SaveAndSignOutNotificationHandler.java index 65fac8b0aa1..71b660deb7c 100644 --- a/src/main/java/uk/gov/hmcts/divorce/citizen/notification/SaveAndSignOutNotificationHandler.java +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/notification/SaveAndSignOutNotificationHandler.java @@ -3,12 +3,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.idam.IdamService; import uk.gov.hmcts.divorce.idam.User; import uk.gov.hmcts.divorce.notification.CommonContent; import uk.gov.hmcts.divorce.notification.NotificationService; import uk.gov.hmcts.divorce.solicitor.service.CcdAccessService; +import static uk.gov.hmcts.divorce.notification.CommonContent.SMART_SURVEY; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT; import static uk.gov.hmcts.divorce.notification.EmailTemplateName.SAVE_SIGN_OUT; @Component @@ -26,17 +29,21 @@ public class SaveAndSignOutNotificationHandler { @Autowired private CommonContent commonContent; - public void notifyApplicant(CaseData caseData, Long caseId, String userToken) { + public void notifyApplicant(State state, CaseData caseData, Long caseId, String userToken) { User user = idamService.retrieveUser(userToken); boolean isApplicant1 = ccdAccessService.isApplicant1(userToken, caseId); final var applicant = isApplicant1 ? caseData.getApplicant1() : caseData.getApplicant2(); final var partner = isApplicant1 ? caseData.getApplicant2() : caseData.getApplicant1(); + final var emailTemplate = State.InformationRequested.equals(state) ? REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT : SAVE_SIGN_OUT; + final var templateContent = commonContent.mainTemplateVars(caseData, caseId, applicant, partner); + templateContent.put(SMART_SURVEY, commonContent.getSmartSurvey()); + notificationService.sendEmail( user.getUserDetails().getSub(), - SAVE_SIGN_OUT, - commonContent.mainTemplateVars(caseData, caseId, applicant, partner), + emailTemplate, + templateContent, applicant.getLanguagePreference(), caseId ); diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/service/SwitchToSoleService.java b/src/main/java/uk/gov/hmcts/divorce/citizen/service/SwitchToSoleService.java index 42535fc8391..7c7de1c2298 100644 --- a/src/main/java/uk/gov/hmcts/divorce/citizen/service/SwitchToSoleService.java +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/service/SwitchToSoleService.java @@ -1,6 +1,7 @@ package uk.gov.hmcts.divorce.citizen.service; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import uk.gov.hmcts.ccd.sdk.type.Document; @@ -188,6 +189,10 @@ private void removeCaseUserRoles(final Long caseId, final String s2sToken, final String userId, final UserRole role) { + if (StringUtils.isEmpty(userId)) { + log.info("Switch to sole User ID is empty, skipping {} role removal for case {}", role, caseId); + return; + } caseAssignmentApi.removeCaseUserRoles( auth, @@ -202,6 +207,11 @@ private void addCaseUserRoles(final Long caseId, final String userId, final UserRole role) { + if (StringUtils.isEmpty(userId)) { + log.info("Switch to sole User ID is empty, skipping {} role grant for case {}", role, caseId); + return; + } + caseAssignmentApi.addCaseUserRoles( auth, s2sToken, diff --git a/src/main/java/uk/gov/hmcts/divorce/common/ccd/HighLevelDataSetupApp.java b/src/main/java/uk/gov/hmcts/divorce/common/ccd/HighLevelDataSetupApp.java index b85207704c4..c8822ed92ba 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/ccd/HighLevelDataSetupApp.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/ccd/HighLevelDataSetupApp.java @@ -31,6 +31,7 @@ public class HighLevelDataSetupApp extends DataLoaderToDefinitionStore { new CcdRoleConfig("citizen", "PUBLIC"), new CcdRoleConfig("caseworker-divorce", "PUBLIC"), new CcdRoleConfig("caseworker", "PUBLIC"), + new CcdRoleConfig("caseworker-divorce-rparobot", "PUBLIC"), new CcdRoleConfig("payments", "PUBLIC"), new CcdRoleConfig("pui-case-manager", "PUBLIC"), new CcdRoleConfig("pui-finance-manager", "PUBLIC"), diff --git a/src/main/java/uk/gov/hmcts/divorce/common/event/Applicant2SolicitorApplyForFinalOrder.java b/src/main/java/uk/gov/hmcts/divorce/common/event/Applicant2SolicitorApplyForFinalOrder.java index c6ccf56092d..b7de08461c5 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/event/Applicant2SolicitorApplyForFinalOrder.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/event/Applicant2SolicitorApplyForFinalOrder.java @@ -15,10 +15,12 @@ import uk.gov.hmcts.divorce.common.notification.Applicant2SolicitorAppliedForFinalOrderNotification; import uk.gov.hmcts.divorce.common.service.ApplyForFinalOrderService; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.notification.NotificationDispatcher; import uk.gov.hmcts.divorce.payment.PaymentService; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.divorce.payment.model.PbaResponse; import uk.gov.hmcts.divorce.solicitor.event.page.HelpWithFeesPageForApplicant2SolFinalOrder; import uk.gov.hmcts.divorce.solicitor.event.page.SolFinalOrderPayAccount; @@ -42,9 +44,6 @@ import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; -import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; -import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; -import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; @Component @Slf4j @@ -66,6 +65,9 @@ public class Applicant2SolicitorApplyForFinalOrder implements CCDConfig conf public AboutToStartOrSubmitResponse aboutToStart(final CaseDetails details) { log.info("{} about to start callback invoked for Case Id: {}", FINAL_ORDER_REQUESTED_APP2_SOL, details.getId()); - CaseData data = details.getData(); - log.info("Retrieving order summary"); - final OrderSummary orderSummary = paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE); - - data.getFinalOrder().setApplicant2SolFinalOrderFeeOrderSummary(orderSummary); - - data.getFinalOrder().setApplicant2SolFinalOrderFeeInPounds( - NumberFormat.getNumberInstance().format( - new BigDecimal(orderSummary.getPaymentTotal()).movePointLeft(2) - ) - ); + prepareOrderSummary(details.getData(), details.getId()); return AboutToStartOrSubmitResponse.builder() - .data(data) + .data(details.getData()) .build(); } @@ -142,8 +134,8 @@ public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails dynamicList.getValue().getLabel()); if (pbaNumber.isPresent()) { final PbaResponse response = paymentService.processPbaPayment( - updatedData, updatedDetails.getId(), + updatedFo.getApplicant2FinalOrderFeeServiceRequestReference(), updatedData.getApplicant2().getSolicitor(), pbaNumber.get(), updatedFo.getApplicant2SolFinalOrderFeeOrderSummary(), @@ -191,4 +183,18 @@ public SubmittedCallbackResponse submitted(CaseDetails details, return SubmittedCallbackResponse.builder().build(); } + + private void prepareOrderSummary(CaseData data, long caseId) { + final FinalOrder finalOrder = data.getFinalOrder(); + + final OrderSummary orderSummary = paymentSetupService.createFinalOrderFeeOrderSummary(data, caseId); + + finalOrder.setApplicant2FinalOrderFeeOrderSummary(orderSummary); + finalOrder.setApplicant2SolFinalOrderFeeOrderSummary(orderSummary); + finalOrder.setApplicant2SolFinalOrderFeeInPounds( + NumberFormat.getNumberInstance().format(new BigDecimal( + orderSummary.getPaymentTotal()).movePointLeft(2) + ) + ); + } } diff --git a/src/main/java/uk/gov/hmcts/divorce/common/event/SwitchedToSoleCo.java b/src/main/java/uk/gov/hmcts/divorce/common/event/SwitchedToSoleCo.java index ce9b7b3bc4a..cf92caac737 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/event/SwitchedToSoleCo.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/event/SwitchedToSoleCo.java @@ -89,7 +89,7 @@ public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails aboutToSubmit(CaseDetails aboutToSubmit(CaseDetails templateVars(CaseData caseData, Long id, Applicant a templateVars.put(DATE_FINAL_ORDER_ELIGIBLE_FROM_PLUS_3_MONTHS, caseData.getFinalOrder().getDateFinalOrderEligibleToRespondent() .format(getDateTimeFormatterForPreferredLanguage(applicant.getLanguagePreference()))); - templateVars.put(FINAL_ORDER_OVERDUE_DATE, caseData.getFinalOrder().getDateFinalOrderEligibleFrom().plusMonths(12) + templateVars.put(FINAL_ORDER_OVERDUE_DATE, caseData.getConditionalOrder().getGrantedDate().plusMonths(12) .format(getDateTimeFormatterForPreferredLanguage(applicant.getLanguagePreference()))); return templateVars; } @@ -166,7 +166,7 @@ private Map commonSolicitorTemplateVars(CaseData caseData, final templateVars.put(IS_DISSOLUTION, !caseData.isDivorce() ? YES : NO); templateVars.put(IS_SOLE, caseData.getApplicationType().isSole() ? YES : NO); templateVars.put(IS_JOINT, !caseData.getApplicationType().isSole() ? YES : NO); - templateVars.put(FINAL_ORDER_OVERDUE_DATE, caseData.getFinalOrder().getDateFinalOrderEligibleFrom().plusMonths(12) + templateVars.put(FINAL_ORDER_OVERDUE_DATE, caseData.getConditionalOrder().getGrantedDate().plusMonths(12) .format(getDateTimeFormatterForPreferredLanguage(applicant.getLanguagePreference()))); return templateVars; diff --git a/src/main/java/uk/gov/hmcts/divorce/common/notification/AwaitingFinalOrderReminderNotification.java b/src/main/java/uk/gov/hmcts/divorce/common/notification/AwaitingFinalOrderReminderNotification.java index 87210692ff4..1b1179dc0e7 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/notification/AwaitingFinalOrderReminderNotification.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/notification/AwaitingFinalOrderReminderNotification.java @@ -67,7 +67,7 @@ private Map templateVars(CaseData caseData, Long id, Applicant a templateVars.put(IS_REMINDER, YES); templateVars.put(DATE_FINAL_ORDER_ELIGIBLE_FROM_PLUS_3_MONTHS, caseData.getFinalOrder().getDateFinalOrderEligibleToRespondent().format(DATE_TIME_FORMATTER)); - templateVars.put(FINAL_ORDER_OVERDUE_DATE, caseData.getFinalOrder().getDateFinalOrderEligibleFrom().plusMonths(12) + templateVars.put(FINAL_ORDER_OVERDUE_DATE, caseData.getConditionalOrder().getGrantedDate().plusMonths(12) .format(getDateTimeFormatterForPreferredLanguage(applicant.getLanguagePreference()))); return templateVars; } diff --git a/src/main/java/uk/gov/hmcts/divorce/common/service/task/SetDateSubmitted.java b/src/main/java/uk/gov/hmcts/divorce/common/service/task/SetDateSubmitted.java index caf48d424d7..b6dc92f026d 100644 --- a/src/main/java/uk/gov/hmcts/divorce/common/service/task/SetDateSubmitted.java +++ b/src/main/java/uk/gov/hmcts/divorce/common/service/task/SetDateSubmitted.java @@ -14,6 +14,9 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFDecision; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; @Component @@ -29,7 +32,15 @@ public CaseDetails apply(final CaseDetails cas final CaseData caseData = caseDetails.getData(); final State state = caseDetails.getState(); - EnumSet submittedStates = EnumSet.of(Submitted, AwaitingDocuments, AwaitingHWFDecision); + EnumSet submittedStates = + EnumSet.of( + Submitted, + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingHWFDecision + ); if (submittedStates.contains(state) || submittedStates.contains(caseData.getApplication().getWelshPreviousState())) { caseData.getApplication().setDateSubmitted(LocalDateTime.now(clock)); diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/Application.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/Application.java index c8ce179d8fc..b7339f0d6a4 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/Application.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/Application.java @@ -13,6 +13,7 @@ import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.ccd.sdk.type.OrderSummary; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.divorce.divorcecase.model.access.Applicant1DeleteAccess; import uk.gov.hmcts.divorce.divorcecase.model.access.Applicant2Access; import uk.gov.hmcts.divorce.divorcecase.model.access.CaseworkerAccess; import uk.gov.hmcts.divorce.divorcecase.model.access.DefaultAccess; @@ -241,13 +242,13 @@ public class Application { @CCD( label = "Here are your order details", - access = {DefaultAccess.class} + access = {DefaultAccess.class, Applicant1DeleteAccess.class} ) private OrderSummary applicationFeeOrderSummary; @CCD( label = "Application Fee Service Request Reference", - access = {DefaultAccess.class} + access = {DefaultAccess.class, Applicant1DeleteAccess.class} ) private String applicationFeeServiceRequestReference; diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java index c3a24b2f368..1355abe396f 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseData.java @@ -69,6 +69,7 @@ import static uk.gov.hmcts.divorce.divorcecase.model.WhoDivorcing.WIFE; import static uk.gov.hmcts.divorce.document.model.DocumentType.CONDITIONAL_ORDER_APPLICATION; import static uk.gov.hmcts.divorce.document.model.DocumentType.FINAL_ORDER_APPLICATION; +import static uk.gov.hmcts.divorce.document.model.DocumentType.REQUEST_FOR_INFORMATION_RESPONSE_DOC; @JsonIgnoreProperties(ignoreUnknown = true) @Data @@ -331,13 +332,16 @@ public class CaseData { @Builder.Default private SentNotifications sentNotifications = new SentNotifications(); + @JsonUnwrapped + @Builder.Default + private RequestForInformationList requestForInformationList = new RequestForInformationList(); + @CCD( label = "Case matches", typeOverride = Collection, typeParameterOverride = "CaseMatch", access = {CaseworkerAccess.class} ) - @JsonInclude(JsonInclude.Include.NON_EMPTY) // Only include in JSON if non-empty @Builder.Default private List> caseMatches = new ArrayList<>(); @@ -490,13 +494,15 @@ public void deriveAndPopulateApplicantGenderDetails() { public void updateCaseDataWithPaymentDetails( OrderSummary applicationFeeOrderSummary, CaseData caseData, - String paymentReference + String paymentReference, + String serviceRequest ) { var payment = Payment .builder() .amount(parseInt(applicationFeeOrderSummary.getPaymentTotal())) .channel("online") .feeCode(applicationFeeOrderSummary.getFees().get(0).getValue().getCode()) + .serviceRequestReference(serviceRequest) .reference(paymentReference) .status(SUCCESS) .build(); @@ -586,6 +592,12 @@ public void reclassifyScannedDocumentToChosenDocumentType(DocumentType documentT finalOrder.setScannedD36Form(divorceDocument.getDocumentLink()); finalOrder.setDateD36FormScanned(scannedDocument.getScannedDate()); } + + if (REQUEST_FOR_INFORMATION_RESPONSE_DOC.equals(documentType)) { + RequestForInformationOfflineResponseDraft offlineDraft = + this.getRequestForInformationList().getRequestForInformationOfflineResponseDraft(); + offlineDraft.addDocument(divorceDocument); + } } @JsonIgnore diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseDocuments.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseDocuments.java index 105952cd2f7..4acc8da138a 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseDocuments.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseDocuments.java @@ -189,7 +189,10 @@ public enum ScannedDocumentSubtypes implements HasLabel { D84NVA("D84NVA"), @JsonProperty("D36N") - D36N("D36N"); + D36N("D36N"), + + @JsonProperty("RFIR") + RFIR("RFIR"); private final String label; } @@ -207,6 +210,9 @@ public enum OfflineDocumentReceived implements HasLabel { @JsonProperty("D36") FO_D36("Application for a final order (D36)"), + @JsonProperty("RFIR") + RFI_RESPONSE("Request for Information Response"), + @JsonProperty("Other") OTHER("Other"); diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/FeeDetails.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/FeeDetails.java index 6c68e0fc682..8398cd0c6ed 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/FeeDetails.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/FeeDetails.java @@ -10,6 +10,8 @@ import uk.gov.hmcts.ccd.sdk.api.CCD; import uk.gov.hmcts.ccd.sdk.type.DynamicList; import uk.gov.hmcts.ccd.sdk.type.OrderSummary; +import uk.gov.hmcts.divorce.divorcecase.model.access.CaseworkerAndSuperUserAccess; +import uk.gov.hmcts.divorce.divorcecase.model.access.CaseworkerDeleteAccess; import static uk.gov.hmcts.ccd.sdk.type.FieldType.FixedList; import static uk.gov.hmcts.divorce.divorcecase.model.ServicePaymentMethod.FEE_PAY_BY_ACCOUNT; @@ -22,7 +24,8 @@ public class FeeDetails { @CCD( - label = "Here are your order details" + label = "Here are your order details", + access = {CaseworkerAndSuperUserAccess.class, CaseworkerDeleteAccess.class} ) private OrderSummary orderSummary; @@ -55,6 +58,9 @@ public class FeeDetails { ) private String helpWithFeesReferenceNumber; + @CCD(label = "Service request reference") + private String serviceRequestReference; + @JsonIgnore public boolean isPaymentMethodPba() { return FEE_PAY_BY_ACCOUNT.equals(this.getPaymentMethod()); diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/FinalOrder.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/FinalOrder.java index b229de1a7c6..c778edc1638 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/FinalOrder.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/FinalOrder.java @@ -17,6 +17,7 @@ import uk.gov.hmcts.ccd.sdk.type.OrderSummary; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; import uk.gov.hmcts.divorce.divorcecase.model.access.Applicant2Access; +import uk.gov.hmcts.divorce.divorcecase.model.access.Applicant2DeleteAccess; import uk.gov.hmcts.divorce.divorcecase.model.access.DefaultAccess; import uk.gov.hmcts.divorce.divorcecase.model.access.SystemUpdateAndSuperUserAccess; @@ -227,7 +228,7 @@ public enum Granted implements HasLabel { @CCD( label = "Here are your order details", - access = {DefaultAccess.class} + access = {DefaultAccess.class, Applicant2DeleteAccess.class} ) private OrderSummary applicant2SolFinalOrderFeeOrderSummary; @@ -239,13 +240,13 @@ public enum Granted implements HasLabel { @CCD( label = "Here are your order details", - access = {DefaultAccess.class, Applicant2Access.class} + access = {DefaultAccess.class, Applicant2DeleteAccess.class} ) private OrderSummary applicant2FinalOrderFeeOrderSummary; @CCD( label = "Final Order Fee Service Request Reference", - access = {DefaultAccess.class, Applicant2Access.class} + access = {DefaultAccess.class, Applicant2DeleteAccess.class} ) private String applicant2FinalOrderFeeServiceRequestReference; @@ -307,6 +308,7 @@ public void updateFinalOrderWithApp2SolPaymentDetails( .amount(parseInt(finalOrderFeeOrderSummary.getPaymentTotal())) .channel("online") .feeCode(finalOrderFeeOrderSummary.getFees().get(0).getValue().getCode()) + .serviceRequestReference(applicant2FinalOrderFeeServiceRequestReference) .reference(paymentReference) .status(SUCCESS) .build(); diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/GeneralEmail.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/GeneralEmail.java index 81532038c69..979a0c271d7 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/GeneralEmail.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/GeneralEmail.java @@ -124,8 +124,10 @@ private boolean identicalEmailDetails(GeneralEmailDetails deliveredEmail) { .collect(Collectors.toSet()); return deliveredEmailDocLinks.equals(generalEmailDocLinks()) - && deliveredEmail.getGeneralEmailParties().equals(generalEmailParties) - && deliveredEmail.getGeneralEmailBody().equals(generalEmailDetails); + && generalEmailParties != null + && generalEmailParties.equals(deliveredEmail.getGeneralEmailParties()) + && generalEmailDetails != null + && generalEmailDetails.equals(deliveredEmail.getGeneralEmailBody()); } @JsonIgnore diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/SolicitorService.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/SolicitorService.java index 59bd2f0a7e3..bfb8e59276a 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/SolicitorService.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/SolicitorService.java @@ -110,6 +110,25 @@ public enum ServiceProcessedByProcessServer implements HasLabel { @CCD(label = " ") private String truthStatement; + @CCD(label = "Is this the first attempt to serve the documents?") + private YesOrNo firstAttemptToServe; + + @CCD(label = "Were the documents served previously returned by the post office as undelivered?") + private YesOrNo documentsPreviouslyReturned; + + @CCD( + label = "Date the previous service was returned as undelivered", + typeOverride = Date + ) + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate datePreviousServiceReturned; + + @CCD( + label = "Please enter further details of the previous service that was returned as undelivered.", + typeOverride = TextArea + ) + private String detailsOfPreviousService; + public String getTruthStatement() { return "I believe that the facts stated in the application are true."; } diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/State.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/State.java index a2d2df383d1..421efa5d8a5 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/State.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/State.java @@ -224,6 +224,13 @@ public enum State { ) AwaitingLegalAdvisorReferral, + @CCD( + label = "Awaiting requested information", + hint = "### Case number: ${hyphenatedCaseRef}\n ### ${applicant1LastName} and ${applicant2LastName}\n", + access = {DefaultStateAccess.class} + ) + AwaitingRequestedInformation, + @CCD( label = "Awaiting respondent final order payment", hint = "### Case number: ${hyphenatedCaseRef}\n ### ${applicant1LastName} and ${applicant2LastName}\n", @@ -231,6 +238,7 @@ public enum State { ) AwaitingFinalOrderPayment, + @CCD( label = "Awaiting service", hint = "### Case number: ${hyphenatedCaseRef}\n ### ${applicant1LastName} and ${applicant2LastName}\n", @@ -357,6 +365,13 @@ public enum State { ) GeneralConsiderationComplete, + @CCD( + label = "Information Requested", + hint = "### Case number: ${hyphenatedCaseRef}\n ### ${applicant1LastName} and ${applicant2LastName}\n", + access = {DefaultStateAccessExcludingCAA.class} + ) + InformationRequested, + @CCD( label = "Issued to bailiff", hint = "### Case number: ${hyphenatedCaseRef}\n ### ${applicant1LastName} and ${applicant2LastName}\n", @@ -420,6 +435,13 @@ public enum State { ) BulkCaseReject, + @CCD( + label = "Requested Information Submitted", + hint = "### Case number: ${hyphenatedCaseRef}\n ### ${applicant1LastName} and ${applicant2LastName}\n", + access = {DefaultStateAccessExcludingCAA.class} + ) + RequestedInformationSubmitted, + @CCD( label = "Respondent Final order requested", hint = "### Case number: ${hyphenatedCaseRef}\n ### ${applicant1LastName} and ${applicant2LastName}\n", @@ -478,7 +500,9 @@ public enum State { Applicant2Approved, AwaitingPayment, AwaitingHWFDecision, - AwaitingDocuments + AwaitingDocuments, + AwaitingRequestedInformation, + RequestedInformationSubmitted ); public static final EnumSet POST_SUBMISSION_STATES_WITH_WITHDRAWN_AND_REJECTED = EnumSet.complementOf(EnumSet.of( @@ -510,7 +534,11 @@ public enum State { Applicant2Approved, AwaitingPayment, AwaitingHWFDecision, + AwaitingHWFEvidence, + AwaitingHWFPartPayment, AwaitingDocuments, + AwaitingRequestedInformation, + RequestedInformationSubmitted, Submitted, Withdrawn, Rejected, diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/UserRole.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/UserRole.java index 2cf4d2576cb..3826939ed74 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/UserRole.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/UserRole.java @@ -16,6 +16,7 @@ public enum UserRole implements HasRole { SYSTEMUPDATE("caseworker-divorce-systemupdate", "CRUD"), JUDGE("caseworker-divorce-judge", "CRU"), NOC_APPROVER("caseworker-approver", "CRU"), + RPA_ROBOT("caseworker-divorce-rparobot", "CRU"), SOLICITOR("caseworker-divorce-solicitor", "CRU"), APPLICANT_1_SOLICITOR("[APPONESOLICITOR]", "CRU"), diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/CaseworkerAndSuperUserAccess.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/CaseworkerAndSuperUserAccess.java index cca64ba54cb..0e9060e4a25 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/CaseworkerAndSuperUserAccess.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/CaseworkerAndSuperUserAccess.java @@ -9,7 +9,6 @@ import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; -import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SYSTEMUPDATE; @@ -18,7 +17,6 @@ public class CaseworkerAndSuperUserAccess implements HasAccessControl { @Override public SetMultimap getGrants() { SetMultimap grants = HashMultimap.create(); - grants.putAll(SOLICITOR, Permissions.READ); grants.putAll(LEGAL_ADVISOR, Permissions.READ); grants.putAll(JUDGE, Permissions.READ); diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/tab/ApplicationTab.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/tab/ApplicationTab.java index 2af4c268d42..62d8eabbe72 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/tab/ApplicationTab.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/tab/ApplicationTab.java @@ -391,6 +391,10 @@ private void addService(final Tab.TabBuilder tabBuilder) { tabBuilder .label("Label-SolicitorService", "serviceMethod=\"solicitorService\"", "### Solicitor Service") .field("serviceMethod", SOLE_APPLICATION) + .field("solServiceFirstAttemptToServe", "serviceMethod=\"solicitorService\"") + .field("solServiceDocumentsPreviouslyReturned", "serviceMethod=\"solicitorService\"") + .field("solServiceDetailsOfPreviousService", "serviceMethod=\"solicitorService\"") + .field("solServiceDatePreviousServiceReturned", "serviceMethod=\"solicitorService\"") .field("solServiceDateOfService", "serviceMethod=\"solicitorService\"") .field("solServiceDocumentsServed", "serviceMethod=\"solicitorService\"") .field("solServiceOnWhomServed", "serviceMethod=\"solicitorService\"") diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/tab/CaseTypeTab.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/tab/CaseTypeTab.java index e2920d0534b..731666def14 100644 --- a/src/main/java/uk/gov/hmcts/divorce/divorcecase/tab/CaseTypeTab.java +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/tab/CaseTypeTab.java @@ -23,6 +23,7 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingLegalAdvisorReferral; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPayment; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPronouncement; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingService; import static uk.gov.hmcts.divorce.divorcecase.model.State.ClarificationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.ConditionalOrderDrafted; @@ -31,8 +32,10 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.FinalOrderComplete; import static uk.gov.hmcts.divorce.divorcecase.model.State.FinalOrderPending; import static uk.gov.hmcts.divorce.divorcecase.model.State.FinalOrderRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; import static uk.gov.hmcts.divorce.divorcecase.model.State.JSAwaitingLA; import static uk.gov.hmcts.divorce.divorcecase.model.State.LAReview; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.RespondentFinalOrderRequested; import static uk.gov.hmcts.divorce.divorcecase.model.State.SeparationOrderGranted; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; @@ -120,6 +123,7 @@ public void configure(final ConfigBuilder configBuild buildCorrespondenceTab(configBuilder); buildAmendedApplicationTab(configBuilder); buildChangeOfRepresentativeTab(configBuilder); + buildRequestForInformationTab(configBuilder); // Commented out as requested by service team. This can't be available for super users. Maybe we need a "Developer" role? //buildLetterPackTab(configBuilder); @@ -143,8 +147,20 @@ private void buildAosTab(ConfigBuilder configBuilder) SUPER_USER, APPLICANT_1_SOLICITOR, APPLICANT_2_SOLICITOR) .showCondition("applicationType=\"soleApplication\" AND coSwitchedToSole!=\"Yes\" AND " + notShowForState( - Draft, AwaitingHWFDecision, AwaitingPayment, Submitted, AwaitingDocuments, - AwaitingAos, AosDrafted, AosOverdue, AwaitingService)) + Draft, + AwaitingHWFDecision, + AwaitingPayment, + Submitted, + AwaitingDocuments, + AwaitingRequestedInformation, + InformationRequested, + RequestedInformationSubmitted, + AwaitingAos, + AosDrafted, + AosOverdue, + AwaitingService + ) + ) .field("applicant2Offline", NEVER_SHOW) .label("LabelAosTabOnlineResponse-Heading", "applicant2Offline=\"No\"", "## This is an online AoS response") @@ -242,6 +258,12 @@ private void buildDocumentsTab(ConfigBuilder configBu .field("coProofOfServiceUploadDocuments"); } + private void buildRequestForInformationTab(ConfigBuilder configBuilder) { + configBuilder.tab("requestsForInformation", "Requests For Information") + .forRoles(CASE_WORKER, LEGAL_ADVISOR, JUDGE, SUPER_USER) + .field("requestsForInformation"); + } + private void buildCorrespondenceTab(ConfigBuilder configBuilder) { configBuilder.tab("correspondence", "Correspondence") .forRoles(CASE_WORKER, LEGAL_ADVISOR, JUDGE, SUPER_USER) diff --git a/src/main/java/uk/gov/hmcts/divorce/document/content/DocmosisTemplateConstants.java b/src/main/java/uk/gov/hmcts/divorce/document/content/DocmosisTemplateConstants.java index b3f3d7927b6..3680e623550 100644 --- a/src/main/java/uk/gov/hmcts/divorce/document/content/DocmosisTemplateConstants.java +++ b/src/main/java/uk/gov/hmcts/divorce/document/content/DocmosisTemplateConstants.java @@ -10,6 +10,7 @@ public final class DocmosisTemplateConstants { public static final String CASE_REFERENCE = "caseReference"; public static final String ISSUE_DATE = "issueDate"; public static final String ISSUE_DATE_POPULATED = "issueDatePopulated"; + public static final String NOT_YET_ISSUED = "notYetIssued"; public static final String APPLICANT_1_FIRST_NAME = "applicant1FirstName"; public static final String APPLICANT_1_MIDDLE_NAME = "applicant1MiddleName"; public static final String APPLICANT_1_LAST_NAME = "applicant1LastName"; diff --git a/src/main/java/uk/gov/hmcts/divorce/document/content/templatecontent/FinalOrderCanApplyTemplateContent.java b/src/main/java/uk/gov/hmcts/divorce/document/content/templatecontent/FinalOrderCanApplyTemplateContent.java index 7317a8fcee6..838d7cb983b 100644 --- a/src/main/java/uk/gov/hmcts/divorce/document/content/templatecontent/FinalOrderCanApplyTemplateContent.java +++ b/src/main/java/uk/gov/hmcts/divorce/document/content/templatecontent/FinalOrderCanApplyTemplateContent.java @@ -65,7 +65,7 @@ public Map getTemplateContent(CaseData caseData, Long caseId, Ap templateContent.put(IS_DIVORCE, caseData.isDivorce()); templateContent.put(DATE, LocalDate.now().format(DATE_TIME_FORMATTER)); templateContent.put(IS_JOINT, !caseData.getApplicationType().isSole()); - templateContent.put(FINAL_ORDER_OVERDUE_DATE, caseData.getFinalOrder().getDateFinalOrderEligibleFrom().plusMonths(12) + templateContent.put(FINAL_ORDER_OVERDUE_DATE, caseData.getConditionalOrder().getGrantedDate().plusMonths(12) .format(getDateTimeFormatterForPreferredLanguage(applicant.getLanguagePreference()))); templateContent.put(PARTNER, getPartnerInfo(caseData,applicant,applicant.getLanguagePreference())); diff --git a/src/main/java/uk/gov/hmcts/divorce/document/model/DocumentType.java b/src/main/java/uk/gov/hmcts/divorce/document/model/DocumentType.java index 8dbedd9c24d..12305de838e 100644 --- a/src/main/java/uk/gov/hmcts/divorce/document/model/DocumentType.java +++ b/src/main/java/uk/gov/hmcts/divorce/document/model/DocumentType.java @@ -253,7 +253,10 @@ public enum DocumentType implements HasLabel { SWITCH_TO_SOLE_CO_LETTER("Switch to Sole Conditional Order Letter", true), @JsonProperty("grantOfRepresentation") - GRANT_OF_REPRESENTATION("NoC grant of representation letter", true); + GRANT_OF_REPRESENTATION("NoC grant of representation letter", true), + + @JsonProperty("requestForInformationResponseDoc") + REQUEST_FOR_INFORMATION_RESPONSE_DOC("RFI response document", true); private final String label; private final boolean potentiallyConfidential; diff --git a/src/main/java/uk/gov/hmcts/divorce/idam/IdamService.java b/src/main/java/uk/gov/hmcts/divorce/idam/IdamService.java index 72e2bace09a..f129abca850 100644 --- a/src/main/java/uk/gov/hmcts/divorce/idam/IdamService.java +++ b/src/main/java/uk/gov/hmcts/divorce/idam/IdamService.java @@ -2,6 +2,8 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import feign.FeignException; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -13,6 +15,7 @@ import static uk.gov.hmcts.divorce.common.config.ControllerConstants.BEARER_PREFIX; @Service +@Slf4j public class IdamService { @Value("${idam.systemupdate.username}") private String systemUpdateUserName; @@ -20,6 +23,12 @@ public class IdamService { @Value("${idam.systemupdate.password}") private String systemUpdatePassword; + @Value("${idam.divorce.username}") + private String oldDivorceUserName; + + @Value("${idam.divorce.password}") + private String oldDivorcePassword; + @Autowired private IdamClient idamClient; @@ -36,6 +45,17 @@ public User retrieveSystemUpdateUserDetails() { return retrieveUser(getCachedIdamOauth2Token(systemUpdateUserName, systemUpdatePassword)); } + public User retrieveOldSystemUpdateUserDetails() { + User user = null; + + try { + user = retrieveUser(getCachedIdamOauth2Token(oldDivorceUserName, oldDivorcePassword)); + } catch (FeignException e) { + log.info("Exception in retrieveOldSystemUpdateUserDetails {} for user:{}", e.getCause(), oldDivorceUserName); + } + return user; + } + private String getCachedIdamOauth2Token(String username, String password) { String userToken = cache.getIfPresent(username); if (userToken == null) { diff --git a/src/main/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemApplyNoticeOfChange.java b/src/main/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemApplyNoticeOfChange.java index 1c1bc594659..18f7953ded1 100644 --- a/src/main/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemApplyNoticeOfChange.java +++ b/src/main/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemApplyNoticeOfChange.java @@ -18,6 +18,7 @@ import uk.gov.hmcts.divorce.noticeofchange.client.AssignCaseAccessClient; import uk.gov.hmcts.divorce.noticeofchange.service.ChangeOfRepresentativeService; import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.ccd.client.model.AboutToStartOrSubmitCallbackResponse; @@ -27,7 +28,7 @@ import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; import static uk.gov.hmcts.divorce.caseworker.event.NoticeType.NEW_DIGITAL_SOLICITOR_NEW_ORG; -import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; +import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_ISSUE_STATES; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_1_SOLICITOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; @@ -44,6 +45,8 @@ public class SystemApplyNoticeOfChange implements CCDConfig { public static final String NOTICE_OF_CHANGE_APPLIED = "notice-of-change-applied"; public static final String LETTER_TYPE_GRANT_OF_REPRESENTATION = "grant-of-representation"; + public static final String NOTICE_OF_CHANGE_FAILED_ERROR = "Notice of change failed with the following error(s) for CaseID {}:"; + public static final String NOTIFICATION_FAILED_ERROR = "nocCitizenToSolsNotifications failed for case Id: {} with message: {}"; private final AuthTokenGenerator authTokenGenerator; private final ObjectMapper objectMapper; @@ -58,7 +61,7 @@ public class SystemApplyNoticeOfChange implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(NOTICE_OF_CHANGE_APPLIED) - .forStates(POST_SUBMISSION_STATES) + .forStates(POST_ISSUE_STATES) .name("Notice Of Change Applied") .grant(CREATE_READ_UPDATE, NOC_APPROVER) .grantHistoryOnly(LEGAL_ADVISOR, JUDGE, CASE_WORKER, SUPER_USER) @@ -88,7 +91,7 @@ public AboutToStartOrSubmitResponse aboutToStart(final CaseDeta List responseErrors = response.getErrors(); if (Objects.nonNull(responseErrors) && !responseErrors.isEmpty()) { - log.info("Notice of change failed with the following error(s) for CaseID {}:", details.getId()); + log.info(NOTICE_OF_CHANGE_FAILED_ERROR, details.getId()); responseErrors.forEach(log::info); return AboutToStartOrSubmitResponse.builder() @@ -100,8 +103,12 @@ public AboutToStartOrSubmitResponse aboutToStart(final CaseDeta CaseData responseData = objectMapper.convertValue(data, CaseData.class); - notificationDispatcher.sendNOC(nocCitizenToSolsNotifications, caseData, + try { + notificationDispatcher.sendNOC(nocCitizenToSolsNotifications, caseData, beforeCaseData, details.getId(), isApplicant1, NEW_DIGITAL_SOLICITOR_NEW_ORG); + } catch (final NotificationTemplateException e) { + log.error(NOTIFICATION_FAILED_ERROR, details.getId(), e.getMessage(), e); + } return AboutToStartOrSubmitResponse.builder() .data(responseData) diff --git a/src/main/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemRequestNoticeOfChange.java b/src/main/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemRequestNoticeOfChange.java index 2c2d77e4e61..f9698d04dc0 100644 --- a/src/main/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemRequestNoticeOfChange.java +++ b/src/main/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemRequestNoticeOfChange.java @@ -23,7 +23,7 @@ import java.util.List; import static uk.gov.hmcts.divorce.common.ccd.CcdPageConfiguration.NEVER_SHOW; -import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES; +import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_ISSUE_STATES; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; @@ -54,7 +54,7 @@ public class SystemRequestNoticeOfChange implements CCDConfig configBuilder) { new PageBuilder(configBuilder .event(NOTICE_OF_CHANGE_REQUESTED) - .forStates(POST_SUBMISSION_STATES) + .forStates(POST_ISSUE_STATES) .name("Notice Of Change Requested") .grant(CREATE_READ_UPDATE, ORGANISATION_CASE_ACCESS_ADMINISTRATOR) .grantHistoryOnly(LEGAL_ADVISOR, JUDGE, CASE_WORKER, SUPER_USER) diff --git a/src/main/java/uk/gov/hmcts/divorce/notification/ApplicantNotification.java b/src/main/java/uk/gov/hmcts/divorce/notification/ApplicantNotification.java index 88253b8e1d2..31bb96bfa3d 100644 --- a/src/main/java/uk/gov/hmcts/divorce/notification/ApplicantNotification.java +++ b/src/main/java/uk/gov/hmcts/divorce/notification/ApplicantNotification.java @@ -35,4 +35,8 @@ default void sendToApplicant1OldSolicitor(final CaseData oldCaseData, final Long default void sendToApplicant2OldSolicitor(final CaseData oldCaseData, final Long caseId) { //No operation } + + default void sendToOtherRecipient(final CaseData caseData, final Long caseId) { + //No operation + } } diff --git a/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java b/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java index bc658fd4358..23ab13ad47b 100644 --- a/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java +++ b/src/main/java/uk/gov/hmcts/divorce/notification/CommonContent.java @@ -135,6 +135,8 @@ public class CommonContent { public static final String SPOUSE_WELSH = "priod"; public static final String SMART_SURVEY = "smartSurvey"; + public static final String REQUEST_FOR_INFORMATION_DETAILS = "request information details"; + public static final String SENT_TO_BOTH_APPLICANTS = "sentToBothApplicants"; public static final String GENERAL_FEE = "generalFee"; public static final String FINAL_ORDER_FEE = "fee"; public static final String WEB_FORM_TEXT = "webformText"; @@ -163,7 +165,10 @@ public Map mainTemplateVars(final CaseData caseData, templateVars.put(COURT_EMAIL, config.getTemplateVars().get(caseData.isDivorce() ? DIVORCE_COURT_EMAIL : DISSOLUTION_COURT_EMAIL)); templateVars.put(SIGN_IN_URL, getSignInUrl(caseData)); - templateVars.put(WEBFORM_URL, config.getTemplateVars().get(WEBFORM_URL)); + templateVars.put(WEBFORM_URL, + WELSH.equals(applicant.getLanguagePreference()) + ? config.getTemplateVars().get(WEBFORM_CY_URL) + : config.getTemplateVars().get(WEBFORM_URL)); templateVars.put(WEB_FORM_TEXT, getContactWebFormText(applicant.getLanguagePreference())); templateVars.put(SMART_SURVEY, getSmartSurvey()); @@ -269,7 +274,7 @@ public String getPartnerWelshContent(CaseData caseData, Applicant partner) { } } - public Map conditionalOrderTemplateVars(final CaseData caseData, + public Map jointTemplateVars(final CaseData caseData, final Long id, final Applicant applicant, final Applicant partner) { @@ -293,6 +298,28 @@ public Map conditionalOrderTemplateVars(final CaseData caseData, return templateVars; } + public Map conditionalOrderTemplateVars(final CaseData caseData, + final Long id, + final Applicant applicant, + final Applicant partner) { + final Map templateVars = jointTemplateVars(caseData, id, applicant, partner); + + templateVars.put(JOINT_CONDITIONAL_ORDER, !caseData.getApplicationType().isSole() ? YES : NO); + + return templateVars; + } + + public Map requestForInformationTemplateVars(final CaseData caseData, + final Long id, + final Applicant applicant, + final Applicant partner) { + final Map templateVars = jointTemplateVars(caseData, id, applicant, partner); + + templateVars.put(IS_JOINT, !caseData.getApplicationType().isSole() ? YES : NO); + + return templateVars; + } + public String getSignInUrl(CaseData caseData) { return config.getTemplateVars().get(caseData.isDivorce() ? SIGN_IN_DIVORCE_URL : SIGN_IN_DISSOLUTION_URL); } diff --git a/src/main/java/uk/gov/hmcts/divorce/notification/EmailTemplateName.java b/src/main/java/uk/gov/hmcts/divorce/notification/EmailTemplateName.java index 48b4ea47262..25545bc38b4 100644 --- a/src/main/java/uk/gov/hmcts/divorce/notification/EmailTemplateName.java +++ b/src/main/java/uk/gov/hmcts/divorce/notification/EmailTemplateName.java @@ -1,6 +1,7 @@ package uk.gov.hmcts.divorce.notification; public enum EmailTemplateName { + REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT, SAVE_SIGN_OUT, OUTSTANDING_ACTIONS, APPLICATION_SUBMITTED, @@ -124,6 +125,15 @@ public enum EmailTemplateName { NOC_CITIZEN_TO_SOL_EMAIL_CITIZEN, NOC_TO_SOLS_EMAIL_NEW_SOL, NOC_TO_SOLS_EMAIL_OLD_SOL, + REQUEST_FOR_INFORMATION_SOLE, + REQUEST_FOR_INFORMATION_SOLICITOR, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD, + REQUEST_FOR_INFORMATION_OTHER, + REQUEST_FOR_INFORMATION_JOINT, + REQUEST_FOR_INFORMATION_RESPONSE, + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, NFD_APP1_SOLICITOR_APPLIED_FOR_FINAL_ORDER, AUTOMATED_DAILY_REPORT, REINVITE_CITIZEN_TO_CASE, diff --git a/src/main/java/uk/gov/hmcts/divorce/notification/NotificationDispatcher.java b/src/main/java/uk/gov/hmcts/divorce/notification/NotificationDispatcher.java index f0570b82007..386a5f10c76 100644 --- a/src/main/java/uk/gov/hmcts/divorce/notification/NotificationDispatcher.java +++ b/src/main/java/uk/gov/hmcts/divorce/notification/NotificationDispatcher.java @@ -1,12 +1,23 @@ package uk.gov.hmcts.divorce.notification; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import uk.gov.hmcts.divorce.caseworker.event.NoticeType; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; import static org.apache.commons.lang3.StringUtils.isBlank; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.APPLICANT; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.OTHER; +@Slf4j @Service public class NotificationDispatcher { @@ -73,4 +84,85 @@ private boolean applicantRepresentedBefore(final boolean isApplicant1, final Cas return (isApplicant1 && previousCaseData.getApplicant1().isRepresented()) || (!isApplicant1 && previousCaseData.getApplicant2().isRepresented()); } + + public void sendRequestForInformationNotification(ApplicantNotification applicantNotification, CaseData caseData, Long caseId) + throws NotificationTemplateException { + + RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + if (APPLICANT.equals(requestForInformation.getRequestForInformationSoleParties()) + || APPLICANT1.equals(requestForInformation.getRequestForInformationJointParties())) { + requestForInformationSendToApplicant1(applicantNotification, caseData, caseId); + } else if (APPLICANT2.equals(requestForInformation.getRequestForInformationJointParties())) { + requestForInformationSendToApplicant2(applicantNotification, caseData, caseId); + } else if (BOTH.equals(requestForInformation.getRequestForInformationJointParties())) { + requestForInformationSendToApplicant1(applicantNotification, caseData, caseId); + requestForInformationSendToApplicant2(applicantNotification, caseData, caseId); + } else if (OTHER.equals(requestForInformation.getRequestForInformationSoleParties()) + || RequestForInformationJointParties.OTHER.equals(requestForInformation.getRequestForInformationJointParties())) { + + applicantNotification.sendToOtherRecipient(caseData, caseId); + } else { + throw new NotificationTemplateException( + "Unable to send Request For Information Notification for Case Id " + caseId + ". RequestForInformation parties not set."); + } + } + + public void sendRequestForInformationResponseNotification(ApplicantNotification applicantNotification, CaseData caseData, Long caseId) + throws NotificationTemplateException { + + RequestForInformationResponseParties requestForInformationResponseParties = + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse().getRequestForInformationResponseParties(); + + if (RequestForInformationResponseParties.APPLICANT1.equals(requestForInformationResponseParties)) { + applicantNotification.sendToApplicant1(caseData, caseId); + } else if (RequestForInformationResponseParties.APPLICANT2.equals(requestForInformationResponseParties)) { + applicantNotification.sendToApplicant2(caseData, caseId); + } else { + throw new NotificationTemplateException( + "Unable to send Request For Information Response Notification for Case Id " + + caseId + + ". RequestForInformationResponse parties not set." + ); + } + } + + public void sendRequestForInformationResponsePartnerNotification( + ApplicantNotification applicantNotification, + CaseData caseData, + Long caseId + ) throws NotificationTemplateException { + + RequestForInformationResponseParties requestForInformationResponseParties = + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse().getRequestForInformationResponseParties(); + + if (RequestForInformationResponseParties.APPLICANT1.equals(requestForInformationResponseParties) + || RequestForInformationResponseParties.APPLICANT1SOLICITOR.equals(requestForInformationResponseParties)) { + requestForInformationSendToApplicant2(applicantNotification, caseData, caseId); + } else if (RequestForInformationResponseParties.APPLICANT2.equals(requestForInformationResponseParties) + || RequestForInformationResponseParties.APPLICANT2SOLICITOR.equals(requestForInformationResponseParties)) { + requestForInformationSendToApplicant1(applicantNotification, caseData, caseId); + } else { + throw new NotificationTemplateException( + "Unable to send Request For Information Response Partner Notification for Case Id " + + caseId + + ". RequestForInformationResponse parties not set." + ); + } + } + + private void requestForInformationSendToApplicant1(ApplicantNotification applicantNotification, CaseData caseData, Long caseId) { + if (caseData.getApplicant1().isRepresented()) { + applicantNotification.sendToApplicant1Solicitor(caseData, caseId); + } else { + applicantNotification.sendToApplicant1(caseData, caseId); + } + } + + private void requestForInformationSendToApplicant2(ApplicantNotification applicantNotification, CaseData caseData, Long caseId) { + if (caseData.getApplicant2().isRepresented()) { + applicantNotification.sendToApplicant2Solicitor(caseData, caseId); + } else { + applicantNotification.sendToApplicant2(caseData, caseId); + } + } } diff --git a/src/main/java/uk/gov/hmcts/divorce/payment/PaymentPbaClient.java b/src/main/java/uk/gov/hmcts/divorce/payment/PaymentPbaClient.java index 732e57f5700..32304aee1a1 100644 --- a/src/main/java/uk/gov/hmcts/divorce/payment/PaymentPbaClient.java +++ b/src/main/java/uk/gov/hmcts/divorce/payment/PaymentPbaClient.java @@ -2,6 +2,7 @@ import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestHeader; import uk.gov.hmcts.divorce.payment.model.CreditAccountPaymentRequest; @@ -14,10 +15,11 @@ @SuppressWarnings("PMD.UseObjectForClearerAPI") public interface PaymentPbaClient { - @PostMapping(value = "/credit-account-payments") + @PostMapping(value = "/service-request/{serviceRequestReference}/pba-payments") ResponseEntity creditAccountPayment( @RequestHeader(AUTHORIZATION) String authorisation, @RequestHeader(SERVICE_AUTHORIZATION) String serviceAuthorisation, + @PathVariable("serviceRequestReference") String serviceRequestReference, CreditAccountPaymentRequest creditAccountPaymentRequest); } diff --git a/src/main/java/uk/gov/hmcts/divorce/payment/PaymentService.java b/src/main/java/uk/gov/hmcts/divorce/payment/PaymentService.java index 05c4121c7b6..76e32dad790 100644 --- a/src/main/java/uk/gov/hmcts/divorce/payment/PaymentService.java +++ b/src/main/java/uk/gov/hmcts/divorce/payment/PaymentService.java @@ -11,7 +11,6 @@ import uk.gov.hmcts.ccd.sdk.type.Fee; import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.ccd.sdk.type.OrderSummary; -import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.Solicitor; import uk.gov.hmcts.divorce.payment.model.CasePaymentRequest; import uk.gov.hmcts.divorce.payment.model.CreateServiceRequestBody; @@ -29,6 +28,7 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; +import java.util.UUID; import static java.util.Collections.singletonList; import static org.springframework.http.HttpHeaders.AUTHORIZATION; @@ -36,7 +36,6 @@ import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.util.CollectionUtils.isEmpty; import static uk.gov.hmcts.ccd.sdk.type.Fee.getValueInPence; -import static uk.gov.hmcts.divorce.divorcecase.NoFaultDivorce.getCaseType; import static uk.gov.hmcts.divorce.payment.FeesAndPaymentsUtil.penceToPounds; import static uk.gov.hmcts.divorce.payment.model.PbaErrorMessage.CAE0001; import static uk.gov.hmcts.divorce.payment.model.PbaErrorMessage.CAE0003; @@ -160,14 +159,14 @@ public OrderSummary getOrderSummaryByServiceEvent(String service, String event, .build(); } - public PbaResponse processPbaPayment(CaseData caseData, - Long caseId, + public PbaResponse processPbaPayment(Long caseId, + String serviceRequestReference, Solicitor solicitor, String pbaNumber, OrderSummary orderSummary, String feeAccountReference) { - log.info("Processing PBA payment for case id {}", caseId); + log.info("Processing PBA payment for case id {}, against service reference {}", caseId, serviceRequestReference); ResponseEntity paymentResponseEntity = null; @@ -175,13 +174,14 @@ public PbaResponse processPbaPayment(CaseData caseData, paymentResponseEntity = paymentPbaClient.creditAccountPayment( httpServletRequest.getHeader(AUTHORIZATION), authTokenGenerator.generate(), - creditAccountPaymentRequest(caseData, caseId, solicitor, pbaNumber, orderSummary, feeAccountReference) + serviceRequestReference, + creditAccountPaymentRequest(solicitor, pbaNumber, orderSummary, feeAccountReference) ); String paymentReference = Optional.ofNullable(paymentResponseEntity) .map(response -> Optional.ofNullable(response.getBody()) - .map(CreditAccountPaymentResponse::getReference) + .map(CreditAccountPaymentResponse::getPaymentReference) .orElseGet(() -> null) ) .orElseGet(() -> null); @@ -318,39 +318,19 @@ private String populateErrorResponse( return errorMessage; } - private CreditAccountPaymentRequest creditAccountPaymentRequest(CaseData caseData, - Long caseId, - Solicitor solicitor, + private CreditAccountPaymentRequest creditAccountPaymentRequest(Solicitor solicitor, String pbaNumber, OrderSummary orderSummary, String feeAccountReference) { - var creditAccountPaymentRequest = new CreditAccountPaymentRequest(); - creditAccountPaymentRequest.setService(DIVORCE_SERVICE); - creditAccountPaymentRequest.setCurrency(GBP); - creditAccountPaymentRequest.setAccountNumber(pbaNumber); - creditAccountPaymentRequest.setCaseType(getCaseType()); - - creditAccountPaymentRequest.setOrganisationName(solicitor.getOrganisationPolicy().getOrganisation().getOrganisationName()); - - creditAccountPaymentRequest.setCustomerReference(feeAccountReference); - - final Fee fee = getFeeValue(orderSummary); - creditAccountPaymentRequest.setDescription(fee.getDescription()); - - creditAccountPaymentRequest.setAmount(orderSummary.getPaymentTotal()); - creditAccountPaymentRequest.setCcdCaseNumber(String.valueOf(caseId)); - - List paymentItemList = populateFeesPaymentItems( - caseId, - orderSummary.getPaymentTotal(), - fee, - caseData.getApplication().getFeeAccountReference() - ); - - creditAccountPaymentRequest.setFees(paymentItemList); - - return creditAccountPaymentRequest; + return CreditAccountPaymentRequest.builder() + .currency(GBP) + .accountNumber(pbaNumber) + .organisationName(solicitor.getOrganisationPolicy().getOrganisation().getOrganisationName()) + .customerReference(feeAccountReference) + .idempotencyKey(String.valueOf(UUID.randomUUID())) + .amount(penceToPounds(orderSummary.getPaymentTotal())) + .build(); } private CreateServiceRequestBody buildServiceRequestBody( @@ -373,25 +353,6 @@ private CreateServiceRequestBody buildServiceRequestBody( ).build(); } - private List populateFeesPaymentItems( - Long caseId, - String paymentTotal, - Fee fee, - String reference - ) { - var paymentItem = PaymentItem - .builder() - .ccdCaseNumber(String.valueOf(caseId)) - .calculatedAmount(penceToPounds(paymentTotal)) - .code(fee.getCode()) - .reference(reference) - .version(fee.getVersion()) - .build(); - - - return singletonList(paymentItem); - } - private Fee getFeeValue(OrderSummary orderSummary) { // We are always interested in the first fee. There may be a change in the future ListValue feeItem = orderSummary.getFees().get(0); diff --git a/src/main/java/uk/gov/hmcts/divorce/payment/model/CreditAccountPaymentRequest.java b/src/main/java/uk/gov/hmcts/divorce/payment/model/CreditAccountPaymentRequest.java index beb08e5af59..b31e93c5ae8 100644 --- a/src/main/java/uk/gov/hmcts/divorce/payment/model/CreditAccountPaymentRequest.java +++ b/src/main/java/uk/gov/hmcts/divorce/payment/model/CreditAccountPaymentRequest.java @@ -2,46 +2,30 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; import lombok.Data; import lombok.ToString; -import java.util.List; import java.util.Optional; @Data @ToString @JsonIgnoreProperties(ignoreUnknown = true) +@Builder public class CreditAccountPaymentRequest { - @JsonProperty("ccd_case_number") - private String ccdCaseNumber; - - @JsonProperty("account_number") - private String accountNumber; - - @JsonProperty("amount") private String amount; - @JsonProperty("fees") - private List fees; - - @JsonProperty("service") - private String service; + private String currency; @JsonProperty("customer_reference") private String customerReference; - @JsonProperty("site_id") - private String siteId; - - @JsonProperty("case_type") - private String caseType; - - @JsonProperty("description") - private String description; + @JsonProperty("account_number") + private String accountNumber; - @JsonProperty("currency") - private String currency; + @JsonProperty("idempotency_key") + private String idempotencyKey; @JsonProperty("organisation_name") private String organisationName; diff --git a/src/main/java/uk/gov/hmcts/divorce/payment/model/CreditAccountPaymentResponse.java b/src/main/java/uk/gov/hmcts/divorce/payment/model/CreditAccountPaymentResponse.java index 229cc18cf75..d02ea9d64a7 100644 --- a/src/main/java/uk/gov/hmcts/divorce/payment/model/CreditAccountPaymentResponse.java +++ b/src/main/java/uk/gov/hmcts/divorce/payment/model/CreditAccountPaymentResponse.java @@ -7,7 +7,6 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; -import uk.gov.hmcts.ccd.sdk.type.Fee; import java.util.List; @@ -19,23 +18,20 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class CreditAccountPaymentResponse { - @JsonProperty("account_number") - private String accountNumber; - - @JsonProperty("ccd_case_number") - private String ccdCaseNumber; + @JsonProperty("payment_reference") + private String paymentReference; - @JsonProperty("amount") private int amount; - @JsonProperty("fees") - private List fees; + private String currency; + + @JsonProperty("customer_reference") + private String customerReference; - @JsonProperty("date_updated") - private String dateUpdated; + @JsonProperty("account_number") + private String accountNumber; - @JsonProperty("method") - private String method; + private String status; @JsonProperty("status_histories") private List statusHistories; @@ -43,48 +39,6 @@ public class CreditAccountPaymentResponse { @JsonProperty("date_created") private String dateCreated; - @JsonProperty("service_name") - private String serviceName; - - @JsonProperty("channel") - private String channel; - - @JsonProperty("description") - private String description; - @JsonProperty("organisation_name") private String organisationName; - - @JsonProperty("payment_reference") - private String paymentReference; - - @JsonProperty("external_provider") - private String externalProvider; - - @JsonProperty("reference") - private String reference; - - @JsonProperty("case_reference") - private String caseReference; - - @JsonProperty("customer_reference") - private String customerReference; - - @JsonProperty("external_reference") - private String externalReference; - - @JsonProperty("site_id") - private String siteId; - - @JsonProperty("payment_group_reference") - private String paymentGroupReference; - - @JsonProperty("currency") - private String currency; - - @JsonProperty("id") - private String id; - - @JsonProperty("status") - private String status; } diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorConfirmService.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorConfirmService.java index 032597470f0..7e7864290f9 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorConfirmService.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorConfirmService.java @@ -8,6 +8,7 @@ import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.ccd.sdk.type.YesOrNo; import uk.gov.hmcts.divorce.common.ccd.PageBuilder; import uk.gov.hmcts.divorce.common.service.ConfirmService; import uk.gov.hmcts.divorce.common.service.SubmitConfirmService; @@ -58,6 +59,12 @@ public void configure(ConfigBuilder configBuilder) { .label("respondentLabel", "Name of Respondent - ${applicant2FirstName} ${applicant2LastName}") .complex(CaseData::getApplication) .complex(Application::getSolicitorService) + .mandatory(SolicitorService::getFirstAttemptToServe) + .mandatory(SolicitorService::getDocumentsPreviouslyReturned, "solServiceFirstAttemptToServe=\"No\"") + .mandatory(SolicitorService::getDetailsOfPreviousService, "solServiceDocumentsPreviouslyReturned=\"Yes\"" + + " AND solServiceFirstAttemptToServe=\"No\"") + .mandatory(SolicitorService::getDatePreviousServiceReturned, "solServiceDocumentsPreviouslyReturned=\"Yes\"" + + " AND solServiceFirstAttemptToServe=\"No\"") .mandatory(SolicitorService::getDateOfService) .mandatory(SolicitorService::getDocumentsServed) .mandatory(SolicitorService::getOnWhomServed) @@ -103,6 +110,20 @@ public AboutToStartOrSubmitResponse aboutToSubmit(final CaseDet log.info("Due date after submit Task is {}", updateDetails.getData().getDueDate()); + final SolicitorService solicitorService = caseData.getApplication().getSolicitorService(); + + if (solicitorService.getFirstAttemptToServe() == YesOrNo.YES) { + solicitorService.setDocumentsPreviouslyReturned(null); + solicitorService.setDetailsOfPreviousService(null); + solicitorService.setDatePreviousServiceReturned(null); + } + + if (solicitorService.getFirstAttemptToServe() == YesOrNo.NO + && solicitorService.getDocumentsPreviouslyReturned() == YesOrNo.NO) { + solicitorService.setDetailsOfPreviousService(null); + solicitorService.setDatePreviousServiceReturned(null); + } + return AboutToStartOrSubmitResponse.builder() .data(updateDetails.getData()) .state(updateDetails.getState()) diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorGeneralApplication.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorGeneralApplication.java index 56314b5d7a9..853a9fb0bcb 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorGeneralApplication.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorGeneralApplication.java @@ -137,8 +137,8 @@ public AboutToStartOrSubmitResponse aboutToSubmit(final CaseDet } final PbaResponse response = paymentService.processPbaPayment( - data, details.getId(), + generalApplication.getGeneralApplicationFee().getServiceRequestReference(), invokingSolicitor, generalApplication.getGeneralApplicationFee().getPbaNumber(), generalApplication.getGeneralApplicationFee().getOrderSummary(), @@ -148,7 +148,12 @@ public AboutToStartOrSubmitResponse aboutToSubmit(final CaseDet final OrderSummary generalApplicationFeeOrderSummary = generalApplication.getGeneralApplicationFee().getOrderSummary(); if (response.getHttpStatus() == CREATED) { - data.updateCaseDataWithPaymentDetails(generalApplicationFeeOrderSummary, data, response.getPaymentReference()); + data.updateCaseDataWithPaymentDetails( + generalApplicationFeeOrderSummary, + data, + response.getPaymentReference(), + generalApplication.getGeneralApplicationFee().getServiceRequestReference() + ); } else { return AboutToStartOrSubmitResponse.builder() .data(details.getData()) diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorSubmitApplication.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorSubmitApplication.java index 558a12c6315..3e5abb0e3c1 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorSubmitApplication.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorSubmitApplication.java @@ -1,7 +1,7 @@ package uk.gov.hmcts.divorce.solicitor.event; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import uk.gov.hmcts.ccd.sdk.api.CCDConfig; import uk.gov.hmcts.ccd.sdk.api.CaseDetails; @@ -15,6 +15,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.payment.PaymentService; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.divorce.payment.model.PbaResponse; import uk.gov.hmcts.divorce.solicitor.event.page.HelpWithFeesPage; import uk.gov.hmcts.divorce.solicitor.event.page.SolConfirmJointApplication; @@ -42,24 +43,18 @@ import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; import static uk.gov.hmcts.divorce.divorcecase.validation.ApplicationValidation.validateReadyForPayment; -import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_ISSUE; -import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_DIVORCE; -import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_DIVORCE; @Slf4j @Component +@RequiredArgsConstructor public class SolicitorSubmitApplication implements CCDConfig { public static final String SOLICITOR_SUBMIT = "solicitor-submit-application"; - @Autowired - private PaymentService paymentService; - - @Autowired - private SolPayment solPayment; - - @Autowired - private SubmissionService submissionService; + private final PaymentService paymentService; + private final SolPayment solPayment; + private final SubmissionService submissionService; + private final PaymentSetupService paymentSetupService; @Override public void configure(final ConfigBuilder configBuilder) { @@ -81,13 +76,15 @@ public AboutToStartOrSubmitResponse aboutToStart(final CaseDeta log.info("{} about to start callback invoked for Case Id: {}", SOLICITOR_SUBMIT, details.getId()); log.info("Retrieving order summary"); - final OrderSummary orderSummary = paymentService.getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE, KEYWORD_DIVORCE); final CaseData caseData = details.getData(); - caseData.getApplication().setApplicationFeeOrderSummary(orderSummary); - caseData.getApplication().setSolApplicationFeeInPounds( + var application = caseData.getApplication(); + OrderSummary orderSummary = paymentSetupService.createApplicationFeeOrderSummary(caseData, details.getId()); + + application.setApplicationFeeOrderSummary(orderSummary); + application.setSolApplicationFeeInPounds( NumberFormat.getNumberInstance().format( - new BigDecimal(orderSummary.getPaymentTotal()).movePointLeft(2) + new BigDecimal(application.getApplicationFeeOrderSummary().getPaymentTotal()).movePointLeft(2) ) ); @@ -126,8 +123,8 @@ public AboutToStartOrSubmitResponse aboutToSubmit(final CaseDet final Optional pbaNumber = application.getPbaNumber(); if (pbaNumber.isPresent()) { final PbaResponse response = paymentService.processPbaPayment( - caseData, caseId, + caseData.getApplication().getApplicationFeeServiceRequestReference(), caseData.getApplicant1().getSolicitor(), pbaNumber.get(), caseData.getApplication().getApplicationFeeOrderSummary(), @@ -135,7 +132,12 @@ public AboutToStartOrSubmitResponse aboutToSubmit(final CaseDet ); if (response.getHttpStatus() == CREATED) { - caseData.updateCaseDataWithPaymentDetails(applicationFeeOrderSummary, caseData, response.getPaymentReference()); + caseData.updateCaseDataWithPaymentDetails( + applicationFeeOrderSummary, + caseData, + response.getPaymentReference(), + caseData.getApplication().getApplicationFeeServiceRequestReference() + ); } else { return AboutToStartOrSubmitResponse.builder() .data(details.getData()) diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationPaymentSummary.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationPaymentSummary.java index 8788ac6a51e..f4594d8ebb8 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationPaymentSummary.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationPaymentSummary.java @@ -17,6 +17,7 @@ public void addTo(final PageBuilder pageBuilder) { .complex(CaseData::getGeneralApplication) .complex(GeneralApplication::getGeneralApplicationFee) .mandatoryNoSummary(FeeDetails::getOrderSummary) + .readonlyNoSummary(FeeDetails::getServiceRequestReference) .done() .done() .label( diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationSelectFee.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationSelectFee.java index 30916975e71..4b6c359f4ee 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationSelectFee.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationSelectFee.java @@ -1,19 +1,24 @@ package uk.gov.hmcts.divorce.solicitor.event.page; -import org.springframework.beans.factory.annotation.Autowired; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; import uk.gov.hmcts.ccd.sdk.type.DynamicList; -import uk.gov.hmcts.ccd.sdk.type.OrderSummary; import uk.gov.hmcts.divorce.common.ccd.CcdPageConfiguration; import uk.gov.hmcts.divorce.common.ccd.PageBuilder; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.FeeDetails; import uk.gov.hmcts.divorce.divorcecase.model.GeneralApplication; +import uk.gov.hmcts.divorce.divorcecase.model.GeneralApplicationFee; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.payment.PaymentService; import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; +import uk.gov.hmcts.divorce.solicitor.service.CcdAccessService; +import static org.springframework.cloud.openfeign.security.OAuth2AccessTokenInterceptor.AUTHORIZATION; import static uk.gov.hmcts.divorce.divorcecase.model.GeneralApplicationFee.FEE0227; import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; @@ -21,13 +26,19 @@ import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; @Component +@RequiredArgsConstructor public class GeneralApplicationSelectFee implements CcdPageConfiguration { - @Autowired - private PaymentService paymentService; + private final PaymentService paymentService; - @Autowired - private PbaService pbaService; + private final PbaService pbaService; + + private final CcdAccessService ccdAccessService; + + private final HttpServletRequest httpServletRequest; + + @Value("${idam.client.redirect_uri}") + private String redirectUrl; @Override public void addTo(final PageBuilder pageBuilder) { @@ -36,7 +47,7 @@ public void addTo(final PageBuilder pageBuilder) { .pageLabel("Select Fee Type") .complex(CaseData::getGeneralApplication) .mandatory(GeneralApplication::getGeneralApplicationFeeType) - .done(); + .done(); } public AboutToStartOrSubmitResponse midEvent( @@ -45,21 +56,49 @@ public AboutToStartOrSubmitResponse midEvent( ) { final CaseData caseData = details.getData(); - - final String keyword = - FEE0227.getLabel().equals(caseData.getGeneralApplication().getGeneralApplicationFeeType().getLabel()) - ? KEYWORD_NOTICE - : KEYWORD_WITHOUT_NOTICE; - - OrderSummary orderSummary = paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, keyword); - caseData.getGeneralApplication().getGeneralApplicationFee().setOrderSummary(orderSummary); + var generalApplication = caseData.getGeneralApplication(); DynamicList pbaNumbersDynamicList = pbaService.populatePbaDynamicList(); - caseData.getGeneralApplication().getGeneralApplicationFee().setPbaNumbers(pbaNumbersDynamicList); + generalApplication.getGeneralApplicationFee().setPbaNumbers(pbaNumbersDynamicList); + + prepareCaseDataForGeneralApplicationPayment(details); return AboutToStartOrSubmitResponse.builder() .data(caseData) .build(); } + + private void prepareCaseDataForGeneralApplicationPayment(CaseDetails details) { + CaseData data = details.getData(); + GeneralApplicationFee feeType = data.getGeneralApplication().getGeneralApplicationFeeType(); + FeeDetails feeDetails = data.getGeneralApplication().getGeneralApplicationFee(); + + prepareOrderSummary(feeType, feeDetails); + prepareServiceRequest(details.getId(), data, feeDetails); + } + + private void prepareOrderSummary(GeneralApplicationFee feeType, FeeDetails feeDetails) { + String keyword = FEE0227.getLabel().equals(feeType.getLabel()) + ? KEYWORD_NOTICE + : KEYWORD_WITHOUT_NOTICE; + + var orderSummary = paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, keyword); + feeDetails.setOrderSummary(orderSummary); + } + + private void prepareServiceRequest(long caseId, CaseData data, FeeDetails feeDetails) { + final String serviceRequest = paymentService.createServiceRequestReference( + redirectUrl, caseId, responsiblePartyName(caseId, data), feeDetails.getOrderSummary() + ); + feeDetails.setServiceRequestReference(serviceRequest); + } + + private String responsiblePartyName(long caseId, CaseData data) { + String authHeader = httpServletRequest.getHeader(AUTHORIZATION); + + return ccdAccessService.isApplicant1(authHeader, caseId) + ? data.getApplicant1().getFullName() + : data.getApplicant2().getFullName(); + } } diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolFinalOrderPayment.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolFinalOrderPayment.java index 9c5080b958c..02632ff3f59 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolFinalOrderPayment.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolFinalOrderPayment.java @@ -1,8 +1,9 @@ package uk.gov.hmcts.divorce.solicitor.event.page; import feign.FeignException; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; @@ -12,16 +13,22 @@ import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; import java.util.List; @Component @Slf4j +@RequiredArgsConstructor public class SolFinalOrderPayment implements CcdPageConfiguration { - @Autowired - private PbaService pbaService; + private final PbaService pbaService; + + private final PaymentSetupService paymentSetupService; + + @Value("${idam.client.redirect_uri}") + private String redirectUrl; @Override public void addTo(final PageBuilder pageBuilder) { @@ -33,6 +40,11 @@ public void addTo(final PageBuilder pageBuilder) { "FOApp2SolAmountToPay", "Amount to pay: **ยฃ${applicant2SolFinalOrderFeeInPounds}**") .complex(CaseData::getFinalOrder) + .mandatory(FinalOrder::getApplicant2SolFinalOrderFeeOrderSummary) + .readonly( + FinalOrder::getApplicant2FinalOrderFeeServiceRequestReference, + "applicant2SolPaymentHowToPay=\"NEVER\"" + ) .mandatory(FinalOrder::getApplicant2SolPaymentHowToPay) .done(); } @@ -46,8 +58,9 @@ public AboutToStartOrSubmitResponse midEvent( log.info("Mid-event callback triggered for SolFinalOrderPayment page Case Id: {}", caseId); final CaseData caseData = details.getData(); + final FinalOrder finalOrder = caseData.getFinalOrder(); - if (!caseData.getFinalOrder().isSolicitorPaymentMethodPba()) { + if (!finalOrder.isSolicitorPaymentMethodPba()) { log.info("Payment method is not PBA for Case Id: {}", caseId); return AboutToStartOrSubmitResponse.builder() .data(caseData) @@ -58,7 +71,12 @@ public AboutToStartOrSubmitResponse midEvent( final DynamicList pbaNumbersDynamicList = pbaService.populatePbaDynamicList(); log.info("PBA Numbers {}, Case Id: {}", pbaNumbersDynamicList, caseId); - caseData.getFinalOrder().setFinalOrderPbaNumbers(pbaNumbersDynamicList); + finalOrder.setFinalOrderPbaNumbers(pbaNumbersDynamicList); + + String serviceRequest = paymentSetupService.createFinalOrderFeeServiceRequest( + caseData, caseId, redirectUrl, finalOrder.getApplicant2SolFinalOrderFeeOrderSummary() + ); + finalOrder.setApplicant2FinalOrderFeeServiceRequestReference(serviceRequest); return AboutToStartOrSubmitResponse.builder() .data(caseData) diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolFinalOrderPaymentSummary.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolFinalOrderPaymentSummary.java index 2d57e6505ea..471432f4d97 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolFinalOrderPaymentSummary.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolFinalOrderPaymentSummary.java @@ -18,6 +18,7 @@ public void addTo(final PageBuilder pageBuilder) { .pageLabel("Order Summary") .complex(CaseData::getFinalOrder) .mandatory(FinalOrder::getApplicant2SolFinalOrderFeeOrderSummary) + .readonlyNoSummary(FinalOrder::getApplicant2FinalOrderFeeServiceRequestReference, ALWAYS_HIDE) .complex(FinalOrder::getApplicant2SolFinalOrderHelpWithFees) .readonlyNoSummary(HelpWithFees::getReferenceNumber, ALWAYS_HIDE) .done() diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPayment.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPayment.java index f1fd4aea00a..8e7dd73f146 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPayment.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPayment.java @@ -1,8 +1,9 @@ package uk.gov.hmcts.divorce.solicitor.event.page; import feign.FeignException; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; @@ -12,16 +13,21 @@ import uk.gov.hmcts.divorce.divorcecase.model.Application; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; import java.util.List; @Component @Slf4j +@RequiredArgsConstructor public class SolPayment implements CcdPageConfiguration { - @Autowired - private PbaService pbaService; + private final PbaService pbaService; + private final PaymentSetupService paymentSetupService; + + @Value("${idam.client.redirect_uri}") + private String redirectUrl; @Override public void addTo(final PageBuilder pageBuilder) { @@ -34,7 +40,9 @@ public void addTo(final PageBuilder pageBuilder) { "LabelSolPaymentPara-1", "Amount to pay: **ยฃ${solApplicationFeeInPounds}**") .complex(CaseData::getApplication) - .mandatory(Application::getSolPaymentHowToPay) + .mandatory(Application::getApplicationFeeOrderSummary) + .readonly(Application::getApplicationFeeServiceRequestReference, "solPaymentHowToPay=\"NEVER\"") + .mandatory(Application::getSolPaymentHowToPay) .done(); } @@ -59,7 +67,13 @@ public AboutToStartOrSubmitResponse midEvent( final DynamicList pbaNumbersDynamicList = pbaService.populatePbaDynamicList(); log.info("PBA Numbers {}, Case Id: {}", pbaNumbersDynamicList, caseId); - caseData.getApplication().setPbaNumbers(pbaNumbersDynamicList); + Application application = caseData.getApplication(); + application.setPbaNumbers(pbaNumbersDynamicList); + + String serviceRequest = paymentSetupService.createApplicationFeeServiceRequest( + caseData, caseId, redirectUrl + ); + application.setApplicationFeeServiceRequestReference(serviceRequest); return AboutToStartOrSubmitResponse.builder() .data(caseData) diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPaymentSummary.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPaymentSummary.java index 92ea5b9a088..0c6ec07b795 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPaymentSummary.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPaymentSummary.java @@ -17,7 +17,8 @@ public void addTo(final PageBuilder pageBuilder) { .page("SolPaymentSummary") .pageLabel("Order Summary") .complex(CaseData::getApplication) - .mandatory(Application::getApplicationFeeOrderSummary) + .mandatory(Application::getApplicationFeeOrderSummary) + .readonlyNoSummary(Application::getApplicationFeeServiceRequestReference) .done() .label( "LabelSolPaySummaryFeeAccountPara-1", diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/service/SolicitorCreateApplicationService.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/service/SolicitorCreateApplicationService.java index f12b84d30ba..12c32394f70 100644 --- a/src/main/java/uk/gov/hmcts/divorce/solicitor/service/SolicitorCreateApplicationService.java +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/service/SolicitorCreateApplicationService.java @@ -14,6 +14,7 @@ import uk.gov.hmcts.divorce.solicitor.service.task.InitialiseSolicitorCreatedApplication; import uk.gov.hmcts.divorce.solicitor.service.task.SetApplicant1SolicitorAddress; import uk.gov.hmcts.divorce.solicitor.service.task.SetApplicantGender; +import uk.gov.hmcts.divorce.solicitor.service.task.SetApplicationFeeServiceRequest; import uk.gov.hmcts.divorce.solicitor.service.task.SolicitorCourtDetails; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; @@ -45,6 +46,9 @@ public class SolicitorCreateApplicationService { @Autowired private SetApplicantGender setApplicantGender; + @Autowired + private SetApplicationFeeServiceRequest setApplicationFeeServiceRequest; + public CaseDetails aboutToSubmit(final CaseDetails caseDetails) { return caseTasks( @@ -52,7 +56,8 @@ public CaseDetails aboutToSubmit(final CaseDetails { public static final String SYSTEM_NOTIFY_RESPONDENT_APPLY_FINAL_ORDER = "system-notify-respondent-apply-final-order"; - @Autowired - private NotificationDispatcher notificationDispatcher; + private final NotificationDispatcher notificationDispatcher; - @Autowired - private RespondentApplyForFinalOrderNotification respondentApplyForFinalOrderNotification; + private final RespondentApplyForFinalOrderNotification respondentApplyForFinalOrderNotification; + + private final PaymentSetupService paymentSetupService; + + @Value("${idam.client.redirect_uri}") + private String redirectUrl; @Override public void configure(final ConfigBuilder configBuilder) { @@ -59,8 +67,27 @@ public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetailsbuilder() .data(caseData) .build(); } + + private void prepareCaseDataForFinalOrderPbaPayment(CaseData data, long caseId) { + FinalOrder finalOrder = data.getFinalOrder(); + + OrderSummary orderSummary = paymentSetupService.createFinalOrderFeeOrderSummary(data, caseId); + + String serviceRequest = paymentSetupService.createFinalOrderFeeServiceRequest( + data, caseId, redirectUrl, orderSummary + ); + + finalOrder.setApplicant2FinalOrderFeeOrderSummary(orderSummary); + finalOrder.setApplicant2SolFinalOrderFeeOrderSummary(orderSummary); + finalOrder.setApplicant2FinalOrderFeeServiceRequestReference(serviceRequest); + } } diff --git a/src/main/java/uk/gov/hmcts/divorce/systemupdate/service/CcdSearchService.java b/src/main/java/uk/gov/hmcts/divorce/systemupdate/service/CcdSearchService.java index 5c81bc91648..2e1f1fc106a 100644 --- a/src/main/java/uk/gov/hmcts/divorce/systemupdate/service/CcdSearchService.java +++ b/src/main/java/uk/gov/hmcts/divorce/systemupdate/service/CcdSearchService.java @@ -544,5 +544,53 @@ public Map> updateCountsByStateAndLastModifiedDate( return aggregatedResults; } -} + public List searchForOldDivorceCasesWithQuery(final BoolQueryBuilder query, + final User user, + final String serviceAuth) { + + final Set allCaseDetails = new HashSet<>(); + int from = 0; + int totalResults = pageSize; + + try { + while (totalResults == pageSize && allCaseDetails.size() <= totalMaxResults) { + final SearchResult searchResult = searchOldDivorceCasesWithQuery(from, pageSize, query, user, serviceAuth); + + log.info("Search result old divorce cases is {}", searchResult.toString()); + final List pageResults = searchResult.getCases(); + allCaseDetails.addAll(pageResults); + + from += pageSize; + totalResults = pageResults.size(); + } + } catch (final FeignException e) { + final String message = String.format( + "Failed to complete search for Old Divorce Cases with query %s", query.toString()); + log.info(message, e); + throw new CcdSearchCaseException(message, e); + } + log.info("old cases query returned {} for query {}", allCaseDetails.size(), query.toString()); + return allCaseDetails.stream().toList(); + } + + private SearchResult searchOldDivorceCasesWithQuery(final int from, + final int size, + final BoolQueryBuilder query, + final User user, + final String serviceAuth) { + + final SearchSourceBuilder sourceBuilder = SearchSourceBuilder + .searchSource() + .sort(DUE_DATE, ASC) + .query(query) + .from(from) + .size(size); + + return coreCaseDataApi.searchCases( + user.getAuthToken(), + serviceAuth, + "DIVORCE", + sourceBuilder.toString()); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/systemupdate/service/task/GenerateApplyForFinalOrderDocument.java b/src/main/java/uk/gov/hmcts/divorce/systemupdate/service/task/GenerateApplyForFinalOrderDocument.java index 79a05468bad..d1f38b42474 100644 --- a/src/main/java/uk/gov/hmcts/divorce/systemupdate/service/task/GenerateApplyForFinalOrderDocument.java +++ b/src/main/java/uk/gov/hmcts/divorce/systemupdate/service/task/GenerateApplyForFinalOrderDocument.java @@ -47,7 +47,7 @@ public void generateApplyForFinalOrder(final CaseData caseData, DocumentType finalOrderDocType = isApplicant1 ? FINAL_ORDER_CAN_APPLY_APP1 : FINAL_ORDER_CAN_APPLY_APP2; final Map templateVars = commonContent.templateContentCanApplyForCoOrFo(caseData, caseId, applicant, partner, now.toLocalDate()); - templateVars.put(FINAL_ORDER_OVERDUE_DATE, caseData.getFinalOrder().getDateFinalOrderEligibleFrom().plusMonths(12) + templateVars.put(FINAL_ORDER_OVERDUE_DATE, caseData.getConditionalOrder().getGrantedDate().plusMonths(12) .format(getDateTimeFormatterForPreferredLanguage(applicant.getLanguagePreference()))); caseDataDocumentService.renderDocumentAndUpdateCaseData( diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 4b3850eabb4..1165bbf910b 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -59,6 +59,7 @@ uk: smartSurvey: https://www.smartsurvey.co.uk/s/NFD_Feedback/?pageurl=email templates: english: + REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT: 'e15fa45a-f100-4b07-b1ca-ad96c633a30d' SAVE_SIGN_OUT: 'bfa39c3e-0077-4c66-a1fe-497cded9e73c' OUTSTANDING_ACTIONS: 'b077b50a-6fe2-48bd-aac5-2da5deceac86' APPLICATION_SUBMITTED: '3b819c22-09e8-45a9-bbcb-e4ab1dbf96fd' @@ -112,17 +113,17 @@ uk: SOLICITOR_CLARIFICATION_SUBMITTED: 'bd003622-b92b-4552-9489-34823d948c3b' CITIZEN_CLARIFICATION_SUBMITTED: '703c1cd3-b87b-4288-8a22-10b201b743b5' CITIZEN_PARTNER_CLARIFICATION_SUBMITTED: 'f920cda2-bb38-4050-972a-a9fabccc31d4' - SOLICITOR_CO_REFUSED_SOLE_JOINT: 'b1590f77-3cdb-4f80-abae-4f4f13f06e26' + SOLICITOR_CO_REFUSED_SOLE_JOINT: '24a593b6-81eb-41ff-bccc-8b9796354b7c' CITIZEN_APPLICATION_WITHDRAWN: '952beacb-0e73-4fde-94d4-60cfcbec2d63' - CITIZEN_APPLY_FOR_CONDITIONAL_ORDER: '5c080949-2ce1-420b-bbf9-a51272dd983b' - CITIZEN_APPLIED_FOR_CONDITIONAL_ORDER: '1845c608-0642-48e9-b91d-839b53ac9b9e' - APPLICANT1_SOLICITOR_APPLIED_FOR_CONDITIONAL_ORDER: 'ee403b64-1df6-4630-a233-881e85be0395' - JOINT_APPLIED_FOR_CONDITIONAL_ORDER: '0d8c59f1-aea3-4245-8bca-eb0d5558f0fd' - JOINT_SOLICITOR_APPLIED_FOR_CO_OR_FO_ORDER: 'aba8cda5-779e-4943-8fe6-13b7f6a03cf3' - JOINT_PARTNER_APPLIED_FOR_CONDITIONAL_ORDER: '32778ad1-0315-41cc-927b-2c008be35435' - SOLICITOR_CONDITIONAL_ORDER_PRONOUNCED: '4789bd02-0d9b-40cc-ab08-4790b0480323' - JOINT_BOTH_APPLIED_FOR_CONDITIONAL_ORDER: '6caef42a-91d7-40e5-b440-cbb48daf1415' - CITIZEN_CONDITIONAL_ORDER_PRONOUNCED: 'adbb43fa-b7d2-4184-8bd9-4b5632f57b7a' + CITIZEN_APPLY_FOR_CONDITIONAL_ORDER: 'ac38984c-d05b-4036-8963-672c03b6ed75' + CITIZEN_APPLIED_FOR_CONDITIONAL_ORDER: '406322e1-7bfe-405b-b2b8-d249f759fadf' + APPLICANT1_SOLICITOR_APPLIED_FOR_CONDITIONAL_ORDER: '9d8f6c26-07d5-4da2-97e1-c126a9d30f59' + JOINT_APPLIED_FOR_CONDITIONAL_ORDER: 'b729d25a-b578-4a1f-8207-dea3d2ac877d' + JOINT_SOLICITOR_APPLIED_FOR_CO_OR_FO_ORDER: '96dac743-6d10-4122-adc7-7b29e75ebcc0' + JOINT_PARTNER_APPLIED_FOR_CONDITIONAL_ORDER: '4fafe287-cdd5-432e-aa24-631bf449ccec' + SOLICITOR_CONDITIONAL_ORDER_PRONOUNCED: 'd2d0eff2-d719-48b5-9019-cee81e2afa57' + JOINT_BOTH_APPLIED_FOR_CONDITIONAL_ORDER: '29ae2a99-d163-4d04-8ebc-7850acb1aa28' + CITIZEN_CONDITIONAL_ORDER_PRONOUNCED: '4240b63b-5d5a-4cf9-a578-e0a5d19e1d21' CITIZEN_CONDITIONAL_ORDER_REFUSED: '2247b12e-e560-42a8-af40-dd192ab17f74' CITIZEN_CONDITIONAL_ORDER_REFUSED_FOR_AMENDMENT: '556b79db-acb8-4cb6-b25b-bd0fd6d60927' CITIZEN_CONDITIONAL_ORDER_ENTITLEMENT_GRANTED: 'affe507c-7c08-4129-9d15-fa7e9a0783c9' @@ -135,60 +136,70 @@ uk: SOLE_AOS_SUBMITTED_APPLICANT_1_SOLICITOR: 'b714670c-1d7a-4983-a52d-00229d0bbf80' SOLE_AOS_SUBMITTED_RESPONDENT_SOLICITOR: '6041e6eb-9026-438e-b357-a9606a258368' SOLICITOR_APPLICANT1_DISPUTE_ANSWER_RECEIVED: '2cef1a0c-a42e-4e67-b0cf-08c043ffce31' - DISPUTE_FORM_OVERDUE: '325a1603-499c-4a96-af50-92931d4b68ff' - BAILIFF_SERVICE_UNSUCCESSFUL: 'c40abaaf-dc6a-4dc0-815c-a411822d988b' - BAILIFF_SERVICE_SUCCESSFUL: '61332826-713e-49a7-ba37-563cb9261c65' - APPLICANT_SWITCH_TO_SOLE: '29c1cdb1-bf0c-4f27-bb52-b99f040e8f53' - JOINT_APPLICATION_ENDED: 'f7929730-decd-41fd-b596-de71699f6c7e' - PARTNER_SWITCHED_TO_SOLE_CO: 'd5f8728e-3eba-4bc5-b3db-80f6685a4941' - APPLICANT_APPLY_FOR_FINAL_ORDER: 'e2262285-0134-467c-a92b-80f53f61fb80' - SOLE_APPLIED_FOR_FINAL_ORDER: '8d383c07-c934-4b33-b803-9f138e1ff36c' + DISPUTE_FORM_OVERDUE: 'b03f0e3b-b160-4621-afbb-909511696395' + BAILIFF_SERVICE_UNSUCCESSFUL: 'fc274f76-2df7-42b4-aeac-d6867dd2f85d' + BAILIFF_SERVICE_SUCCESSFUL: '62f0669b-9fc4-4898-a763-e3e6cbd8c632' + APPLICANT_SWITCH_TO_SOLE: '44c2cd90-6543-4265-a33c-602af3a14830' + JOINT_APPLICATION_ENDED: 'b402deba-a7f3-4330-800a-dbfed8f2b3ba' + PARTNER_SWITCHED_TO_SOLE_CO: '3bdca528-32b0-4d74-bf6a-360c344de4b0' + APPLICANT_APPLY_FOR_FINAL_ORDER: '7d11db75-f985-4f16-b3e5-753a6ceba32b' + SOLE_APPLIED_FOR_FINAL_ORDER: '96f78eae-afd3-4eec-b354-4e3772e658dc' SOLE_RESPONDENT_APPLIED_FOR_FINAL_ORDER: '13e14f62-cff2-46cc-bdf6-2221b2e8425f' - PARTNER_HAS_SWITCHED_TO_SOLE_FINAL_ORDER: 'a59f92c1-026b-4da2-ae64-7ba93bb7fa29' - SOLICITOR_PARTNER_HAS_SWITCHED_TO_SOLE_FINAL_ORDER: 'dc1a3d51-3e93-4f22-b268-ab0f86676f1f' - JOINT_ONE_APPLICANT_APPLIED_FOR_FINAL_ORDER: '6db9a294-1613-4926-867a-4302cb0ad6dc' - JOINT_APPLICANT_OTHER_PARTY_APPLIED_FOR_FINAL_ORDER: 'b51e2cc6-5db0-4ef6-956f-8ec5b1c9c8df' + PARTNER_HAS_SWITCHED_TO_SOLE_FINAL_ORDER: '5331ed65-560f-4a9b-966e-2f96ca0b9ae7' + SOLICITOR_PARTNER_HAS_SWITCHED_TO_SOLE_FINAL_ORDER: 'cc5c8d92-75ae-4f07-a7ec-4a9b759cf4c3' + JOINT_ONE_APPLICANT_APPLIED_FOR_FINAL_ORDER: 'f09c935a-0d3c-4b26-a881-6035d5687e09' + JOINT_APPLICANT_OTHER_PARTY_APPLIED_FOR_FINAL_ORDER: '9950fb96-9953-4353-a394-e3adca9e6e55' RESPONDENT_APPLY_FOR_FINAL_ORDER: '341966ab-e39e-4a09-a81e-a8cd0c260dd0' - RESPONDENT_SOLICITOR_APPLY_FOR_FINAL_ORDER: 'f7f00a8a-5fbc-4868-ae67-acbbb890d3de' - POST_INFORMATION_TO_COURT: '03d15445-c91d-44b0-a8b0-a7755864381e' - GENERAL_APPLICATION_RECEIVED: '97a51838-789b-4c18-b4b8-9b4ff32e831f' - GENERAL_APPLICATION_SUCCESSFUL: 'b22ad794-a768-4b1a-86a8-3bf5c713ea2b' - SERVICE_APPLICATION_REJECTED: 'db7f376b-ab61-4316-b59f-83fe570b8f23' - SERVICE_APPLICATION_GRANTED: '2b7cc04e-2c21-4c06-9ae4-e7109a3efde4' + RESPONDENT_SOLICITOR_APPLY_FOR_FINAL_ORDER: '023ad723-3152-4f8e-bafe-6f64f33d6510' + POST_INFORMATION_TO_COURT: 'ee9e3661-4b9a-459a-9e22-3aac41b5edd8' + GENERAL_APPLICATION_RECEIVED: '80f4ee70-1269-46e1-9460-51786a980fc1' + GENERAL_APPLICATION_SUCCESSFUL: '67af4496-44f5-41fa-a6ea-408017516cb8' + SERVICE_APPLICATION_REJECTED: 'bbdfd770-4f43-40dc-a3af-5dffdacdf0d3' + SERVICE_APPLICATION_GRANTED: '5e545da8-8c68-4a21-99e5-5c44a51b2085' SERVICE_APPLICATION_REJECTED_SOLICITOR: 'ba1c3408-4413-4f18-9685-6aacf161f293' SERVICE_APPLICATION_GRANTED_SOLICITOR: '88cce629-73e8-43df-83a1-468b00201cd4' - RESPONDENT_SOLICITOR_HAS_NOT_RESPONDED: 'e872dbc4-e1f9-454c-830c-3e86ccb03251' - APPLY_FOR_FINAL_ORDER_SOLICITOR: '91c049a6-d2ee-4682-bea2-8a74fa1b75a6' - JOINT_APPLY_FOR_CONDITIONAL_FINAL_ORDER_SOLICITOR: '00c04fec-dc80-4ec9-82f1-f6eab43d656c' - JOINT_APPLICANT_CAN_SWITCH_TO_SOLE: '4fa1278a-8c8c-49da-b1ee-9d289f5e2436' - JOINT_APPLICANT_SOLICITOR_CAN_SWITCH_TO_SOLE: '979f2885-7856-45d9-81a5-3c6b2979f906' - JOINT_SOLICITOR_OTHER_PARTY_APPLIED_FOR_CONDITIONAL_ORDER: '34116ad2-305f-4db8-852f-9d7652c2ae41' - JOINT_APPLICANT_SOLICITOR_CAN_SWITCH_TO_SOLE_FINAL_ORDER: '35dde12d-cc37-4ccd-b712-788cba8590ce' - SOLICITOR_FINAL_ORDER_GRANTED: '0936ca51-ab0d-4e97-b95b-9d306bbe74f9' + RESPONDENT_SOLICITOR_HAS_NOT_RESPONDED: '074fe108-7ed8-4f03-93c2-4e53d35281ea' + APPLY_FOR_FINAL_ORDER_SOLICITOR: '51ab0a9d-2718-4487-95fb-eef393ebc51d' + JOINT_APPLY_FOR_CONDITIONAL_FINAL_ORDER_SOLICITOR: 'a8df6647-72d3-4e0a-8f6d-16188346caa4' + JOINT_APPLICANT_CAN_SWITCH_TO_SOLE: 'a846e3d9-0748-4f55-b503-4e7f0baa9153' + JOINT_APPLICANT_SOLICITOR_CAN_SWITCH_TO_SOLE: '50b6f04c-73ba-49a6-9bf5-340d636c098e' + JOINT_SOLICITOR_OTHER_PARTY_APPLIED_FOR_CONDITIONAL_ORDER: 'ac89ec11-6795-44ea-997e-c49dc156ab31' + JOINT_APPLICANT_SOLICITOR_CAN_SWITCH_TO_SOLE_FINAL_ORDER: '71ef1ab8-5f7f-4568-b01b-bc7904a04e31' + SOLICITOR_FINAL_ORDER_GRANTED: '291e0b67-8bbb-4070-8d1f-142073895eb9' APPLICANTS_FINAL_ORDER_GRANTED: '174da37d-df36-4407-b9ee-eae1f94b4f6b' - SOLICITOR_SOLE_APPLICATION_FOR_CONDITIONAL_ORDER: '72a3f5f8-598f-420b-8041-3f6541e48779' - SOLICITOR_OTHER_PARTY_MADE_SOLE_APPLICATION_FOR_CONDITIONAL_ORDER: 'd3d73525-b455-456e-843a-0638554f08ff' - JOINT_SOLICITOR_OTHER_PARTY_APPLIED_FOR_FINAL_ORDER: 'c47d63ac-9c9f-404e-9d10-a78e3c994f3d' - JOINT_BOTH_APPLICANTS_APPLIED_FOR_FINAL_ORDER: 'd80c83e1-143a-4b4b-819d-628c4de62ccc' - OTHER_APPLICANT_NOT_APPLIED_FOR_FINAL_ORDER: '086715a5-6a1b-4d70-8a30-0fc50b62535e' - INTEND_TO_SWITCH_TO_SOLE_FO: 'b734941e-37d8-47b8-aef8-6c51412bcbc1' - PARTNER_INTENDS_TO_SWITCH_TO_SOLE_FO: '360465c3-2da5-403a-8163-5487cc214aa7' - OTHER_APPLICANT_INTENDS_TO_SWITCH_TO_SOLE_FO_SOLICITOR: '8de7c96b-8582-4df4-aa74-89b33ad8454d' - OTHER_APPLICANT_INTENDS_TO_SWITCH_TO_SOLE_FO_CITIZEN: '360465c3-2da5-403a-8163-5487cc214aa7' - APPLICANT_SOLICITOR_SWITCH_TO_SOLE_AFTER_INTENTION_FO: 'e414d40e-de04-4aa9-b85c-fc939417f0fd' + SOLICITOR_SOLE_APPLICATION_FOR_CONDITIONAL_ORDER: '2739cd5a-35ec-4cb6-86a4-38477c2984ad' + SOLICITOR_OTHER_PARTY_MADE_SOLE_APPLICATION_FOR_CONDITIONAL_ORDER: '26032624-eb14-4012-8ca4-673d915f5c02' + JOINT_SOLICITOR_OTHER_PARTY_APPLIED_FOR_FINAL_ORDER: 'd4cdc219-d046-4662-8111-e72074864515' + JOINT_BOTH_APPLICANTS_APPLIED_FOR_FINAL_ORDER: 'b496963d-2ce0-4345-a5f6-13015dd162d0' + OTHER_APPLICANT_NOT_APPLIED_FOR_FINAL_ORDER: '7a97eeb5-7b58-4e86-a61e-57cb21ddb1e1' + INTEND_TO_SWITCH_TO_SOLE_FO: 'e6b98a64-ddcd-48cf-a9b5-774006c8ee78' + PARTNER_INTENDS_TO_SWITCH_TO_SOLE_FO: '7e5e6d89-62a6-4e9b-b044-ab7b27186a35' + OTHER_APPLICANT_INTENDS_TO_SWITCH_TO_SOLE_FO_SOLICITOR: '10a45ed1-9b42-4a09-9ebc-ff8005d82a13' + OTHER_APPLICANT_INTENDS_TO_SWITCH_TO_SOLE_FO_CITIZEN: '7e5e6d89-62a6-4e9b-b044-ab7b27186a35' + APPLICANT_SOLICITOR_SWITCH_TO_SOLE_AFTER_INTENTION_FO: '2486536c-481d-4dd3-8934-91faa5aa37f5' FINAL_ORDER_GRANTED_SWITCH_TO_SOLE_APPLICANT: '174da37d-df36-4407-b9ee-eae1f94b4f6b' FINAL_ORDER_GRANTED_SWITCH_TO_SOLE_RESPONDENT: 'abf7a206-5818-4a52-9e66-7bbc3a2471a0' - APPLICANT_SWITCH_TO_SOLE_AFTER_INTENTION_FO: '0e70b66c-dba8-4b7d-8f2e-9f1d05614310' - APPLICANT_2_SOLICITOR_APPLIED_FOR_FINAL_ORDER: '5c9afec8-9cb3-4688-aad9-602800e0f514' + APPLICANT_SWITCH_TO_SOLE_AFTER_INTENTION_FO: '3f3298fd-21fe-45aa-81ea-8a49155a9f69' + APPLICANT_2_SOLICITOR_APPLIED_FOR_FINAL_ORDER: '6c71903e-4186-4d48-96ee-18f6cf7af0a9' NOC_CITIZEN_TO_SOL_EMAIL_CITIZEN: 'd4eb2375-2d61-4a08-88b0-457430056c18' NOC_TO_SOLS_EMAIL_NEW_SOL: '77995dff-d5ad-48ee-a3a8-676dd1a878e3' NOC_TO_SOLS_EMAIL_OLD_SOL: '98c2a7a4-79c4-410c-909d-10db0108fe8d' + REQUEST_FOR_INFORMATION_SOLE: 'e8bab44a-c06b-4538-bab9-ef9bbb0dab11' + REQUEST_FOR_INFORMATION_SOLICITOR: 'ec9f7b0c-391f-4883-95a0-c496f8f29c00' + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY: '583bda64-88e2-47e4-8cf7-df2c0b09eea0' + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD: 'e1533d11-8a9e-439a-85ba-568b3feae41d' + REQUEST_FOR_INFORMATION_OTHER: 'eb4acb56-09c3-4898-a5df-2fa5c47a2434' + REQUEST_FOR_INFORMATION_JOINT: 'bd19eafc-d265-41ba-ae8b-68e458390a09' + REQUEST_FOR_INFORMATION_RESPONSE: 'c6aaa2cc-892b-4f43-a8e6-3b51723faf47' + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER: '313f12d3-3a8b-43a8-bbc6-bc0ee18b9ee1' + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS: '552f0eed-8df6-4282-bd1f-c5f6b4db9c42' NFD_APP1_SOLICITOR_APPLIED_FOR_FINAL_ORDER: '1b1f508d-2d91-447c-836b-d9ec0da004b1' AUTOMATED_DAILY_REPORT: '641cb2b6-5895-46c6-886c-8696096e1a43' REINVITE_CITIZEN_TO_CASE: '4b746cbd-e65d-4686-bc57-d5771c857c67' CITIZEN_EMAIL_UPDATED: 'a2168463-ca67-4b0b-a511-a76c25576fac' welsh: - SAVE_SIGN_OUT: '6e9526c5-9380-4bf1-879a-b8e401e39a6f' + REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT: '6fed53fd-8558-4f9a-8804-8b1004221083' + SAVE_SIGN_OUT: '01202704-dfb7-48a3-91bd-1bbbbd4ee511' OUTSTANDING_ACTIONS: '474d5778-8886-4df7-b0ea-006ca28eab8e' APPLICATION_SUBMITTED: 'b7a78a0c-b163-4293-86d0-41d773275a8f' SOLICITOR_JOINT_APPLICATION_SUBMITTED: '12795f00-f656-4df2-bd8b-0921ebd18080' #TODO: Welsh translation needed @@ -197,10 +208,10 @@ uk: JOINT_APPLICANT2_ANSWERS_SENT_FOR_REVIEW_APPLICANT1_REPRESENTED: '2cba9d42-5ef5-4ba1-8daf-334a465f0515' #TODO: Welsh translation needed JOINT_APPLICANT2_REMINDER_WHEN_APPLICANT1_REPRESENTED: '3ce780ec-ee6f-4b9f-95e1-88ac4cc143f1' #TODO: Welsh translation needed JOINT_APPLICANT2_ANSWERS_SENT_FOR_REVIEW_SOLICITOR: 'fe99afcf-475d-482b-a4fa-4be0f83b148c' #TODO: Welsh translation needed - JOINT_APPLICANT2_REQUEST_CHANGES: '92191d4c-6078-4859-ad4d-8320d49b262d' + JOINT_APPLICANT2_REQUEST_CHANGES: '6d5a79eb-d8d3-4351-896d-d82da5492b84' SOLICITOR_APPLICANT2_REQUESTED_CHANGES: 'dd1eb1e4-47a5-4eb6-bdb5-bbfe32ee0317' #TODO: Welsh translation needed APPLICANT2_APPLICANT1_SOLICITOR_REPRESENTED_REQUESTED_CHANGES : '368360ce-1cc0-4e85-ae8c-49d8006e414a' #TODO: Welsh translation needed - JOINT_APPLICANT1_APPLICANT2_APPROVED: '7d8db5c5-4b9c-4221-8fed-8c542d45a6af' + JOINT_APPLICANT1_APPLICANT2_APPROVED: 'e16d7ee9-c44a-472c-93d2-c934de728221' JOINT_APPLICANT1_APPLICANT2_APPROVED_WITHOUT_HWF: 'daa34b25-755b-4d62-bca5-4c9d27a887bd' JOINT_APPLICANT2_APPLICANT2_APPROVED: '2ae37675-64d3-4b91-9194-a6ec8ae9e37b' JOINT_APPLICANT2_APPLICANT2_APPROVED_SOLICITOR: '63691685-75ff-4a00-b7bd-398a22a06d17' @@ -241,18 +252,18 @@ uk: SOLICITOR_CLARIFICATION_SUBMITTED: 'bd003622-b92b-4552-9489-34823d948c3b' CITIZEN_CLARIFICATION_SUBMITTED: 'd0c2d95a-c7c5-428a-920d-8571ef975382' CITIZEN_PARTNER_CLARIFICATION_SUBMITTED: 'ea5f5c5a-cebb-4919-87fd-15722934a7d6' - SOLICITOR_CO_REFUSED_SOLE_JOINT: 'b1590f77-3cdb-4f80-abae-4f4f13f06e26' + SOLICITOR_CO_REFUSED_SOLE_JOINT: '24a593b6-81eb-41ff-bccc-8b9796354b7c' CITIZEN_APPLICATION_WITHDRAWN: '471ea2f0-9be6-40bf-93e5-148f66fb9b56' - SOLICITOR_CONDITIONAL_ORDER_PRONOUNCED: '4789bd02-0d9b-40cc-ab08-4790b0480323' - CITIZEN_APPLY_FOR_CONDITIONAL_ORDER: '7a6298f7-70e7-4fd6-8c6d-95c2b96e21d4' - CITIZEN_APPLIED_FOR_CONDITIONAL_ORDER: 'a72d5f64-ac56-49c8-99f2-047a96d1fd87' - APPLICANT1_SOLICITOR_APPLIED_FOR_CONDITIONAL_ORDER: 'ee403b64-1df6-4630-a233-881e85be0395' - JOINT_APPLIED_FOR_CONDITIONAL_ORDER: '7e5978a9-4ceb-45ca-9a41-b3f2822a0824' - JOINT_PARTNER_APPLIED_FOR_CONDITIONAL_ORDER: '444fbd4b-5c40-4e42-99a0-761cd07933a6' - JOINT_BOTH_APPLIED_FOR_CONDITIONAL_ORDER: 'd6b94713-02d8-4851-916f-6bb927a68b00' + SOLICITOR_CONDITIONAL_ORDER_PRONOUNCED: 'd2d0eff2-d719-48b5-9019-cee81e2afa57' #TODO: Update Welsh translation + CITIZEN_APPLY_FOR_CONDITIONAL_ORDER: '7804c030-8d6d-4464-8420-de6105e61692' + CITIZEN_APPLIED_FOR_CONDITIONAL_ORDER: '87f8ca2a-5304-4dce-9acd-e51fd29ca460' + APPLICANT1_SOLICITOR_APPLIED_FOR_CONDITIONAL_ORDER: '9d8f6c26-07d5-4da2-97e1-c126a9d30f59' #TODO: Update Welsh translation + JOINT_APPLIED_FOR_CONDITIONAL_ORDER: '3d2d2f28-faa6-464a-8330-439ad4fcfffd' + JOINT_PARTNER_APPLIED_FOR_CONDITIONAL_ORDER: '29007f5f-82f5-4dbd-8d9b-f4cb6446843b' + JOINT_BOTH_APPLIED_FOR_CONDITIONAL_ORDER: 'ca3f20d9-d64e-4875-bc7b-e6e0f76ba286' JOINT_SOLICITOR_APPLIED_FOR_CONDITIONAL_ORDER: 'aba8cda5-779e-4943-8fe6-13b7f6a03cf3' - JOINT_SOLICITOR_APPLIED_FOR_CO_OR_FO_ORDER: 'aba8cda5-779e-4943-8fe6-13b7f6a03cf3' - CITIZEN_CONDITIONAL_ORDER_PRONOUNCED: 'e7704d69-cf1d-4015-88ba-b998bab99008' + JOINT_SOLICITOR_APPLIED_FOR_CO_OR_FO_ORDER: '96dac743-6d10-4122-adc7-7b29e75ebcc0' #TODO: Update Welsh translation + CITIZEN_CONDITIONAL_ORDER_PRONOUNCED: '9669c85c-45d2-4d8a-a6e0-48531ad2e171' CITIZEN_CONDITIONAL_ORDER_REFUSED: '3241af91-b9fd-457f-8124-bce924a1b1f0' CITIZEN_CONDITIONAL_ORDER_REFUSED_FOR_AMENDMENT: '9673c9c0-8ade-4cec-82a2-a54cb8afdbc3' CITIZEN_CONDITIONAL_ORDER_ENTITLEMENT_GRANTED: '4c8118ab-9a71-4e73-ab5a-740dd9fe678c' @@ -265,52 +276,62 @@ uk: SOLE_AOS_SUBMITTED_APPLICANT_1_SOLICITOR: 'b714670c-1d7a-4983-a52d-00229d0bbf80' SOLE_AOS_SUBMITTED_RESPONDENT_SOLICITOR: '6041e6eb-9026-438e-b357-a9606a258368' SOLICITOR_APPLICANT1_DISPUTE_ANSWER_RECEIVED: '2cef1a0c-a42e-4e67-b0cf-08c043ffce31' - DISPUTE_FORM_OVERDUE: '705321e1-adda-4dc8-b46d-e5519418821a' - APPLICANT_SWITCH_TO_SOLE: '8f022f2d-0e8e-4e89-b944-7cfe74a68056' - JOINT_APPLICATION_ENDED: '27e89abf-c793-4891-97ce-f54cd9ac0803' - APPLICANT_APPLY_FOR_FINAL_ORDER: '9a89d8c0-55a5-4534-a9c5-77ff4ed956ad' - PARTNER_SWITCHED_TO_SOLE_CO: '9b6ffba8-fc6f-4e57-a660-17765e8c9bd0' + DISPUTE_FORM_OVERDUE: '39591502-d276-44e2-8799-f0ac0a343cdc' + APPLICANT_SWITCH_TO_SOLE: '04177ebd-ad6d-41ac-9cfc-a3f49ddba18c' + JOINT_APPLICATION_ENDED: '8da821e8-2acf-4454-9bfc-c42625b3cec0' + APPLICANT_APPLY_FOR_FINAL_ORDER: '3a943efc-a7cc-4c57-ae64-f5d61809ac9d' + PARTNER_SWITCHED_TO_SOLE_CO: 'c7043294-5c73-4d06-b9e0-f76aa042f5b5' RESPONDENT_APPLY_FOR_FINAL_ORDER: 'b43171d2-9f81-40a4-9662-46e3e750cd45' - RESPONDENT_SOLICITOR_APPLY_FOR_FINAL_ORDER: 'f7f00a8a-5fbc-4868-ae67-acbbb890d3de' - POST_INFORMATION_TO_COURT: 'c407bd28-fd6c-40d6-b320-dd4acb135ec0' - GENERAL_APPLICATION_RECEIVED: '1f4cd3f9-1092-4703-826c-0685131e6dff' - GENERAL_APPLICATION_SUCCESSFUL: 'b22ad794-a768-4b1a-86a8-3bf5c713ea2b' - SERVICE_APPLICATION_REJECTED: '0b0b9648-3551-45f0-ac23-ebfc15c01633' - SERVICE_APPLICATION_GRANTED: 'b95dc623-7508-4310-9f99-6057a6242c2a' + RESPONDENT_SOLICITOR_APPLY_FOR_FINAL_ORDER: '023ad723-3152-4f8e-bafe-6f64f33d6510' # TODO: Welsh translation + POST_INFORMATION_TO_COURT: 'c6152a5f-c62c-460c-ac4a-6456910cd6a8' + GENERAL_APPLICATION_RECEIVED: '4040e4b4-3354-4002-8831-78e0b1fd24aa' + GENERAL_APPLICATION_SUCCESSFUL: '67af4496-44f5-41fa-a6ea-408017516cb8' # TODO: Welsh translation + SERVICE_APPLICATION_REJECTED: '635fa93c-fdf2-4251-af8b-fd1fdc1238b2' + SERVICE_APPLICATION_GRANTED: '92eb5bda-7921-4d06-a852-ba33bbbc3df5' SERVICE_APPLICATION_REJECTED_SOLICITOR: 'c8675e71-ff1e-4dff-9780-4db3c3d12bcf' SERVICE_APPLICATION_GRANTED_SOLICITOR: '860accd8-e153-4df2-a076-f5322b65448e' - RESPONDENT_SOLICITOR_HAS_NOT_RESPONDED: 'e872dbc4-e1f9-454c-830c-3e86ccb03251' - BAILIFF_SERVICE_UNSUCCESSFUL: 'af3c1d06-0485-4443-b544-005348f15c4e' - BAILIFF_SERVICE_SUCCESSFUL: 'ec30a7ef-8e51-45ae-a018-c5ff2622db40' - APPLY_FOR_FINAL_ORDER_SOLICITOR: '70dd90fe-e702-4daa-8f69-4ab41518d9a6' - JOINT_APPLY_FOR_CONDITIONAL_FINAL_ORDER_SOLICITOR: '00c04fec-dc80-4ec9-82f1-f6eab43d656c' - JOINT_APPLICANT_CAN_SWITCH_TO_SOLE: '508ec880-2585-411f-bc2b-4316217fb5d8' - JOINT_APPLICANT_SOLICITOR_CAN_SWITCH_TO_SOLE: '979f2885-7856-45d9-81a5-3c6b2979f906' #TODO: Welsh translation needed - SOLICITOR_FINAL_ORDER_GRANTED: '0936ca51-ab0d-4e97-b95b-9d306bbe74f9' #TODO - Welsh translation needed - JOINT_APPLICANT_SOLICITOR_CAN_SWITCH_TO_SOLE_FINAL_ORDER: '35dde12d-cc37-4ccd-b712-788cba8590ce' #TODO - Welsh translation needed + RESPONDENT_SOLICITOR_HAS_NOT_RESPONDED: '074fe108-7ed8-4f03-93c2-4e53d35281ea' # TODO: Welsh translation + BAILIFF_SERVICE_UNSUCCESSFUL: '246fa327-b75d-412f-8f13-e8467715f61f' + BAILIFF_SERVICE_SUCCESSFUL: 'b51433ea-244e-44b5-b9bf-e09676b1fc3f' + APPLY_FOR_FINAL_ORDER_SOLICITOR: '51ab0a9d-2718-4487-95fb-eef393ebc51d' # TODO: Welsh translation + JOINT_APPLY_FOR_CONDITIONAL_FINAL_ORDER_SOLICITOR: 'a8df6647-72d3-4e0a-8f6d-16188346caa4' #TODO: Welsh translation needed + JOINT_APPLICANT_CAN_SWITCH_TO_SOLE: 'e4f584a8-c0e3-4d5d-bd6d-d5b9c085b7bb' + JOINT_APPLICANT_SOLICITOR_CAN_SWITCH_TO_SOLE: '50b6f04c-73ba-49a6-9bf5-340d636c098e' #TODO: Welsh translation needed + SOLICITOR_FINAL_ORDER_GRANTED: '291e0b67-8bbb-4070-8d1f-142073895eb9' #TODO - Welsh translation needed + JOINT_APPLICANT_SOLICITOR_CAN_SWITCH_TO_SOLE_FINAL_ORDER: '71ef1ab8-5f7f-4568-b01b-bc7904a04e31' #TODO - Welsh translation needed APPLICANTS_FINAL_ORDER_GRANTED: '5f98efbd-87a5-46a7-b8dc-c53645cee8b5' - SOLICITOR_SOLE_APPLICATION_FOR_CONDITIONAL_ORDER: '72a3f5f8-598f-420b-8041-3f6541e48779' #TODO - Welsh translation needed - SOLICITOR_OTHER_PARTY_MADE_SOLE_APPLICATION_FOR_CONDITIONAL_ORDER: 'd3d73525-b455-456e-843a-0638554f08ff' #TODO - Welsh translation needed - JOINT_SOLICITOR_OTHER_PARTY_APPLIED_FOR_FINAL_ORDER: 'c47d63ac-9c9f-404e-9d10-a78e3c994f3d' #TODO - Welsh translation needed - OTHER_APPLICANT_NOT_APPLIED_FOR_FINAL_ORDER: '26aaa755-2cb1-422c-be80-08ff15f9be27' - JOINT_BOTH_APPLICANTS_APPLIED_FOR_FINAL_ORDER: '488c2dd6-d38f-4869-b716-0743be52c6cb' - JOINT_ONE_APPLICANT_APPLIED_FOR_FINAL_ORDER: '7cddc0bf-5b2f-4b4a-b4b6-28cfc22d1cd5' - JOINT_APPLICANT_OTHER_PARTY_APPLIED_FOR_FINAL_ORDER: '83a57815-f1a2-4ac8-92b4-5597b9bd5b38' - SOLE_APPLIED_FOR_FINAL_ORDER: '7e64d645-8973-4bac-95ca-49e54c423655' + SOLICITOR_SOLE_APPLICATION_FOR_CONDITIONAL_ORDER: '2739cd5a-35ec-4cb6-86a4-38477c2984ad' #TODO - Welsh translation needed + SOLICITOR_OTHER_PARTY_MADE_SOLE_APPLICATION_FOR_CONDITIONAL_ORDER: '26032624-eb14-4012-8ca4-673d915f5c02' #TODO - Welsh translation needed + JOINT_SOLICITOR_OTHER_PARTY_APPLIED_FOR_FINAL_ORDER: 'd4cdc219-d046-4662-8111-e72074864515' #TODO - Welsh translation needed + OTHER_APPLICANT_NOT_APPLIED_FOR_FINAL_ORDER: '6fb2ef5a-01bf-4899-891f-4fe6d17cb7b0' + JOINT_BOTH_APPLICANTS_APPLIED_FOR_FINAL_ORDER: '0f7f606e-61d2-441d-9a3a-1625ebb1d9cd' + JOINT_ONE_APPLICANT_APPLIED_FOR_FINAL_ORDER: 'f8dea177-bff5-4dde-80be-90fcd9782eda' + JOINT_APPLICANT_OTHER_PARTY_APPLIED_FOR_FINAL_ORDER: 'd0a9423f-d473-40e4-865e-32a4e3b25792' + SOLE_APPLIED_FOR_FINAL_ORDER: 'cc7e91c7-28aa-4098-a6f0-aafb0424908b' SOLE_RESPONDENT_APPLIED_FOR_FINAL_ORDER: 'bca5ccab-7012-4c66-8744-d4a3767ef880' PARTNER_HAS_SWITCHED_TO_SOLE_FINAL_ORDER: '24d6bb85-82de-49a5-ae97-aec15ecc0ee8' - INTEND_TO_SWITCH_TO_SOLE_FO: '2b44b8b2-8a2e-4029-974e-bd14458b1f0b' - PARTNER_INTENDS_TO_SWITCH_TO_SOLE_FO: 'd5e65a52-3132-422d-ac8b-0d285650d475' + INTEND_TO_SWITCH_TO_SOLE_FO: '0eb5deb5-851d-4ced-b793-0d836e8f9101' + PARTNER_INTENDS_TO_SWITCH_TO_SOLE_FO: 'af46da5d-57bf-4e77-891f-2929d3fef037' SOLICITOR_PARTNER_HAS_SWITCHED_TO_SOLE_FINAL_ORDER: 'dc1a3d51-3e93-4f22-b268-ab0f86676f1f' #TODO - Welsh translation needed - OTHER_APPLICANT_INTENDS_TO_SWITCH_TO_SOLE_FO_SOLICITOR: '8de7c96b-8582-4df4-aa74-89b33ad8454d' #TODO - Welsh translation needed - OTHER_APPLICANT_INTENDS_TO_SWITCH_TO_SOLE_FO_CITIZEN: '360465c3-2da5-403a-8163-5487cc214aa7' #TODO - Welsh translation needed - APPLICANT_SOLICITOR_SWITCH_TO_SOLE_AFTER_INTENTION_FO: 'e414d40e-de04-4aa9-b85c-fc939417f0fd' #TODO - Welsh translation needed + OTHER_APPLICANT_INTENDS_TO_SWITCH_TO_SOLE_FO_SOLICITOR: '10a45ed1-9b42-4a09-9ebc-ff8005d82a13' #TODO - Welsh translation needed + OTHER_APPLICANT_INTENDS_TO_SWITCH_TO_SOLE_FO_CITIZEN: '7e5e6d89-62a6-4e9b-b044-ab7b27186a35' #TODO - Welsh translation needed + APPLICANT_SOLICITOR_SWITCH_TO_SOLE_AFTER_INTENTION_FO: '2486536c-481d-4dd3-8934-91faa5aa37f5' #TODO - Welsh translation needed FINAL_ORDER_GRANTED_SWITCH_TO_SOLE_APPLICANT: '174da37d-df36-4407-b9ee-eae1f94b4f6b' #TODO - Welsh translation needed FINAL_ORDER_GRANTED_SWITCH_TO_SOLE_RESPONDENT: 'abf7a206-5818-4a52-9e66-7bbc3a2471a0' #TODO - Welsh translation needed - APPLICANT_SWITCH_TO_SOLE_AFTER_INTENTION_FO: '7d86d77b-125e-4ea6-ac09-d9aa0b755714' + APPLICANT_SWITCH_TO_SOLE_AFTER_INTENTION_FO: '9baabc53-5776-49b0-8f86-d3fd3b5d4205' + APPLICANT_2_SOLICITOR_APPLIED_FOR_FINAL_ORDER: '6c71903e-4186-4d48-96ee-18f6cf7af0a9' #TODO - Welsh translation needed NOC_CITIZEN_TO_SOL_EMAIL_CITIZEN: 'b3786fd2-4a72-4c5f-b4ed-6635647b1520' NOC_TO_SOLS_EMAIL_NEW_SOL: '5fb73af1-33e6-4d0c-ab39-6c32263cbf43' NOC_TO_SOLS_EMAIL_OLD_SOL: '977d2857-dcf1-4bc2-97fb-f9b1e13af907' + REQUEST_FOR_INFORMATION_SOLE: 'b28844a6-b59e-4f85-96d4-333eb7b948d0' + REQUEST_FOR_INFORMATION_SOLICITOR: '40da03ef-21a7-4c26-94bb-1fd6f728e60b' + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY: '4572bc59-fa7a-4360-96eb-4c6e344b0db6' + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD: 'ca6a2993-2464-4d54-920f-8bb699444266' + REQUEST_FOR_INFORMATION_OTHER: 'fa165b2a-b049-482e-8bd7-5667b06c59e1' + REQUEST_FOR_INFORMATION_JOINT: '7339db7d-4f0a-4791-93ef-263af65bf676' + REQUEST_FOR_INFORMATION_RESPONSE: '80a8b4e6-1f4b-4827-9366-233a7fa3f83b' + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER: '605e5c07-add5-4412-81e2-f11b556ea73b' + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS: '107dfd07-7745-4bb8-abc6-11c9ba19e691' NFD_APP1_SOLICITOR_APPLIED_FOR_FINAL_ORDER: 'cfeb62ee-7f2c-4612-bf87-35205ccb26b5' REINVITE_CITIZEN_TO_CASE: '1c682066-7088-4a98-be05-d1ea6cf26f84' CITIZEN_EMAIL_UPDATED: '981cdc17-4dc2-4561-a02b-df38f40970e5' @@ -331,7 +352,9 @@ idam: id: 'divorce' secret: ${IDAM_CLIENT_SECRET:123456} redirect_uri: ${IDAM_API_REDIRECT_URL:http://localhost:3001/oauth2/callback} - + divorce: + username: ${IDAM_OLD_DIVORCE_USERNAME:dummysystemupdate@test.com} + password: ${IDAM_OLD_DIVORCE_PASSWORD:dummy} fee: api: baseUrl: ${FEE_API_URL:http://fees-register-api-aat.service.core-compute-aat.internal} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerFindMatchesTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerFindMatchesTest.java index ab88d03b7b5..0b05e3fab39 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerFindMatchesTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerFindMatchesTest.java @@ -3,6 +3,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -14,6 +17,7 @@ import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.divorce.divorcecase.model.Application; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseDataOldDivorce; import uk.gov.hmcts.divorce.divorcecase.model.CaseMatch; import uk.gov.hmcts.divorce.divorcecase.model.MarriageDetails; import uk.gov.hmcts.divorce.divorcecase.model.State; @@ -25,11 +29,15 @@ import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -43,6 +51,12 @@ class CaseworkerFindMatchesTest { public static final String NAME_ONE = "John Doe"; public static final String NAME_TWO = "Jane Doe"; + public static final String POSTCODE_1 = "AB1 2CD"; + public static final String POSTCODE_2 = "EF3 4GH"; + public static final String PETITIONER_TOWN = "PetitionerTown"; + public static final String RESPONDENT_TOWN = "RespondentTown"; + public static final String EXPECTED = "12345"; + public static final String MARRIAGE_DATE = "2000-01-01"; @Mock private CcdSearchService ccdSearchService; @@ -93,9 +107,10 @@ void shouldReturnMatchesOnAboutToStart() { AboutToStartOrSubmitResponse response = caseworkerFindMatches.aboutToStart(caseDetails); assertThat(response.getData().getCaseMatches()) - .as("Should return exactly 1 match on about-to-start event") - .hasSize(1); + .as("Should return exactly 2 match on about-to-start event") + .hasSize(2); verify(ccdSearchService).searchForAllCasesWithQuery(any(), any(), any()); + verify(ccdSearchService).searchForOldDivorceCasesWithQuery(any(), any(), any()); } @@ -110,7 +125,7 @@ void shouldNotAddDuplicateReferenceMatches() { assertThat(response.getData().getCaseMatches()) .as("Should not add duplicate matches but include distinct match") - .hasSize(1); + .hasSize(2); } @@ -134,6 +149,28 @@ void testAddMatches() { .hasSize(1); } + @Test + void shouldSetCaseMatchesToNullWhenNewMatchesIsEmpty() { + CaseData caseData = buildEmptyCaseData(); + + ListValue existingMatch = ListValue.builder() + .id("1") + .value(CaseMatch.builder() + .caseLink(CaseLink.builder().caseReference("123456").build()) + .build()) + .build(); + caseData.getCaseMatches().add(existingMatch); + + List newMatches = new ArrayList<>(); + + caseworkerFindMatches.setToNewMatches(caseData, newMatches); + + assertThat(caseData.getCaseMatches()) + .as("Should set case matches to null when new matches list is empty") + .isNull(); + } + + private CaseDetails buildCaseDetails() { CaseData caseData = buildEmptyCaseData(); CaseDetails caseDetails = new CaseDetails<>(); @@ -173,12 +210,58 @@ private CaseDetails buildCaseDetailsWithExistingMatch() { } private void mockDependencies(CaseData caseData) { + String petitionerName = caseData.getApplication().getMarriageDetails().getApplicant1Name(); + String respondentName = caseData.getApplication().getMarriageDetails().getApplicant2Name(); + String marriageDate = caseData.getApplication().getMarriageDetails().getDate().toString(); + + CaseDataOldDivorce oldCaseData = mockOldDivorceCaseData(petitionerName, respondentName, marriageDate); + when(idamService.retrieveSystemUpdateUserDetails()).thenReturn(mock(User.class)); when(authTokenGenerator.generate()).thenReturn("serviceAuthToken"); - when(ccdSearchService.searchForAllCasesWithQuery(any(), any(), any())).thenReturn(mockCaseMatchDetails()); - when(objectMapper.convertValue(any(Map.class), eq(CaseData.class))).thenReturn(caseData); + + when(ccdSearchService.searchForAllCasesWithQuery(any(), any(), any())) + .thenReturn(mockCaseMatchDetails()); + when(objectMapper.convertValue(any(Map.class), eq(CaseData.class))) + .thenReturn(caseData); + when(objectMapper.convertValue(any(Map.class), eq(CaseDataOldDivorce.class))) + .thenReturn(oldCaseData); + when(ccdSearchService.searchForOldDivorceCasesWithQuery(any(), any(), any())) + .thenReturn(mockCaseMatchTwoDetails(oldCaseData)); + } + + private CaseDataOldDivorce mockOldDivorceCaseData(String petitionerName, String respondentName, String marriageDate) { + CaseDataOldDivorce oldCaseData = new CaseDataOldDivorce(); + oldCaseData.setD8MarriagePetitionerName(petitionerName); + oldCaseData.setD8MarriageRespondentName(respondentName); + oldCaseData.setD8MarriageDate(marriageDate); + oldCaseData.setD8PetitionerPostCode("OLD1 1AA"); + oldCaseData.setD8RespondentPostCode("OLD2 2BB"); + oldCaseData.setD8PetitionerPostTown("Old Petitioner Town"); + oldCaseData.setD8RespondentPostTown("Old Respondent Town"); + return oldCaseData; + } + + private List mockCaseMatchTwoDetails(CaseDataOldDivorce oldCaseData) { + Map mockDataMap = new HashMap<>(); + mockDataMap.put("D8MarriagePetitionerName", oldCaseData.getD8MarriagePetitionerName()); + mockDataMap.put("D8MarriageRespondentName", oldCaseData.getD8MarriageRespondentName()); + mockDataMap.put("D8MarriageDate", oldCaseData.getD8MarriageDate()); + mockDataMap.put("D8PetitionerPostCode", oldCaseData.getD8PetitionerPostCode()); + mockDataMap.put("D8RespondentPostCode", oldCaseData.getD8RespondentPostCode()); + mockDataMap.put("D8PetitionerPostTown", oldCaseData.getD8PetitionerPostTown()); + mockDataMap.put("D8RespondentPostTown", oldCaseData.getD8RespondentPostTown()); + + uk.gov.hmcts.reform.ccd.client.model.CaseDetails mockCaseDetails = + uk.gov.hmcts.reform.ccd.client.model.CaseDetails.builder() + .id(67894L) + .state(State.Holding.name()) + .data(mockDataMap) + .build(); + + return List.of(mockCaseDetails); } + private List mockCaseMatchDetails() { uk.gov.hmcts.reform.ccd.client.model.CaseDetails mockCaseDetails = uk.gov.hmcts.reform.ccd.client.model.CaseDetails.builder() .build(); @@ -195,4 +278,94 @@ private Map mockCaseData() { mockCaseData.put("marriageDate", "2000-01-01"); return mockCaseData; } + + @Test + void shouldTransformOldCaseToMatchingCasesList() { + uk.gov.hmcts.reform.ccd.client.model.CaseDetails caseDetail = mock(uk.gov.hmcts.reform.ccd.client.model.CaseDetails.class); + Map mockData = Map.of( + "D8MarriagePetitionerName", NAME_ONE, + "D8MarriageRespondentName", NAME_TWO, + "D8MarriageDate", "2000-01-01", + "D8PetitionerPostCode", "AB1 2CD", + "D8RespondentPostCode", "EF3 4GH", + "D8PetitionerPostTown", "PetitionerTown", + "D8RespondentPostTown", "RespondentTown" + ); + when(caseDetail.getData()).thenReturn(mockData); + when(caseDetail.getId()).thenReturn(12345L); + + CaseDataOldDivorce caseDataOldDivorce = new CaseDataOldDivorce(); + caseDataOldDivorce.setD8MarriagePetitionerName(NAME_ONE); + caseDataOldDivorce.setD8MarriageRespondentName(NAME_TWO); + caseDataOldDivorce.setD8MarriageDate(MARRIAGE_DATE); + caseDataOldDivorce.setD8PetitionerPostCode(POSTCODE_1); + caseDataOldDivorce.setD8RespondentPostCode(POSTCODE_2); + caseDataOldDivorce.setD8PetitionerPostTown(PETITIONER_TOWN); + caseDataOldDivorce.setD8RespondentPostTown(RESPONDENT_TOWN); + + when(objectMapper.convertValue(mockData, CaseDataOldDivorce.class)).thenReturn(caseDataOldDivorce); + + List caseMatches = caseworkerFindMatches.transformOldCaseToMatchingCasesList(Collections.singletonList(caseDetail)); + + assertThat(caseMatches).hasSize(1); + CaseMatch caseMatch = caseMatches.get(0); + + assertThat(caseMatch.getApplicant1Name()).isEqualTo(NAME_ONE); + assertThat(caseMatch.getApplicant2Name()).isEqualTo(NAME_TWO); + assertThat(caseMatch.getDate()).isEqualTo(LocalDate.of(2000, 1, 1)); + assertThat(caseMatch.getApplicant1Postcode()).isEqualTo(POSTCODE_1); + assertThat(caseMatch.getApplicant2Postcode()).isEqualTo(POSTCODE_2); + assertThat(caseMatch.getApplicant1Town()).isEqualTo(PETITIONER_TOWN); + assertThat(caseMatch.getApplicant2Town()).isEqualTo(RESPONDENT_TOWN); + assertThat(caseMatch.getCaseLink().getCaseReference()).isEqualTo(EXPECTED); + } + + // Test input trying to match all possibles encountered in prod + static List provideTestInputs() { + return List.of( + "Willy Wonka ", // Trailing space + "Willy Wonka (name changed by Deed Poll)", // Parentheses with trailing space + "Willy Wonka.=", // Illegal characters at the end + "Willy Wonka***", // Trailing asterisks + "Willy Wonka!", // Exclamation mark at the end + "Willy Wonka (Mr. Ritchie)", // Parentheses at the end + "Willy Wonka.=", // Mix of illegal characters + "_Willy Wonka_", // Underscores around the name + "Willy Wonka / Mr. Ritchie " + ); + } + + @ParameterizedTest + @MethodSource("provideTestInputs") + void testRegexPatternAfterClean(String input) { + // Assert that all inputs match to "Willy Wonka" + String[] expectedNames = new String[]{"Willy", "Wonka", "Mr", "Ritchie"}; + String[] cleanedName = caseworkerFindMatches.normalizeAndSplit(input); + for (int i = 0; i < cleanedName.length; i++) { + String regexPattern = caseworkerFindMatches.generateRegexPattern(cleanedName[i]); + Pattern pattern = Pattern.compile(regexPattern); + // Verify that the regex matches the cleaned name + String expectedName = expectedNames[i]; + assertTrue(pattern.matcher(expectedName).matches(), cleanedName[i]); + } + } + + @ParameterizedTest + @CsvSource({ + "'John Doe / Jane Smith', 4", // 1 slash -> split into 4 parts + "'Alice ; Bob, Charlieรฉ', 3", // 2 illegal chars ( ; ,) -> split into 3 parts + "'John Doe; ** Jane Smith', 4", // 1 slash -> split into 4 parts + "'part1 (part2) part3 / part4 : part5', 5", // 4 illegal chars ( ( ) / : ) -> split into 5 parts + "'namewithoutslash', 1", // No illegal chars -> 1 part + "'hello!world', 2", // 1 illegal char (!) -> split into 2 parts + "'Aliรงia,,,Bob', 2", // 2 commas -> split into 2 parts + "'first %last ( name changed by deed poll )', 2", // "name changed by deed poll" is removed -> 2 parts + "'hello world//example', 3", // 1 double slash (//) -> split into 3 parts + "'hello world [hi world]', 4" // 2 illegal chars -> split into 4 parts + }) + void testNormalizeAndSplit(String input, int expectedSplits) { + String[] result = caseworkerFindMatches.normalizeAndSplit(input); + + assertEquals(expectedSplits, result.length); + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerOfflineDocumentVerifiedTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerOfflineDocumentVerifiedTest.java index 45637c1ab93..a3873c66804 100644 --- a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerOfflineDocumentVerifiedTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerOfflineDocumentVerifiedTest.java @@ -1,5 +1,6 @@ package uk.gov.hmcts.divorce.caseworker.event; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -17,6 +18,8 @@ import uk.gov.hmcts.ccd.sdk.type.ListValue; import uk.gov.hmcts.ccd.sdk.type.ScannedDocument; import uk.gov.hmcts.ccd.sdk.type.ScannedDocumentType; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponseNotification; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; import uk.gov.hmcts.divorce.citizen.notification.conditionalorder.Applicant1AppliedForConditionalOrderNotification; import uk.gov.hmcts.divorce.common.service.GeneralReferralService; import uk.gov.hmcts.divorce.common.service.HoldingPeriodService; @@ -31,12 +34,18 @@ import uk.gov.hmcts.divorce.divorcecase.model.ConditionalOrder; import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.OfflineWhoApplying; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseSoleParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.document.model.DivorceDocument; import uk.gov.hmcts.divorce.idam.IdamService; import uk.gov.hmcts.divorce.idam.User; import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; import uk.gov.hmcts.divorce.systemupdate.service.CcdUpdateService; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.idam.client.models.UserInfo; @@ -50,6 +59,7 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -57,6 +67,8 @@ import static uk.gov.hmcts.ccd.sdk.type.ScannedDocumentType.FORM; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerOfflineDocumentVerified.CASEWORKER_OFFLINE_DOCUMENT_VERIFIED; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerOfflineDocumentVerified.NO_REQUEST_FOR_INFORMATION_ERROR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerOfflineDocumentVerified.NO_REQUEST_FOR_INFORMATION_POST_ISSUE_ERROR; import static uk.gov.hmcts.divorce.common.event.SwitchedToSoleCo.SWITCH_TO_SOLE_CO; import static uk.gov.hmcts.divorce.common.event.SwitchedToSoleFinalOrderOffline.SWITCH_TO_SOLE_FO_OFFLINE; import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.JOINT_APPLICATION; @@ -65,6 +77,7 @@ import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.CO_D84; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.FO_D36; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.OTHER; +import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.RFI_RESPONSE; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.ScannedDocumentSubtypes.D10; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.ScannedDocumentSubtypes.D36; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.ScannedDocumentSubtypes.D84; @@ -72,13 +85,17 @@ import static uk.gov.hmcts.divorce.divorcecase.model.HowToRespondApplication.DISPUTE_DIVORCE; import static uk.gov.hmcts.divorce.divorcecase.model.OfflineApplicationType.JOINT; import static uk.gov.hmcts.divorce.divorcecase.model.OfflineApplicationType.SWITCH_TO_SOLE; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingAmendedApplication; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingLegalAdvisorReferral; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPronouncement; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; import static uk.gov.hmcts.divorce.divorcecase.model.State.FinalOrderRequested; import static uk.gov.hmcts.divorce.divorcecase.model.State.Holding; import static uk.gov.hmcts.divorce.divorcecase.model.State.InBulkActionCase; import static uk.gov.hmcts.divorce.divorcecase.model.State.JSAwaitingLA; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; import static uk.gov.hmcts.divorce.divorcecase.model.State.RespondentFinalOrderRequested; import static uk.gov.hmcts.divorce.divorcecase.model.SupplementaryCaseType.JUDICIAL_SEPARATION; import static uk.gov.hmcts.divorce.document.model.DocumentType.CONDITIONAL_ORDER_APPLICATION; @@ -93,6 +110,12 @@ import static uk.gov.hmcts.divorce.testutil.TestConstants.CASEWORKER_USER_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_AUTH_TOKEN; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addOfflineResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addRfiResponseDocumentToCaseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.buildOfflineDraft; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationCaseDetails; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.setSendNotificationFlagOnLatestOfflineResponse; @ExtendWith(MockitoExtension.class) class CaseworkerOfflineDocumentVerifiedTest { @@ -109,6 +132,12 @@ class CaseworkerOfflineDocumentVerifiedTest { @Mock private Applicant1AppliedForConditionalOrderNotification app1AppliedForConditionalOrderNotification; + @Mock + private CitizenRequestForInformationResponseNotification citizenRequestForInformationResponseNotification; + + @Mock + private CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + @Mock private CcdUpdateService ccdUpdateService; @@ -1111,7 +1140,7 @@ void shouldNotReclassifyDocumentAndSetScannedSubtypeReceivedToNullIfScannedDocIs } @ParameterizedTest - @CsvSource({"D10,AOS_D10", "D84,CO_D84", "D36,FO_D36", ","}) + @CsvSource({"D10,AOS_D10", "D84,CO_D84", "D36,FO_D36", "RFIR,RFI_RESPONSE", ","}) void shouldSetTypeOfDocumentAttachedFromScannedDocumentSubtype(final ScannedDocumentSubtypes subtype, final OfflineDocumentReceived typeOfDocumentAttached) { final CaseData caseData = CaseData.builder() @@ -1129,7 +1158,7 @@ void shouldSetTypeOfDocumentAttachedFromScannedDocumentSubtype(final ScannedDocu } @ParameterizedTest - @CsvSource({"AOS_D10", "CO_D84", "FO_D36"}) + @CsvSource({"AOS_D10", "CO_D84", "FO_D36", "RFI_RESPONSE"}) void shouldNotOverwriteTypeOfDocumentAttachedIfScannedDocumentSubtypeNull(final OfflineDocumentReceived typeOfDocumentAttached) { final CaseData caseData = CaseData.builder() .documents(CaseDocuments.builder() @@ -1374,4 +1403,281 @@ void shouldNotSendOfflineNotificationsForConditionalOrderWhenJudicialSeparation( verifyNoInteractions(notificationDispatcher); } + + @Test + void shouldThrowErrorIfNoRFIOnCaseWhenRequestForInformationResponseSelected() { + CaseData caseData = caseData(); + caseData.getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + AboutToStartOrSubmitResponse response = caseworkerOfflineDocumentVerified.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_REQUEST_FOR_INFORMATION_ERROR); + } + + @Test + void shouldThrowErrorIfCaseHasBeenIssuedWhenRequestForInformationResponseSelected() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(); + CaseData caseData = caseDetails.getData(); + caseData.getApplication().setIssueDate(LocalDate.now()); + caseData.getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + AboutToStartOrSubmitResponse response = caseworkerOfflineDocumentVerified.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_REQUEST_FOR_INFORMATION_POST_ISSUE_ERROR); + } + + @Test + void shouldClearDefaultRequestForInformationOfflineResponseDraft() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(); + buildOfflineDraft(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.APPLICANT, true, false, true, false); + addRfiResponseDocumentToCaseData(caseDetails.getData(), clock); + + AboutToStartOrSubmitResponse response = caseworkerOfflineDocumentVerified.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationOfflineResponseDraft()) + .isEqualTo(new RequestForInformationOfflineResponseDraft()); + } + + @Test + void shouldSetStateToRequestedInformationSubmittedWhenAllDocumentsUploaded() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(); + buildOfflineDraft(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.APPLICANT, true, false, true, false); + addRfiResponseDocumentToCaseData(caseDetails.getData(), clock); + + AboutToStartOrSubmitResponse response = caseworkerOfflineDocumentVerified.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldSetStateToAwaitingRequestedInformationWhenAllDocumentsNotUploaded() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(); + buildOfflineDraft(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.APPLICANT, true, false, false, false); + addRfiResponseDocumentToCaseData(caseDetails.getData(), clock); + + AboutToStartOrSubmitResponse response = caseworkerOfflineDocumentVerified.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(AwaitingRequestedInformation); + } + + @Test + void shouldNotSendNotificationsOnWhenSendNotificationFlagSetToNo() { + CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationSoleParties.APPLICANT, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.APPLICANT); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), false); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + verifyNoInteractions(notificationDispatcher); + } + + @Test + void shouldNotSendNotificationsOnSoleCaseIfRfiSentToOther() { + CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationSoleParties.OTHER, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.OTHER); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + verifyNoInteractions(notificationDispatcher); + } + + @Test + void shouldNotSendNotificationsOnJointCaseIfRfiSentToOther() { + CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.OTHER, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseJointParties.OTHER); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + verifyNoInteractions(notificationDispatcher); + } + + @Test + void shouldSendNotificationToRespondingPartyOnlyOnSoleCase() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.APPLICANT); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldSendNotificationToRespondingPartyOnlyOnJointCaseWhenRfiNotSentToBothParties() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT1, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseJointParties.APPLICANT1); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldReturnErrorWhenSendNotificationToRespondingPartyFails() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.APPLICANT); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldSendNotificationToRespondingPartyAndPartnerOnJointCaseWhenRfiSentToBothParties() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(BOTH, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseJointParties.APPLICANT1); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verify(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorWhenSendNotificationToRespondingPartyFailsOnJointCaseWhenRfiSentToBothParties() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(BOTH, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseJointParties.APPLICANT1); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + verify(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorWhenSendNotificationToRespondingPartyPartnerFailsOnJointCaseWhenRfiSentToBothParties() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(BOTH, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseJointParties.APPLICANT1); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + } + + @Test + void shouldReturnErrorsWhenSendNotificationsFailOnJointCaseWhenRfiSentToBothParties() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(BOTH, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseJointParties.APPLICANT1); + setSendNotificationFlagOnLatestOfflineResponse(caseDetails.getData(), true); + caseDetails.getData().getDocuments().setTypeOfDocumentAttached(RFI_RESPONSE); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + caseworkerOfflineDocumentVerified.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenCreateServiceRequestTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenCreateServiceRequestTest.java index c8588208ac0..a15ac07d1af 100644 --- a/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenCreateServiceRequestTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenCreateServiceRequestTest.java @@ -8,26 +8,27 @@ import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.type.OrderSummary; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static uk.gov.hmcts.divorce.citizen.event.CitizenCreateServiceRequest.CITIZEN_CREATE_SERVICE_REQUEST; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingFinalOrderPayment; import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPayment; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_PAYMENT_CALLBACK_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; @ExtendWith(MockitoExtension.class) class CitizenCreateServiceRequestTest { @Mock - private CitizenSubmitApplication citizenSubmitApplication; - - @Mock - private RespondentApplyForFinalOrder respondentApplyForFinalOrder; + private PaymentSetupService paymentSetupService; @InjectMocks private CitizenCreateServiceRequest citizenCreateServiceRequest; @@ -47,29 +48,40 @@ void shouldAddConfigurationToConfigBuilder() { public void shouldSetServiceRequestForApplicationPaymentIfCaseIsInAwaitingPayment() { final CaseDetails caseDetails = new CaseDetails<>(); final CaseData caseData = new CaseData(); + caseData.setCitizenPaymentCallbackUrl(TEST_PAYMENT_CALLBACK_URL); long caseId = TEST_CASE_ID; caseDetails.setState(AwaitingPayment); caseDetails.setData(caseData); caseDetails.setId(caseId); - citizenCreateServiceRequest.aboutToSubmit(caseDetails, caseDetails); + when(paymentSetupService.createApplicationFeeServiceRequest(caseData, caseId, TEST_PAYMENT_CALLBACK_URL)) + .thenReturn(TEST_SERVICE_REFERENCE); + + var response = citizenCreateServiceRequest.aboutToSubmit(caseDetails, caseDetails); - verify(citizenSubmitApplication).setServiceRequestReferenceForApplicationPayment(caseData, caseId); + assertThat(response.getData().getApplication().getApplicationFeeServiceRequestReference()) + .isEqualTo(TEST_SERVICE_REFERENCE); } @Test public void shouldSetServiceRequestForFinalOrderPaymentIfCaseIsInAwaitingFinalOrderPayment() { final CaseDetails caseDetails = new CaseDetails<>(); final CaseData caseData = new CaseData(); - long caseId = TEST_CASE_ID; + final OrderSummary orderSummary = OrderSummary.builder().build(); caseDetails.setState(AwaitingFinalOrderPayment); + caseData.getFinalOrder().setApplicant2FinalOrderFeeOrderSummary(orderSummary); + caseData.setCitizenPaymentCallbackUrl(TEST_PAYMENT_CALLBACK_URL); caseDetails.setData(caseData); - caseDetails.setId(caseId); + caseDetails.setId(TEST_CASE_ID); + + when(paymentSetupService.createFinalOrderFeeServiceRequest(caseData, TEST_CASE_ID, TEST_PAYMENT_CALLBACK_URL, orderSummary)) + .thenReturn(TEST_SERVICE_REFERENCE); - citizenCreateServiceRequest.aboutToSubmit(caseDetails, caseDetails); + var response = citizenCreateServiceRequest.aboutToSubmit(caseDetails, caseDetails); - verify(respondentApplyForFinalOrder).setServiceRequestReferenceForFinalOrderPayment(caseData, caseId); + assertThat(response.getData().getFinalOrder().getApplicant2FinalOrderFeeServiceRequestReference()) + .isEqualTo(TEST_SERVICE_REFERENCE); } } diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndCloseTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndCloseTest.java index 15f3cbb66af..0fb0f793158 100644 --- a/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndCloseTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenSaveAndCloseTest.java @@ -61,6 +61,6 @@ public void givenCallbackIsInvokedThenSendEmail() { citizenSaveAndClose.submitted(details, details); - verify(saveAndSignOutNotificationHandler).notifyApplicant(caseData, 123456789L, "token"); + verify(saveAndSignOutNotificationHandler).notifyApplicant(details.getState(), caseData, 123456789L, "token"); } } diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplicationTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplicationTest.java index 13f883ba46b..df2cc3f299e 100644 --- a/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplicationTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenSubmitApplicationTest.java @@ -20,6 +20,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.payment.PaymentService; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.divorce.solicitor.service.SolicitorSubmitJointApplicationService; import java.time.LocalDate; @@ -37,9 +38,6 @@ import static uk.gov.hmcts.divorce.divorcecase.model.ContactDetailsType.PRIVATE; import static uk.gov.hmcts.divorce.divorcecase.model.DivorceOrDissolution.DIVORCE; import static uk.gov.hmcts.divorce.divorcecase.model.Gender.MALE; -import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_ISSUE; -import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_DIVORCE; -import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_DIVORCE; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; @@ -55,6 +53,9 @@ class CitizenSubmitApplicationTest { @Mock private PaymentService paymentService; + @Mock + private PaymentSetupService paymentSetupService; + @Mock private SubmissionService submissionService; @@ -118,14 +119,11 @@ public void givenEventStartedWithValidCaseThenChangeStateAndSetOrderSummary() { caseDetails.setId(caseId); var orderSummary = orderSummary(); - when(paymentService.getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE, KEYWORD_DIVORCE)) - .thenReturn(orderSummary()); - - when(paymentService.getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE, KEYWORD_DIVORCE)) + when(paymentSetupService.createApplicationFeeOrderSummary(caseData, TEST_CASE_ID)) .thenReturn(orderSummary()); - when(paymentService.createServiceRequestReference( - null, caseId, caseData.getApplicant1().getFullName(), orderSummary + when(paymentSetupService.createApplicationFeeServiceRequest( + caseData, caseId, caseData.getCitizenPaymentCallbackUrl() )).thenReturn(TEST_SERVICE_REFERENCE); final AboutToStartOrSubmitResponse response = citizenSubmitApplication.aboutToSubmit(caseDetails, caseDetails); @@ -134,7 +132,7 @@ public void givenEventStartedWithValidCaseThenChangeStateAndSetOrderSummary() { assertThat(response.getData().getApplication().getApplicationFeeOrderSummary()).isEqualTo(orderSummary); assertThat(response.getData().getApplication().getApplicationFeeServiceRequestReference()).isEqualTo(TEST_SERVICE_REFERENCE); - verify(paymentService).getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE, KEYWORD_DIVORCE); + verify(paymentSetupService).createApplicationFeeOrderSummary(caseData, caseId); } @Test @@ -156,11 +154,11 @@ public void givenEventStartedWithValidJointCaseThenChangeStateAndSetOrderSummary var orderSummary = orderSummary(); - when(paymentService.getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE, KEYWORD_DIVORCE)) + when(paymentSetupService.createApplicationFeeOrderSummary(caseData, TEST_CASE_ID)) .thenReturn(orderSummary()); - when(paymentService.createServiceRequestReference( - null, caseId, caseData.getApplicant1().getFullName(), orderSummary + when(paymentSetupService.createApplicationFeeServiceRequest( + caseData, caseId, caseData.getCitizenPaymentCallbackUrl() )).thenReturn(TEST_SERVICE_REFERENCE); final AboutToStartOrSubmitResponse response = citizenSubmitApplication.aboutToSubmit(caseDetails, caseDetails); @@ -169,7 +167,7 @@ public void givenEventStartedWithValidJointCaseThenChangeStateAndSetOrderSummary assertThat(response.getData().getApplication().getApplicationFeeOrderSummary()).isEqualTo(orderSummary); assertThat(response.getData().getApplication().getApplicationFeeServiceRequestReference()).isEqualTo(TEST_SERVICE_REFERENCE); - verify(paymentService).getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE, KEYWORD_DIVORCE); + verify(paymentSetupService).createApplicationFeeOrderSummary(caseData, caseId); } @Test diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/event/RespondentApplyForFinalOrderTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/event/RespondentApplyForFinalOrderTest.java index a7f0ac041cf..f40a1cc9d2b 100644 --- a/src/test/java/uk/gov/hmcts/divorce/citizen/event/RespondentApplyForFinalOrderTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/event/RespondentApplyForFinalOrderTest.java @@ -15,14 +15,11 @@ import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.HelpWithFees; import uk.gov.hmcts.divorce.divorcecase.model.State; -import uk.gov.hmcts.divorce.payment.PaymentService; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; -import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; -import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; @@ -32,7 +29,7 @@ public class RespondentApplyForFinalOrderTest { private ApplyForFinalOrderService applyForFinalOrderService; @Mock - private PaymentService paymentService; + private PaymentSetupService paymentSetupService; @InjectMocks private RespondentApplyForFinalOrder respondentApplyForFinalOrder; @@ -55,12 +52,11 @@ public void givenRespondentDoesNotNeedHwfThenChangeStateToAwaitingPaymentAndSetO final var orderSummary = orderSummary(); caseDetails.setId(caseId); - when(paymentService.getOrderSummaryByServiceEvent( - SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE - )).thenReturn(orderSummary); + when(paymentSetupService.createFinalOrderFeeOrderSummary(caseData, TEST_CASE_ID)) + .thenReturn(orderSummary); - when(paymentService.createServiceRequestReference( - null, caseId, caseData.getApplicant2().getFullName(), orderSummary + when(paymentSetupService.createFinalOrderFeeServiceRequest( + caseData, caseId, null, orderSummary )).thenReturn(TEST_SERVICE_REFERENCE); diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/notification/SaveAndSignOutNotificationHandlerTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/notification/SaveAndSignOutNotificationHandlerTest.java index 61e981f927b..d131aae5fda 100644 --- a/src/test/java/uk/gov/hmcts/divorce/citizen/notification/SaveAndSignOutNotificationHandlerTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/notification/SaveAndSignOutNotificationHandlerTest.java @@ -18,6 +18,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT; import static uk.gov.hmcts.divorce.notification.EmailTemplateName.SAVE_SIGN_OUT; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.validApplicant2CaseData; @@ -50,7 +53,7 @@ void shouldCallSendEmailToApp1WhenNotifyApplicantIsInvokedForGivenCaseData() { when(idamService.retrieveUser(eq(USER_TOKEN))).thenReturn(user); when(ccdAccessService.isApplicant1(eq(USER_TOKEN), eq(CASE_ID))).thenReturn(true); - saveAndSignOutNotificationHandler.notifyApplicant(caseData, CASE_ID, USER_TOKEN); + saveAndSignOutNotificationHandler.notifyApplicant(Submitted, caseData, CASE_ID, USER_TOKEN); verify(commonContent).mainTemplateVars(caseData, CASE_ID, caseData.getApplicant1(), caseData.getApplicant2()); verify(notificationService).sendEmail( @@ -62,6 +65,25 @@ void shouldCallSendEmailToApp1WhenNotifyApplicantIsInvokedForGivenCaseData() { ); } + @Test + void shouldCallSendEmailToApp1WhenNotifyApplicantIsInvokedForGivenCaseDataWhenInformationRequested() { + CaseData caseData = validApplicant2CaseData(); + User user = new User(USER_TOKEN, UserInfo.builder().sub(TEST_USER_EMAIL).build()); + when(idamService.retrieveUser(eq(USER_TOKEN))).thenReturn(user); + when(ccdAccessService.isApplicant1(eq(USER_TOKEN), eq(CASE_ID))).thenReturn(true); + + saveAndSignOutNotificationHandler.notifyApplicant(InformationRequested, caseData, CASE_ID, USER_TOKEN); + + verify(commonContent).mainTemplateVars(caseData, CASE_ID, caseData.getApplicant1(), caseData.getApplicant2()); + verify(notificationService).sendEmail( + eq(TEST_USER_EMAIL), + eq(REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT), + any(), + eq(ENGLISH), + eq(CASE_ID) + ); + } + @Test void shouldCallSendEmailToApp2WhenNotifyApplicantIsInvokedForGivenCaseData() { CaseData caseData = validApplicant2CaseData(); @@ -70,7 +92,7 @@ void shouldCallSendEmailToApp2WhenNotifyApplicantIsInvokedForGivenCaseData() { when(ccdAccessService.isApplicant1(eq(USER_TOKEN), eq(CASE_ID))).thenReturn(false); - saveAndSignOutNotificationHandler.notifyApplicant(caseData, CASE_ID, USER_TOKEN); + saveAndSignOutNotificationHandler.notifyApplicant(Submitted, caseData, CASE_ID, USER_TOKEN); verify(commonContent).mainTemplateVars(caseData, CASE_ID, caseData.getApplicant2(), caseData.getApplicant1()); verify(notificationService).sendEmail( @@ -81,4 +103,24 @@ void shouldCallSendEmailToApp2WhenNotifyApplicantIsInvokedForGivenCaseData() { eq(CASE_ID) ); } + + @Test + void shouldCallSendEmailToApp2WhenNotifyApplicantIsInvokedForGivenCaseDataWhenInformationRequested() { + CaseData caseData = validApplicant2CaseData(); + User user = new User(USER_TOKEN, UserInfo.builder().sub(TEST_USER_EMAIL).build()); + when(idamService.retrieveUser(eq(USER_TOKEN))).thenReturn(user); + when(ccdAccessService.isApplicant1(eq(USER_TOKEN), eq(CASE_ID))).thenReturn(false); + + + saveAndSignOutNotificationHandler.notifyApplicant(InformationRequested, caseData, CASE_ID, USER_TOKEN); + + verify(commonContent).mainTemplateVars(caseData, CASE_ID, caseData.getApplicant2(), caseData.getApplicant1()); + verify(notificationService).sendEmail( + eq(TEST_USER_EMAIL), + eq(REQUEST_FOR_INFORMATION_SAVE_SIGN_OUT), + any(), + eq(ENGLISH), + eq(CASE_ID) + ); + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/service/SwitchToSoleServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/service/SwitchToSoleServiceTest.java index b533df4e2bd..72b5d1d4be3 100644 --- a/src/test/java/uk/gov/hmcts/divorce/citizen/service/SwitchToSoleServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/service/SwitchToSoleServiceTest.java @@ -29,6 +29,7 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -278,6 +279,70 @@ void shouldSwitchSolicitorUserRolesIfApplicant2SolicitorTriggeredSwitchToSoleCo( verifyNoMoreInteractions(caseAssignmentApi); } + @Test + void shouldGracefullyHandleMissingUserIDToProcessOfflineCases() { + final long caseId = TEST_CASE_ID; + final CaseData caseData = CaseData.builder() + .applicant1(Applicant.builder().solicitorRepresented(YES).build()) + .applicant2(Applicant.builder().solicitorRepresented(YES).build()) + .build(); + + final CaseAssignmentUserRolesResource caseRolesResponse = CaseAssignmentUserRolesResource.builder() + .caseAssignmentUserRoles(List.of( + CaseAssignmentUserRole.builder().userId(null).caseRole("[APPTWOSOLICITOR]").build(), + CaseAssignmentUserRole.builder().userId("2").caseRole("[APPONESOLICITOR]").build() + )) + .build(); + + final var userDetails = UserInfo.builder().uid(CASEWORKER_USER_ID).build(); + final User user = new User(CASEWORKER_AUTH_TOKEN, userDetails); + + when(idamService.retrieveSystemUpdateUserDetails()).thenReturn(user); + when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN); + when(caseAssignmentApi.getUserRoles(CASEWORKER_AUTH_TOKEN, TEST_SERVICE_AUTH_TOKEN, List.of(String.valueOf(caseId)))) + .thenReturn(caseRolesResponse); + when(ccdAccessService.getCaseAssignmentRequest(caseId, "2", APPLICANT_1_SOLICITOR)) + .thenReturn(getCaseAssignmentRequest("2", APPLICANT_1_SOLICITOR)); + when(ccdAccessService.getCaseAssignmentRequest(caseId, "2", APPLICANT_2_SOLICITOR)) + .thenReturn(getCaseAssignmentRequest("2", APPLICANT_2_SOLICITOR)); + + switchToSoleService.switchUserRoles(caseData, caseId); + + verify(ccdAccessService, never()).getCaseAssignmentRequest(caseId, "1", APPLICANT_1_SOLICITOR); + verify(ccdAccessService, never()).getCaseAssignmentRequest(caseId, "1", APPLICANT_2_SOLICITOR); + verify(caseAssignmentApi) + .getUserRoles( + CASEWORKER_AUTH_TOKEN, + TEST_SERVICE_AUTH_TOKEN, + List.of(String.valueOf(caseId)) + ); + verify(caseAssignmentApi) + .removeCaseUserRoles( + CASEWORKER_AUTH_TOKEN, + TEST_SERVICE_AUTH_TOKEN, + getCaseAssignmentRequest("2", APPLICANT_1_SOLICITOR) + ); + verify(caseAssignmentApi, never()) + .removeCaseUserRoles( + CASEWORKER_AUTH_TOKEN, + TEST_SERVICE_AUTH_TOKEN, + getCaseAssignmentRequest("1", APPLICANT_2_SOLICITOR) + ); + verify(caseAssignmentApi, never()) + .addCaseUserRoles( + CASEWORKER_AUTH_TOKEN, + TEST_SERVICE_AUTH_TOKEN, + getCaseAssignmentRequest("1", APPLICANT_1_SOLICITOR) + ); + verify(caseAssignmentApi) + .addCaseUserRoles( + CASEWORKER_AUTH_TOKEN, + TEST_SERVICE_AUTH_TOKEN, + getCaseAssignmentRequest("2", APPLICANT_2_SOLICITOR) + ); + verifyNoMoreInteractions(caseAssignmentApi); + } + @Test void shouldSwitchCitizenAndSolicitorUserRolesIfApplicant2SolicitorTriggeredSwitchToSoleAndApplicant1Citizen() { final long caseId = TEST_CASE_ID; diff --git a/src/test/java/uk/gov/hmcts/divorce/common/event/Applicant2SolicitorApplyForFinalOrderTest.java b/src/test/java/uk/gov/hmcts/divorce/common/event/Applicant2SolicitorApplyForFinalOrderTest.java index 0280f92bf87..9eacaa69745 100644 --- a/src/test/java/uk/gov/hmcts/divorce/common/event/Applicant2SolicitorApplyForFinalOrderTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/common/event/Applicant2SolicitorApplyForFinalOrderTest.java @@ -24,6 +24,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.notification.NotificationDispatcher; import uk.gov.hmcts.divorce.payment.PaymentService; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.divorce.payment.model.PbaResponse; import uk.gov.hmcts.divorce.solicitor.event.page.SolFinalOrderPayment; @@ -44,12 +45,10 @@ import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.SOLE_APPLICATION; import static uk.gov.hmcts.divorce.divorcecase.model.SolicitorPaymentMethod.FEE_PAY_BY_ACCOUNT; import static uk.gov.hmcts.divorce.divorcecase.model.State.RespondentFinalOrderRequested; -import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; -import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; -import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_NAME; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; @@ -85,6 +84,9 @@ class Applicant2SolicitorApplyForFinalOrderTest { @Mock private PaymentService paymentService; + @Mock + private PaymentSetupService paymentSetupService; + @InjectMocks private Applicant2SolicitorApplyForFinalOrder applicant2SolicitorApplyForFinalOrder; @@ -109,7 +111,7 @@ void shouldSetOrderSummaryAndSolicitorFeesInPoundsAndSolicitorRolesAndPbaNumbers caseDetails.setData(caseData); caseDetails.setId(caseId); - when(paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE)).thenReturn(orderSummary); + when(paymentSetupService.createFinalOrderFeeOrderSummary(caseData, caseId)).thenReturn(orderSummary); when(orderSummary.getPaymentTotal()).thenReturn("16700"); var midEventCaseData = caseData(); @@ -160,13 +162,14 @@ void shouldAddFinalOrderPaymentIfPaymentsExists() { caseData.getFinalOrder().setApplicant2SolFinalOrderFeeAccountReference(FEE_ACCOUNT_REF); caseData.getFinalOrder().setApplicant2FinalOrderStatementOfTruth(YES); caseData.getFinalOrder().setApplicant2SolFinalOrderFeeOrderSummary(orderSummary); + caseData.getFinalOrder().setApplicant2FinalOrderFeeServiceRequestReference(TEST_SERVICE_REFERENCE); caseDetails.setData(caseData); caseDetails.setId(TEST_CASE_ID); PbaResponse pbaResponse = new PbaResponse(CREATED, null, "1234"); when(paymentService.processPbaPayment( - caseData, TEST_CASE_ID, + TEST_SERVICE_REFERENCE, caseData.getApplicant2().getSolicitor(), PBA_NUMBER, orderSummary, @@ -193,6 +196,7 @@ void shouldReturnErrorIfFinalOrderPaymentIfPaymentFails() { .organisationPolicy(organisationPolicy()) .build()); caseData.getFinalOrder().setApplicant2SolPaymentHowToPay(FEE_PAY_BY_ACCOUNT); + caseData.getFinalOrder().setApplicant2FinalOrderFeeServiceRequestReference(TEST_SERVICE_REFERENCE); caseData.getFinalOrder().setFinalOrderPbaNumbers( DynamicList.builder() .value(DynamicListElement.builder().label(PBA_NUMBER).build()) @@ -206,8 +210,8 @@ void shouldReturnErrorIfFinalOrderPaymentIfPaymentFails() { PbaResponse pbaResponse = new PbaResponse(HttpStatus.BAD_REQUEST, "Payment Failed", "1234"); when(paymentService.processPbaPayment( - caseData, TEST_CASE_ID, + TEST_SERVICE_REFERENCE, caseData.getApplicant2().getSolicitor(), PBA_NUMBER, orderSummary, diff --git a/src/test/java/uk/gov/hmcts/divorce/common/notification/AwaitingFinalOrderNotificationTest.java b/src/test/java/uk/gov/hmcts/divorce/common/notification/AwaitingFinalOrderNotificationTest.java index 790714ba44e..eb2668e7009 100644 --- a/src/test/java/uk/gov/hmcts/divorce/common/notification/AwaitingFinalOrderNotificationTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/common/notification/AwaitingFinalOrderNotificationTest.java @@ -124,7 +124,6 @@ void shouldSendAwaitingFinalOrderEmailToApplicant1IfNotRepresentedAndJoint() { final var applicant2 = getApplicant2(MALE); data.setApplicant2(applicant2); data.setApplicationType(JOINT_APPLICATION); - when(commonContent.conditionalOrderTemplateVars(data, TEST_CASE_ID, data.getApplicant1(), data.getApplicant2())) .thenReturn(getConditionalOrderTemplateVars(JOINT_APPLICATION)); @@ -255,7 +254,6 @@ void shouldSendAwaitingFinalOrderEmailToApplicant2IfNotRepresentedAndJointInWels data.setApplicant2(applicant2); data.setApplicationType(JOINT_APPLICATION); data.getApplicant2().setLanguagePreferenceWelsh(YesOrNo.YES); - when(commonContent.conditionalOrderTemplateVars(data, TEST_CASE_ID, data.getApplicant2(), data.getApplicant1())) .thenReturn(getConditionalOrderTemplateVars(JOINT_APPLICATION)); @@ -317,7 +315,7 @@ void shouldSendEmailToApplicant1SolicitorJointNotification() { .build(); data.getApplication().setIssueDate(LocalDate.of(2021, 6, 18)); LocalDate coGrantedDate = LocalDate.of(2021, 10, 18); - data.getFinalOrder().setDateFinalOrderEligibleFrom(coGrantedDate); + data.getConditionalOrder().setGrantedDate(coGrantedDate); when(commonContent.basicTemplateVars(data, TEST_CASE_ID, data.getApplicant1().getLanguagePreference())) .thenReturn(getBasicTemplateVars()); @@ -357,7 +355,7 @@ void shouldSendEmailToApplicant2SolicitorJointNotificationWithReference() { .build(); data.getApplication().setIssueDate(LocalDate.of(2021, 6, 18)); LocalDate coGrantedDate = LocalDate.of(2021, 10, 18); - data.getFinalOrder().setDateFinalOrderEligibleFrom(coGrantedDate); + data.getConditionalOrder().setGrantedDate(coGrantedDate); when(commonContent.basicTemplateVars(data, TEST_CASE_ID, data.getApplicant2().getLanguagePreference())) .thenReturn(getBasicTemplateVars()); @@ -440,6 +438,7 @@ void shouldNotSendCanApplyForFinalOrderLettersToOfflineApplicant2InSoleApplicati caseData.setApplicationType(SOLE_APPLICATION); awaitingFinalOrderNotification.sendToApplicant2Offline(caseData, TEST_CASE_ID); + verifyNoInteractions(applyForFinalOrderDocumentPack); } } diff --git a/src/test/java/uk/gov/hmcts/divorce/divorcecase/model/access/CaseworkerAndSuperUserAccessTest.java b/src/test/java/uk/gov/hmcts/divorce/divorcecase/model/access/CaseworkerAndSuperUserAccessTest.java index 730f8aa5c7b..58574e4bb79 100644 --- a/src/test/java/uk/gov/hmcts/divorce/divorcecase/model/access/CaseworkerAndSuperUserAccessTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/divorcecase/model/access/CaseworkerAndSuperUserAccessTest.java @@ -14,7 +14,6 @@ import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; -import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SYSTEMUPDATE; @@ -26,9 +25,8 @@ void shouldGrantCaseworkerAndSuperUserAccess() { final SetMultimap grants = new CaseworkerAndSuperUserAccess().getGrants(); assertThat(grants) - .hasSize(14) + .hasSize(13) .contains( - entry(SOLICITOR, R), entry(LEGAL_ADVISOR, R), entry(JUDGE, R), entry(SUPER_USER, C), diff --git a/src/test/java/uk/gov/hmcts/divorce/document/content/templatecontent/FinalOrderCanApplyTemplateContentTest.java b/src/test/java/uk/gov/hmcts/divorce/document/content/templatecontent/FinalOrderCanApplyTemplateContentTest.java index 46d9d5494d6..126f540c6d6 100644 --- a/src/test/java/uk/gov/hmcts/divorce/document/content/templatecontent/FinalOrderCanApplyTemplateContentTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/document/content/templatecontent/FinalOrderCanApplyTemplateContentTest.java @@ -81,7 +81,7 @@ void testGetTemplateContent() { CaseData caseData = validApplicant1CaseData(); caseData.getApplicant1().setLanguagePreferenceWelsh(YesOrNo.NO); - caseData.getFinalOrder().setDateFinalOrderEligibleFrom(LocalDate.now()); + caseData.getConditionalOrder().setGrantedDate(LocalDate.now()); caseData.getApplicant1().setAddress(AddressGlobalUK.builder().addressLine1("test value").build()); when(docmosisCommonContent.getBasicDocmosisTemplateContent( @@ -100,7 +100,7 @@ void testGetTemplateContent() { expectedEntries.put(DIVORCE_AND_DISSOLUTION_HEADER, DIVORCE_AND_DISSOLUTION_HEADER_TEXT); expectedEntries.put(COURTS_AND_TRIBUNALS_SERVICE_HEADER, COURTS_AND_TRIBUNALS_SERVICE_HEADER_TEXT); expectedEntries.put(CASE_REFERENCE, formatId(TEST_CASE_ID)); - expectedEntries.put(FINAL_ORDER_OVERDUE_DATE, caseData.getFinalOrder().getDateFinalOrderEligibleFrom().plusMonths(12) + expectedEntries.put(FINAL_ORDER_OVERDUE_DATE, caseData.getConditionalOrder().getGrantedDate().plusMonths(12) .format(getDateTimeFormatterForPreferredLanguage(caseData.getApplicant1().getLanguagePreference()))); expectedEntries.put(CONTACT_EMAIL, CONTACT_DIVORCE_EMAIL); expectedEntries.put(PHONE_AND_OPENING_TIMES, PHONE_AND_OPENING_TIMES_TEXT); diff --git a/src/test/java/uk/gov/hmcts/divorce/idam/IdamServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/idam/IdamServiceTest.java index a1523ea635a..59793b43cba 100644 --- a/src/test/java/uk/gov/hmcts/divorce/idam/IdamServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/idam/IdamServiceTest.java @@ -24,7 +24,7 @@ import static uk.gov.hmcts.divorce.testutil.TestDataHelper.feignException; @ExtendWith(MockitoExtension.class) -public class IdamServiceTest { +class IdamServiceTest { @InjectMocks private IdamService idamService; @@ -33,7 +33,7 @@ public class IdamServiceTest { private IdamClient idamClient; @Test - public void shouldRetrieveUserWhenValidAuthorizationTokenIsPassed() { + void shouldRetrieveUserWhenValidAuthorizationTokenIsPassed() { when(idamClient.getUserInfo(SYSTEM_UPDATE_AUTH_TOKEN)) .thenReturn(userDetails()); @@ -45,7 +45,7 @@ public void shouldRetrieveUserWhenValidAuthorizationTokenIsPassed() { } @Test - public void shouldThrowFeignUnauthorizedExceptionWhenInValidAuthorizationTokenIsPassed() { + void shouldThrowFeignUnauthorizedExceptionWhenInValidAuthorizationTokenIsPassed() { doThrow(feignException(401, "Failed to retrieve Idam user")) .when(idamClient).getUserInfo("Bearer invalid_token"); @@ -55,7 +55,7 @@ public void shouldThrowFeignUnauthorizedExceptionWhenInValidAuthorizationTokenIs } @Test - public void shouldNotThrowExceptionAndRetrieveSystemUpdateUserSuccessfully() { + void shouldNotThrowExceptionAndRetrieveSystemUpdateUserSuccessfully() { setSystemUserCredentials(); when(idamClient.getAccessToken(TEST_SYSTEM_UPDATE_USER_EMAIL, TEST_SYSTEM_USER_PASSWORD)) @@ -73,7 +73,7 @@ public void shouldNotThrowExceptionAndRetrieveSystemUpdateUserSuccessfully() { } @Test - public void shouldThrowFeignUnauthorizedExceptionWhenSystemUpdateUserCredentialsAreInvalid() { + void shouldThrowFeignUnauthorizedExceptionWhenSystemUpdateUserCredentialsAreInvalid() { setSystemUserCredentials(); doThrow(feignException(401, "Failed to retrieve Idam user")) @@ -84,11 +84,34 @@ public void shouldThrowFeignUnauthorizedExceptionWhenSystemUpdateUserCredentials .hasMessageContaining("Failed to retrieve Idam user"); } + @Test + void shouldRetrieveOldSystemUpdateUserDetailsSuccessfully() { + setDivorceUserCredentials(); + + when(idamClient.getAccessToken(TEST_SYSTEM_UPDATE_USER_EMAIL, TEST_SYSTEM_USER_PASSWORD)) + .thenReturn(SYSTEM_UPDATE_AUTH_TOKEN); + + when(idamClient.getUserInfo(SYSTEM_UPDATE_AUTH_TOKEN)) + .thenReturn(userDetails()); + + assertThatCode(() -> idamService.retrieveOldSystemUpdateUserDetails()) + .doesNotThrowAnyException(); + + verify(idamClient).getAccessToken(TEST_SYSTEM_UPDATE_USER_EMAIL, TEST_SYSTEM_USER_PASSWORD); + verify(idamClient).getUserInfo(SYSTEM_UPDATE_AUTH_TOKEN); + verifyNoMoreInteractions(idamClient); + } + private void setSystemUserCredentials() { ReflectionTestUtils.setField(idamService, "systemUpdateUserName", TEST_SYSTEM_UPDATE_USER_EMAIL); ReflectionTestUtils.setField(idamService, "systemUpdatePassword", TEST_SYSTEM_USER_PASSWORD); } + private void setDivorceUserCredentials() { + ReflectionTestUtils.setField(idamService, "oldDivorceUserName", TEST_SYSTEM_UPDATE_USER_EMAIL); + ReflectionTestUtils.setField(idamService, "oldDivorcePassword", TEST_SYSTEM_USER_PASSWORD); + } + private UserInfo userDetails() { return UserInfo .builder() diff --git a/src/test/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemApplyNoticeOfChangeTest.java b/src/test/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemApplyNoticeOfChangeTest.java index 224fdcecb3b..5fefb48772a 100644 --- a/src/test/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemApplyNoticeOfChangeTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/noticeofchange/event/SystemApplyNoticeOfChangeTest.java @@ -29,6 +29,7 @@ import uk.gov.hmcts.divorce.noticeofchange.model.AcaRequest; import uk.gov.hmcts.divorce.noticeofchange.service.ChangeOfRepresentativeService; import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; import uk.gov.hmcts.divorce.testutil.TestDataHelper; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.ccd.client.model.AboutToStartOrSubmitCallbackResponse; @@ -39,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; @@ -182,6 +184,39 @@ void shouldNotApplyNoticeOfChangeWhenErrorsThrown() { ); } + @Test + void shouldNotFailEventWhenNotificationsFail() { + setup(); + Applicant applicant = TestDataHelper.applicantRepresentedBySolicitor(); + final ChangeOrganisationRequest changeOrganisationRequest = getChangeOrganisationRequestField("[APPONESOLICITOR]", + "APPLICANT_1_SOLICITOR"); + + CaseData caseData = CaseData.builder().applicant1(applicant).changeOrganisationRequestField(changeOrganisationRequest).build(); + + var details = CaseDetails.builder().id(TEST_CASE_ID).data(caseData).build(); + AcaRequest acaRequest = AcaRequest.acaRequest(details); + Map expectedData = expectedData(caseData); + when(objectMapper.convertValue(expectedData, CaseData.class)).thenReturn(caseData); + doThrow(new NotificationTemplateException("some error")) + .when(notificationDispatcher).sendNOC(nocCitizenToSolsNotifications, caseData, null, + TEST_CASE_ID, true, NEW_DIGITAL_SOLICITOR_NEW_ORG); + + AboutToStartOrSubmitCallbackResponse response = AboutToStartOrSubmitCallbackResponse + .builder().data(expectedData).build(); + when(assignCaseAccessClient.applyNoticeOfChange(TEST_AUTHORIZATION_TOKEN, TEST_SERVICE_AUTH_TOKEN, acaRequest)) + .thenReturn(response); + + systemApplyNoticeOfChange.aboutToStart(details); + + verify(assignCaseAccessClient).applyNoticeOfChange(TEST_AUTHORIZATION_TOKEN, TEST_SERVICE_AUTH_TOKEN, acaRequest); + verify(changeOfRepresentativeService).buildChangeOfRepresentative(caseData, null, SOLICITOR_NOTICE_OF_CHANGE.getValue(), true); + verify(notificationDispatcher).sendNOC(nocCitizenToSolsNotifications, caseData, null, + TEST_CASE_ID, true, NEW_DIGITAL_SOLICITOR_NEW_ORG); + + assertEquals(NO, caseData.getConditionalOrder().getConditionalOrderApplicant1Questions().getIsSubmitted()); + assertEquals(NO, caseData.getConditionalOrder().getConditionalOrderApplicant1Questions().getIsDrafted()); + } + private Map expectedData(final CaseData caseData) { ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build(); diff --git a/src/test/java/uk/gov/hmcts/divorce/notification/CommonContentTest.java b/src/test/java/uk/gov/hmcts/divorce/notification/CommonContentTest.java index be75e35c0cf..a26f3975b4b 100644 --- a/src/test/java/uk/gov/hmcts/divorce/notification/CommonContentTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/notification/CommonContentTest.java @@ -15,6 +15,8 @@ import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.Gender; import uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationList; import uk.gov.hmcts.divorce.divorcecase.model.Solicitor; import uk.gov.hmcts.divorce.document.content.DocmosisCommonContent; @@ -41,6 +43,7 @@ import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; import static uk.gov.hmcts.divorce.divorcecase.model.RefusalOption.MORE_INFO; import static uk.gov.hmcts.divorce.divorcecase.model.RefusalOption.REJECT; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.APPLICANT_1_FULL_NAME; import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.APPLICANT_2_FULL_NAME; import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.CASE_REFERENCE; @@ -230,6 +233,26 @@ void shouldSetTemplateVarsForSoleApplication() { ); } + @Test + void shouldSetTemplateVarsForRequestForInformationSole() { + final CaseData caseData = CaseData.builder() + .applicationType(SOLE_APPLICATION) + .divorceOrDissolution(DIVORCE) + .build(); + + final Map templateVars = commonContent + .requestForInformationTemplateVars(caseData, TEST_CASE_ID, getApplicant(), respondent()); + + assertThat(templateVars) + .isNotEmpty() + .contains( + entry(IS_JOINT, "no"), + entry(HUSBAND_JOINT, "no"), + entry(WIFE_JOINT, "no"), + entry(CIVIL_PARTNER_JOINT, "no") + ); + } + @Test void shouldSetTemplateVarsForJointDivorceApplicationWhenPartnerIsMale() { final CaseData caseData = CaseData.builder() @@ -250,6 +273,31 @@ void shouldSetTemplateVarsForJointDivorceApplicationWhenPartnerIsMale() { ); } + @Test + void shouldSetTemplateVarsForRequestForInformationJointDivorceWhenPartnerIsMale() { + final CaseData caseData = CaseData.builder() + .applicationType(JOINT_APPLICATION) + .divorceOrDissolution(DIVORCE) + .requestForInformationList(RequestForInformationList.builder() + .requestForInformation(RequestForInformation.builder() + .requestForInformationJointParties(APPLICANT1) + .build()) + .build()) + .build(); + + final Map templateVars = commonContent + .requestForInformationTemplateVars(caseData, TEST_CASE_ID, getApplicant(FEMALE), getApplicant(MALE)); + + assertThat(templateVars) + .isNotEmpty() + .contains( + entry(IS_JOINT, "yes"), + entry(HUSBAND_JOINT, "yes"), + entry(WIFE_JOINT, "no"), + entry(CIVIL_PARTNER_JOINT, "no") + ); + } + @Test void shouldNotThrowNpeIfGenderIsNull() { final CaseData caseData = CaseData.builder() @@ -283,6 +331,31 @@ void shouldSetTemplateVarsForJointDivorceApplicationWhenPartnerIsFemale() { ); } + @Test + void shouldSetTemplateVarsForRequestForInformationJointDivorceWhenPartnerIsFemale() { + final CaseData caseData = CaseData.builder() + .applicationType(JOINT_APPLICATION) + .divorceOrDissolution(DIVORCE) + .requestForInformationList(RequestForInformationList.builder() + .requestForInformation(RequestForInformation.builder() + .requestForInformationJointParties(APPLICANT1) + .build()) + .build()) + .build(); + + final Map templateVars = commonContent + .requestForInformationTemplateVars(caseData, TEST_CASE_ID, getApplicant(MALE), getApplicant(FEMALE)); + + assertThat(templateVars) + .isNotEmpty() + .contains( + entry(IS_JOINT, "yes"), + entry(HUSBAND_JOINT, "no"), + entry(WIFE_JOINT, "yes"), + entry(CIVIL_PARTNER_JOINT, "no") + ); + } + @Test void shouldSetTemplateVarsForJointDissolution() { final CaseData caseData = CaseData.builder() @@ -303,6 +376,31 @@ void shouldSetTemplateVarsForJointDissolution() { ); } + @Test + void shouldSetTemplateVarsForRequestForInformationJointDissolution() { + final CaseData caseData = CaseData.builder() + .applicationType(JOINT_APPLICATION) + .divorceOrDissolution(DISSOLUTION) + .requestForInformationList(RequestForInformationList.builder() + .requestForInformation(RequestForInformation.builder() + .requestForInformationJointParties(APPLICANT1) + .build()) + .build()) + .build(); + + final Map templateVars = commonContent + .requestForInformationTemplateVars(caseData, TEST_CASE_ID, getApplicant(MALE), getApplicant(FEMALE)); + + assertThat(templateVars) + .isNotEmpty() + .contains( + entry(IS_JOINT, "yes"), + entry(HUSBAND_JOINT, "no"), + entry(WIFE_JOINT, "no"), + entry(CIVIL_PARTNER_JOINT, "yes") + ); + } + @Test void shouldReturnProfessionalSignInUrl() { Long caseId = 123456789L; diff --git a/src/test/java/uk/gov/hmcts/divorce/notification/NotificationDispatcherTest.java b/src/test/java/uk/gov/hmcts/divorce/notification/NotificationDispatcherTest.java index 84b9de81683..04b0fe17451 100644 --- a/src/test/java/uk/gov/hmcts/divorce/notification/NotificationDispatcherTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/notification/NotificationDispatcherTest.java @@ -1,14 +1,22 @@ package uk.gov.hmcts.divorce.notification; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.divorce.caseworker.event.NoticeType; import uk.gov.hmcts.divorce.divorcecase.model.Applicant; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -16,7 +24,17 @@ import static org.mockito.Mockito.when; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.JOINT_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.SOLE_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.APPLICANT; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.OTHER; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.applicantRepresentedBySolicitor; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getApplicant; @ExtendWith(MockitoExtension.class) @@ -283,4 +301,375 @@ void shouldNotSendNOCToOldSolIfNewOrgAssignedAndDidNotHaveRepresentationBefore_A public static class TestNotification implements ApplicantNotification { } + + @Test + void shouldSendRequestForInformationToApplicantIfNotRepresentedOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToApplicantSolicitorIfRepresentedOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToApplicant1IfNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToApplicant1SolicitorIfRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToApplicant2IfNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant2(getApplicant()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant2(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToApplicant2SolicitorIfRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToBothApplicantsIfNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant2(getApplicant()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1(caseData, TEST_CASE_ID); + verify(applicantNotification).sendToApplicant2(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToBothApplicantsSolicitorsIfRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + verify(applicantNotification).sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToApplicant1SolicitorIfRepresentedAndApplicant2IfNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(getApplicant()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + verify(applicantNotification).sendToApplicant2(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToApplicant1IfNotRepresentedAndApplicant2SolicitorIfRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1(caseData, TEST_CASE_ID); + verify(applicantNotification).sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToOtherRecipientOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToOtherRecipient(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationToOtherRecipientOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation() + .setRequestForInformationJointParties(RequestForInformationJointParties.OTHER); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToOtherRecipient(caseData, TEST_CASE_ID); + } + + @Test + void sendRequestForInformationNotificationShouldThrowExceptionWhenPartiesNotSet() { + CaseData caseData = caseData(); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationNotification(applicantNotification, caseData, TEST_CASE_ID); + }); + } + + @Test + void shouldSendRequestForInformationResponseEmailToApplicant1() { + CaseData caseData = caseData(); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT1); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponseNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationResponseEmailToApplicant2() { + CaseData caseData = caseData(); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT2); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponseNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant2(caseData, TEST_CASE_ID); + } + + @Test + void sendRequestForInformationResponseNotificationShouldThrowExceptionWhenPartiesNotSet() { + CaseData caseData = caseData(); + caseData.getRequestForInformationList().addRequestToList(new RequestForInformation()); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(new RequestForInformationResponse()); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification(applicantNotification, caseData, TEST_CASE_ID); + }); + } + + @Test + void shouldSendRequestForInformationPartnerResponseEmailToApplicant1WhenCitizenResponds() { + CaseData caseData = caseData(); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT2); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationPartnerResponseEmailToApplicant1WhenSolicitorResponds() { + CaseData caseData = caseData(); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT2SOLICITOR); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationPartnerResponseEmailToApplicant1SolicitorWhenRepresentedAndCitizenResponds() { + CaseData caseData = caseData(); + caseData.getApplicant1().setSolicitorRepresented(YES); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT2); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationPartnerResponseEmailToApplicant1SolicitorWhenRepresentedAndSolicitorResponds() { + CaseData caseData = caseData(); + caseData.getApplicant1().setSolicitorRepresented(YES); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT2SOLICITOR); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationPartnerResponseEmailToApplicant2WhenCitizenResponds() { + CaseData caseData = caseData(); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT1); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant2(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationPartnerResponseEmailToApplicant2WhenSolicitorResponds() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT1SOLICITOR); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant2(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationPartnerResponseEmailToApplicant2SolicitorWhenRepresentedAndCitizenResponds() { + CaseData caseData = caseData(); + caseData.getApplicant2().setSolicitorRepresented(YES); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT1); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void shouldSendRequestForInformationPartnerResponseEmailToApplicant2SolicitorWhenRepresentedAndSolicitorResponds() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setSolicitorRepresented(YES); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(caseData, RequestForInformationResponseParties.APPLICANT1SOLICITOR); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + + verify(applicantNotification).sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + } + + @Test + void sendRequestForInformationResponsePartnerNotificationShouldThrowExceptionWhenPartiesNotSet() { + CaseData caseData = caseData(); + caseData.getRequestForInformationList().addRequestToList(new RequestForInformation()); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(new RequestForInformationResponse()); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification(applicantNotification, caseData, TEST_CASE_ID); + }); + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/payment/PaymentServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/payment/PaymentServiceTest.java index be10d0cf288..39f388b59de 100644 --- a/src/test/java/uk/gov/hmcts/divorce/payment/PaymentServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/payment/PaymentServiceTest.java @@ -242,6 +242,7 @@ public void shouldProcessPbaPaymentSuccessfullyWhenPbaAccountIsValid() { when(paymentPbaClient.creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ) ).thenReturn(responseEntity); @@ -249,7 +250,7 @@ public void shouldProcessPbaPaymentSuccessfullyWhenPbaAccountIsValid() { when(responseEntity.getStatusCode()).thenReturn(CREATED); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getErrorMessage()).isNull(); assertThat(response.getHttpStatus()).isEqualTo(CREATED); @@ -259,6 +260,7 @@ public void shouldProcessPbaPaymentSuccessfullyWhenPbaAccountIsValid() { verify(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); } @@ -283,11 +285,12 @@ public void shouldReturn403WithErrorCodeCae0004WhenAccountIsDeleted() throws Exc .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(FORBIDDEN); assertThat(response.getErrorMessage()) @@ -318,11 +321,12 @@ public void shouldReturn403WithErrorCodeCae0003WhenAccountIsHold() throws Except .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(FORBIDDEN); assertThat(response.getErrorMessage()) @@ -343,11 +347,12 @@ public void shouldReturn4InternalServerErrorWhenResponseEntityIsNull() { when(paymentPbaClient.creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) )).thenReturn(null); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(INTERNAL_SERVER_ERROR); assertThat(response.getErrorMessage()) @@ -373,11 +378,12 @@ public void shouldReturnGenericErrorWhenGatewayTimeout() throws Exception { .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(GATEWAY_TIMEOUT); assertThat(response.getErrorMessage()) @@ -409,11 +415,12 @@ public void shouldReturnGeneralErrorWhenOtherHttpError() throws Exception { .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(SERVICE_UNAVAILABLE); assertThat(response.getErrorMessage()) @@ -441,11 +448,12 @@ public void shouldReturnGenericErrorWhenInternalServerError() throws Exception { .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(INTERNAL_SERVER_ERROR); assertThat(response.getErrorMessage()) @@ -477,11 +485,12 @@ public void shouldReturn403WithErrorCodeCae0001WhenAccountHasInsufficientBalance .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(FORBIDDEN); assertThat(response.getErrorMessage()) @@ -513,11 +522,12 @@ public void shouldReturnGeneralErrorWhenErrorCodeIsUnknown() throws Exception { .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(FORBIDDEN); assertThat(response.getErrorMessage()) @@ -544,11 +554,12 @@ public void shouldReturn404WhenPaymentAccountIsNotFound() { .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(NOT_FOUND); assertThat(response.getErrorMessage()) @@ -580,11 +591,12 @@ public void shouldReturnGeneralErrorWhenThereIsAnErrorParsingPaymentResponse() t .when(paymentPbaClient).creditAccountPayment( eq(TEST_AUTHORIZATION_TOKEN), eq(TEST_SERVICE_AUTH_TOKEN), + eq(TEST_SERVICE_REFERENCE), any(CreditAccountPaymentRequest.class) ); PbaResponse response = paymentService.processPbaPayment( - caseData, TEST_CASE_ID, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); + TEST_CASE_ID, TEST_SERVICE_REFERENCE, solicitor(), PBA_NUMBER, orderSummaryWithFee(), FEE_ACCOUNT_REF); assertThat(response.getHttpStatus()).isEqualTo(FORBIDDEN); assertThat(response.getErrorMessage()) @@ -614,7 +626,6 @@ private static CreditAccountPaymentResponse buildPaymentClientResponse( return CreditAccountPaymentResponse.builder() .dateCreated("2021-08-18T10:22:33.449+0000") .status("Failed") - .paymentGroupReference("2020-1601893353478") .statusHistories( singletonList( StatusHistoriesItem.builder() diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorConfirmServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorConfirmServiceTest.java index 998482fee7e..324ee48a30d 100644 --- a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorConfirmServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorConfirmServiceTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; import static uk.gov.hmcts.divorce.common.service.ConfirmService.DOCUMENTS_NOT_UPLOADED_ERROR; import static uk.gov.hmcts.divorce.divorcecase.model.ServiceMethod.COURT_SERVICE; @@ -203,4 +204,78 @@ public void shouldNotThrowValidationErrorWhenProcessedByProcessServerButDocument assertThat(response.getWarnings()).isNull(); assertThat(response.getErrors()).isNull(); } + + @Test + void shouldResetPreviousServiceAttemptFieldsIfFirstServiceAttempt() { + final CaseData caseData = caseData(); + caseData.getApplication().setSolSignStatementOfTruth(YES); + caseData.getApplication().setServiceMethod(SOLICITOR_SERVICE); + caseData.getApplication().getSolicitorService().setFirstAttemptToServe(YES); + caseData.getApplication().getSolicitorService().setDocumentsPreviouslyReturned(YES); + caseData.getApplication().getSolicitorService().setDetailsOfPreviousService("details"); + caseData.getApplication().getSolicitorService().setDatePreviousServiceReturned( + LocalDate.of(2021, 1, 1)); + + final CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final CaseDetails updatedCaseDetails = new CaseDetails<>(); + updatedCaseDetails.setData(caseData); + + when(submitConfirmService.submitConfirmService(caseDetails)).thenReturn(updatedCaseDetails); + AboutToStartOrSubmitResponse response = solicitorConfirmService.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getData().getApplication().getSolicitorService().getDocumentsPreviouslyReturned()).isNull(); + assertThat(response.getData().getApplication().getSolicitorService().getDetailsOfPreviousService()).isNull(); + assertThat(response.getData().getApplication().getSolicitorService().getDatePreviousServiceReturned()).isNull(); + } + + @Test + void shouldNotResetPreviousServiceAttemptFieldsIfNotFirstServiceAttempt() { + final CaseData caseData = caseData(); + caseData.getApplication().setSolSignStatementOfTruth(YES); + caseData.getApplication().setServiceMethod(SOLICITOR_SERVICE); + caseData.getApplication().getSolicitorService().setFirstAttemptToServe(NO); + caseData.getApplication().getSolicitorService().setDocumentsPreviouslyReturned(YES); + caseData.getApplication().getSolicitorService().setDetailsOfPreviousService("details"); + caseData.getApplication().getSolicitorService().setDatePreviousServiceReturned( + LocalDate.of(2021, 1, 1)); + + final CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final CaseDetails updatedCaseDetails = new CaseDetails<>(); + updatedCaseDetails.setData(caseData); + + when(submitConfirmService.submitConfirmService(caseDetails)).thenReturn(updatedCaseDetails); + AboutToStartOrSubmitResponse response = solicitorConfirmService.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getData().getApplication().getSolicitorService().getDocumentsPreviouslyReturned()).isNotNull(); + assertThat(response.getData().getApplication().getSolicitorService().getDetailsOfPreviousService()).isNotNull(); + assertThat(response.getData().getApplication().getSolicitorService().getDatePreviousServiceReturned()).isNotNull(); + } + + @Test + void shouldResetPreviousDocumentsReturnedFieldsIfDocumentsNotReturned() { + final CaseData caseData = caseData(); + caseData.getApplication().setSolSignStatementOfTruth(YES); + caseData.getApplication().setServiceMethod(SOLICITOR_SERVICE); + caseData.getApplication().getSolicitorService().setFirstAttemptToServe(NO); + caseData.getApplication().getSolicitorService().setDocumentsPreviouslyReturned(NO); + caseData.getApplication().getSolicitorService().setDetailsOfPreviousService("details"); + caseData.getApplication().getSolicitorService().setDatePreviousServiceReturned( + LocalDate.of(2021, 1, 1)); + + final CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final CaseDetails updatedCaseDetails = new CaseDetails<>(); + updatedCaseDetails.setData(caseData); + + when(submitConfirmService.submitConfirmService(caseDetails)).thenReturn(updatedCaseDetails); + AboutToStartOrSubmitResponse response = solicitorConfirmService.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getData().getApplication().getSolicitorService().getDetailsOfPreviousService()).isNull(); + assertThat(response.getData().getApplication().getSolicitorService().getDatePreviousServiceReturned()).isNull(); + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorGeneralApplicationTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorGeneralApplicationTest.java index fc57b515f92..1dca0fcd701 100644 --- a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorGeneralApplicationTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorGeneralApplicationTest.java @@ -60,6 +60,7 @@ import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTH_HEADER_VALUE; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_AUTHORIZATION_TOKEN; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.LOCAL_DATE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getListOfDivorceDocumentListValue; @@ -242,6 +243,7 @@ void shouldAddPaymentToApplicationPaymentsIfPaymentSuccessful() { .generalApplicationFee( FeeDetails.builder() .orderSummary(generalApplicationOrderSummary) + .serviceRequestReference(TEST_SERVICE_REFERENCE) .accountReferenceNumber(FEE_ACCOUNT_REF) .pbaNumbers( DynamicList.builder() @@ -295,8 +297,8 @@ void shouldAddPaymentToApplicationPaymentsIfPaymentSuccessful() { final var pbaResponse = new PbaResponse(CREATED, null, "1234"); when(paymentService.processPbaPayment( - caseData, TEST_CASE_ID, + TEST_SERVICE_REFERENCE, applicant1Solicitor, PBA_NUMBER, generalApplicationOrderSummary, @@ -419,6 +421,7 @@ void shouldReturnErrorsIfPaymentIsUnsuccessful() { .generalApplicationFee( FeeDetails.builder() .orderSummary(generalApplicationOrderSummary) + .serviceRequestReference(TEST_SERVICE_REFERENCE) .accountReferenceNumber(FEE_ACCOUNT_REF) .pbaNumbers( DynamicList.builder() @@ -461,8 +464,8 @@ void shouldReturnErrorsIfPaymentIsUnsuccessful() { final var pbaResponse = new PbaResponse(FORBIDDEN, "Account balance insufficient", null); when(paymentService.processPbaPayment( - caseData, TEST_CASE_ID, + TEST_SERVICE_REFERENCE, applicant2Solicitor, PBA_NUMBER, generalApplicationOrderSummary, diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorSubmitApplicationTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorSubmitApplicationTest.java index 6c198dde692..e417f17d0da 100644 --- a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorSubmitApplicationTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/SolicitorSubmitApplicationTest.java @@ -24,6 +24,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.payment.PaymentService; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.divorce.payment.model.PbaResponse; import uk.gov.hmcts.divorce.solicitor.event.page.SolPayment; @@ -35,7 +36,6 @@ import static java.lang.Integer.parseInt; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.FORBIDDEN; @@ -45,19 +45,14 @@ import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPayment; import static uk.gov.hmcts.divorce.divorcecase.model.State.Draft; import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; -import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_ISSUE; -import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_DIVORCE; -import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_DIVORCE; import static uk.gov.hmcts.divorce.solicitor.event.SolicitorSubmitApplication.SOLICITOR_SUBMIT; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_ORG_ID; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_ORG_NAME; -import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; -import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseDataWithMarriageDate; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getFeeListValue; -import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getPbaNumbersForAccount; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.validApplicant1CaseData; @ExtendWith(MockitoExtension.class) @@ -77,6 +72,9 @@ public class SolicitorSubmitApplicationTest { @Mock private PaymentService paymentService; + @Mock + private PaymentSetupService paymentSetupService; + @Mock private SubmissionService submissionService; @@ -90,29 +88,18 @@ public class SolicitorSubmitApplicationTest { void shouldSetOrderSummaryAndSolicitorFeesInPoundsAndSolicitorRolesAndPbaNumbersWhenAboutToStartIsInvoked() { final long caseId = TEST_CASE_ID; - final String authorization = "authorization"; - final OrderSummary orderSummary = mock(OrderSummary.class); final CaseDetails caseDetails = new CaseDetails<>(); final CaseData caseData = CaseData.builder().build(); caseDetails.setData(caseData); caseDetails.setId(caseId); - when(paymentService.getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE, KEYWORD_DIVORCE)).thenReturn(orderSummary); - when(orderSummary.getPaymentTotal()).thenReturn("55000"); - - var midEventCaseData = caseDataWithMarriageDate(); - midEventCaseData.getApplication().setPbaNumbers(getPbaNumbersForAccount("PBA0012345")); + when(paymentSetupService.createApplicationFeeOrderSummary(caseData, TEST_CASE_ID)) + .thenReturn(orderSummary); - var pbaResponse - = AboutToStartOrSubmitResponse.builder() - .data(midEventCaseData) - .build(); - - final AboutToStartOrSubmitResponse response = - solicitorSubmitApplication.aboutToStart(caseDetails); + var response = solicitorSubmitApplication.aboutToStart(caseDetails); - assertThat(response.getData().getApplication().getApplicationFeeOrderSummary()).isEqualTo(orderSummary); - assertThat(response.getData().getApplication().getSolApplicationFeeInPounds()).isEqualTo("550"); + assertThat(response.getData().getApplication().getApplicationFeeOrderSummary()) + .isEqualTo(orderSummary); } @Test @@ -148,11 +135,12 @@ void shouldAddPaymentIfPaymentsExists() { caseData.getApplication().setSolSignStatementOfTruth(YES); caseData.getApplication().setSolSignStatementOfTruth(YES); caseData.getApplication().setApplicationFeeOrderSummary(orderSummary); + caseData.getApplication().setApplicationFeeServiceRequestReference(TEST_SERVICE_REFERENCE); caseDetails.setData(caseData); caseDetails.setId(TEST_CASE_ID); PbaResponse pbaResponse = new PbaResponse(CREATED, null, "1234"); - when(paymentService.processPbaPayment(caseData, TEST_CASE_ID, null, PBA_NUMBER, orderSummary, FEE_ACCOUNT_REF)) + when(paymentService.processPbaPayment(TEST_CASE_ID, TEST_SERVICE_REFERENCE, null, PBA_NUMBER, orderSummary, FEE_ACCOUNT_REF)) .thenReturn(pbaResponse); final CaseDetails expectedCaseDetails = new CaseDetails<>(); @@ -270,6 +258,7 @@ void shouldReturnWithoutErrorIfSolStatementOfTruthIsNull() { .build() ); caseData.getApplication().setFeeAccountReference(FEE_ACCOUNT_REF); + caseData.getApplication().setApplicationFeeServiceRequestReference(TEST_SERVICE_REFERENCE); final CaseDetails caseDetails = new CaseDetails<>(); caseDetails.setId(TEST_CASE_ID); @@ -286,7 +275,8 @@ void shouldReturnWithoutErrorIfSolStatementOfTruthIsNull() { when(submissionService.submitApplication(caseDetails)).thenReturn(expectedCaseDetails); var pbaResponse = new PbaResponse(CREATED, null, "1234"); - when(paymentService.processPbaPayment(caseData, TEST_CASE_ID, null, PBA_NUMBER, orderSummary, FEE_ACCOUNT_REF)) + when(paymentService.processPbaPayment( + TEST_CASE_ID, TEST_SERVICE_REFERENCE, null, PBA_NUMBER, orderSummary, FEE_ACCOUNT_REF)) .thenReturn(pbaResponse); final AboutToStartOrSubmitResponse response = solicitorSubmitApplication @@ -445,12 +435,13 @@ void shouldSetStateToSubmittedIfPaymentMethodIsPbaAndPbaPaymentIsProcessedSucces ); caseData.getApplication().setFeeAccountReference(FEE_ACCOUNT_REF); caseData.getApplication().setApplicationFeeOrderSummary(orderSummary); + caseData.getApplication().setApplicationFeeServiceRequestReference(TEST_SERVICE_REFERENCE); caseDetails.setData(caseData); caseDetails.setId(TEST_CASE_ID); final var pbaResponse = new PbaResponse(CREATED, null, "1234"); - when(paymentService.processPbaPayment(caseData, TEST_CASE_ID, null, PBA_NUMBER, orderSummary, FEE_ACCOUNT_REF)) + when(paymentService.processPbaPayment(TEST_CASE_ID, TEST_SERVICE_REFERENCE, null, PBA_NUMBER, orderSummary, FEE_ACCOUNT_REF)) .thenReturn(pbaResponse); mockExpectedCaseDetails(caseDetails, caseData, Submitted); @@ -476,12 +467,13 @@ void shouldReturnErrorMessageIfPaymentMethodIsPbaAndPbaPaymentIsIsNotSuccessful( ); caseData.getApplication().setFeeAccountReference(FEE_ACCOUNT_REF); caseData.getApplication().setApplicationFeeOrderSummary(orderSummary); + caseData.getApplication().setApplicationFeeServiceRequestReference(TEST_SERVICE_REFERENCE); caseDetails.setData(caseData); caseDetails.setId(TEST_CASE_ID); final var pbaResponse = new PbaResponse(FORBIDDEN, "Account balance insufficient", null); - when(paymentService.processPbaPayment(caseData, TEST_CASE_ID, null, PBA_NUMBER, orderSummary, FEE_ACCOUNT_REF)) + when(paymentService.processPbaPayment(TEST_CASE_ID, TEST_SERVICE_REFERENCE, null, PBA_NUMBER, orderSummary, FEE_ACCOUNT_REF)) .thenReturn(pbaResponse); final AboutToStartOrSubmitResponse response = diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationSelectFeeTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationSelectFeeTest.java index 50fd27de49f..30a9e6540ac 100644 --- a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationSelectFeeTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/page/GeneralApplicationSelectFeeTest.java @@ -1,5 +1,6 @@ package uk.gov.hmcts.divorce.solicitor.event.page; +import jakarta.servlet.http.HttpServletRequest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -14,6 +15,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.payment.PaymentService; import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; +import uk.gov.hmcts.divorce.solicitor.service.CcdAccessService; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -25,6 +27,10 @@ import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_WITHOUT_NOTICE; import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; +import static uk.gov.hmcts.divorce.testutil.TestConstants.APP_1_SOL_AUTH_TOKEN; +import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTHORIZATION; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; @ExtendWith(MockitoExtension.class) @@ -36,6 +42,12 @@ public class GeneralApplicationSelectFeeTest { @Mock private PbaService pbaService; + @Mock + private CcdAccessService ccdAccessService; + + @Mock + private HttpServletRequest servletRequest; + @InjectMocks private GeneralApplicationSelectFee page; @@ -45,14 +57,15 @@ void shouldSetGeneralApplicationOrderSummaryIfWithNoticeFeeIsChosen() { caseData.setGeneralApplication(GeneralApplication.builder() .generalApplicationFeeType(FEE0227) .build()); + String applicantName = caseData.getApplicant1().getFullName(); final CaseDetails details = new CaseDetails<>(); details.setData(caseData); + details.setId(TEST_CASE_ID); final OrderSummary orderSummary = OrderSummary.builder().build(); - when(paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE)) - .thenReturn(orderSummary); + stubServiceRequestAndOrderSummaryCreation(orderSummary, KEYWORD_NOTICE, applicantName); AboutToStartOrSubmitResponse response = page.midEvent(details, details); @@ -61,6 +74,10 @@ void shouldSetGeneralApplicationOrderSummaryIfWithNoticeFeeIsChosen() { response.getData().getGeneralApplication().getGeneralApplicationFee().getOrderSummary(), orderSummary ); + assertEquals( + response.getData().getGeneralApplication().getGeneralApplicationFee().getServiceRequestReference(), + TEST_SERVICE_REFERENCE + ); } @Test @@ -69,14 +86,15 @@ void shouldSetGeneralApplicationOrderSummaryIfWithoutNoticeFeeIsChosen() { caseData.setGeneralApplication(GeneralApplication.builder() .generalApplicationFeeType(FEE0228) .build()); + String applicantName = caseData.getApplicant1().getFullName(); final CaseDetails details = new CaseDetails<>(); details.setData(caseData); + details.setId(TEST_CASE_ID); final OrderSummary orderSummary = OrderSummary.builder().build(); - when(paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_WITHOUT_NOTICE)) - .thenReturn(orderSummary); + stubServiceRequestAndOrderSummaryCreation(orderSummary, KEYWORD_WITHOUT_NOTICE, applicantName); AboutToStartOrSubmitResponse response = page.midEvent(details, details); @@ -85,6 +103,10 @@ void shouldSetGeneralApplicationOrderSummaryIfWithoutNoticeFeeIsChosen() { response.getData().getGeneralApplication().getGeneralApplicationFee().getOrderSummary(), orderSummary ); + assertEquals( + response.getData().getGeneralApplication().getGeneralApplicationFee().getServiceRequestReference(), + TEST_SERVICE_REFERENCE + ); } @Test @@ -96,6 +118,7 @@ void shouldPopulatePbaNumbersDynamicList() { final CaseDetails details = new CaseDetails<>(); details.setData(caseData); + details.setId(TEST_CASE_ID); DynamicList pbaNumbers = mock(DynamicList.class); @@ -109,4 +132,13 @@ void shouldPopulatePbaNumbersDynamicList() { pbaNumbers ); } + + private void stubServiceRequestAndOrderSummaryCreation(OrderSummary orderSummary, String keyword, String applicantName) { + when(paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, keyword)) + .thenReturn(orderSummary); + when(servletRequest.getHeader(AUTHORIZATION)).thenReturn(APP_1_SOL_AUTH_TOKEN); + when(ccdAccessService.isApplicant1(APP_1_SOL_AUTH_TOKEN, TEST_CASE_ID)).thenReturn(true); + when(paymentService.createServiceRequestReference(null, TEST_CASE_ID, applicantName, orderSummary)) + .thenReturn(TEST_SERVICE_REFERENCE); + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPaymentTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPaymentTest.java index 9fad2f71a07..dd2bdfb796f 100644 --- a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPaymentTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/page/SolPaymentTest.java @@ -13,6 +13,7 @@ import uk.gov.hmcts.ccd.sdk.type.DynamicListElement; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; import java.util.List; @@ -21,6 +22,8 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; @@ -30,6 +33,7 @@ import static uk.gov.hmcts.divorce.divorcecase.model.SolicitorPaymentMethod.FEES_HELP_WITH; import static uk.gov.hmcts.divorce.divorcecase.model.SolicitorPaymentMethod.FEE_PAY_BY_ACCOUNT; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; @ExtendWith(MockitoExtension.class) @@ -38,6 +42,9 @@ public class SolPaymentTest { @Mock private PbaService pbaService; + @Mock + private PaymentSetupService paymentSetupService; + @InjectMocks private SolPayment solPayment; @@ -64,6 +71,8 @@ void shouldRetrieveAndSetPbaNumbersWhenPaymentMethodIsPba() { when(pbaService.populatePbaDynamicList()) .thenReturn(pbaNumbers); + when(paymentSetupService.createApplicationFeeServiceRequest(any(CaseData.class), eq(TEST_CASE_ID), eq(null))) + .thenReturn(TEST_SERVICE_REFERENCE); AboutToStartOrSubmitResponse response = solPayment.midEvent(details, details); @@ -73,6 +82,8 @@ void shouldRetrieveAndSetPbaNumbersWhenPaymentMethodIsPba() { assertThat(pbaNumbersResponse.getListItems()) .extracting("label") .containsExactlyInAnyOrder("PBA0012345", "PBA0012346"); + assertThat(response.getData().getApplication().getApplicationFeeServiceRequestReference()) + .isEqualTo(TEST_SERVICE_REFERENCE); } @Test diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/service/SolicitorCreateApplicationApplicationServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/service/SolicitorCreateApplicationApplicationServiceTest.java index 65b9c95fed7..72a8fd160cf 100644 --- a/src/test/java/uk/gov/hmcts/divorce/solicitor/service/SolicitorCreateApplicationApplicationServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/service/SolicitorCreateApplicationApplicationServiceTest.java @@ -19,6 +19,7 @@ import uk.gov.hmcts.divorce.solicitor.service.task.InitialiseSolicitorCreatedApplication; import uk.gov.hmcts.divorce.solicitor.service.task.SetApplicant1SolicitorAddress; import uk.gov.hmcts.divorce.solicitor.service.task.SetApplicantGender; +import uk.gov.hmcts.divorce.solicitor.service.task.SetApplicationFeeServiceRequest; import uk.gov.hmcts.divorce.solicitor.service.task.SolicitorCourtDetails; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; @@ -69,6 +70,9 @@ class SolicitorCreateApplicationApplicationServiceTest { @Mock private SetApplicantGender setApplicantGender; + @Mock + private SetApplicationFeeServiceRequest setApplicationFeeServiceRequest; + @InjectMocks private SolicitorCreateApplicationService solicitorCreateApplicationService; @@ -86,6 +90,7 @@ void shouldCompleteStepsToCreateApplication() { when(setApplicant1SolicitorAddress.apply(caseDetails)).thenReturn(caseDetails); when(divorceApplicationDraft.apply(caseDetails)).thenReturn(caseDetails); when(setApplicantGender.apply(caseDetails)).thenReturn(caseDetails); + when(setApplicationFeeServiceRequest.apply(caseDetails)).thenReturn(caseDetails); final CaseDetails result = solicitorCreateApplicationService.aboutToSubmit(caseDetails); @@ -94,6 +99,7 @@ void shouldCompleteStepsToCreateApplication() { verify(initialiseSolicitorCreatedApplication).apply(caseDetails); verify(solicitorCourtDetails).apply(caseDetails); verify(divorceApplicationDraft).apply(caseDetails); + verify(setApplicationFeeServiceRequest).apply(caseDetails); } @Test diff --git a/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemNotifyRespondentApplyFinalOrderTest.java b/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemNotifyRespondentApplyFinalOrderTest.java index 53531ceaa1f..c034d52f0e6 100644 --- a/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemNotifyRespondentApplyFinalOrderTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/systemupdate/event/SystemNotifyRespondentApplyFinalOrderTest.java @@ -10,12 +10,15 @@ import uk.gov.hmcts.ccd.sdk.api.CaseDetails; import uk.gov.hmcts.ccd.sdk.api.Event; import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.ccd.sdk.type.OrderSummary; import uk.gov.hmcts.ccd.sdk.type.YesOrNo; import uk.gov.hmcts.divorce.common.notification.RespondentApplyForFinalOrderNotification; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; @@ -25,6 +28,7 @@ import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; import static uk.gov.hmcts.divorce.testutil.TestDataHelper.respondent; @@ -43,6 +47,9 @@ class SystemNotifyRespondentApplyFinalOrderTest { @Mock private RespondentApplyForFinalOrderNotification respondentApplyForFinalOrderNotification; + @Mock + private PaymentSetupService paymentSetupService; + @Test void shouldAddConfigurationToConfigBuilder() { final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); @@ -71,6 +78,38 @@ void shouldSetFinalOrderReminderSentApplicant2ToYes() { assertThat(response.getData().getFinalOrder().getFinalOrderReminderSentApplicant2()).isEqualTo(YesOrNo.YES); } + @Test + void shouldPrepareCaseDataForPbaPaymentIfTheRespondentIsRepresented() { + final CaseData caseData = caseData(); + caseData.setApplicant2(respondent()); + caseData.getApplicant2().setSolicitorRepresented(YesOrNo.YES); + + final CaseDetails details = new CaseDetails<>(); + details.setId(TEST_CASE_ID); + details.setData(caseData); + + OrderSummary orderSummary = new OrderSummary(); + + when(httpServletRequest.getHeader(AUTHORIZATION)) + .thenReturn("auth header"); + + when(paymentSetupService.createFinalOrderFeeOrderSummary(caseData, TEST_CASE_ID)) + .thenReturn(orderSummary); + + when(paymentSetupService.createFinalOrderFeeServiceRequest(caseData, TEST_CASE_ID, null, orderSummary)) + .thenReturn(TEST_SERVICE_REFERENCE); + + final AboutToStartOrSubmitResponse response = + systemNotifyRespondentApplyFinalOrder.aboutToSubmit(details, details); + + FinalOrder responseFinalOrder = response.getData().getFinalOrder(); + + assertThat(responseFinalOrder.getFinalOrderReminderSentApplicant2()).isEqualTo(YesOrNo.YES); + assertThat(responseFinalOrder.getApplicant2FinalOrderFeeServiceRequestReference()).isEqualTo(TEST_SERVICE_REFERENCE); + assertThat(responseFinalOrder.getApplicant2FinalOrderFeeOrderSummary()).isEqualTo(orderSummary); + assertThat(responseFinalOrder.getApplicant2SolFinalOrderFeeOrderSummary()).isEqualTo(orderSummary); + } + @Test void shouldSendNotification() { final CaseData caseData = caseData(); diff --git a/src/test/java/uk/gov/hmcts/divorce/systemupdate/service/CcdSearchServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/systemupdate/service/CcdSearchServiceTest.java index a5eb9391312..a284358fa5d 100644 --- a/src/test/java/uk/gov/hmcts/divorce/systemupdate/service/CcdSearchServiceTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/systemupdate/service/CcdSearchServiceTest.java @@ -772,7 +772,7 @@ void shouldReturnSolePaperApplicationsWhereApplicant2OfflineFlagShouldBeSet() { final List searchResult = ccdSearchService.searchSolePaperApplicationsWhereApplicant2OfflineFlagShouldBeSet(user, SERVICE_AUTHORIZATION); - assertThat(searchResult).hasSize(100); + assertThat(searchResult.size()).isEqualTo(100); } @Test @@ -937,7 +937,6 @@ private SearchSourceBuilder searchSourceBuilderForCreatedOrListedCasesWithCasesT @Test void shouldReturnAggregatedResultsByStateAndLastStateModifiedDate() { - // Arrange ReturnedCaseDetails case1 = ReturnedCaseDetails.builder() .id(1L) .state(State.Submitted) // Assuming State is an enum or object with SUBMITTED @@ -1044,4 +1043,77 @@ void shouldReturnAggregatedResultsByStateAndLastStateModifiedDateFromSearch() { assertThat(result.get("AwaitingAos")).containsEntry("2023-08-20",1L); } + @Test + void shouldReturnAllOldDivorceCases() { + final BoolQueryBuilder query = boolQuery().must(matchQuery("someField", "someValue")); + final User user = new User(SYSTEM_UPDATE_AUTH_TOKEN, UserInfo.builder().build()); + + final List caseDetailsList1 = createCaseDetailsList(PAGE_SIZE, 1); + final List caseDetailsList2 = createCaseDetailsList(PAGE_SIZE - 1, PAGE_SIZE + 1); + final SearchResult searchResult1 = SearchResult.builder().total(PAGE_SIZE).cases(caseDetailsList1).build(); + final SearchResult searchResult2 = SearchResult.builder().total(PAGE_SIZE - 1).cases(caseDetailsList2).build(); + + when(coreCaseDataApi.searchCases( + SYSTEM_UPDATE_AUTH_TOKEN, + SERVICE_AUTHORIZATION, + "DIVORCE", + getSearchSourceBuilder(0, PAGE_SIZE, query).toString())) + .thenReturn(searchResult1); + + when(coreCaseDataApi.searchCases( + SYSTEM_UPDATE_AUTH_TOKEN, + SERVICE_AUTHORIZATION, + "DIVORCE", + getSearchSourceBuilder(PAGE_SIZE, PAGE_SIZE, query).toString())) + .thenReturn(searchResult2); + + final List result = ccdSearchService.searchForOldDivorceCasesWithQuery(query, user, SERVICE_AUTHORIZATION); + + assertThat(result).hasSize(PAGE_SIZE * 2 - 1); + } + + @Test + void shouldReturnEmptyListWhenNoOldDivorceCasesFound() { + final BoolQueryBuilder query = boolQuery().must(matchQuery("someField", "someValue")); + final User user = new User(SYSTEM_UPDATE_AUTH_TOKEN, UserInfo.builder().build()); + + final SearchResult emptySearchResult = SearchResult.builder().total(0).cases(emptyList()).build(); + + when(coreCaseDataApi.searchCases( + SYSTEM_UPDATE_AUTH_TOKEN, + SERVICE_AUTHORIZATION, + "DIVORCE", + getSearchSourceBuilder(0, PAGE_SIZE, query).toString())) + .thenReturn(emptySearchResult); + + final List result = ccdSearchService.searchForOldDivorceCasesWithQuery(query, user, SERVICE_AUTHORIZATION); + + assertThat(result).isEmpty(); + } + + @Test + void shouldThrowCcdSearchCaseExceptionWhenFeignExceptionOccurs() { + final BoolQueryBuilder query = boolQuery().must(matchQuery("someField", "someValue")); + final User user = new User(SYSTEM_UPDATE_AUTH_TOKEN, UserInfo.builder().build()); + + doThrow(feignException(500, "Internal Server Error")) + .when(coreCaseDataApi).searchCases( + SYSTEM_UPDATE_AUTH_TOKEN, SERVICE_AUTHORIZATION, "DIVORCE", + getSearchSourceBuilder(0, PAGE_SIZE, query).toString()); + + final CcdSearchCaseException exception = assertThrows( + CcdSearchCaseException.class, + () -> ccdSearchService.searchForOldDivorceCasesWithQuery(query, user, SERVICE_AUTHORIZATION)); + + assertThat(exception.getMessage()).contains("Failed to complete search for Old Divorce Cases"); + } + + private SearchSourceBuilder getSearchSourceBuilder(final int from, final int size, final BoolQueryBuilder query) { + return SearchSourceBuilder + .searchSource() + .sort(DUE_DATE, ASC) + .query(query) + .from(from) + .size(size); + } } diff --git a/src/test/java/uk/gov/hmcts/divorce/systemupdate/service/task/GenerateApplyForFinalOrderDocumentTest.java b/src/test/java/uk/gov/hmcts/divorce/systemupdate/service/task/GenerateApplyForFinalOrderDocumentTest.java index 3da565d852a..da33912cbf0 100644 --- a/src/test/java/uk/gov/hmcts/divorce/systemupdate/service/task/GenerateApplyForFinalOrderDocumentTest.java +++ b/src/test/java/uk/gov/hmcts/divorce/systemupdate/service/task/GenerateApplyForFinalOrderDocumentTest.java @@ -47,7 +47,7 @@ import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getApplicantWithAddress; @ExtendWith(MockitoExtension.class) -public class GenerateApplyForFinalOrderDocumentTest { +class GenerateApplyForFinalOrderDocumentTest { @Mock private CaseDataDocumentService caseDataDocumentService; @@ -71,6 +71,7 @@ void shouldGenerateWithSoleDivorceContent() { caseData.setApplicant1(getApplicantWithAddress()); caseData.getApplicant1().setLanguagePreferenceWelsh(NO); caseData.getFinalOrder().setDateFinalOrderEligibleFrom(LocalDate.now()); + caseData.getConditionalOrder().setGrantedDate(LocalDate.now()); final Map templateContent = new HashMap<>(); templateContent.put(CASE_REFERENCE, formatId(TEST_CASE_ID)); @@ -116,6 +117,7 @@ void shouldGenerateWithSoleDivorceContentApp2() { caseData.setApplicant1(getApplicantWithAddress()); caseData.getApplicant1().setLanguagePreferenceWelsh(NO); caseData.getFinalOrder().setDateFinalOrderEligibleFrom(LocalDate.now()); + caseData.getConditionalOrder().setGrantedDate(LocalDate.now()); final Map templateContent = new HashMap<>(); templateContent.put(CASE_REFERENCE, formatId(TEST_CASE_ID)); @@ -161,6 +163,7 @@ void shouldGenerateWithJointDivorceContent() { caseData.setApplicant1(getApplicantWithAddress()); caseData.getApplicant1().setLanguagePreferenceWelsh(NO); caseData.getFinalOrder().setDateFinalOrderEligibleFrom(LocalDate.now()); + caseData.getConditionalOrder().setGrantedDate(LocalDate.now()); final Map templateContent = new HashMap<>(); templateContent.put(CASE_REFERENCE, formatId(TEST_CASE_ID)); @@ -206,6 +209,7 @@ void shouldGenerateWithDissolutionContent() { caseData.setApplicant1(getApplicantWithAddress()); caseData.getApplicant1().setLanguagePreferenceWelsh(NO); caseData.getFinalOrder().setDateFinalOrderEligibleFrom(LocalDate.now()); + caseData.getConditionalOrder().setGrantedDate(LocalDate.now()); final Map templateContent = new HashMap<>(); templateContent.put(CASE_REFERENCE, formatId(TEST_CASE_ID)); diff --git a/src/test/java/uk/gov/hmcts/divorce/testutil/TestConstants.java b/src/test/java/uk/gov/hmcts/divorce/testutil/TestConstants.java index cdd94ca8a93..b538b88d488 100644 --- a/src/test/java/uk/gov/hmcts/divorce/testutil/TestConstants.java +++ b/src/test/java/uk/gov/hmcts/divorce/testutil/TestConstants.java @@ -12,6 +12,8 @@ public final class TestConstants { public static final String TEST_FIRST_NAME = "test_first_name"; public static final String TEST_MIDDLE_NAME = "test_middle_name"; public static final String TEST_LAST_NAME = "test_last_name"; + public static final String TEST_OTHER_NAME = "test_other_name"; + public static final String TEST_OTHER_EMAIL = "test_other@test.com"; public static final String TEST_APP2_FIRST_NAME = "test_app2_first_name"; public static final String TEST_APP2_MIDDLE_NAME = "test_app2_middle_name"; public static final String TEST_APP2_LAST_NAME = "test_app2_last_name"; @@ -31,6 +33,7 @@ public final class TestConstants { public static final String APP2_SOLICITOR_UPDATE_CONTACT_DETAILS_MID_EVENT_URL = "/callbacks/mid-event?page=Applicant2SolUpdateContactDetails"; public static final String CREATE_GENERAL_ORDER_MID_EVENT_URL = "/callbacks/mid-event?page=CreateGeneralOrder"; + public static final String GENERAL_APPLICATION_SELECT_FEE_MID_EVENT_URL = "/callbacks/mid-event?page=generalApplicationSelectFeeType"; public static final String CO_REFUSAL_ORDER_WITH_MORE_INFO_MID_EVENT_URL = "/callbacks/mid-event?page=refusalOrderClarification"; public static final String CO_REFUSAL_ORDER_WITH_AMENDMENTS_MID_EVENT_URL = "/callbacks/mid-event?page=amendApplication"; @@ -103,6 +106,10 @@ public final class TestConstants { public static final String TEST_FINANCIAL_ORDER_POLICY_HINT_TEXT = "A financial order is a legal document that describes how the " + "money and property will be divided. The application will be done separately."; + public static final String TEST_TEXT = "Test"; + + public static final String SMART_SURVEY_TEST_URL = "smartSurveyTestUrl"; + private TestConstants() { } } diff --git a/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java b/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java index db32cbb5bd1..c4e9cb53f64 100644 --- a/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java +++ b/src/test/java/uk/gov/hmcts/divorce/testutil/TestDataHelper.java @@ -34,6 +34,7 @@ import uk.gov.hmcts.divorce.divorcecase.model.Application; import uk.gov.hmcts.divorce.divorcecase.model.ApplicationType; import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments; import uk.gov.hmcts.divorce.divorcecase.model.CaseInvite; import uk.gov.hmcts.divorce.divorcecase.model.ConditionalOrder; import uk.gov.hmcts.divorce.divorcecase.model.ConditionalOrderCourt; @@ -58,8 +59,18 @@ import uk.gov.hmcts.divorce.divorcecase.model.MarriageDetails; import uk.gov.hmcts.divorce.divorcecase.model.Payment; import uk.gov.hmcts.divorce.divorcecase.model.PaymentStatus; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationList; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseSoleParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties; import uk.gov.hmcts.divorce.divorcecase.model.Solicitor; import uk.gov.hmcts.divorce.divorcecase.model.SolicitorService; +import uk.gov.hmcts.divorce.divorcecase.model.State; import uk.gov.hmcts.divorce.divorcecase.model.UserRole; import uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants; import uk.gov.hmcts.divorce.document.model.ConfidentialDivorceDocument; @@ -72,6 +83,7 @@ import uk.gov.hmcts.reform.ccd.client.model.CallbackRequest; import uk.gov.hmcts.reform.ccd.client.model.CaseDetails; +import java.time.Clock; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; @@ -87,6 +99,7 @@ import static java.lang.String.format; import static java.lang.String.join; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.time.LocalDateTime.now; import static java.util.Collections.singletonList; import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.isNotEmpty; @@ -101,6 +114,8 @@ import static uk.gov.hmcts.divorce.divorcecase.model.ApplicantPrayer.DissolveDivorce.DISSOLVE_DIVORCE; import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.JOINT_APPLICATION; import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.SOLE_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.OTHER; +import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.OfflineDocumentReceived.RFI_RESPONSE; import static uk.gov.hmcts.divorce.divorcecase.model.CaseDocuments.builder; import static uk.gov.hmcts.divorce.divorcecase.model.ConditionalOrderCourt.BURY_ST_EDMUNDS; import static uk.gov.hmcts.divorce.divorcecase.model.ContactDetailsType.PRIVATE; @@ -110,8 +125,15 @@ import static uk.gov.hmcts.divorce.divorcecase.model.Gender.MALE; import static uk.gov.hmcts.divorce.divorcecase.model.JurisdictionConnections.APP_1_APP_2_RESIDENT; import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.APPLICANT; import static uk.gov.hmcts.divorce.divorcecase.model.ServiceMethod.COURT_SERVICE; import static uk.gov.hmcts.divorce.divorcecase.model.ServiceMethod.SOLICITOR_SERVICE; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; import static uk.gov.hmcts.divorce.divorcecase.model.SupplementaryCaseType.NA; import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.APPLICANT_1_FULL_NAME; import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.APPLICANT_2_FULL_NAME; @@ -150,6 +172,7 @@ import static uk.gov.hmcts.divorce.notification.CommonContent.LAST_NAME; import static uk.gov.hmcts.divorce.notification.CommonContent.PARTNER; import static uk.gov.hmcts.divorce.notification.CommonContent.RESPONDENT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SENT_TO_BOTH_APPLICANTS; import static uk.gov.hmcts.divorce.notification.CommonContent.SIGN_IN_DISSOLUTION_URL; import static uk.gov.hmcts.divorce.notification.CommonContent.SIGN_IN_DIVORCE_URL; import static uk.gov.hmcts.divorce.notification.CommonContent.SIGN_IN_URL; @@ -158,6 +181,7 @@ import static uk.gov.hmcts.divorce.notification.FormatUtil.DATE_TIME_FORMATTER; import static uk.gov.hmcts.divorce.notification.FormatUtil.formatId; import static uk.gov.hmcts.divorce.systemupdate.service.task.GenerateCertificateOfEntitlementHelper.IS_JOINT; +import static uk.gov.hmcts.divorce.testutil.ClockTestUtil.setMockClock; import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_FIRST_NAME; import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_LAST_NAME; import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_SIGN_IN_DISSOLUTION_TEST_URL; @@ -182,6 +206,7 @@ import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_EMAIL; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_FIRM_NAME; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_TEXT; import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; public class TestDataHelper { @@ -575,7 +600,7 @@ public static CaseData validCaseWithCourtHearing() { public static CaseData validCaseDataForAwaitingFinalOrder() { CaseData caseData = validCaseWithCourtHearing(); LocalDateTime dateAndTimeOfHearing = caseData.getConditionalOrder().getDateAndTimeOfHearing(); - + caseData.getConditionalOrder().setGrantedDate(caseData.getConditionalOrder().getDecisionDate()); FinalOrder finalOrder = caseData.getFinalOrder(); finalOrder.setDateFinalOrderEligibleFrom(caseData.getFinalOrder().getDateFinalOrderEligibleFrom(dateAndTimeOfHearing)); @@ -850,6 +875,36 @@ public static Map getConditionalOrderTemplateVars(ApplicationTyp return templateVars; } + public static Map getRequestForInformationTemplateVars() { + Map templateVars = getMainTemplateVars(); + templateVars.put(IS_JOINT, CommonContent.NO); + templateVars.put(WIFE_JOINT, CommonContent.NO); + templateVars.put(HUSBAND_JOINT, CommonContent.NO); + templateVars.put(CIVIL_PARTNER_JOINT, CommonContent.NO); + templateVars.put(SENT_TO_BOTH_APPLICANTS, CommonContent.NO); + + return templateVars; + } + + public static Map getRequestForInformationTemplateVars(ApplicationType applicationType, + RequestForInformationJointParties parties, + Boolean isDivorce, + Applicant partner) { + Map templateVars = getRequestForInformationTemplateVars(); + + if (applicationType.equals(JOINT_APPLICATION) && parties.equals(BOTH)) { + templateVars.put(IS_JOINT, CommonContent.YES); + templateVars.put(SENT_TO_BOTH_APPLICANTS, CommonContent.YES); + if (isDivorce) { + templateVars.put(HUSBAND_JOINT, MALE.equals(partner.getGender()) ? CommonContent.YES : CommonContent.NO); + templateVars.put(WIFE_JOINT, FEMALE.equals(partner.getGender()) ? CommonContent.YES : CommonContent.NO); + } else { + templateVars.put(CIVIL_PARTNER_JOINT, CommonContent.YES); + } + } + return templateVars; + } + public static Map getBasicTemplateVars() { Map templateVars = new HashMap<>(); templateVars.put(APPLICATION_REFERENCE, FORMATTED_TEST_CASE_ID); @@ -1364,4 +1419,290 @@ public static CaseData buildCaseDataCOPronounced(final YesOrNo isOffline, final .build()) .build(); } + + public static uk.gov.hmcts.ccd.sdk.api.CaseDetails getRequestForInformationCaseDetails() { + CaseData caseData = getRequestForInformationBaseData(SOLE_APPLICATION, true, false); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + setRequestForInformationBaseRequestValues(caseData); + + return getRequestForInformationBaseDetails(caseData); + } + + public static uk.gov.hmcts.ccd.sdk.api.CaseDetails getRequestForInformationCaseDetails( + RequestForInformationSoleParties soleParties, + Boolean applicantRepresented, + Boolean applicant2Represented + ) { + CaseData caseData = getRequestForInformationBaseData(SOLE_APPLICATION, applicantRepresented, applicant2Represented); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(soleParties); + setRequestForInformationBaseRequestValues(caseData); + + return getRequestForInformationBaseDetails(caseData); + } + + public static uk.gov.hmcts.ccd.sdk.api.CaseDetails getRequestForInformationCaseDetails( + RequestForInformationJointParties jointParties, + Boolean applicantRepresented, + Boolean applicant2Represented + ) { + CaseData caseData = getRequestForInformationBaseData(JOINT_APPLICATION, applicantRepresented, applicant2Represented); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(jointParties); + setRequestForInformationBaseRequestValues(caseData); + + return getRequestForInformationBaseDetails(caseData); + } + + public static CaseData getRequestForInformationBaseData(ApplicationType applicationType, + Boolean applicantRepresented, + Boolean applicant2Represented) { + final CaseData caseData = caseData(); + caseData.setApplicationType(applicationType); + if (applicantRepresented) { + caseData.setApplicant1(applicantRepresentedBySolicitor()); + } + if (applicant2Represented) { + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setGender(MALE); + } else { + caseData.setApplicant2(getApplicant(MALE)); + } + + return caseData; + } + + public static void setRequestForInformationBaseRequestValues(CaseData caseData) { + caseData.getRequestForInformationList().getRequestForInformation().setValues(caseData); + caseData.getRequestForInformationList().addRequestToList(caseData.getRequestForInformationList().getRequestForInformation()); + } + + public static uk.gov.hmcts.ccd.sdk.api.CaseDetails getRequestForInformationBaseDetails(CaseData caseData) { + final uk.gov.hmcts.ccd.sdk.api.CaseDetails details = new uk.gov.hmcts.ccd.sdk.api.CaseDetails<>(); + details.setData(caseData); + details.setState(InformationRequested); + details.setId(TEST_CASE_ID); + + return details; + } + + public static boolean isApplicant2(CaseData caseData, Applicant applicant) { + return caseData.getApplicant2().equals(applicant); + } + + public static RequestForInformationResponseDraft getRequestForInformationResponseDraft(CaseData caseData, Applicant applicant) { + RequestForInformationResponseDraft draft; + if (isApplicant2(caseData, applicant)) { + draft = applicant.isRepresented() + ? caseData.getRequestForInformationList().getRequestForInformationResponseApplicant2Solicitor() + : caseData.getRequestForInformationList().getRequestForInformationResponseApplicant2(); + } else { + draft = applicant.isRepresented() + ? caseData.getRequestForInformationList().getRequestForInformationResponseApplicant1Solicitor() + : caseData.getRequestForInformationList().getRequestForInformationResponseApplicant1(); + } + + return draft; + } + + public static void addDocumentToRequestForInformationResponseDraft(RequestForInformationResponseDraft draft) { + final DivorceDocument uploadedDocument = documentWithType(null).getValue(); + draft.addDocument(uploadedDocument); + } + + public static void addDocumentToRequestForInformationOfflineResponseDraft(RequestForInformationOfflineResponseDraft draft) { + final DivorceDocument uploadedDocument = documentWithType(null).getValue(); + draft.addDocument(uploadedDocument); + } + + private static void clearDraft(RequestForInformationList requestForInformationList, boolean isApplicant2, boolean isRepresented) { + if (isApplicant2) { + if (isRepresented) { + requestForInformationList.setRequestForInformationResponseApplicant2Solicitor(new RequestForInformationResponseDraft()); + } else { + requestForInformationList.setRequestForInformationResponseApplicant2(new RequestForInformationResponseDraft()); + } + } else { + if (isRepresented) { + requestForInformationList.setRequestForInformationResponseApplicant1Solicitor(new RequestForInformationResponseDraft()); + } else { + requestForInformationList.setRequestForInformationResponseApplicant1(new RequestForInformationResponseDraft()); + } + } + } + + private static RequestForInformationResponseParties getResponseParty(boolean isApplicant2, boolean isRepresented) { + if (isApplicant2) { + return isRepresented ? APPLICANT2SOLICITOR : APPLICANT2; + } else { + return isRepresented ? APPLICANT1SOLICITOR : APPLICANT1; + } + } + + public static void buildOfflineDraft(CaseData caseData, RequestForInformationOfflineResponseSoleParties soleParty, + boolean addDetails, boolean addDocument, boolean setAllDocsUploaded, boolean sendNotifications) { + caseData.getRequestForInformationList().getRequestForInformationOfflineResponseDraft().setRfiOfflineSoleResponseParties(soleParty); + buildOfflineDraft(caseData, addDetails, addDocument, setAllDocsUploaded, sendNotifications); + } + + public static void buildOfflineDraft(CaseData caseData, RequestForInformationOfflineResponseJointParties jointParty, + boolean addDetails, boolean addDocument, boolean setAllDocsUploaded, boolean sendNotifications) { + caseData.getRequestForInformationList().getRequestForInformationOfflineResponseDraft().setRfiOfflineJointResponseParties( + jointParty + ); + buildOfflineDraft(caseData, addDetails, addDocument, setAllDocsUploaded, sendNotifications); + } + + public static void buildOfflineDraft(CaseData caseData, + boolean addDetails, + boolean addDocument, + boolean setAllDocsUploaded, + boolean sendNotifications + ) { + RequestForInformationOfflineResponseDraft draft = + caseData.getRequestForInformationList().getRequestForInformationOfflineResponseDraft(); + if (addDetails) { + draft.setRfiOfflineDraftResponseDetails(TEST_TEXT); + } + if (addDocument) { + addDocumentToRequestForInformationOfflineResponseDraft(draft); + } + if (setAllDocsUploaded) { + draft.setRfiOfflineAllDocumentsUploaded(YES); + } else { + draft.setRfiOfflineAllDocumentsUploaded(NO); + } + if (sendNotifications) { + draft.setRfiOfflineResponseSendNotifications(YES); + } else { + draft.setRfiOfflineResponseSendNotifications(NO); + } + } + + public static void buildDraft(CaseData caseData, + Applicant applicant, + boolean addDetails, + boolean addDocument, + boolean setCannotUpload + ) { + RequestForInformationResponseDraft draft = getRequestForInformationResponseDraft(caseData, applicant); + if (addDetails) { + draft.setRfiDraftResponseDetails(TEST_TEXT); + } + if (addDocument) { + addDocumentToRequestForInformationResponseDraft(draft); + } + if (setCannotUpload) { + draft.setRfiDraftResponseCannotUploadDocs(YES); + } + } + + public static void addResponseToLatestRequestForInformation(CaseData caseData, Applicant applicant) { + final boolean isApplicant2 = isApplicant2(caseData, applicant); + final RequestForInformationList requestForInformationList = caseData.getRequestForInformationList(); + final RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + buildDraft(caseData, applicant, true, true, false); + + requestForInformationResponse.setValues(caseData, getResponseParty(isApplicant2, applicant.isRepresented())); + + requestForInformationList.getLatestRequest().addResponseToList(requestForInformationResponse); + + clearDraft(requestForInformationList, isApplicant2, applicant.isRepresented()); + } + + public static void addCannotUploadResponseToLatestRequestForInformation(CaseData caseData, Applicant applicant) { + final boolean isApplicant2 = isApplicant2(caseData, applicant); + final RequestForInformationList requestForInformationList = caseData.getRequestForInformationList(); + final RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + buildDraft(caseData, applicant, true, false, true); + + requestForInformationResponse.setValues(caseData, getResponseParty(isApplicant2, applicant.isRepresented())); + + requestForInformationList.getLatestRequest().addResponseToList(requestForInformationResponse); + + clearDraft(requestForInformationList, isApplicant2, applicant.isRepresented()); + } + + public static void addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation(CaseData caseData, + RequestForInformationOfflineResponseSoleParties soleParty) { + buildOfflineDraft(caseData, soleParty, true, true, false, false); + addOfflineResponseToLatestRequestForInformation(caseData); + } + + public static void addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation(CaseData caseData, + RequestForInformationOfflineResponseJointParties jointParty) { + buildOfflineDraft(caseData, jointParty, true, true, false, false); + addOfflineResponseToLatestRequestForInformation(caseData); + } + + public static void addOfflineResponseToLatestRequestForInformation(CaseData caseData, + RequestForInformationOfflineResponseSoleParties soleParty) { + buildOfflineDraft(caseData, soleParty, true, true, true, false); + addOfflineResponseToLatestRequestForInformation(caseData); + } + + public static void addOfflineResponseToLatestRequestForInformation(CaseData caseData, + RequestForInformationOfflineResponseJointParties jointParty) { + buildOfflineDraft(caseData, jointParty, true, true, true, false); + addOfflineResponseToLatestRequestForInformation(caseData); + } + + private static void addOfflineResponseToLatestRequestForInformation(CaseData caseData) { + final RequestForInformationList requestForInformationList = caseData.getRequestForInformationList(); + final RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + + requestForInformationResponse.setValues( + caseData, + caseData.getRequestForInformationList().getRequestForInformationOfflineResponseDraft() + ); + + requestForInformationList.getLatestRequest().addResponseToList(requestForInformationResponse); + + caseData.getRequestForInformationList().setRequestForInformationOfflineResponseDraft( + new RequestForInformationOfflineResponseDraft() + ); + } + + public static void setSendNotificationFlagOnLatestOfflineResponse(CaseData caseData, boolean sendNotifications) { + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse().setRfiOfflineResponseNotificationsRequested( + YesOrNo.from(sendNotifications) + ); + } + + public static void addRfiResponseDocumentToCaseData(CaseData caseData, Clock clock) { + setMockClock(clock); + + final Document document = Document.builder() + .url("/filename") + .binaryUrl("/filename/binary") + .filename("filename") + .build(); + final ListValue scannedRfirDocument = ListValue + .builder() + .id(OTHER.getLabel()) + .value( + ScannedDocument.builder() + .scannedDate(now(clock)) + .fileName("RFIR.pdf") + .type(ScannedDocumentType.OTHER) + .url(document) + .build() + ) + .build(); + caseData.setDocuments( + CaseDocuments.builder() + .typeOfDocumentAttached(RFI_RESPONSE) + .scannedDocuments(List.of(scannedRfirDocument)) + .scannedDocumentNames( + DynamicList + .builder() + .value( + DynamicListElement + .builder() + .label("RFIR.pdf") + .build() + ) + .build() + ) + .build() + ); + } } From adc17f3891fdad33d39d53e44037650807fe39e1 Mon Sep 17 00:00:00 2001 From: pallavijustice Date: Mon, 30 Dec 2024 18:30:15 +0000 Subject: [PATCH 16/16] NFDIV-4356 - Merge from master --- ...espondent-solicitor-apply-final-order.json | 83 + .../GeneralApplicationSelectFeeIT.java | 130 ++ ...seworkerRemoveFailedSolNoticeOfChange.java | 61 + .../event/CaseworkerRemoveNote.java | 65 + .../CaseworkerRequestForInformation.java | 325 +++ ...seworkerRequestForInformationResponse.java | 210 ++ .../RequestForInformationNotification.java | 234 ++ ...CitizenRespondToRequestForInformation.java | 206 ++ ...estForInformationResponseNotification.java | 97 + ...nformationResponsePartnerNotification.java | 184 ++ .../divorcecase/model/CaseDataOldDivorce.java | 70 + .../model/RequestForInformation.java | 159 ++ .../model/RequestForInformationAuthParty.java | 24 + .../RequestForInformationJointParties.java | 24 + .../model/RequestForInformationList.java | 110 + ...estForInformationOfflineResponseDraft.java | 93 + ...nformationOfflineResponseJointParties.java | 27 + ...InformationOfflineResponseSoleParties.java | 21 + .../model/RequestForInformationResponse.java | 207 ++ .../RequestForInformationResponseDraft.java | 60 + .../RequestForInformationResponseParties.java | 27 + .../RequestForInformationSoleParties.java | 18 + .../model/access/Applicant1DeleteAccess.java | 21 + .../model/access/Applicant2DeleteAccess.java | 21 + .../divorce/payment/PaymentSetupService.java | 72 + ...SolicitorRespondRequestForInformation.java | 162 ++ ...SolicitorRespondRequestForInformation.java | 163 ++ .../task/SetApplicationFeeServiceRequest.java | 39 + ...rkerRemoveFailedSolNoticeOfChangeTest.java | 69 + .../event/CaseworkerRemoveNoteTest.java | 88 + ...rkerRequestForInformationResponseTest.java | 340 +++ .../CaseworkerRequestForInformationTest.java | 1873 +++++++++++++++++ ...RequestForInformationNotificationTest.java | 371 ++++ ...zenRespondToRequestForInformationTest.java | 684 ++++++ ...orInformationResponseNotificationTest.java | 241 +++ ...mationResponsePartnerNotificationTest.java | 476 +++++ .../event/page/SolFinalOrderPaymentTest.java | 59 + .../payment/PaymentSetupServiceTest.java | 173 ++ ...citorRespondRequestForInformationTest.java | 315 +++ ...citorRespondRequestForInformationTest.java | 284 +++ .../SetApplicationFeeServiceRequestTest.java | 53 + 41 files changed, 7939 insertions(+) create mode 100644 src/functionalTest/resources/responses/response-system-notify-respondent-solicitor-apply-final-order.json create mode 100644 src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/GeneralApplicationSelectFeeIT.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveFailedSolNoticeOfChange.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveNote.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformation.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationResponse.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/caseworker/service/notification/RequestForInformationNotification.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenRespondToRequestForInformation.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponseNotification.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponsePartnerNotification.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseDataOldDivorce.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformation.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationAuthParty.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationJointParties.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationList.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseDraft.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseJointParties.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseSoleParties.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponse.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponseDraft.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponseParties.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationSoleParties.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/Applicant1DeleteAccess.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/Applicant2DeleteAccess.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/payment/PaymentSetupService.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/solicitor/event/Applicant1SolicitorRespondRequestForInformation.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/solicitor/event/Applicant2SolicitorRespondRequestForInformation.java create mode 100644 src/main/java/uk/gov/hmcts/divorce/solicitor/service/task/SetApplicationFeeServiceRequest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveFailedSolNoticeOfChangeTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveNoteTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationResponseTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/caseworker/service/notification/RequestForInformationNotificationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenRespondToRequestForInformationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponseNotificationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponsePartnerNotificationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/common/event/page/SolFinalOrderPaymentTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/payment/PaymentSetupServiceTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/solicitor/event/Applicant1SolicitorRespondRequestForInformationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/solicitor/event/Applicant2SolicitorRespondRequestForInformationTest.java create mode 100644 src/test/java/uk/gov/hmcts/divorce/solicitor/service/task/SetApplicationFeeServiceRequestTest.java diff --git a/src/functionalTest/resources/responses/response-system-notify-respondent-solicitor-apply-final-order.json b/src/functionalTest/resources/responses/response-system-notify-respondent-solicitor-apply-final-order.json new file mode 100644 index 00000000000..a2ab4a767c8 --- /dev/null +++ b/src/functionalTest/resources/responses/response-system-notify-respondent-solicitor-apply-final-order.json @@ -0,0 +1,83 @@ +{ + "data": { + "accessCode": "", + "applicant1ContactDetailsType": "private", + "applicant1Email": "simulate-delivered2@notifications.service.gov.uk", + "applicant1FinancialOrder": "No", + "applicant1FirstName": "test_first_name_male", + "applicant1Gender": "male", + "applicant1Address": {}, + "sentNotifications": {}, + "changeOfRepresentatives": [], + "coCronRetriesRemindApplicantApplyCo": 0, + "applicant1LanguagePreferenceWelsh": "No", + "applicant1LastName": "test_last_name", + "applicant1LegalProceedings": "Yes", + "applicant1MiddleName": "test_middle_name", + "applicant1PrayerDissolveDivorce": ["dissolveDivorce"], + "applicant1StatementOfTruth": "Yes", + "applicant2ContactDetailsType": "public", + "applicant2Email": "simulate-delivered@notifications.service.gov.uk", + "applicant2FinancialOrder": "No", + "applicant2FirstName": "test_first_name_female", + "applicant2Gender": "female", + "applicant2Address": {}, + "applicant2InviteEmailAddress": "simulate-delivered@notifications.service.gov.uk", + "applicant2LanguagePreferenceWelsh": "No", + "applicant2LastName": "test_last_name", + "applicant2MiddleName": "test_middle_name", + "applicant2PrayerDissolveDivorce": ["dissolveDivorce"], + "applicant2StatementOfTruth": "Yes", + "applicant2SolicitorRepresented": "Yes", + "applicant2SolicitorEmail": "testsol@test.com", + "applicant2SolicitorName": "The Solicitor", + "applicant2FinalOrderFeeOrderSummary": { + "Fees": [ + { + "value": { + "FeeAmount": "${json-unit.any-string}", + "FeeCode": "${json-unit.any-string}", + "FeeDescription": "${json-unit.any-string}", + "FeeVersion": "${json-unit.any-string}" + } + } + ], + "PaymentTotal": "${json-unit.any-string}" + }, + "applicant2SolFinalOrderFeeOrderSummary": { + "Fees": [ + { + "value": { + "FeeAmount": "${json-unit.any-string}", + "FeeCode": "${json-unit.any-string}", + "FeeDescription": "${json-unit.any-string}", + "FeeVersion": "${json-unit.any-string}" + } + } + ], + "PaymentTotal": "${json-unit.any-string}" + }, + "applicant2FinalOrderFeeServiceRequestReference": "${json-unit.any-string}", + "applicationType": "soleApplication", + "dataVersion": "${json-unit.any-number}", + "dateFinalOrderEligibleFrom": "2022-03-10", + "dateFinalOrderEligibleToRespondent": "2022-06-10", + "divorceOrDissolution": "dissolution", + "supplementaryCaseType": "notApplicable", + "documentUploadComplete": "Yes", + "dueDate": "2020-04-28", + "issueDate": "2020-05-28", + "jurisdictionApplicant1Residence": "Yes", + "jurisdictionApplicant2Residence": "Yes", + "jurisdictionConnections": [ + "A" + ], + "marriageApplicant1Name": "test_first_name test_last_name", + "marriageApplicant2Name": "test_first_name test_last_name", + "marriageDate": "1990-06-10", + "marriagePlaceOfMarriage": "Somewhere", + "solServiceTruthStatement": "I believe that the facts stated in the application are true.", + "warnings": [], + "finalOrderReminderSentApplicant2": "Yes" + } +} diff --git a/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/GeneralApplicationSelectFeeIT.java b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/GeneralApplicationSelectFeeIT.java new file mode 100644 index 00000000000..54e0e1126ca --- /dev/null +++ b/src/integrationTest/java/uk/gov/hmcts/divorce/solicitor/GeneralApplicationSelectFeeIT.java @@ -0,0 +1,130 @@ +package uk.gov.hmcts.divorce.solicitor; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import uk.gov.hmcts.divorce.common.config.WebMvcConfig; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.FeeDetails; +import uk.gov.hmcts.divorce.divorcecase.model.GeneralApplication; +import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; +import uk.gov.hmcts.divorce.solicitor.service.CcdAccessService; +import uk.gov.hmcts.divorce.testutil.FeesWireMock; +import uk.gov.hmcts.divorce.testutil.PaymentWireMock; +import uk.gov.hmcts.divorce.testutil.TestDataHelper; +import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; +import uk.gov.hmcts.reform.ccd.client.model.CallbackRequest; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static uk.gov.hmcts.divorce.divorcecase.model.GeneralApplicationFee.FEE0227; +import static uk.gov.hmcts.divorce.divorcecase.model.ServicePaymentMethod.FEE_PAY_BY_ACCOUNT; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingAos; +import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; +import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; +import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; +import static uk.gov.hmcts.divorce.solicitor.event.SolicitorGeneralApplication.SOLICITOR_GENERAL_APPLICATION; +import static uk.gov.hmcts.divorce.testutil.FeesWireMock.stubForFeesLookup; +import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.buildServiceReferenceRequest; +import static uk.gov.hmcts.divorce.testutil.PaymentWireMock.stubCreateServiceRequest; +import static uk.gov.hmcts.divorce.testutil.TestConstants.AUTHORIZATION; +import static uk.gov.hmcts.divorce.testutil.TestConstants.GENERAL_APPLICATION_SELECT_FEE_MID_EVENT_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.SERVICE_AUTHORIZATION; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_AUTHORIZATION_TOKEN; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_AUTH_TOKEN; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.callbackRequest; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@DirtiesContext +@ContextConfiguration(initializers = { + PaymentWireMock.PropertiesInitializer.class, + FeesWireMock.PropertiesInitializer.class +}) +public class GeneralApplicationSelectFeeIT { + private static final String PBA_NUMBER = "PBA0012345"; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private AuthTokenGenerator serviceTokenGenerator; + + @MockBean + private CcdAccessService ccdAccessService; + + @MockBean + private PbaService pbaService; + + @MockBean + private WebMvcConfig webMvcConfig; + + @BeforeAll + static void setUp() { + PaymentWireMock.start(); + FeesWireMock.start(); + } + + @AfterAll + static void tearDown() { + PaymentWireMock.stopAndReset(); + FeesWireMock.stopAndReset(); + } + + @Test + public void createsServiceRequestAndOrderSummaryToPrepareCaseForPayment() throws Exception { + final CaseData caseData = CaseData.builder() + .generalApplication(GeneralApplication.builder() + .generalApplicationFeeType(FEE0227) + .generalApplicationFee( + FeeDetails.builder().paymentMethod(FEE_PAY_BY_ACCOUNT).build() + ).build() + ).build(); + + CallbackRequest request = callbackRequest(caseData, SOLICITOR_GENERAL_APPLICATION); + request.getCaseDetails().setState(AwaitingAos.name()); + + when(serviceTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN); + when(ccdAccessService.isApplicant1(any(String.class), any(Long.class))).thenReturn(true); + + stubForFeesLookup(TestDataHelper.getFeeResponseAsJson(), EVENT_GENERAL, SERVICE_OTHER, KEYWORD_NOTICE); + stubCreateServiceRequest(OK, buildServiceReferenceRequest(caseData, caseData.getApplicant1())); + + mockMvc.perform(post(GENERAL_APPLICATION_SELECT_FEE_MID_EVENT_URL) + .contentType(APPLICATION_JSON) + .header(SERVICE_AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN) + .content(objectMapper.writeValueAsString(request)) + .accept(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.generalApplicationFeeOrderSummary.PaymentTotal") + .value("1000") + ) + .andExpect(jsonPath("$.data.generalApplicationFeeServiceRequestReference") + .value(TEST_SERVICE_REFERENCE) + ) + .andReturn() + .getResponse() + .getContentAsString(); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveFailedSolNoticeOfChange.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveFailedSolNoticeOfChange.java new file mode 100644 index 00000000000..5306bfd0ff5 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveFailedSolNoticeOfChange.java @@ -0,0 +1,61 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import java.util.Collections; + +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CaseworkerRemoveFailedSolNoticeOfChange implements CCDConfig { + public static final String CASEWORKER_REMOVE_FAILED_SOL_NOC_REQUEST = "caseworker-remove-failed-sol-noc-request"; + public static final String NO_NOC_REQUEST_ERROR = "No NoC Request Found for Case Id: "; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CASEWORKER_REMOVE_FAILED_SOL_NOC_REQUEST) + .forAllStates() + .name("Remove Failed Sol NoC Request") + .description("Remove Failed Solicitor Notice of Change Request") + .showSummary() + .showEventNotes() + .aboutToSubmitCallback(this::aboutToSubmit) + .grant(CREATE_READ_UPDATE, SUPER_USER) + .grantHistoryOnly(SUPER_USER, LEGAL_ADVISOR, JUDGE)); + } + + public AboutToStartOrSubmitResponse aboutToSubmit( + final CaseDetails details, + final CaseDetails beforeDetails + ) { + log.info("{} about to submit callback invoked for Case Id: {}", CASEWORKER_REMOVE_FAILED_SOL_NOC_REQUEST, details.getId()); + + if (details.getData().getChangeOrganisationRequestField() == null) { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(NO_NOC_REQUEST_ERROR + details.getId())) + .build(); + } + + details.getData().setChangeOrganisationRequestField(null); + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .build(); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveNote.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveNote.java new file mode 100644 index 00000000000..fbbf70a2654 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveNote.java @@ -0,0 +1,65 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import java.util.Collections; + +import static uk.gov.hmcts.divorce.divorcecase.model.State.POST_SUBMISSION_STATES_WITH_WITHDRAWN_AND_REJECTED; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE; + +@Component +@Slf4j +public class CaseworkerRemoveNote implements CCDConfig { + + public static final String CASEWORKER_REMOVE_NOTE = "caseworker-remove-note"; + + public static final String ERROR_NOTE_ADDED = "Please use the add notes event if you would like to add new notes."; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CASEWORKER_REMOVE_NOTE) + .forStates(POST_SUBMISSION_STATES_WITH_WITHDRAWN_AND_REJECTED) + .name("Remove note") + .description("Remove case note") + .showEventNotes() + .grant(CREATE_READ_UPDATE_DELETE, SUPER_USER) + .grantHistoryOnly(CASE_WORKER, LEGAL_ADVISOR, JUDGE)) + .page("removeNotes", this::midEvent) + .pageLabel("Remove case note") + .optional(CaseData::getNotes) + .done(); + } + + public AboutToStartOrSubmitResponse midEvent(final CaseDetails details, + final CaseDetails beforeDetails) { + + final var beforeNotes = beforeDetails.getData().getNotes(); + final var notes = details.getData().getNotes(); + boolean hasAddedNotes = notes != null && (beforeNotes == null || beforeNotes.size() < notes.size()); + + if (hasAddedNotes) { + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .errors(Collections.singletonList(ERROR_NOTE_ADDED)) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .build(); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformation.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformation.java new file mode 100644 index 00000000000..f6100f4b485 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformation.java @@ -0,0 +1,325 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.caseworker.service.notification.RequestForInformationNotification; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationList; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.SOLE_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.APPLICANT; +import static uk.gov.hmcts.divorce.divorcecase.model.State.Applicant2Approved; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingApplicant1Response; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingApplicant2Response; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFDecision; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFEvidence; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFPartPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.Draft; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; + +@Slf4j +@RequiredArgsConstructor +@Component +public class CaseworkerRequestForInformation implements CCDConfig { + + public static final String CASEWORKER_REQUEST_FOR_INFORMATION = "caseworker-request-for-information"; + public static final String REQUEST_FOR_INFORMATION_NOTIFICATION_FAILED_ERROR + = "Unable to send Request for Information Notification for Case Id: "; + public static final String NO_VALID_EMAIL_ERROR = "You cannot send an email because no email address has been provided for: "; + public static final String NOT_ONLINE_ERROR = "You cannot send an email because the following party is offline: "; + public static final String THE_APPLICANT = "the Applicant"; + public static final String APPLICANT_1 = "Applicant 1"; + public static final String APPLICANT_2 = "Applicant 2"; + public static final String SOLICITOR = "'s Solicitor"; + public static final String NO_VALID_EMAIL_PROVIDED_ERROR = "You must provide a valid email address."; + public static final String USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR = + "Please use create general letter event to request information from offline parties."; + public static final String USE_CREATE_GENERAL_LETTER_FOR_RESPONDENT_ERROR = + "Please use create general letter event to request information from the respondent"; + + public static final String USE_CREATE_GENERAL_EMAIL_FOR_RESPONDENT_ERROR = + "Please use create general email event to request information from the respondent"; + public static final String USE_CORRECT_PARTY_ERROR = "Please use the correct option to contact online parties."; + + private final RequestForInformationNotification requestForInformationNotification; + + private final NotificationDispatcher notificationDispatcher; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CASEWORKER_REQUEST_FOR_INFORMATION) + .forStates(Draft, + AwaitingHWFDecision, + AwaitingHWFEvidence, + AwaitingHWFPartPayment, + AwaitingPayment, + AwaitingDocuments, + AwaitingApplicant1Response, + AwaitingApplicant2Response, + Applicant2Approved, + InformationRequested, + AwaitingRequestedInformation, + RequestedInformationSubmitted, + Submitted) + .name("Request For Information") + .description("Request for information") + .showSummary() + .showEventNotes() + .endButtonLabel("Submit") + .aboutToSubmitCallback(this::aboutToSubmit) + .grant(CREATE_READ_UPDATE, CASE_WORKER) + .grantHistoryOnly(SUPER_USER, LEGAL_ADVISOR, JUDGE)) + .page("requestForInformation", this::midEvent) + .pageLabel("Request For Information") + .readonlyNoSummary(CaseData::getApplicationType, "requestForInformationSoleParties=\"NEVER_SHOW\"") + .complex(CaseData::getRequestForInformationList) + .complex(RequestForInformationList::getRequestForInformation) + .mandatory(RequestForInformation::getRequestForInformationSoleParties, "applicationType=\"soleApplication\"") + .mandatory(RequestForInformation::getRequestForInformationJointParties, "applicationType=\"jointApplication\"") + .mandatory(RequestForInformation::getRequestForInformationName, "requestForInformationSoleParties=\"other\" " + + "OR requestForInformationJointParties=\"other\"") + .mandatory(RequestForInformation::getRequestForInformationEmailAddress, "requestForInformationSoleParties=\"other\" " + + "OR requestForInformationJointParties=\"other\"") + .mandatory(RequestForInformation::getRequestForInformationDetails) + .done() + .done(); + } + + public AboutToStartOrSubmitResponse midEvent(CaseDetails details, + CaseDetails detailsBefore) { + + log.info("{} midEvent callback invoked for Case Id: {}", CASEWORKER_REQUEST_FOR_INFORMATION, details.getId()); + + List errors = new ArrayList<>(); + areApplicantsOnline(details.getData(), errors); + areEmailsValid(details.getData(), errors); + if (!errors.isEmpty()) { + return AboutToStartOrSubmitResponse.builder() + .errors(errors) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .build(); + } + + public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails details, + CaseDetails beforeDetails) { + + log.info("{} about to submit callback invoked for Case Id: {}", CASEWORKER_REQUEST_FOR_INFORMATION, details.getId()); + + final CaseData caseData = details.getData(); + final RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + + requestForInformation.setValues(caseData); + + caseData.getRequestForInformationList().addRequestToList(requestForInformation); + + try { + notificationDispatcher.sendRequestForInformationNotification( + requestForInformationNotification, + caseData, + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error("Request for Information Notification for Case Id {} failed with message: {}", details.getId(), e.getMessage(), e); + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(REQUEST_FOR_INFORMATION_NOTIFICATION_FAILED_ERROR + details.getId())) + .build(); + } + + //Prevent pre-populating fields for new request + caseData.getRequestForInformationList().setRequestForInformation(new RequestForInformation()); + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .state(InformationRequested) + .build(); + } + + private String getErrorString(String errorStart, CaseData caseData, Applicant applicant) { + String error = errorStart; + if (caseData.getApplicationType().isSole()) { + error += THE_APPLICANT; + } else { + error += caseData.getApplicant1().equals(applicant) ? APPLICANT_1 : APPLICANT_2; + } + if (applicant.isRepresented()) { + error += SOLICITOR; + } + return error; + } + + private boolean isApplicantEmailInvalid(Applicant applicant) { + return applicant.isRepresented() + ? isEmpty(applicant.getSolicitor().getEmail()) + : isEmpty(applicant.getEmail()); + } + + private void isEmailValid(CaseData caseData, Applicant applicant, List errors) { + if (isApplicantEmailInvalid(applicant)) { + errors.add(getErrorString(NO_VALID_EMAIL_ERROR, caseData, applicant)); + errors.add(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + } + + private void areBothEmailsValid(CaseData caseData, List errors) { + boolean applicant1EmailInvalid = isApplicantEmailInvalid(caseData.getApplicant1()); + boolean applicant2EmailInvalid = isApplicantEmailInvalid(caseData.getApplicant2()); + if (applicant1EmailInvalid) { + errors.add(getErrorString(NO_VALID_EMAIL_ERROR, caseData, caseData.getApplicant1())); + } + if (applicant2EmailInvalid) { + errors.add(getErrorString(NO_VALID_EMAIL_ERROR, caseData, caseData.getApplicant2())); + } + if (applicant1EmailInvalid || applicant2EmailInvalid) { + errors.add(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + } + + private void doesEmailMatch(String email, Applicant applicant, CaseData caseData, List errors) { + final boolean appIsRespondent = SOLE_APPLICATION.equals(caseData.getApplicationType()) + && applicant.equals(caseData.getApplicant2()); + + if (null != applicant.getEmail() && email.equals(applicant.getEmail().toLowerCase().trim())) { + if (appIsRespondent) { + errors.add( + applicant.isApplicantOffline() + ? USE_CREATE_GENERAL_LETTER_FOR_RESPONDENT_ERROR + "." + : USE_CREATE_GENERAL_EMAIL_FOR_RESPONDENT_ERROR + "." + ); + } else { + errors.add( + applicant.isApplicantOffline() + ? USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + : USE_CORRECT_PARTY_ERROR + ); + } + } else if (applicant.isRepresented() + && null != applicant.getSolicitor().getEmail() + && email.equals(applicant.getSolicitor().getEmail().toLowerCase().trim())) { + + if (appIsRespondent) { + errors.add( + applicant.isApplicantOffline() + ? USE_CREATE_GENERAL_LETTER_FOR_RESPONDENT_ERROR + SOLICITOR + "." + : USE_CREATE_GENERAL_EMAIL_FOR_RESPONDENT_ERROR + SOLICITOR + "." + ); + } else { + errors.add( + applicant.isApplicantOffline() + ? USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + : USE_CORRECT_PARTY_ERROR + ); + } + } + } + + private void doesEmailMatchApplicantOrSolicitor(CaseData caseData, String email, List errors) { + final int errorSize = errors.size(); + doesEmailMatch(email, caseData.getApplicant1(), caseData, errors); + if (errors.size() == errorSize) { + doesEmailMatch(email, caseData.getApplicant2(), caseData, errors); + } + } + + private void isOtherEmailValid(CaseData caseData, String email, List errors) { + if (null == email) { + errors.add(NO_VALID_EMAIL_PROVIDED_ERROR); + } else { + String cleanEmail = email.toLowerCase().trim(); + if (isEmpty(cleanEmail) || cleanEmail.length() < 6) { //shortest valid web email address = ?@?.?? + errors.add(NO_VALID_EMAIL_PROVIDED_ERROR); + } else { + doesEmailMatchApplicantOrSolicitor(caseData, cleanEmail, errors); + } + } + } + + private void areEmailsValid(CaseData caseData, List errors) { + RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + + if (caseData.getApplicationType().isSole()) { + switch (requestForInformation.getRequestForInformationSoleParties()) { + case APPLICANT -> isEmailValid(caseData, caseData.getApplicant1(), errors); + case OTHER -> isOtherEmailValid(caseData, requestForInformation.getRequestForInformationEmailAddress(), errors); + default -> { } + } + } else { + switch (requestForInformation.getRequestForInformationJointParties()) { + case APPLICANT1 -> isEmailValid(caseData, caseData.getApplicant1(), errors); + case APPLICANT2 -> isEmailValid(caseData, caseData.getApplicant2(), errors); + case BOTH -> areBothEmailsValid(caseData, errors); + case OTHER -> isOtherEmailValid(caseData, requestForInformation.getRequestForInformationEmailAddress(), errors); + default -> { } + } + } + } + + private void isApplicantOnline(CaseData caseData, Applicant applicant, List errors) { + if (applicant.isApplicantOffline()) { + errors.add(getErrorString(NOT_ONLINE_ERROR, caseData, applicant)); + errors.add(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + } + + private void areBothApplicantsOnline(CaseData caseData, List errors) { + boolean applicant1Offline = caseData.getApplicant1().isApplicantOffline(); + boolean applicant2Offline = caseData.getApplicant2().isApplicantOffline(); + if (applicant1Offline) { + errors.add(getErrorString(NOT_ONLINE_ERROR, caseData, caseData.getApplicant1())); + } + if (applicant2Offline) { + errors.add(getErrorString(NOT_ONLINE_ERROR, caseData, caseData.getApplicant2())); + } + if (applicant1Offline || applicant2Offline) { + errors.add(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + } + + private void areApplicantsOnline(CaseData caseData, List errors) { + RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + + if (caseData.getApplicationType().isSole()) { + if (APPLICANT.equals(requestForInformation.getRequestForInformationSoleParties())) { + isApplicantOnline(caseData, caseData.getApplicant1(), errors); + } + } else { + switch (requestForInformation.getRequestForInformationJointParties()) { + case APPLICANT1 -> isApplicantOnline(caseData, caseData.getApplicant1(), errors); + case APPLICANT2 -> isApplicantOnline(caseData, caseData.getApplicant2(), errors); + case BOTH -> areBothApplicantsOnline(caseData, errors); + default -> { } + } + } + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationResponse.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationResponse.java new file mode 100644 index 00000000000..e2a44cacca9 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationResponse.java @@ -0,0 +1,210 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponseNotification; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationList; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; +import uk.gov.hmcts.reform.ccd.client.model.SubmittedCallbackResponse; + +import java.util.Collections; + +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.OTHER; +import static uk.gov.hmcts.divorce.divorcecase.model.State.Applicant2Approved; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingApplicant1Response; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingApplicant2Response; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingDocuments; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFDecision; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFEvidence; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingHWFPartPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingPayment; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.Draft; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER_BULK_SCAN; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CaseworkerRequestForInformationResponse implements CCDConfig { + public static final String CASEWORKER_REQUEST_FOR_INFORMATION_RESPONSE = "caseworker-request-for-information-response"; + private static final String ALWAYS_HIDE = "rfiOfflineSoleResponseParties=\"ALWAYS_HIDE\""; + public static final String NO_REQUEST_FOR_INFORMATION_ERROR = + "There is no Request for Information on the case."; + public static final String REQUEST_FOR_INFORMATION_RESPONSE_NOTIFICATION_FAILED_ERROR + = "Request for Information Response Notification for Case Id {} failed with message: {}"; + public static final String REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR + = "Request for Information Response Partner Notification for Case Id {} failed with message: {}"; + private final NotificationDispatcher notificationDispatcher; + private final CitizenRequestForInformationResponseNotification citizenRequestForInformationResponseNotification; + private final CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CASEWORKER_REQUEST_FOR_INFORMATION_RESPONSE) + .forStates(Draft, + AwaitingHWFDecision, + AwaitingHWFEvidence, + AwaitingHWFPartPayment, + AwaitingPayment, + AwaitingDocuments, + AwaitingApplicant1Response, + AwaitingApplicant2Response, + Applicant2Approved, + InformationRequested, + AwaitingRequestedInformation, + RequestedInformationSubmitted, + Submitted) + .name("Add RFI Response") + .description("Add RFI Response") + .aboutToStartCallback(this::aboutToStart) + .aboutToSubmitCallback(this::aboutToSubmit) + .submittedCallback(this::submitted) + .showEventNotes() + .showSummary() + .grant(CREATE_READ_UPDATE, CASE_WORKER_BULK_SCAN, CASE_WORKER, SUPER_USER) + .grantHistoryOnly(LEGAL_ADVISOR, SOLICITOR, JUDGE)) + .page("addTextRfiResponse") + .readonlyNoSummary(CaseData::getApplicationType, ALWAYS_HIDE) + .complex(CaseData::getRequestForInformationList) + .complex(RequestForInformationList::getRequestForInformationOfflineResponseDraft) + .mandatoryWithoutDefaultValue( + RequestForInformationOfflineResponseDraft::getRfiOfflineSoleResponseParties, + "applicationType=\"soleApplication\"", + "Select sender of response" + ) + .mandatoryWithoutDefaultValue( + RequestForInformationOfflineResponseDraft::getRfiOfflineJointResponseParties, + "applicationType=\"jointApplication\"", + "Select sender of response" + ) + .mandatory( + RequestForInformationOfflineResponseDraft::getRfiOfflineResponseOtherName, + "rfiOfflineSoleResponseParties=\"other\" OR rfiOfflineJointResponseParties=\"other\"" + ) + .optional( + RequestForInformationOfflineResponseDraft::getRfiOfflineResponseOtherEmail, + "rfiOfflineSoleResponseParties=\"other\" OR rfiOfflineJointResponseParties=\"other\"" + ) + .mandatory(RequestForInformationOfflineResponseDraft::getRfiOfflineAllDocumentsUploaded) + .mandatory(RequestForInformationOfflineResponseDraft::getRfiOfflineDraftResponseDetails) + .done() + .done(); + } + + public AboutToStartOrSubmitResponse aboutToStart(CaseDetails details) { + log.info("{} about to start callback invoked for Case Id: {}", CASEWORKER_REQUEST_FOR_INFORMATION_RESPONSE, details.getId()); + if (details.getData().getRequestForInformationList().getRequestsForInformation() == null + || details.getData().getRequestForInformationList().getRequestsForInformation().isEmpty()) { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(NO_REQUEST_FOR_INFORMATION_ERROR)) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .build(); + } + + public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails details, + CaseDetails beforeDetails) { + + log.info("{} about to submit callback invoked for Case Id: {}", CASEWORKER_REQUEST_FOR_INFORMATION_RESPONSE, details.getId()); + CaseData caseData = details.getData(); + + final boolean allDocumentsUploaded = YES.equals( + caseData.getRequestForInformationList().getRequestForInformationOfflineResponseDraft().getRfiOfflineAllDocumentsUploaded() + ); + final State state = allDocumentsUploaded ? RequestedInformationSubmitted : AwaitingRequestedInformation; + + final RequestForInformationResponse response = new RequestForInformationResponse(); + final RequestForInformationOfflineResponseDraft offlineDraft = + caseData.getRequestForInformationList().getRequestForInformationOfflineResponseDraft(); + response.setValues(caseData, offlineDraft); + caseData.getRequestForInformationList().getLatestRequest().addResponseToList(response); + + caseData.getRequestForInformationList().setRequestForInformationOfflineResponseDraft( + new RequestForInformationOfflineResponseDraft() + ); + + return AboutToStartOrSubmitResponse.builder() + .data(caseData) + .state(state) + .build(); + } + + public SubmittedCallbackResponse submitted(CaseDetails details, CaseDetails beforeDetails) { + log.info("{} submitted callback invoked for Case Id: {}", CASEWORKER_REQUEST_FOR_INFORMATION_RESPONSE, details.getId()); + + final CaseData caseData = details.getData(); + final RequestForInformation latestRequest = caseData.getRequestForInformationList().getLatestRequest(); + final RequestForInformationSoleParties soleParties = latestRequest.getRequestForInformationSoleParties(); + final RequestForInformationJointParties jointParties = latestRequest.getRequestForInformationJointParties(); + + if ((caseData.getApplicationType().isSole() && !RequestForInformationSoleParties.OTHER.equals(soleParties)) + || (!caseData.getApplicationType().isSole() && !OTHER.equals(jointParties)) + ) { + try { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseData, + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error( + REQUEST_FOR_INFORMATION_RESPONSE_NOTIFICATION_FAILED_ERROR, + details.getId(), + e.getMessage(), + e + ); + } + + if (!caseData.getApplicationType().isSole() + && BOTH.equals(caseData.getRequestForInformationList().getLatestRequest().getRequestForInformationJointParties())) { + try { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseData, + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error( + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR, + details.getId(), + e.getMessage(), + e + ); + } + } + } + + return SubmittedCallbackResponse.builder().build(); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/caseworker/service/notification/RequestForInformationNotification.java b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/notification/RequestForInformationNotification.java new file mode 100644 index 00000000000..54039a5cd26 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/caseworker/service/notification/RequestForInformationNotification.java @@ -0,0 +1,234 @@ +package uk.gov.hmcts.divorce.caseworker.service.notification; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.notification.ApplicantNotification; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.EmailTemplateName; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.time.LocalDate; +import java.util.Map; + +import static java.lang.String.join; +import static java.util.Objects.nonNull; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.ISSUE_DATE_POPULATED; +import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.NOT_YET_ISSUED; +import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.RECIPIENT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.APPLICANT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.CIVIL_PARTNER_JOINT; +import static uk.gov.hmcts.divorce.notification.CommonContent.DATE_OF_ISSUE; +import static uk.gov.hmcts.divorce.notification.CommonContent.HUSBAND_JOINT; +import static uk.gov.hmcts.divorce.notification.CommonContent.IS_JOINT; +import static uk.gov.hmcts.divorce.notification.CommonContent.IS_SOLE; +import static uk.gov.hmcts.divorce.notification.CommonContent.NO; +import static uk.gov.hmcts.divorce.notification.CommonContent.REQUEST_FOR_INFORMATION_DETAILS; +import static uk.gov.hmcts.divorce.notification.CommonContent.RESPONDENT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SENT_TO_BOTH_APPLICANTS; +import static uk.gov.hmcts.divorce.notification.CommonContent.SIGN_IN_URL; +import static uk.gov.hmcts.divorce.notification.CommonContent.SMART_SURVEY; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_REFERENCE; +import static uk.gov.hmcts.divorce.notification.CommonContent.WIFE_JOINT; +import static uk.gov.hmcts.divorce.notification.CommonContent.YES; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_JOINT; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_OTHER; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SOLE; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SOLICITOR; +import static uk.gov.hmcts.divorce.notification.FormatUtil.DATE_TIME_FORMATTER; + +@Component +@RequiredArgsConstructor +@Slf4j +public class RequestForInformationNotification implements ApplicantNotification { + + public static final String REQUEST_FOR_INFORMATION_SOLE_NOTIFICATION_TO_FOR_CASE_ID = + "Sending Request For Information Sole Notification to {} for case id: {}"; + + private final NotificationService notificationService; + private final CommonContent commonContent; + + @Override + public void sendToApplicant1Solicitor(final CaseData caseData, final Long caseId) { + log.info(REQUEST_FOR_INFORMATION_SOLE_NOTIFICATION_TO_FOR_CASE_ID, + caseData.getApplicationType().isSole() ? "applicant solicitor" : "applicant 1 solicitor", caseId); + + RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + + notificationService.sendEmail( + caseData.getApplicant1().getSolicitor().getEmail(), + REQUEST_FOR_INFORMATION_SOLICITOR, + solicitorTemplateContent( + caseData, + caseId, + caseData.getApplicant1(), + caseData.getApplicant2(), + requestForInformation.getRequestForInformationDetails() + ), + caseData.getApplicant1().getLanguagePreference(), + caseId + ); + } + + @Override + public void sendToApplicant1(CaseData caseData, Long caseId) { + log.info(REQUEST_FOR_INFORMATION_SOLE_NOTIFICATION_TO_FOR_CASE_ID, + caseData.getApplicationType().isSole() ? "applicant" : "applicant 1", caseId); + + RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + + EmailTemplateName emailTemplate = caseData.getApplicationType().isSole() + ? REQUEST_FOR_INFORMATION_SOLE + : REQUEST_FOR_INFORMATION_JOINT; + + notificationService.sendEmail( + caseData.getApplicant1().getEmail(), + emailTemplate, + applicantTemplateContent( + caseData, + caseId, + caseData.getApplicant1(), + caseData.getApplicant2(), + requestForInformation.getRequestForInformationDetails() + ), + caseData.getApplicant1().getLanguagePreference(), + caseId + ); + } + + @Override + public void sendToApplicant2(CaseData caseData, Long caseId) { + log.info(REQUEST_FOR_INFORMATION_SOLE_NOTIFICATION_TO_FOR_CASE_ID, "applicant 2", caseId); + + RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + + notificationService.sendEmail( + caseData.getApplicant2().getEmail(), + REQUEST_FOR_INFORMATION_JOINT, + applicantTemplateContent( + caseData, + caseId, + caseData.getApplicant2(), + caseData.getApplicant1(), + requestForInformation.getRequestForInformationDetails() + ), + caseData.getApplicant2().getLanguagePreference(), + caseId + ); + } + + @Override + public void sendToApplicant2Solicitor(final CaseData caseData, final Long caseId) { + log.info(REQUEST_FOR_INFORMATION_SOLE_NOTIFICATION_TO_FOR_CASE_ID, "applicant 2 solicitor", caseId); + + RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + + notificationService.sendEmail( + caseData.getApplicant2().getSolicitor().getEmail(), + REQUEST_FOR_INFORMATION_SOLICITOR, + solicitorTemplateContent( + caseData, + caseId, + caseData.getApplicant2(), + caseData.getApplicant1(), + requestForInformation.getRequestForInformationDetails() + ), + caseData.getApplicant2().getLanguagePreference(), + caseId + ); + } + + @Override + public void sendToOtherRecipient(final CaseData caseData, final Long caseId) { + log.info(REQUEST_FOR_INFORMATION_SOLE_NOTIFICATION_TO_FOR_CASE_ID, "other recipient", caseId); + + RequestForInformation requestForInformation = caseData.getRequestForInformationList().getRequestForInformation(); + + notificationService.sendEmail( + requestForInformation.getRequestForInformationEmailAddress(), + REQUEST_FOR_INFORMATION_OTHER, + otherRecipientTemplateContent(caseData, caseId, caseData.getApplicant1(), caseData.getApplicant2(), requestForInformation), + LanguagePreference.ENGLISH, + caseId + ); + } + + private Map solicitorTemplateContent(final CaseData caseData, + final Long caseId, + final Applicant applicant, + final Applicant partner, + final String requestForInformationDetails) { + Map templateVars = commonContent.mainTemplateVars(caseData, caseId, applicant, partner); + LocalDate issueDate = caseData.getApplication().getIssueDate(); + + templateVars.put(APPLICANT_NAME, + join(" ", caseData.getApplicant1().getFirstName(), caseData.getApplicant1().getLastName())); + templateVars.put(RESPONDENT_NAME, + join(" ", caseData.getApplicant2().getFirstName(), caseData.getApplicant2().getLastName())); + templateVars.put(IS_SOLE, caseData.getApplicationType().isSole() ? YES : NO); + templateVars.put(IS_JOINT, !caseData.getApplicationType().isSole() ? YES : NO); + templateVars.put(SOLICITOR_NAME, applicant.getSolicitor().getName()); + templateVars.put(DATE_OF_ISSUE, issueDate != null ? issueDate.format(DATE_TIME_FORMATTER) : ""); + templateVars.put(ISSUE_DATE_POPULATED, issueDate != null ? YES : NO); + templateVars.put(NOT_YET_ISSUED, issueDate == null ? YES : NO); + templateVars.put(SOLICITOR_REFERENCE, nonNull(applicant.getSolicitor().getReference()) + ? applicant.getSolicitor().getReference() + : "not provided"); + templateVars.put(REQUEST_FOR_INFORMATION_DETAILS, requestForInformationDetails); + templateVars.put(SIGN_IN_URL, commonContent.getProfessionalUsersSignInUrl(caseId)); + templateVars.put(SMART_SURVEY, commonContent.getSmartSurvey()); + + return templateVars; + } + + private void suppressJointTemplateText(Map templateVars) { + templateVars.put(HUSBAND_JOINT, NO); + templateVars.put(WIFE_JOINT, NO); + templateVars.put(CIVIL_PARTNER_JOINT, NO); + templateVars.put(IS_JOINT, NO); + templateVars.put(SENT_TO_BOTH_APPLICANTS, NO); + } + + private Map applicantTemplateContent(final CaseData caseData, + final Long caseId, + final Applicant applicant, + final Applicant partner, + final String requestForInformationDetails) { + Map templateVars = + commonContent.requestForInformationTemplateVars(caseData, caseId, applicant, partner); + templateVars.put(REQUEST_FOR_INFORMATION_DETAILS, requestForInformationDetails); + templateVars.put(SMART_SURVEY, commonContent.getSmartSurvey()); + + if (BOTH.equals(caseData.getRequestForInformationList().getRequestForInformation().getRequestForInformationJointParties())) { + templateVars.put(SENT_TO_BOTH_APPLICANTS, YES); + } else { + suppressJointTemplateText(templateVars); + } + + return templateVars; + } + + private Map otherRecipientTemplateContent(final CaseData caseData, + final Long caseId, + final Applicant applicant, + final Applicant partner, + final RequestForInformation requestForInformation) { + Map templateVars = + commonContent.mainTemplateVars(caseData, caseId, applicant, partner); + templateVars.put(APPLICANT_NAME, caseData.getApplicant1().getFullName()); + templateVars.put(RESPONDENT_NAME, caseData.getApplicant2().getFullName()); + templateVars.put(RECIPIENT_NAME, requestForInformation.getRequestForInformationName()); + templateVars.put(ISSUE_DATE_POPULATED, caseData.getApplication().getIssueDate() != null ? YES : NO); + templateVars.put(NOT_YET_ISSUED, caseData.getApplication().getIssueDate() == null ? YES : NO); + templateVars.put(REQUEST_FOR_INFORMATION_DETAILS, requestForInformation.getRequestForInformationDetails()); + templateVars.put(SMART_SURVEY, commonContent.getSmartSurvey()); + + return templateVars; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenRespondToRequestForInformation.java b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenRespondToRequestForInformation.java new file mode 100644 index 00000000000..660bc14426a --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/event/CitizenRespondToRequestForInformation.java @@ -0,0 +1,206 @@ +package uk.gov.hmcts.divorce.citizen.event; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponseNotification; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationList; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; +import uk.gov.hmcts.divorce.solicitor.service.CcdAccessService; +import uk.gov.hmcts.reform.ccd.client.model.SubmittedCallbackResponse; + +import java.util.Collections; + +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_2; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CREATOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE_DELETE; + +@Component +@Slf4j +@RequiredArgsConstructor +public class CitizenRespondToRequestForInformation implements CCDConfig { + + public static final String CITIZEN_RESPOND_TO_REQUEST_FOR_INFORMATION = "citizen-respond-request-for-information"; + public static final String CITIZEN_NOT_VALID_FOR_PARTY_START_ERROR = "Unable to apply response. Applicant "; + public static final String CITIZEN_NOT_VALID_FOR_PARTY_MID_ERROR = "not valid for Party ("; + public static final String CITIZEN_NOT_VALID_FOR_PARTY_END_ERROR = ") on latest RFI for Case Id: "; + public static final String REQUEST_FOR_INFORMATION_RESPONSE_NOTIFICATION_FAILED_ERROR + = "Request for Information Response Notification for Case Id {} failed with message: {}"; + public static final String REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR + = "Request for Information Response Partner Notification for Case Id {} failed with message: {}"; + + private final CcdAccessService ccdAccessService; + private final HttpServletRequest request; + private final NotificationDispatcher notificationDispatcher; + private final CitizenRequestForInformationResponseNotification citizenRequestForInformationResponseNotification; + private final CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(CITIZEN_RESPOND_TO_REQUEST_FOR_INFORMATION) + .forState(InformationRequested) + .name("Submit response for rfi") + .description("Submit response for RFI") + .showSummary() + .showEventNotes() + .aboutToSubmitCallback(this::aboutToSubmit) + .submittedCallback(this::submitted) + .grant(CREATE_READ_UPDATE_DELETE, CREATOR, APPLICANT_2) + .grantHistoryOnly( + CASE_WORKER, + SUPER_USER, + LEGAL_ADVISOR, + JUDGE)) + .page("respondRequestForInfo") + .pageLabel("Submit response for RFI"); + } + + public AboutToStartOrSubmitResponse aboutToSubmit(final CaseDetails details, + final CaseDetails beforeDetails) { + + log.info("{} about to submit callback invoked for Case Id: {}", CITIZEN_RESPOND_TO_REQUEST_FOR_INFORMATION, details.getId()); + + final RequestForInformationList requestForInformationList = details.getData().getRequestForInformationList(); + final RequestForInformationResponse response = new RequestForInformationResponse(); + RequestForInformationResponseParties responseParty = isApplicant1(details.getId()) ? APPLICANT1 : APPLICANT2; + + if (applicantCanRespondToRequestForInformation(details.getData(), responseParty)) { + response.setValues(details.getData(), responseParty); + clearDraft(details.getData(), responseParty); + } else { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(buildErrorString(details, responseParty))) + .build(); + } + + requestForInformationList.getLatestRequest().addResponseToList(response); + + final State state = + YES.equals(requestForInformationList.getLatestRequest().getLatestResponse().getRequestForInformationResponseCannotUploadDocs()) + ? AwaitingRequestedInformation + : RequestedInformationSubmitted; + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .state(state) + .build(); + } + + public SubmittedCallbackResponse submitted(CaseDetails details, CaseDetails beforeDetails) { + log.info("{} submitted callback invoked for Case Id: {}", CITIZEN_RESPOND_TO_REQUEST_FOR_INFORMATION, details.getId()); + + try { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + details.getData(), + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error( + REQUEST_FOR_INFORMATION_RESPONSE_NOTIFICATION_FAILED_ERROR, + details.getId(), + e.getMessage(), + e + ); + } + + final RequestForInformation requestForInformation = details.getData().getRequestForInformationList().getLatestRequest(); + if (!details.getData().getApplicationType().isSole() && BOTH.equals(requestForInformation.getRequestForInformationJointParties())) { + try { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + details.getData(), + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error( + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR, + details.getId(), + e.getMessage(), + e + ); + } + } + + return SubmittedCallbackResponse.builder().build(); + } + + private boolean isApplicant1(Long caseId) { + return ccdAccessService.isApplicant1(request.getHeader(AUTHORIZATION), caseId); + } + + private boolean applicantCanRespondToRequestForInformation(CaseData data, RequestForInformationResponseParties party) { + boolean isJoint = !data.getApplicationType().isSole(); + RequestForInformationSoleParties soleParties = + data.getRequestForInformationList().getLatestRequest().getRequestForInformationSoleParties(); + RequestForInformationJointParties jointParties = + data.getRequestForInformationList().getLatestRequest().getRequestForInformationJointParties(); + boolean solePartyApplicant = RequestForInformationSoleParties.APPLICANT.equals(soleParties); + boolean jointPartyApplicant1 = RequestForInformationJointParties.APPLICANT1.equals(jointParties); + boolean jointPartyApplicant2 = RequestForInformationJointParties.APPLICANT2.equals(jointParties); + boolean jointPartyBoth = BOTH.equals(jointParties); + + if (APPLICANT1.equals(party)) { + return isJoint ? jointPartyApplicant1 || jointPartyBoth : solePartyApplicant; + } else { + return isJoint && (jointPartyApplicant2 || jointPartyBoth); + } + } + + private void clearDraft(CaseData data, RequestForInformationResponseParties party) { + RequestForInformationList requestForInformationList = data.getRequestForInformationList(); + + if (APPLICANT1.equals(party)) { + requestForInformationList.setRequestForInformationResponseApplicant1(new RequestForInformationResponseDraft()); + } else { + requestForInformationList.setRequestForInformationResponseApplicant2(new RequestForInformationResponseDraft()); + } + } + + private String buildErrorString(CaseDetails details, RequestForInformationResponseParties party) { + final boolean isSole = details.getData().getApplicationType().isSole(); + final boolean isApplicant1 = APPLICANT1.equals(party); + final RequestForInformation latestRequest = details.getData().getRequestForInformationList().getLatestRequest(); + String requestParty = isSole + ? latestRequest.getRequestForInformationSoleParties().toString() + : latestRequest.getRequestForInformationJointParties().toString(); + String applicant = isApplicant1 ? "1 " : "2 "; + + return CITIZEN_NOT_VALID_FOR_PARTY_START_ERROR + + (isSole && isApplicant1 ? "" : applicant) + + CITIZEN_NOT_VALID_FOR_PARTY_MID_ERROR + + requestParty + + CITIZEN_NOT_VALID_FOR_PARTY_END_ERROR + + details.getId(); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponseNotification.java b/src/main/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponseNotification.java new file mode 100644 index 00000000000..600ba87e0cb --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponseNotification.java @@ -0,0 +1,97 @@ +package uk.gov.hmcts.divorce.citizen.notification; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.notification.ApplicantNotification; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.EmailTemplateName; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.util.Map; + +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.notification.CommonContent.SMART_SURVEY; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_RESPONSE; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CitizenRequestForInformationResponseNotification implements ApplicantNotification { + + public static final String REQUEST_FOR_INFORMATION_RESPONSE_NOTIFICATION_TO_FOR_CASE_ID = + "Sending Request For Information Response Notification to {} for case id: {}"; + + private final NotificationService notificationService; + private final CommonContent commonContent; + + @Override + public void sendToApplicant1(CaseData caseData, Long caseId) { + log.info(REQUEST_FOR_INFORMATION_RESPONSE_NOTIFICATION_TO_FOR_CASE_ID, + caseData.getApplicationType().isSole() ? "applicant" : "applicant 1", caseId); + + RequestForInformationResponse latestResponse = + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse(); + + notificationService.sendEmail( + caseData.getApplicant1().getEmail(), + getEmailTemplateName(latestResponse), + applicantTemplateContent( + caseData, + caseId, + caseData.getApplicant1(), + caseData.getApplicant2() + ), + caseData.getApplicant1().getLanguagePreference(), + caseId + ); + } + + @Override + public void sendToApplicant2(CaseData caseData, Long caseId) { + log.info(REQUEST_FOR_INFORMATION_RESPONSE_NOTIFICATION_TO_FOR_CASE_ID, "applicant 2", caseId); + + RequestForInformationResponse latestResponse = + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse(); + + notificationService.sendEmail( + caseData.getApplicant2().getEmail(), + getEmailTemplateName(latestResponse), + applicantTemplateContent( + caseData, + caseId, + caseData.getApplicant2(), + caseData.getApplicant1() + ), + caseData.getApplicant2().getLanguagePreference(), + caseId + ); + } + + private Map applicantTemplateContent(final CaseData caseData, + final Long caseId, + final Applicant applicant, + final Applicant partner) { + Map templateVars = + commonContent.requestForInformationTemplateVars(caseData, caseId, applicant, partner); + templateVars.put(SMART_SURVEY, commonContent.getSmartSurvey()); + + return templateVars; + } + + private EmailTemplateName getEmailTemplateName(RequestForInformationResponse requestForInformationResponse) { + if (requestForInformationResponse.isOffline()) { + return YES.equals(requestForInformationResponse.getRfiOfflineResponseAllDocumentsUploaded()) + ? REQUEST_FOR_INFORMATION_RESPONSE + : REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS; + } else { + return YES.equals(requestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()) + ? REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS + : REQUEST_FOR_INFORMATION_RESPONSE; + } + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponsePartnerNotification.java b/src/main/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponsePartnerNotification.java new file mode 100644 index 00000000000..0a8c57061be --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponsePartnerNotification.java @@ -0,0 +1,184 @@ +package uk.gov.hmcts.divorce.citizen.notification; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.notification.ApplicantNotification; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.EmailTemplateName; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.time.LocalDate; +import java.util.Map; + +import static java.lang.String.join; +import static java.util.Objects.nonNull; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.notification.CommonContent.APPLICANT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.DATE_OF_ISSUE; +import static uk.gov.hmcts.divorce.notification.CommonContent.RESPONDENT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SIGN_IN_URL; +import static uk.gov.hmcts.divorce.notification.CommonContent.SMART_SURVEY; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_REFERENCE; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_RESPONSE_PARTNER; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD; +import static uk.gov.hmcts.divorce.notification.FormatUtil.DATE_TIME_FORMATTER; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CitizenRequestForInformationResponsePartnerNotification implements ApplicantNotification { + + public static final String REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_TO_FOR_CASE_ID = + "Sending Request For Information Response Partner Notification to {} for case id: {}"; + + private final NotificationService notificationService; + private final CommonContent commonContent; + + @Override + public void sendToApplicant1(CaseData caseData, Long caseId) { + log.info(REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_TO_FOR_CASE_ID, "applicant 1", caseId); + + RequestForInformationResponse latestResponse = + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse(); + + notificationService.sendEmail( + caseData.getApplicant1().getEmail(), + getEmailTemplateName(latestResponse), + applicantTemplateContent( + caseData, + caseId, + caseData.getApplicant1(), + caseData.getApplicant2() + ), + caseData.getApplicant1().getLanguagePreference(), + caseId + ); + } + + @Override + public void sendToApplicant1Solicitor(CaseData caseData, Long caseId) { + log.info(REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_TO_FOR_CASE_ID, "applicant 1 solicitor", caseId); + + RequestForInformationResponse latestResponse = + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse(); + + notificationService.sendEmail( + caseData.getApplicant1().getSolicitor().getEmail(), + getSolicitorEmailTemplateName(latestResponse), + solicitorTemplateContent( + caseData, + caseId, + caseData.getApplicant1(), + caseData.getApplicant2() + ), + caseData.getApplicant1().getLanguagePreference(), + caseId + ); + } + + @Override + public void sendToApplicant2(CaseData caseData, Long caseId) { + log.info(REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_TO_FOR_CASE_ID, "applicant 2", caseId); + + RequestForInformationResponse latestResponse = + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse(); + + notificationService.sendEmail( + caseData.getApplicant2().getEmail(), + getEmailTemplateName(latestResponse), + applicantTemplateContent( + caseData, + caseId, + caseData.getApplicant2(), + caseData.getApplicant1() + ), + caseData.getApplicant2().getLanguagePreference(), + caseId + ); + } + + @Override + public void sendToApplicant2Solicitor(CaseData caseData, Long caseId) { + log.info(REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_TO_FOR_CASE_ID, "applicant 2 solicitor", caseId); + + RequestForInformationResponse latestResponse = + caseData.getRequestForInformationList().getLatestRequest().getLatestResponse(); + + notificationService.sendEmail( + caseData.getApplicant2().getSolicitor().getEmail(), + getSolicitorEmailTemplateName(latestResponse), + solicitorTemplateContent( + caseData, + caseId, + caseData.getApplicant2(), + caseData.getApplicant1() + ), + caseData.getApplicant2().getLanguagePreference(), + caseId + ); + } + + private Map applicantTemplateContent(final CaseData caseData, + final Long caseId, + final Applicant applicant, + final Applicant partner) { + Map templateVars = + commonContent.requestForInformationTemplateVars(caseData, caseId, applicant, partner); + templateVars.put(SMART_SURVEY, commonContent.getSmartSurvey()); + + return templateVars; + } + + private Map solicitorTemplateContent(final CaseData caseData, + final Long caseId, + final Applicant applicant, + final Applicant partner) { + Map templateVars = commonContent.mainTemplateVars(caseData, caseId, applicant, partner); + LocalDate issueDate = caseData.getApplication().getIssueDate(); + + templateVars.put(APPLICANT_NAME, + join(" ", caseData.getApplicant1().getFirstName(), caseData.getApplicant1().getLastName())); + templateVars.put(RESPONDENT_NAME, + join(" ", caseData.getApplicant2().getFirstName(), caseData.getApplicant2().getLastName())); + templateVars.put(DATE_OF_ISSUE, issueDate != null ? issueDate.format(DATE_TIME_FORMATTER) : ""); + templateVars.put(SOLICITOR_REFERENCE, nonNull(applicant.getSolicitor().getReference()) + ? applicant.getSolicitor().getReference() + : "not provided"); + templateVars.put(SOLICITOR_NAME, applicant.getSolicitor().getName()); + templateVars.put(SIGN_IN_URL, commonContent.getProfessionalUsersSignInUrl(caseId)); + templateVars.put(SMART_SURVEY, commonContent.getSmartSurvey()); + + return templateVars; + } + + private EmailTemplateName getEmailTemplateName(RequestForInformationResponse requestForInformationResponse) { + if (requestForInformationResponse.isOffline()) { + return YES.equals(requestForInformationResponse.getRfiOfflineResponseAllDocumentsUploaded()) + ? REQUEST_FOR_INFORMATION_RESPONSE_PARTNER + : REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS; + } else { + return YES.equals(requestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()) + ? REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS + : REQUEST_FOR_INFORMATION_RESPONSE_PARTNER; + } + } + + private EmailTemplateName getSolicitorEmailTemplateName(RequestForInformationResponse requestForInformationResponse) { + if (requestForInformationResponse.isOffline()) { + return YES.equals(requestForInformationResponse.getRfiOfflineResponseAllDocumentsUploaded()) + ? REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY + : REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD; + } else { + return YES.equals(requestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()) + ? REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD + : REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY; + } + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseDataOldDivorce.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseDataOldDivorce.java new file mode 100644 index 00000000000..e082cb1d3c1 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/CaseDataOldDivorce.java @@ -0,0 +1,70 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Data; + +import java.io.IOException; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonDeserialize(using = CaseDataOldDivorce.CaseDataOldDivorceDeserializer.class) +@Data +public class CaseDataOldDivorce { + + @JsonProperty("D8caseReference") + private String d8caseReference; + + @JsonProperty("D8MarriageDate") + private String d8MarriageDate; + + @JsonProperty("D8MarriagePlaceOfMarriage") + private String d8MarriagePlaceOfMarriage; + + @JsonProperty("D8MarriagePetitionerName") + private String d8MarriagePetitionerName; + + @JsonProperty("D8MarriageRespondentName") + private String d8MarriageRespondentName; + + @JsonProperty("D8RespondentPostCode") + private String d8RespondentPostCode; + + @JsonProperty("D8RespondentPostTown") + private String d8RespondentPostTown; + + @JsonProperty("D8RespondentPostCode") + private String d8PetitionerPostCode; + + @JsonProperty("D8RespondentPostTown") + private String d8PetitionerPostTown; + + public static class CaseDataOldDivorceDeserializer extends JsonDeserializer { + @Override + public CaseDataOldDivorce deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + CaseDataOldDivorce caseData = new CaseDataOldDivorce(); + + caseData.setD8caseReference(rootNode.path("D8caseReference").asText(null)); + caseData.setD8MarriageDate(rootNode.path("D8MarriageDate").asText(null)); + caseData.setD8MarriagePlaceOfMarriage(rootNode.path("D8MarriagePlaceOfMarriage").asText(null)); + caseData.setD8MarriagePetitionerName(rootNode.path("D8MarriagePetitionerName").asText(null)); + caseData.setD8MarriageRespondentName(rootNode.path("D8MarriageRespondentName").asText(null)); + + JsonNode respondentAddressNode = rootNode.path("D8RespondentHomeAddress"); + caseData.setD8RespondentPostCode(respondentAddressNode.path("PostCode").asText(null)); + caseData.setD8RespondentPostTown(respondentAddressNode.path("PostTown").asText(null)); + JsonNode petitionerAddressNode = rootNode.path("D8PetitionerHomeAddress"); + caseData.setD8PetitionerPostCode(petitionerAddressNode.path("PostCode").asText(null)); + caseData.setD8PetitionerPostTown(petitionerAddressNode.path("PostTown").asText(null)); + + return caseData; + } + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformation.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformation.java new file mode 100644 index 00000000000..23bf0f37d22 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformation.java @@ -0,0 +1,159 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +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.ListValue; +import uk.gov.hmcts.divorce.divorcecase.model.access.DefaultAccess; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.Boolean.TRUE; +import static org.apache.commons.collections4.CollectionUtils.isEmpty; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.Collection; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.Email; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.FixedList; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.TextArea; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RequestForInformation { + + @CCD( + label = "Address to sole", + typeOverride = FixedList, + typeParameterOverride = "RequestForInformationSoleParties", + access = {DefaultAccess.class} + ) + private RequestForInformationSoleParties requestForInformationSoleParties; + + @CCD( + label = "Address to joint", + typeOverride = FixedList, + typeParameterOverride = "RequestForInformationJointParties", + access = {DefaultAccess.class} + ) + private RequestForInformationJointParties requestForInformationJointParties; + + @CCD( + label = "Name", + access = {DefaultAccess.class} + ) + private String requestForInformationName; + + @CCD( + label = "Email address", + typeOverride = Email, + access = {DefaultAccess.class} + ) + private String requestForInformationEmailAddress; + + @CCD( + label = "Secondary name", + access = {DefaultAccess.class} + ) + private String requestForInformationSecondaryName; + + @CCD( + label = "Secondary email address", + typeOverride = Email, + access = {DefaultAccess.class} + ) + private String requestForInformationSecondaryEmailAddress; + + @CCD( + label = "Date/time of request", + access = {DefaultAccess.class} + ) + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS") + private LocalDateTime requestForInformationDateTime; + + @CCD( + label = "Please provide details", + typeOverride = TextArea, + access = {DefaultAccess.class} + ) + private String requestForInformationDetails; + + @CCD( + label = "Responses", + typeOverride = Collection, + typeParameterOverride = "RequestForInformationResponse", + access = {DefaultAccess.class} + ) + private List> requestForInformationResponses; + + @JsonIgnore + public void setValues(CaseData caseData) { + this.setRequestForInformationDateTime(LocalDateTime.now()); + + final RequestForInformationSoleParties soleParties = this.getRequestForInformationSoleParties(); + final RequestForInformationJointParties jointParties = this.getRequestForInformationJointParties(); + if (RequestForInformationSoleParties.APPLICANT.equals(soleParties) + || RequestForInformationJointParties.APPLICANT1.equals(jointParties)) { + this.setNameAndEmail(caseData.getApplicant1(), false); + } else if (RequestForInformationJointParties.APPLICANT2.equals(jointParties)) { + this.setNameAndEmail(caseData.getApplicant2(), false); + } else if (RequestForInformationJointParties.BOTH.equals(jointParties)) { + this.setNameAndEmail(caseData.getApplicant1(), false); + this.setNameAndEmail(caseData.getApplicant2(), true); + } + } + + @JsonIgnore + public RequestForInformationAuthParty getAuthorisedResponseParty() { + final RequestForInformationSoleParties soleParties = this.getRequestForInformationSoleParties(); + final RequestForInformationJointParties jointParties = this.getRequestForInformationJointParties(); + if (RequestForInformationSoleParties.APPLICANT.equals(soleParties) + || RequestForInformationJointParties.APPLICANT1.equals(jointParties)) { + return RequestForInformationAuthParty.APPLICANT1; + } else if (RequestForInformationJointParties.APPLICANT2.equals(jointParties)) { + return RequestForInformationAuthParty.APPLICANT2; + } else if (RequestForInformationJointParties.BOTH.equals(jointParties)) { + return RequestForInformationAuthParty.BOTH; + } + + return RequestForInformationAuthParty.OTHER; + } + + @JsonIgnore + private void setNameAndEmail(Applicant applicant, Boolean setSecondary) { + final boolean isRepresented = applicant.isRepresented(); + final String emailAddress = isRepresented ? applicant.getSolicitor().getEmail() : applicant.getEmail(); + final String name = isRepresented ? applicant.getSolicitor().getName() : applicant.getFullName(); + if (TRUE.equals(setSecondary)) { + this.setRequestForInformationSecondaryEmailAddress(emailAddress); + this.setRequestForInformationSecondaryName(name); + } else { + this.setRequestForInformationEmailAddress(emailAddress); + this.setRequestForInformationName(name); + } + } + + @JsonIgnore + public void addResponseToList(RequestForInformationResponse requestForInformationResponse) { + final ListValue newResponse = new ListValue<>(); + newResponse.setValue(requestForInformationResponse); + + if (isEmpty(this.getRequestForInformationResponses())) { + List> responses = new ArrayList<>(); + responses.add(newResponse); + this.setRequestForInformationResponses(responses); + } else { + this.getRequestForInformationResponses().add(0, newResponse); + } + } + + @JsonIgnore + public RequestForInformationResponse getLatestResponse() { + return this.getRequestForInformationResponses().get(0).getValue(); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationAuthParty.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationAuthParty.java new file mode 100644 index 00000000000..c0e18280d13 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationAuthParty.java @@ -0,0 +1,24 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import uk.gov.hmcts.ccd.sdk.api.HasLabel; + +@Getter +@AllArgsConstructor +public enum RequestForInformationAuthParty implements HasLabel { + @JsonProperty("applicant1") + APPLICANT1("Applicant 1"), + + @JsonProperty("applicant2") + APPLICANT2("Applicant 2"), + + @JsonProperty("both") + BOTH("Both"), + + @JsonProperty("other") + OTHER("Other"); + + private final String label; +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationJointParties.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationJointParties.java new file mode 100644 index 00000000000..087b177cbc8 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationJointParties.java @@ -0,0 +1,24 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import uk.gov.hmcts.ccd.sdk.api.HasLabel; + +@Getter +@AllArgsConstructor +public enum RequestForInformationJointParties implements HasLabel { + @JsonProperty("applicant1") + APPLICANT1("Applicant 1 / Applicant 1's Solicitor"), + + @JsonProperty("applicant2") + APPLICANT2("Applicant 2 / Applicant 2's Solicitor"), + + @JsonProperty("both") + BOTH("Applicant 1 and 2 / Applicant 1 and 2's Solicitors"), + + @JsonProperty("other") + OTHER("Other - email address"); + + private final String label; +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationList.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationList.java new file mode 100644 index 00000000000..55e1cb33694 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationList.java @@ -0,0 +1,110 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +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.ListValue; +import uk.gov.hmcts.divorce.divorcecase.model.access.Applicant2Access; +import uk.gov.hmcts.divorce.divorcecase.model.access.CaseworkerAccessOnlyAccess; +import uk.gov.hmcts.divorce.divorcecase.model.access.DefaultAccess; + +import java.util.ArrayList; +import java.util.List; + +import static org.apache.commons.collections4.CollectionUtils.isEmpty; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.Collection; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RequestForInformationList { + + @CCD( + label = "Requests for information", + typeOverride = Collection, + typeParameterOverride = "RequestForInformation", + access = {DefaultAccess.class} + ) + private List> requestsForInformation; + + @CCD( + label = "Request for information", + access = {DefaultAccess.class} + ) + @Builder.Default + @JsonUnwrapped + private RequestForInformation requestForInformation = new RequestForInformation(); + + @CCD( + label = "Request for information response Applicant 1 Draft", + access = {DefaultAccess.class} + ) + @Builder.Default + @JsonUnwrapped(prefix = "app1") + private RequestForInformationResponseDraft requestForInformationResponseApplicant1 = new RequestForInformationResponseDraft(); + + @CCD( + label = "Request for information response Applicant 1 Solicitor Draft", + access = {DefaultAccess.class} + ) + @Builder.Default + @JsonUnwrapped(prefix = "app1Sol") + private RequestForInformationResponseDraft requestForInformationResponseApplicant1Solicitor = new RequestForInformationResponseDraft(); + + @CCD( + label = "Request for information response Applicant 2 Draft", + access = {DefaultAccess.class, Applicant2Access.class} + ) + @Builder.Default + @JsonUnwrapped(prefix = "app2") + private RequestForInformationResponseDraft requestForInformationResponseApplicant2 = new RequestForInformationResponseDraft(); + + @CCD( + label = "Request for information response Applicant 2 Solicitor Draft", + access = {DefaultAccess.class} + ) + @Builder.Default + @JsonUnwrapped(prefix = "app2Sol") + private RequestForInformationResponseDraft requestForInformationResponseApplicant2Solicitor = new RequestForInformationResponseDraft(); + + @CCD( + label = "Request for information Offline response Draft", + access = {CaseworkerAccessOnlyAccess.class} + ) + @Builder.Default + @JsonUnwrapped() + private RequestForInformationOfflineResponseDraft requestForInformationOfflineResponseDraft + = new RequestForInformationOfflineResponseDraft(); + + @CCD( + label = "Authorised Request For Information Response Party", + access = {DefaultAccess.class} + ) + private RequestForInformationAuthParty requestForInformationAuthParty; + + @JsonIgnore + public RequestForInformation getLatestRequest() { + return this.getRequestsForInformation().get(0).getValue(); + } + + @JsonIgnore + public void addRequestToList(RequestForInformation requestForInformation) { + final ListValue newRequest = new ListValue<>(); + newRequest.setValue(requestForInformation); + + if (isEmpty(this.getRequestsForInformation())) { + List> requests = new ArrayList<>(); + requests.add(newRequest); + this.setRequestsForInformation(requests); + } else { + this.getRequestsForInformation().add(0, newRequest); + } + + this.setRequestForInformationAuthParty(requestForInformation.getAuthorisedResponseParty()); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseDraft.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseDraft.java new file mode 100644 index 00000000000..b13b09440fe --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseDraft.java @@ -0,0 +1,93 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +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.ListValue; +import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.divorce.divorcecase.model.access.DefaultAccess; +import uk.gov.hmcts.divorce.document.model.DivorceDocument; + +import java.util.ArrayList; +import java.util.List; + +import static uk.gov.hmcts.ccd.sdk.type.FieldType.Collection; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.Email; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.FixedList; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.TextArea; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RequestForInformationOfflineResponseDraft { + + @CCD( + label = "Details", + typeOverride = TextArea, + access = {DefaultAccess.class} + ) + private String rfiOfflineDraftResponseDetails; + + @CCD( + label = "Documents", + typeOverride = Collection, + typeParameterOverride = "DivorceDocument" + ) + private List> rfiOfflineDraftResponseDocs; + + @CCD( + label = "Select sender of document", + typeOverride = FixedList, + typeParameterOverride = "RequestForInformationOfflineResponseSoleParties", + access = {DefaultAccess.class} + ) + private RequestForInformationOfflineResponseSoleParties rfiOfflineSoleResponseParties; + + @CCD( + label = "Select sender of document", + typeOverride = FixedList, + typeParameterOverride = "RequestForInformationOfflineResponseJointParties", + access = {DefaultAccess.class} + ) + private RequestForInformationOfflineResponseJointParties rfiOfflineJointResponseParties; + + @CCD( + label = "Name", + access = {DefaultAccess.class} + ) + private String rfiOfflineResponseOtherName; + + @CCD( + label = "Email address", + typeOverride = Email, + access = {DefaultAccess.class} + ) + private String rfiOfflineResponseOtherEmail; + + @CCD( + label = "All requested documents uploaded", + access = {DefaultAccess.class} + ) + private YesOrNo rfiOfflineAllDocumentsUploaded; + + @CCD( + label = "Send notifications?", + access = {DefaultAccess.class} + ) + private YesOrNo rfiOfflineResponseSendNotifications; + + @JsonIgnore + public void addDocument(DivorceDocument offlineResponseDoc) { + if (this.getRfiOfflineDraftResponseDocs() == null || this.getRfiOfflineDraftResponseDocs().isEmpty()) { + this.setRfiOfflineDraftResponseDocs(new ArrayList<>()); + } + + ListValue offlineResponseDocListValue = new ListValue<>(); + offlineResponseDocListValue.setValue(offlineResponseDoc); + this.getRfiOfflineDraftResponseDocs().add(0, offlineResponseDocListValue); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseJointParties.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseJointParties.java new file mode 100644 index 00000000000..d482d8af3f5 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseJointParties.java @@ -0,0 +1,27 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import uk.gov.hmcts.ccd.sdk.api.HasLabel; + +@Getter +@AllArgsConstructor +public enum RequestForInformationOfflineResponseJointParties implements HasLabel { + @JsonProperty("applicant1") + APPLICANT1("Applicant 1"), + + @JsonProperty("applicant1Solicitor") + APPLICANT1SOLICITOR("Applicant 1's Solicitor"), + + @JsonProperty("applicant2") + APPLICANT2("Applicant 2"), + + @JsonProperty("applicant2Solicitor") + APPLICANT2SOLICITOR("Applicant 2's Solicitor"), + + @JsonProperty("other") + OTHER("Other"); + + private final String label; +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseSoleParties.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseSoleParties.java new file mode 100644 index 00000000000..f0a9db294e8 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationOfflineResponseSoleParties.java @@ -0,0 +1,21 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import uk.gov.hmcts.ccd.sdk.api.HasLabel; + +@Getter +@AllArgsConstructor +public enum RequestForInformationOfflineResponseSoleParties implements HasLabel { + @JsonProperty("applicant") + APPLICANT("Applicant"), + + @JsonProperty("applicantSolicitor") + APPLICANTSOLICITOR("Applicant's Solicitor"), + + @JsonProperty("other") + OTHER("Other"); + + private final String label; +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponse.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponse.java new file mode 100644 index 00000000000..c40a013c835 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponse.java @@ -0,0 +1,207 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +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.ListValue; +import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.divorce.divorcecase.model.access.DefaultAccess; +import uk.gov.hmcts.divorce.document.model.DivorceDocument; + +import java.time.LocalDateTime; +import java.util.List; + +import static uk.gov.hmcts.ccd.sdk.type.FieldType.Collection; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.Email; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.FixedList; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.TextArea; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.OTHER; +import static uk.gov.hmcts.divorce.document.model.DocumentType.REQUEST_FOR_INFORMATION_RESPONSE_DOC; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RequestForInformationResponse { + + @CCD( + label = "Respondee", + typeOverride = FixedList, + typeParameterOverride = "RequestForInformationResponseParties", + access = {DefaultAccess.class} + ) + private RequestForInformationResponseParties requestForInformationResponseParties; + + @CCD( + label = "Name", + access = {DefaultAccess.class} + ) + private String requestForInformationResponseName; + + @CCD( + label = "Email address", + typeOverride = Email, + access = {DefaultAccess.class} + ) + private String requestForInformationResponseEmailAddress; + + @CCD( + label = "Date/time of response", + access = {DefaultAccess.class} + ) + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS") + private LocalDateTime requestForInformationResponseDateTime; + + @CCD( + label = "Provided Response", + typeOverride = TextArea, + access = {DefaultAccess.class} + ) + private String requestForInformationResponseDetails; + + @CCD( + label = "Uploaded documents", + typeOverride = Collection, + typeParameterOverride = "DivorceDocument" + ) + private List> requestForInformationResponseDocs; + + @CCD( + label = "Offline documents", + typeOverride = Collection, + typeParameterOverride = "DivorceDocument" + ) + private List> rfiOfflineResponseDocs; + + @CCD( + label = "Could not upload all or some requested documents", + access = {DefaultAccess.class} + ) + private YesOrNo requestForInformationResponseCannotUploadDocs; + + @CCD( + label = "Offline Response", + access = {DefaultAccess.class} + ) + private YesOrNo requestForInformationResponseOffline; + + @CCD( + label = "All requested documents uploaded", + access = {DefaultAccess.class} + ) + private YesOrNo rfiOfflineResponseAllDocumentsUploaded; + + @CCD( + label = "Notifications requested?", + access = {DefaultAccess.class} + ) + private YesOrNo rfiOfflineResponseNotificationsRequested; + + @JsonIgnore + public void setValues(CaseData caseData, RequestForInformationResponseParties party) { + final Applicant applicant = party.equals(APPLICANT1) || party.equals(APPLICANT1SOLICITOR) + ? caseData.getApplicant1() + : caseData.getApplicant2(); + final String name = party.equals(APPLICANT1SOLICITOR) || party.equals(APPLICANT2SOLICITOR) + ? applicant.getSolicitor().getName() + : applicant.getFullName(); + final String email = party.equals(APPLICANT1SOLICITOR) || party.equals(APPLICANT2SOLICITOR) + ? applicant.getSolicitor().getEmail() + : applicant.getEmail(); + this.setRequestForInformationResponseParties(party); + this.setRequestForInformationResponseName(name); + this.setRequestForInformationResponseEmailAddress(email); + this.setRequestForInformationResponseDateTime(LocalDateTime.now()); + + if (party.equals(APPLICANT1)) { + setDraftValues(caseData.getRequestForInformationList().getRequestForInformationResponseApplicant1()); + } else if (party.equals(APPLICANT2)) { + setDraftValues(caseData.getRequestForInformationList().getRequestForInformationResponseApplicant2()); + } else if (party.equals(APPLICANT1SOLICITOR)) { + setDraftValues(caseData.getRequestForInformationList().getRequestForInformationResponseApplicant1Solicitor()); + } else if (party.equals(APPLICANT2SOLICITOR)) { + setDraftValues(caseData.getRequestForInformationList().getRequestForInformationResponseApplicant2Solicitor()); + } + } + + @JsonIgnore + public void setValues(CaseData caseData, RequestForInformationOfflineResponseDraft offlineDraft) { + final RequestForInformationResponseParties party = getOfflineResponseParty(caseData, offlineDraft); + String name; + String email; + if (party.equals(OTHER)) { + name = offlineDraft.getRfiOfflineResponseOtherName(); + email = offlineDraft.getRfiOfflineResponseOtherEmail(); + } else { + final Applicant applicant = party.equals(APPLICANT1) || party.equals(APPLICANT1SOLICITOR) + ? caseData.getApplicant1() + : caseData.getApplicant2(); + name = party.equals(APPLICANT1SOLICITOR) || party.equals(APPLICANT2SOLICITOR) + ? applicant.getSolicitor().getName() + : applicant.getFullName(); + email = party.equals(APPLICANT1SOLICITOR) || party.equals(APPLICANT2SOLICITOR) + ? applicant.getSolicitor().getEmail() + : applicant.getEmail(); + } + this.setRequestForInformationResponseParties(party); + this.setRequestForInformationResponseName(name); + this.setRequestForInformationResponseEmailAddress(email); + this.setRequestForInformationResponseDateTime(LocalDateTime.now()); + this.setRequestForInformationResponseOffline(YES); + this.setRfiOfflineResponseAllDocumentsUploaded(offlineDraft.getRfiOfflineAllDocumentsUploaded()); + this.setRfiOfflineResponseNotificationsRequested(offlineDraft.getRfiOfflineResponseSendNotifications()); + + this.setRequestForInformationResponseDetails(offlineDraft.getRfiOfflineDraftResponseDetails()); + if (offlineDraft.getRfiOfflineDraftResponseDocs() != null && !offlineDraft.getRfiOfflineDraftResponseDocs().isEmpty()) { + for (ListValue doc : offlineDraft.getRfiOfflineDraftResponseDocs()) { + doc.getValue().setDocumentType(REQUEST_FOR_INFORMATION_RESPONSE_DOC); + } + this.setRfiOfflineResponseDocs(offlineDraft.getRfiOfflineDraftResponseDocs()); + } + } + + @JsonIgnore + private void setDraftValues(RequestForInformationResponseDraft draft) { + this.setRequestForInformationResponseDetails(draft.getRfiDraftResponseDetails()); + this.setRequestForInformationResponseDocs(draft.getRfiDraftResponseDocs()); + if (YES.equals(draft.getRfiDraftResponseCannotUploadDocs())) { + this.setRequestForInformationResponseCannotUploadDocs(YES); + } + } + + private RequestForInformationResponseParties getOfflineResponseParty(CaseData caseData, + RequestForInformationOfflineResponseDraft offlineDraft + ) { + RequestForInformationResponseParties responseParty; + if (caseData.getApplicationType().isSole()) { + switch (offlineDraft.getRfiOfflineSoleResponseParties()) { + case APPLICANT -> responseParty = APPLICANT1; + case APPLICANTSOLICITOR -> responseParty = APPLICANT1SOLICITOR; + default -> responseParty = OTHER; + } + } else { + switch (offlineDraft.getRfiOfflineJointResponseParties()) { + case APPLICANT1 -> responseParty = APPLICANT1; + case APPLICANT1SOLICITOR -> responseParty = APPLICANT1SOLICITOR; + case APPLICANT2 -> responseParty = APPLICANT2; + case APPLICANT2SOLICITOR -> responseParty = APPLICANT2SOLICITOR; + default -> responseParty = OTHER; + } + } + return responseParty; + } + + @JsonIgnore + public boolean isOffline() { + return YES.equals(this.getRequestForInformationResponseOffline()); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponseDraft.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponseDraft.java new file mode 100644 index 00000000000..e4bb00b0382 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponseDraft.java @@ -0,0 +1,60 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +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.ListValue; +import uk.gov.hmcts.ccd.sdk.type.YesOrNo; +import uk.gov.hmcts.divorce.divorcecase.model.access.DefaultAccess; +import uk.gov.hmcts.divorce.document.model.DivorceDocument; + +import java.util.ArrayList; +import java.util.List; + +import static uk.gov.hmcts.ccd.sdk.type.FieldType.Collection; +import static uk.gov.hmcts.ccd.sdk.type.FieldType.TextArea; + +@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class) +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RequestForInformationResponseDraft { + + @CCD( + label = "Write your response below if the court has asked for additional information. If the court has just asked for documents, " + + " then you do not need to write anything unless you think it's useful information.", + typeOverride = TextArea, + access = {DefaultAccess.class} + ) + private String rfiDraftResponseDetails; + + @CCD( + label = "Upload documents", + typeOverride = Collection, + typeParameterOverride = "DivorceDocument" + ) + private List> rfiDraftResponseDocs; + + @CCD( + label = "Cannot upload all or some requested documents", + access = {DefaultAccess.class} + ) + private YesOrNo rfiDraftResponseCannotUploadDocs; + + @JsonIgnore + public void addDocument(DivorceDocument responseDoc) { + if (this.getRfiDraftResponseDocs() == null || this.getRfiDraftResponseDocs().isEmpty()) { + this.setRfiDraftResponseDocs(new ArrayList<>()); + } + + ListValue responseDocListValue = new ListValue<>(); + responseDocListValue.setValue(responseDoc); + this.getRfiDraftResponseDocs().add(0, responseDocListValue); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponseParties.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponseParties.java new file mode 100644 index 00000000000..010a3d720b0 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationResponseParties.java @@ -0,0 +1,27 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import uk.gov.hmcts.ccd.sdk.api.HasLabel; + +@Getter +@AllArgsConstructor +public enum RequestForInformationResponseParties implements HasLabel { + @JsonProperty("applicant1") + APPLICANT1("Applicant 1"), + + @JsonProperty("applicant1Solicitor") + APPLICANT1SOLICITOR("Applicant 1's Solicitor"), + + @JsonProperty("applicant2") + APPLICANT2("Applicant 2"), + + @JsonProperty("applicant2Solicitor") + APPLICANT2SOLICITOR("Applicant 2's Solicitor"), + + @JsonProperty("other") + OTHER("Other"); + + private final String label; +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationSoleParties.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationSoleParties.java new file mode 100644 index 00000000000..250c7cf3058 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/RequestForInformationSoleParties.java @@ -0,0 +1,18 @@ +package uk.gov.hmcts.divorce.divorcecase.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import uk.gov.hmcts.ccd.sdk.api.HasLabel; + +@Getter +@AllArgsConstructor +public enum RequestForInformationSoleParties implements HasLabel { + @JsonProperty("applicant") + APPLICANT("Applicant / Applicant's Solicitor"), + + @JsonProperty("other") + OTHER("Other - email address"); + + private final String label; +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/Applicant1DeleteAccess.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/Applicant1DeleteAccess.java new file mode 100644 index 00000000000..b342fe6c628 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/Applicant1DeleteAccess.java @@ -0,0 +1,21 @@ +package uk.gov.hmcts.divorce.divorcecase.model.access; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; +import uk.gov.hmcts.ccd.sdk.api.HasAccessControl; +import uk.gov.hmcts.ccd.sdk.api.HasRole; +import uk.gov.hmcts.ccd.sdk.api.Permission; + +import static uk.gov.hmcts.ccd.sdk.api.Permission.CRUD; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_1_SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CREATOR; + +public class Applicant1DeleteAccess implements HasAccessControl { + @Override + public SetMultimap getGrants() { + SetMultimap grants = HashMultimap.create(); + grants.putAll(APPLICANT_1_SOLICITOR, CRUD); + grants.putAll(CREATOR, CRUD); + return grants; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/Applicant2DeleteAccess.java b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/Applicant2DeleteAccess.java new file mode 100644 index 00000000000..d37584a3456 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/divorcecase/model/access/Applicant2DeleteAccess.java @@ -0,0 +1,21 @@ +package uk.gov.hmcts.divorce.divorcecase.model.access; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; +import uk.gov.hmcts.ccd.sdk.api.HasAccessControl; +import uk.gov.hmcts.ccd.sdk.api.HasRole; +import uk.gov.hmcts.ccd.sdk.api.Permission; + +import static uk.gov.hmcts.ccd.sdk.api.Permission.CRUD; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_2; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_2_SOLICITOR; + +public class Applicant2DeleteAccess implements HasAccessControl { + @Override + public SetMultimap getGrants() { + SetMultimap grants = HashMultimap.create(); + grants.putAll(APPLICANT_2_SOLICITOR, CRUD); + grants.putAll(APPLICANT_2, CRUD); + return grants; + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/payment/PaymentSetupService.java b/src/main/java/uk/gov/hmcts/divorce/payment/PaymentSetupService.java new file mode 100644 index 00000000000..fc8f59f8ed7 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/payment/PaymentSetupService.java @@ -0,0 +1,72 @@ +package uk.gov.hmcts.divorce.payment; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.ccd.sdk.type.OrderSummary; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; + +import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; +import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_ISSUE; +import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_DIVORCE; +import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; +import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_DIVORCE; +import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; + +@Service +@Slf4j +@RequiredArgsConstructor +public class PaymentSetupService { + + private final PaymentService paymentService; + + public String createApplicationFeeServiceRequest(CaseData data, long caseId, String redirectUrl) { + if (data.getApplication() != null && data.getApplication().getApplicationFeeServiceRequestReference() != null) { + return data.getApplication().getApplicationFeeServiceRequestReference(); + } + + log.info("Application fee service request not found for case id: {}, creating service request", caseId); + + return paymentService.createServiceRequestReference( + redirectUrl, + caseId, + data.getApplicant1().getFullName(), + data.getApplication().getApplicationFeeOrderSummary() + ); + } + + public OrderSummary createApplicationFeeOrderSummary(CaseData data, long caseId) { + if (data.getApplication() != null && data.getApplication().getApplicationFeeOrderSummary() != null) { + return data.getApplication().getApplicationFeeOrderSummary(); + } + + log.info("Application fee order summary not found for case id: {}, creating order summary", caseId); + + return paymentService.getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE, KEYWORD_DIVORCE); + } + + public String createFinalOrderFeeServiceRequest(CaseData data, long caseId, String redirectUrl, OrderSummary orderSummary) { + if (data.getFinalOrder() != null && data.getFinalOrder().getApplicant2FinalOrderFeeServiceRequestReference() != null) { + return data.getFinalOrder().getApplicant2FinalOrderFeeServiceRequestReference(); + } + + log.info("Final order fee service request not found for case id: {}, creating service request", caseId); + + return paymentService.createServiceRequestReference( + redirectUrl, + caseId, + data.getApplicant2().getFullName(), + orderSummary + ); + } + + public OrderSummary createFinalOrderFeeOrderSummary(CaseData data, long caseId) { + if (data.getFinalOrder() != null && data.getFinalOrder().getApplicant2FinalOrderFeeOrderSummary() != null) { + return data.getFinalOrder().getApplicant2FinalOrderFeeOrderSummary(); + } + + log.info("Final order fee order summary not found for case id: {}, creating order summary", caseId); + + return paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/Applicant1SolicitorRespondRequestForInformation.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/Applicant1SolicitorRespondRequestForInformation.java new file mode 100644 index 00000000000..9ad5b8706aa --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/Applicant1SolicitorRespondRequestForInformation.java @@ -0,0 +1,162 @@ +package uk.gov.hmcts.divorce.solicitor.event; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationAuthParty; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationList; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.document.model.DivorceDocument; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; +import uk.gov.hmcts.reform.ccd.client.model.SubmittedCallbackResponse; + +import java.util.Collections; +import java.util.List; + +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationAuthParty.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationAuthParty.OTHER; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_1_SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; + +@Slf4j +@Component +@RequiredArgsConstructor +public class Applicant1SolicitorRespondRequestForInformation implements CCDConfig { + + public static final String APP_1_SOLICITOR_RESPOND_REQUEST_INFO = "app1-solicitor-respond-request-info"; + public static final String MUST_ADD_DOCS_OR_DETAILS_ERROR = "You must upload a document or write a response"; + public static final String NOT_AUTHORISED_TO_RESPOND_ERROR = "You are not authorised to respond to this request."; + public static final String REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR + = "Request for Information Response Partner Notification for Case Id {} failed with message: {}"; + + private final NotificationDispatcher notificationDispatcher; + private final CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(APP_1_SOLICITOR_RESPOND_REQUEST_INFO) + .forState(InformationRequested) + .showCondition("requestForInformationAuthParty=\"applicant1\" OR requestForInformationAuthParty=\"both\"") + .name("Submit Response") + .description("Submit response") + .showSummary() + .showEventNotes() + .endButtonLabel("Submit") + .aboutToStartCallback(this::aboutToStart) + .aboutToSubmitCallback(this::aboutToSubmit) + .submittedCallback(this::submitted) + .grant(CREATE_READ_UPDATE, APPLICANT_1_SOLICITOR) + .grantHistoryOnly(CASE_WORKER, SUPER_USER, LEGAL_ADVISOR, JUDGE)) + .page("requestForInformationResponse", this::midEvent) + .pageLabel("Submit Response") + .complex(CaseData::getRequestForInformationList) + .complex(RequestForInformationList::getRequestForInformationResponseApplicant1Solicitor) + .optional(RequestForInformationResponseDraft::getRfiDraftResponseDetails) + .optional(RequestForInformationResponseDraft::getRfiDraftResponseDocs) + .done() + .done(); + } + + public AboutToStartOrSubmitResponse aboutToStart(CaseDetails details) { + + log.info("{} aboutToStart callback invoked for Case Id: {}", APP_1_SOLICITOR_RESPOND_REQUEST_INFO, details.getId()); + + RequestForInformationAuthParty authParty = details.getData().getRequestForInformationList().getRequestForInformationAuthParty(); + + if (APPLICANT2.equals(authParty) || OTHER.equals(authParty)) { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(NOT_AUTHORISED_TO_RESPOND_ERROR)) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .build(); + } + + public AboutToStartOrSubmitResponse midEvent(CaseDetails details, + CaseDetails detailsBefore) { + + log.info("{} midEvent callback invoked for Case Id: {}", APP_1_SOLICITOR_RESPOND_REQUEST_INFO, details.getId()); + + RequestForInformationResponseDraft response = + details.getData().getRequestForInformationList().getRequestForInformationResponseApplicant1Solicitor(); + List> responseDocs = response.getRfiDraftResponseDocs(); + String responseDetails = response.getRfiDraftResponseDetails(); + + if ((responseDocs == null || responseDocs.isEmpty()) && (responseDetails == null || responseDetails.isEmpty())) { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(MUST_ADD_DOCS_OR_DETAILS_ERROR)) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .build(); + } + + public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails details, + CaseDetails beforeDetails) { + log.info("{} about to submit callback invoked for Case Id: {}", APP_1_SOLICITOR_RESPOND_REQUEST_INFO, details.getId()); + + CaseData data = details.getData(); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(data, APPLICANT1SOLICITOR); + + data.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + //Prevent pre-populating fields for new requests + data.getRequestForInformationList().setRequestForInformationResponseApplicant1Solicitor(new RequestForInformationResponseDraft()); + + return AboutToStartOrSubmitResponse.builder() + .data(data) + .state(RequestedInformationSubmitted) + .build(); + } + + public SubmittedCallbackResponse submitted(CaseDetails details, CaseDetails beforeDetails) { + log.info("{} submitted callback invoked for Case Id: {}", APP_1_SOLICITOR_RESPOND_REQUEST_INFO, details.getId()); + + if (!details.getData().getApplicationType().isSole() + && BOTH.equals(details.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationJointParties()) + ) { + try { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + details.getData(), + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error( + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR, + details.getId(), + e.getMessage(), + e + ); + } + } + + return SubmittedCallbackResponse.builder().build(); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/event/Applicant2SolicitorRespondRequestForInformation.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/Applicant2SolicitorRespondRequestForInformation.java new file mode 100644 index 00000000000..fcada5c0a19 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/event/Applicant2SolicitorRespondRequestForInformation.java @@ -0,0 +1,163 @@ +package uk.gov.hmcts.divorce.solicitor.event; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CCDConfig; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.ConfigBuilder; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; +import uk.gov.hmcts.divorce.common.ccd.PageBuilder; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationAuthParty; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationList; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.document.model.DivorceDocument; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; +import uk.gov.hmcts.reform.ccd.client.model.SubmittedCallbackResponse; + +import java.util.Collections; +import java.util.List; + +import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.SOLE_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationAuthParty.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationAuthParty.OTHER; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.APPLICANT_2_SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.CASE_WORKER; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.JUDGE; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.LEGAL_ADVISOR; +import static uk.gov.hmcts.divorce.divorcecase.model.UserRole.SUPER_USER; +import static uk.gov.hmcts.divorce.divorcecase.model.access.Permissions.CREATE_READ_UPDATE; + +@Slf4j +@Component +@RequiredArgsConstructor +public class Applicant2SolicitorRespondRequestForInformation implements CCDConfig { + + public static final String APP_2_SOLICITOR_RESPOND_REQUEST_INFO = "app2-solicitor-respond-request-info"; + public static final String MUST_ADD_DOCS_OR_DETAILS_ERROR = "You must upload a document or write a response."; + public static final String NOT_AUTHORISED_TO_RESPOND_ERROR = "You are not authorised to respond to this request."; + public static final String REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR + = "Request for Information Response Partner Notification for Case Id {} failed with message: {}"; + + private final NotificationDispatcher notificationDispatcher; + private final CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @Override + public void configure(final ConfigBuilder configBuilder) { + new PageBuilder(configBuilder + .event(APP_2_SOLICITOR_RESPOND_REQUEST_INFO) + .forState(InformationRequested) + .showCondition("requestForInformationAuthParty=\"applicant2\" OR requestForInformationAuthParty=\"both\"") + .name("Submit Response") + .description("Submit response") + .showSummary() + .showEventNotes() + .endButtonLabel("Submit") + .aboutToStartCallback(this::aboutToStart) + .aboutToSubmitCallback(this::aboutToSubmit) + .submittedCallback(this::submitted) + .grant(CREATE_READ_UPDATE, APPLICANT_2_SOLICITOR) + .grantHistoryOnly(CASE_WORKER, SUPER_USER, LEGAL_ADVISOR, JUDGE)) + .page("requestForInformationResponse", this::midEvent) + .pageLabel("Submit Response") + .complex(CaseData::getRequestForInformationList) + .complex(RequestForInformationList::getRequestForInformationResponseApplicant2Solicitor) + .optional(RequestForInformationResponseDraft::getRfiDraftResponseDetails) + .optional(RequestForInformationResponseDraft::getRfiDraftResponseDocs) + .done() + .done(); + } + + public AboutToStartOrSubmitResponse aboutToStart(CaseDetails details) { + + log.info("{} aboutToStart callback invoked for Case Id: {}", APP_2_SOLICITOR_RESPOND_REQUEST_INFO, details.getId()); + + RequestForInformationAuthParty authParty = details.getData().getRequestForInformationList().getRequestForInformationAuthParty(); + + if (SOLE_APPLICATION.equals(details.getData().getApplicationType()) || APPLICANT1.equals(authParty) || OTHER.equals(authParty)) { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(NOT_AUTHORISED_TO_RESPOND_ERROR)) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .build(); + } + + public AboutToStartOrSubmitResponse midEvent(CaseDetails details, + CaseDetails detailsBefore) { + + log.info("{} midEvent callback invoked for Case Id: {}", APP_2_SOLICITOR_RESPOND_REQUEST_INFO, details.getId()); + + RequestForInformationResponseDraft response = + details.getData().getRequestForInformationList().getRequestForInformationResponseApplicant2Solicitor(); + List> responseDocs = response.getRfiDraftResponseDocs(); + String responseDetails = response.getRfiDraftResponseDetails(); + + if ((responseDocs == null || responseDocs.isEmpty()) && (responseDetails == null || responseDetails.isEmpty())) { + return AboutToStartOrSubmitResponse.builder() + .errors(Collections.singletonList(MUST_ADD_DOCS_OR_DETAILS_ERROR)) + .build(); + } + + return AboutToStartOrSubmitResponse.builder() + .data(details.getData()) + .build(); + } + + public AboutToStartOrSubmitResponse aboutToSubmit(CaseDetails details, + CaseDetails beforeDetails) { + log.info("{} about to submit callback invoked for Case Id: {}", APP_2_SOLICITOR_RESPOND_REQUEST_INFO, details.getId()); + + CaseData data = details.getData(); + RequestForInformationResponse requestForInformationResponse = new RequestForInformationResponse(); + requestForInformationResponse.setValues(data, APPLICANT2SOLICITOR); + + data.getRequestForInformationList().getLatestRequest().addResponseToList(requestForInformationResponse); + + //Prevent pre-populating fields for new requests + data.getRequestForInformationList().setRequestForInformationResponseApplicant2Solicitor(new RequestForInformationResponseDraft()); + + return AboutToStartOrSubmitResponse.builder() + .data(data) + .state(RequestedInformationSubmitted) + .build(); + } + + public SubmittedCallbackResponse submitted(CaseDetails details, CaseDetails beforeDetails) { + log.info("{} submitted callback invoked for Case Id: {}", APP_2_SOLICITOR_RESPOND_REQUEST_INFO, details.getId()); + + if (!details.getData().getApplicationType().isSole() + && BOTH.equals(details.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationJointParties()) + ) { + try { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + details.getData(), + details.getId() + ); + } catch (final NotificationTemplateException e) { + log.error( + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER_NOTIFICATION_FAILED_ERROR, + details.getId(), + e.getMessage(), + e + ); + } + } + + return SubmittedCallbackResponse.builder().build(); + } +} diff --git a/src/main/java/uk/gov/hmcts/divorce/solicitor/service/task/SetApplicationFeeServiceRequest.java b/src/main/java/uk/gov/hmcts/divorce/solicitor/service/task/SetApplicationFeeServiceRequest.java new file mode 100644 index 00000000000..133c8dad1ff --- /dev/null +++ b/src/main/java/uk/gov/hmcts/divorce/solicitor/service/task/SetApplicationFeeServiceRequest.java @@ -0,0 +1,39 @@ +package uk.gov.hmcts.divorce.solicitor.service.task; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.type.OrderSummary; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.task.CaseTask; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; + +@Component +@RequiredArgsConstructor +public class SetApplicationFeeServiceRequest implements CaseTask { + + private final PaymentSetupService paymentSetupService; + + @Value("${idam.client.redirect_uri}") + private String redirectUrl; + + @Override + public CaseDetails apply(CaseDetails details) { + var data = details.getData(); + var application = data.getApplication(); + + OrderSummary orderSummary = paymentSetupService.createApplicationFeeOrderSummary( + data, details.getId() + ); + application.setApplicationFeeOrderSummary(orderSummary); + + String serviceRequest = paymentSetupService.createApplicationFeeServiceRequest( + data, details.getId(), redirectUrl + ); + application.setApplicationFeeServiceRequestReference(serviceRequest); + + return details; + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveFailedSolNoticeOfChangeTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveFailedSolNoticeOfChangeTest.java new file mode 100644 index 00000000000..e6a8ef8d358 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveFailedSolNoticeOfChangeTest.java @@ -0,0 +1,69 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.type.ChangeOrganisationRequest; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import static org.assertj.core.api.Assertions.assertThat; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRemoveFailedSolNoticeOfChange.CASEWORKER_REMOVE_FAILED_SOL_NOC_REQUEST; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRemoveFailedSolNoticeOfChange.NO_NOC_REQUEST_ERROR; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; + +@ExtendWith(MockitoExtension.class) +class CaseworkerRemoveFailedSolNoticeOfChangeTest { + + @InjectMocks + private CaseworkerRemoveFailedSolNoticeOfChange caseworkerRemoveFailedSolNoticeOfChange; + + @Test + void configure() { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + caseworkerRemoveFailedSolNoticeOfChange.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CASEWORKER_REMOVE_FAILED_SOL_NOC_REQUEST); + } + + @Test + void shouldReturnValidationErrorWhenNocRequestFieldIsNull() { + var details = getCaseDetails(); + + var result = caseworkerRemoveFailedSolNoticeOfChange.aboutToSubmit(details, details); + + assertThat(result.getErrors()).hasSize(1); + assertThat(result.getErrors()).contains(NO_NOC_REQUEST_ERROR + TEST_CASE_ID); + } + + @Test + void shouldNotReturnValidationErrorAndShouldNullNocRequestField() { + var details = getCaseDetails(); + details.getData().setChangeOrganisationRequestField(new ChangeOrganisationRequest<>()); + + var result = caseworkerRemoveFailedSolNoticeOfChange.aboutToSubmit(details, details); + + assertThat(result.getErrors()).isNullOrEmpty(); + assertThat(result.getData().getChangeOrganisationRequestField()).isNull(); + } + + private CaseDetails getCaseDetails() { + final var details = new CaseDetails(); + final var data = caseData(); + details.setData(data); + details.setId(TEST_CASE_ID); + + return details; + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveNoteTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveNoteTest.java new file mode 100644 index 00000000000..ac53b659404 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRemoveNoteTest.java @@ -0,0 +1,88 @@ +package uk.gov.hmcts.divorce.caseworker.event; + + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.divorce.caseworker.model.CaseNote; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRemoveNote.ERROR_NOTE_ADDED; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; + +@ExtendWith(MockitoExtension.class) +public class CaseworkerRemoveNoteTest { + @InjectMocks + private CaseworkerRemoveNote caseworkerRemoveNote; + + @Test + void shouldAddConfigurationToConfigBuilder() { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + caseworkerRemoveNote.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CaseworkerRemoveNote.CASEWORKER_REMOVE_NOTE); + } + + @Test + void shouldReturnValidationErrorIfNoteIsAdded() { + final ListValue note1 = ListValue.builder() + .value(CaseNote.builder().note("Note 1").build()) + .build(); + CaseData beforeCaseData = CaseData.builder() + .notes(List.of(note1)) + .build(); + CaseDetails beforeDetails = CaseDetails.builder() + .data(beforeCaseData) + .build(); + + final ListValue note2 = ListValue.builder() + .value(CaseNote.builder().note("Note 2").build()) + .build(); + CaseData currentCaseData = CaseData.builder() + .notes(List.of(note1, note2)) + .build(); + CaseDetails currentDetails = CaseDetails.builder() + .data(currentCaseData) + .build(); + + var response = caseworkerRemoveNote.midEvent(currentDetails, beforeDetails); + assertThat(response.getErrors()).contains(ERROR_NOTE_ADDED); + } + + @Test + void shouldNotReturnValidationErrorIfNoteIsRemoved() { + final ListValue note1 = ListValue.builder() + .value(CaseNote.builder().note("Note 1").build()) + .build(); + CaseData beforeCaseData = CaseData.builder() + .notes(List.of(note1)) + .build(); + CaseDetails beforeDetails = CaseDetails.builder() + .data(beforeCaseData) + .build(); + + CaseData currentCaseData = CaseData.builder() + .notes(List.of()) + .build(); + CaseDetails currentDetails = CaseDetails.builder() + .data(currentCaseData) + .build(); + + var response = caseworkerRemoveNote.midEvent(currentDetails, beforeDetails); + assertThat(response.getErrors()).isNullOrEmpty(); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationResponseTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationResponseTest.java new file mode 100644 index 00000000000..79454ae65a9 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationResponseTest.java @@ -0,0 +1,340 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponseNotification; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseSoleParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformationResponse.CASEWORKER_REQUEST_FOR_INFORMATION_RESPONSE; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformationResponse.NO_REQUEST_FOR_INFORMATION_ERROR; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.APPLICANT; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_TEXT; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addOfflineResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.buildOfflineDraft; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationCaseDetails; + +@ExtendWith(MockitoExtension.class) +class CaseworkerRequestForInformationResponseTest { + + @Mock + private NotificationDispatcher notificationDispatcher; + + @Mock + private CitizenRequestForInformationResponseNotification citizenRequestForInformationResponseNotification; + + @Mock + private CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @InjectMocks + private CaseworkerRequestForInformationResponse caseworkerRequestForInformationResponse; + + @Test + void shouldAddConfigurationToConfigBuilder() { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + caseworkerRequestForInformationResponse.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CASEWORKER_REQUEST_FOR_INFORMATION_RESPONSE); + } + + @Test + void shouldReturnErrorWhenNoRequestForInformationOnCase() { + CaseData caseData = caseData(); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformationResponse.aboutToStart(caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_REQUEST_FOR_INFORMATION_ERROR); + } + + @Test + void shouldSetStateToRequestedInformationSubmittedWhenAllDocsProvided() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT, false, false); + buildOfflineDraft(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.APPLICANT, true, false, true, false); + + AboutToStartOrSubmitResponse response = + caseworkerRequestForInformationResponse.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationOfflineResponseDraft()) + .isEqualTo(new RequestForInformationOfflineResponseDraft()); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + + final RequestForInformationResponse requestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getLatestResponse(); + final Applicant applicant = + caseDetails.getData().getApplicant1(); + assertThat(requestForInformationResponse.getRequestForInformationResponseParties()) + .isEqualTo(RequestForInformationResponseParties.APPLICANT1); + assertThat(requestForInformationResponse.getRequestForInformationResponseName()).isEqualTo(applicant.getFullName()); + assertThat(requestForInformationResponse.getRequestForInformationResponseEmailAddress()).isEqualTo(applicant.getEmail()); + assertThat(requestForInformationResponse.getRequestForInformationResponseOffline()).isEqualTo(YES); + assertThat(requestForInformationResponse.getRequestForInformationResponseDocs()).isNull(); + assertThat(requestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(requestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(requestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isNull(); + assertThat(requestForInformationResponse.getRfiOfflineResponseAllDocumentsUploaded()).isEqualTo(YES); + assertThat(requestForInformationResponse.getRfiOfflineResponseDocs()).isNull(); + } + + @Test + void shouldSetStateToAwaitingRequestedInformationWhenAllDocsNotProvided() { + CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT, false, false); + buildOfflineDraft(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.APPLICANT, true, false, false, false); + + AboutToStartOrSubmitResponse response = + caseworkerRequestForInformationResponse.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(AwaitingRequestedInformation); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationOfflineResponseDraft()) + .isEqualTo(new RequestForInformationOfflineResponseDraft()); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + + final RequestForInformationResponse requestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getLatestResponse(); + final Applicant applicant = + caseDetails.getData().getApplicant1(); + assertThat(requestForInformationResponse.getRequestForInformationResponseParties()) + .isEqualTo(RequestForInformationResponseParties.APPLICANT1); + assertThat(requestForInformationResponse.getRequestForInformationResponseName()).isEqualTo(applicant.getFullName()); + assertThat(requestForInformationResponse.getRequestForInformationResponseEmailAddress()).isEqualTo(applicant.getEmail()); + assertThat(requestForInformationResponse.getRequestForInformationResponseOffline()).isEqualTo(YES); + assertThat(requestForInformationResponse.getRequestForInformationResponseDocs()).isNull(); + assertThat(requestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(requestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(requestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isNull(); + assertThat(requestForInformationResponse.getRfiOfflineResponseAllDocumentsUploaded()).isEqualTo(NO); + assertThat(requestForInformationResponse.getRfiOfflineResponseDocs()).isNull(); + } + + @Test + void shouldNotSendNotificationsOnSoleCaseIfRfiSentToOther() { + CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationSoleParties.OTHER, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseSoleParties.OTHER); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + verifyNoInteractions(notificationDispatcher); + } + + @Test + void shouldNotSendNotificationsOnJointCaseIfRfiSentToOther() { + CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.OTHER, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseJointParties.OTHER); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + verifyNoInteractions(notificationDispatcher); + } + + @Test + void shouldSendNotificationToRespondingPartyOnlyOnSoleCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationSoleParties.APPLICANT, false, false); + caseDetails.setId(TEST_CASE_ID); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldSendNotificationToRespondingPartyOnlyOnJointCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT1, false, false); + caseDetails.setId(TEST_CASE_ID); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldReturnErrorWhenSendNotificationToRespondingPartyFailsOnSoleCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationSoleParties.APPLICANT, false, false); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldReturnErrorWhenSendNotificationToRespondingPartyFailsOnJointCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT1, false, false); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldSendNotificationToRespondingPartyAndPartnerOnJointCaseWhenRFISentToBoth() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addOfflineResponseToLatestRequestForInformation(caseDetails.getData(), RequestForInformationOfflineResponseJointParties.APPLICANT1); + caseDetails.setId(TEST_CASE_ID); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verify(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorWhenSendNotificationToPartnerFails() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant1()); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + } + + @Test + void shouldReturnErrorWhenSendNotificationToBothPartiesFails() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant1()); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + caseworkerRequestForInformationResponse.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationTest.java new file mode 100644 index 00000000000..2b8380cab5f --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/event/CaseworkerRequestForInformationTest.java @@ -0,0 +1,1873 @@ +package uk.gov.hmcts.divorce.caseworker.event; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.ccd.sdk.type.ListValue; +import uk.gov.hmcts.divorce.caseworker.service.notification.RequestForInformationNotification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformation; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.APPLICANT_1; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.APPLICANT_2; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.CASEWORKER_REQUEST_FOR_INFORMATION; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.NOT_ONLINE_ERROR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.NO_VALID_EMAIL_ERROR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.NO_VALID_EMAIL_PROVIDED_ERROR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.REQUEST_FOR_INFORMATION_NOTIFICATION_FAILED_ERROR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.SOLICITOR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.THE_APPLICANT; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.USE_CORRECT_PARTY_ERROR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.USE_CREATE_GENERAL_EMAIL_FOR_RESPONDENT_ERROR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR; +import static uk.gov.hmcts.divorce.caseworker.event.CaseworkerRequestForInformation.USE_CREATE_GENERAL_LETTER_FOR_RESPONDENT_ERROR; +import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.JOINT_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.SOLE_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.Gender.MALE; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.APPLICANT; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.OTHER; +import static uk.gov.hmcts.divorce.divorcecase.model.State.InformationRequested; +import static uk.gov.hmcts.divorce.divorcecase.model.State.Submitted; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_OTHER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_OTHER_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_TEXT; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.applicantRepresentedBySolicitor; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getApplicant; + +@ExtendWith(MockitoExtension.class) +class CaseworkerRequestForInformationTest { + + @Mock + private RequestForInformationNotification requestForInformationNotification; + + @Mock + private NotificationDispatcher notificationDispatcher; + + @InjectMocks + private CaseworkerRequestForInformation caseworkerRequestForInformation; + + @Test + void shouldAddConfigurationToConfigBuilder() { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + caseworkerRequestForInformation.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CASEWORKER_REQUEST_FOR_INFORMATION); + } + + @Test + void shouldReturnErrorWhenApplicantOfflineOnSoleCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + THE_APPLICANT, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicantSolicitorOfflineOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + THE_APPLICANT + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1OfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_1, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1SolicitorOfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_1 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant2OfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant(MALE)); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_2, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant2SolicitorOfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_2 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1OnlineApplicant2OfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant(MALE)); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_2, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1OnlineApplicant2SolicitorOfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_2 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1OfflineApplicant2OnlineOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicant2(getApplicant(MALE)); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_1, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1SolicitorOfflineApplicant2OnlineOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicant2(getApplicant(MALE)); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_1 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenBothApplicantsOfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicant2(getApplicant(MALE)); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(3); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_1, + NOT_ONLINE_ERROR + APPLICANT_2, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenBothApplicantSolicitorsOfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setOffline(YES); + caseData.getApplicant2().setGender(MALE); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(3); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_1 + SOLICITOR, + NOT_ONLINE_ERROR + APPLICANT_2 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1OfflineApplicant2SolicitorOfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setOffline(YES); + caseData.getApplicant2().setGender(MALE); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(3); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_1, + NOT_ONLINE_ERROR + APPLICANT_2 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1SolicitorOfflineApplicant2OfflineOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicant2(getApplicant(MALE)); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(3); + assertThat(response.getErrors()).contains( + NOT_ONLINE_ERROR + APPLICANT_1 + SOLICITOR, + NOT_ONLINE_ERROR + APPLICANT_2, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateApplicantEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenNoApplicantEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + THE_APPLICANT, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateApplicantSolicitorEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenNoApplicantSolicitorEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().getSolicitor().setEmail(""); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + THE_APPLICANT + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateOtherEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(TEST_OTHER_EMAIL); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesUnrepresentedOfflineApplicantEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesUnrepresentedOnlineApplicantEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedOfflineApplicantEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedOnlineApplicantEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedApplicantOfflineSolicitorEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_SOLICITOR_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedApplicantOnlineSolicitorEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_SOLICITOR_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesUnrepresentedOfflineRespondentEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(getApplicant(MALE)); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_RESPONDENT_ERROR + "."); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedOnlineRespondentEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_EMAIL_FOR_RESPONDENT_ERROR + "."); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedRespondentOfflineSolicitorEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_SOLICITOR_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_RESPONDENT_ERROR + SOLICITOR + "."); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedRespondentOnlineSolicitorEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_SOLICITOR_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_EMAIL_FOR_RESPONDENT_ERROR + SOLICITOR + "."); + } + + @Test + void shouldReturnErrorWhenNoOtherEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_VALID_EMAIL_PROVIDED_ERROR); + } + + @Test + void shouldReturnErrorWhenEmptyOtherEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(""); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_VALID_EMAIL_PROVIDED_ERROR); + } + + @Test + void shouldReturnErrorWhenShortOtherEmailOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress("a@b.c"); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_VALID_EMAIL_PROVIDED_ERROR); + } + + @Test + void shouldValidateApplicant1EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenNoApplicant1EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_1, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateApplicant1SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenNoApplicant1SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().getSolicitor().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_1 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateApplicant2EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenNoApplicant2EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant()); + caseData.getApplicant2().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_2, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateApplicant2SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenNoApplicant2SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().getSolicitor().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_2 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateBothApplicantEmailsOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenNoApplicantEmailsOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant()); + caseData.getApplicant2().setGender(MALE); + caseData.getApplicant1().setEmail(""); + caseData.getApplicant2().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(3); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_1, + NO_VALID_EMAIL_ERROR + APPLICANT_2, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant1EmailAndNoApplicant2EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant()); + caseData.getApplicant2().setGender(MALE); + caseData.getApplicant2().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_2, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenApplicant2EmailAndNoApplicant1EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant()); + caseData.getApplicant2().setGender(MALE); + caseData.getApplicant1().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_1, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateApplicant1SolicitorAndApplicant2SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setGender(MALE); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenNoApplicantSolicitorEmailsOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setGender(MALE); + caseData.getApplicant1().getSolicitor().setEmail(""); + caseData.getApplicant2().getSolicitor().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(3); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_1 + SOLICITOR, + NO_VALID_EMAIL_ERROR + APPLICANT_2 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenNoApplicant1SolicitorEmailAndApplicant2SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setGender(MALE); + caseData.getApplicant1().getSolicitor().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_1 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenNoApplicant2SolicitorEmailAndApplicant1SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setGender(MALE); + caseData.getApplicant2().getSolicitor().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(2); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_2 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenNoApplicant1EmailAndNoApplicant2SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(getApplicant()); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setGender(MALE); + caseData.getApplicant1().setEmail(""); + caseData.getApplicant2().getSolicitor().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(3); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_1, + NO_VALID_EMAIL_ERROR + APPLICANT_2 + SOLICITOR, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldReturnErrorWhenNoApplicant2EmailAndNoApplicant1SolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(getApplicant()); + caseData.getApplicant2().setGender(MALE); + caseData.getApplicant1().getSolicitor().setEmail(""); + caseData.getApplicant2().setEmail(""); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(3); + assertThat(response.getErrors()).contains( + NO_VALID_EMAIL_ERROR + APPLICANT_1 + SOLICITOR, + NO_VALID_EMAIL_ERROR + APPLICANT_2, + USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR + ); + } + + @Test + void shouldValidateOtherEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(TEST_OTHER_EMAIL); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesUnrepresentedOnlineApplicant1EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesUnrepresentedOfflineApplicant1EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedOnlineApplicant1EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedOfflineApplicant1EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedApplicant1OnlineSolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_SOLICITOR_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedApplicant1OfflineSolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_SOLICITOR_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesUnrepresentedOnlineApplicant2EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(getApplicant(MALE)); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesUnrepresentedOfflineApplicant2EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(getApplicant(MALE)); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedOnlineApplicant2EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedOfflineApplicant2EmailOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_USER_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedApplicant2OnlineSolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_SOLICITOR_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CORRECT_PARTY_ERROR); + } + + @Test + void shouldReturnErrorWhenOtherEmailMatchesRepresentedApplicant2OfflineSolicitorEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.getApplicant1().setEmail(""); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getApplicant2().setOffline(YES); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress( + TEST_SOLICITOR_EMAIL.toUpperCase() + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(USE_CREATE_GENERAL_LETTER_FOR_OFFLINE_PARTIES_ERROR); + } + + @Test + void shouldReturnErrorWhenNoOtherEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_VALID_EMAIL_PROVIDED_ERROR); + } + + @Test + void shouldReturnErrorWhenEmptyOtherEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(""); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_VALID_EMAIL_PROVIDED_ERROR); + } + + @Test + void shouldReturnErrorWhenShortOtherEmailOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties( + RequestForInformationJointParties.OTHER + ); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress("a@b.c"); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NO_VALID_EMAIL_PROVIDED_ERROR); + } + + @Test + void shouldFailAboutToSubmitEventWhenNotificationFails() { + CaseData caseData = caseData(); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + doThrow(new NotificationTemplateException("")) + .when(notificationDispatcher).sendRequestForInformationNotification(requestForInformationNotification, caseData, TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()) + .isEqualTo(Collections.singletonList(REQUEST_FOR_INFORMATION_NOTIFICATION_FAILED_ERROR + TEST_CASE_ID)); + } + + @Test + void shouldClearDefaultRequestForInformationObjectDuringAboutToSubmitEvent() { + CaseData caseData = caseData(); + caseData.getRequestForInformationList().setRequestForInformation(new RequestForInformation()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationName(TEST_OTHER_NAME); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(TEST_OTHER_EMAIL); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDateTime(LocalDateTime.now()); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestForInformation(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartyToApplicant1WhenNotRepresentedOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isEqualTo(APPLICANT); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isEqualTo(caseData.getApplicant1().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()).isEqualTo(caseData.getApplicant1().getFullName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldAddAdditionalRequestToTopOfRequestListWhenListIsNotEmpty() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + final ListValue request = new ListValue<>(); + request.setValue(new RequestForInformation()); + final List> requests = new ArrayList<>(); + requests.add(request); + caseData.getRequestForInformationList().setRequestsForInformation(requests); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(2); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isEqualTo(APPLICANT); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isEqualTo(caseData.getApplicant1().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()).isEqualTo(caseData.getApplicant1().getFullName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartyToApplicant1SolicitorWhenRepresentedOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isEqualTo(APPLICANT); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()) + .isEqualTo(caseData.getApplicant1().getSolicitor().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()) + .isEqualTo(caseData.getApplicant1().getSolicitor().getName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartyToApplicant1WhenNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(APPLICANT1); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isEqualTo(caseData.getApplicant1().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()).isEqualTo(caseData.getApplicant1().getFullName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartyToApplicant1SolicitorWhenRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(APPLICANT1); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()) + .isEqualTo(caseData.getApplicant1().getSolicitor().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()) + .isEqualTo(caseData.getApplicant1().getSolicitor().getName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartyToApplicant2WhenNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(APPLICANT2); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isEqualTo(caseData.getApplicant2().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()).isEqualTo(caseData.getApplicant2().getFullName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartyToApplicant2SolicitorWhenRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(APPLICANT2); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()) + .isEqualTo(caseData.getApplicant2().getSolicitor().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()) + .isEqualTo(caseData.getApplicant2().getSolicitor().getName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartiesToApplicantsWhenNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(BOTH); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isEqualTo(caseData.getApplicant1().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()).isEqualTo(caseData.getApplicant1().getFullName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()) + .isEqualTo(caseData.getApplicant2().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isEqualTo(caseData.getApplicant2().getFullName()); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartiesToApplicantSolicitorsWhenRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(BOTH); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()) + .isEqualTo(caseData.getApplicant1().getSolicitor().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()) + .isEqualTo(caseData.getApplicant1().getSolicitor().getName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()) + .isEqualTo(caseData.getApplicant2().getSolicitor().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()) + .isEqualTo(caseData.getApplicant2().getSolicitor().getName()); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartiesToApplicant1SolicitorAndApplicant2WhenApplicant1RepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.setApplicant2(getApplicant()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(BOTH); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()) + .isEqualTo(caseData.getApplicant1().getSolicitor().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()) + .isEqualTo(caseData.getApplicant1().getSolicitor().getName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()) + .isEqualTo(caseData.getApplicant2().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isEqualTo(caseData.getApplicant2().getFullName()); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartiesToApplicant1AndApplicant2SolicitorWhenApplicant2RepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(BOTH); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isEqualTo(caseData.getApplicant1().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationName()).isEqualTo(caseData.getApplicant1().getFullName()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()) + .isEqualTo(caseData.getApplicant2().getSolicitor().getEmail()); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()) + .isEqualTo(caseData.getApplicant2().getSolicitor().getName()); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartyToOtherOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationName(TEST_OTHER_NAME); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(TEST_OTHER_EMAIL); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isEqualTo(OTHER); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isEqualTo(TEST_OTHER_EMAIL); + assertThat(responseRequestForInformation.getRequestForInformationName()).isEqualTo(TEST_OTHER_NAME); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + + @Test + void shouldSetPartyToOtherOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation() + .setRequestForInformationJointParties(RequestForInformationJointParties.OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationName(TEST_OTHER_NAME); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(TEST_OTHER_EMAIL); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setState(Submitted); + caseDetails.setId(TEST_CASE_ID); + + final AboutToStartOrSubmitResponse response = + caseworkerRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getRequestsForInformation()).hasSize(1); + + final RequestForInformation responseRequestForInformation = + response.getData().getRequestForInformationList().getRequestsForInformation().get(0).getValue(); + + assertThat(responseRequestForInformation.getRequestForInformationSoleParties()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationJointParties()).isEqualTo(RequestForInformationJointParties.OTHER); + assertThat(responseRequestForInformation.getRequestForInformationEmailAddress()).isEqualTo(TEST_OTHER_EMAIL); + assertThat(responseRequestForInformation.getRequestForInformationName()).isEqualTo(TEST_OTHER_NAME); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryEmailAddress()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationSecondaryName()).isNull(); + assertThat(responseRequestForInformation.getRequestForInformationDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformation.getRequestForInformationDateTime()).isNotNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(InformationRequested); + } + +} diff --git a/src/test/java/uk/gov/hmcts/divorce/caseworker/service/notification/RequestForInformationNotificationTest.java b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/notification/RequestForInformationNotificationTest.java new file mode 100644 index 00000000000..4312a6482cc --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/caseworker/service/notification/RequestForInformationNotificationTest.java @@ -0,0 +1,371 @@ +package uk.gov.hmcts.divorce.caseworker.service.notification; + +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.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.time.LocalDate; +import java.util.Map; + +import static java.lang.String.join; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.JOINT_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.ApplicationType.SOLE_APPLICATION; +import static uk.gov.hmcts.divorce.divorcecase.model.Gender.MALE; +import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.APPLICANT; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.OTHER; +import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.ISSUE_DATE_POPULATED; +import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.NOT_YET_ISSUED; +import static uk.gov.hmcts.divorce.document.content.DocmosisTemplateConstants.RECIPIENT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.APPLICANT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.DATE_OF_ISSUE; +import static uk.gov.hmcts.divorce.notification.CommonContent.HUSBAND_JOINT; +import static uk.gov.hmcts.divorce.notification.CommonContent.IS_JOINT; +import static uk.gov.hmcts.divorce.notification.CommonContent.IS_SOLE; +import static uk.gov.hmcts.divorce.notification.CommonContent.NO; +import static uk.gov.hmcts.divorce.notification.CommonContent.REQUEST_FOR_INFORMATION_DETAILS; +import static uk.gov.hmcts.divorce.notification.CommonContent.RESPONDENT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SENT_TO_BOTH_APPLICANTS; +import static uk.gov.hmcts.divorce.notification.CommonContent.SIGN_IN_URL; +import static uk.gov.hmcts.divorce.notification.CommonContent.SMART_SURVEY; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_REFERENCE; +import static uk.gov.hmcts.divorce.notification.CommonContent.YES; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_JOINT; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_OTHER; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SOLE; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SOLICITOR; +import static uk.gov.hmcts.divorce.notification.FormatUtil.DATE_TIME_FORMATTER; +import static uk.gov.hmcts.divorce.testutil.TestConstants.PROFESSIONAL_USERS_SIGN_IN_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.SMART_SURVEY_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_OTHER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_OTHER_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_REFERENCE; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_TEXT; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.applicantRepresentedBySolicitor; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getApplicant; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getMainTemplateVars; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationTemplateVars; + +@ExtendWith(MockitoExtension.class) +class RequestForInformationNotificationTest { + + @Mock + private CommonContent commonContent; + + @Mock + private NotificationService notificationService; + + @InjectMocks + private RequestForInformationNotification requestForInformationNotification; + + @Test + void shouldSendRequestForInformationEmailToApplicantWhenNotRepresentedOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + requestForInformationNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_SOLE, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationEmailToApplicantSolicitorWhenRepresentedOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getApplicant1().getSolicitor().setReference(TEST_REFERENCE); + caseData.getApplication().setIssueDate(LocalDate.now()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(APPLICANT); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getSolicitorTemplateContent(caseData); + templateContent.put(DATE_OF_ISSUE, caseData.getApplication().getIssueDate().format(DATE_TIME_FORMATTER)); + templateContent.put(ISSUE_DATE_POPULATED, YES); + templateContent.put(NOT_YET_ISSUED, NO); + templateContent.put(SOLICITOR_REFERENCE, TEST_REFERENCE); + + requestForInformationNotification.sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationEmailToApplicant1WhenNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + requestForInformationNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_JOINT, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationEmailWithoutSuppressedJointDataTemplateTextWhenNotRepresentedOnJointCaseAndSentToBothApplicants() { + CaseData caseData = caseData(); + caseData.setApplicant2(getApplicant(MALE)); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(BOTH); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars( + caseData.getApplicationType(), BOTH, caseData.isDivorce(), caseData.getApplicant2() + )); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + templateContent.put(IS_JOINT, YES); + templateContent.put(SENT_TO_BOTH_APPLICANTS, YES); + templateContent.put(HUSBAND_JOINT, YES); + + requestForInformationNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_JOINT, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationEmailToApplicant1SolicitorWhenRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant1(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT1); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getSolicitorTemplateContent(caseData); + + requestForInformationNotification.sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationEmailToApplicant2WhenNotRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant2(getApplicant()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + requestForInformationNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_JOINT, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationEmailToApplicant2SolicitorWhenRepresentedOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.setApplicant2(applicantRepresentedBySolicitor()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationJointParties(APPLICANT2); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getSolicitorTemplateContent(caseData); + + requestForInformationNotification.sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationEmailToOtherRecipientOnSoleCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(SOLE_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationSoleParties(OTHER); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationName(TEST_OTHER_NAME); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(TEST_OTHER_EMAIL); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getOtherRecipientTemplateContent(caseData); + + requestForInformationNotification.sendToOtherRecipient(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_OTHER_EMAIL, + REQUEST_FOR_INFORMATION_OTHER, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationEmailToOtherRecipientOnJointCase() { + CaseData caseData = caseData(); + caseData.setApplicationType(JOINT_APPLICATION); + caseData.getRequestForInformationList().getRequestForInformation() + .setRequestForInformationJointParties(RequestForInformationJointParties.OTHER); + caseData.getApplication().setIssueDate(LocalDate.now()); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationName(TEST_OTHER_NAME); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationEmailAddress(TEST_OTHER_EMAIL); + caseData.getRequestForInformationList().getRequestForInformation().setRequestForInformationDetails(TEST_TEXT); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getOtherRecipientTemplateContent(caseData); + templateContent.put(ISSUE_DATE_POPULATED, YES); + templateContent.put(NOT_YET_ISSUED, NO); + + requestForInformationNotification.sendToOtherRecipient(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_OTHER_EMAIL, + REQUEST_FOR_INFORMATION_OTHER, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + private Map getApplicantTemplateContent() { + Map templateVars = getRequestForInformationTemplateVars(); + templateVars.put(REQUEST_FOR_INFORMATION_DETAILS, TEST_TEXT); + templateVars.put(SMART_SURVEY, SMART_SURVEY_TEST_URL); + + return templateVars; + } + + private Map getSolicitorTemplateContent(final CaseData caseData) { + Map templateVars = getMainTemplateVars(); + + templateVars.put(APPLICANT_NAME, + join(" ", caseData.getApplicant1().getFirstName(), caseData.getApplicant1().getLastName())); + templateVars.put(RESPONDENT_NAME, + join(" ", caseData.getApplicant2().getFirstName(), caseData.getApplicant2().getLastName())); + templateVars.put(IS_SOLE, caseData.getApplicationType().isSole() ? YES : NO); + templateVars.put(IS_JOINT, !caseData.getApplicationType().isSole() ? YES : NO); + templateVars.put(SOLICITOR_NAME, TEST_SOLICITOR_NAME); + templateVars.put(DATE_OF_ISSUE, ""); + templateVars.put(ISSUE_DATE_POPULATED, NO); + templateVars.put(NOT_YET_ISSUED, YES); + templateVars.put(SOLICITOR_REFERENCE, "not provided"); + templateVars.put(REQUEST_FOR_INFORMATION_DETAILS, TEST_TEXT); + templateVars.put(SIGN_IN_URL, PROFESSIONAL_USERS_SIGN_IN_URL); + templateVars.put(SMART_SURVEY, SMART_SURVEY_TEST_URL); + + return templateVars; + } + + private Map getOtherRecipientTemplateContent(final CaseData caseData) { + Map templateVars = getMainTemplateVars(); + templateVars.put(APPLICANT_NAME, caseData.getApplicant1().getFullName()); + templateVars.put(RESPONDENT_NAME, caseData.getApplicant2().getFullName()); + templateVars.put(RECIPIENT_NAME, TEST_OTHER_NAME); + templateVars.put(ISSUE_DATE_POPULATED, NO); + templateVars.put(NOT_YET_ISSUED, YES); + templateVars.put(REQUEST_FOR_INFORMATION_DETAILS, TEST_TEXT); + templateVars.put(SMART_SURVEY, SMART_SURVEY_TEST_URL); + + return templateVars; + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenRespondToRequestForInformationTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenRespondToRequestForInformationTest.java new file mode 100644 index 00000000000..59c7a34aa45 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/event/CitizenRespondToRequestForInformationTest.java @@ -0,0 +1,684 @@ +package uk.gov.hmcts.divorce.citizen.event; + +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponseNotification; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; +import uk.gov.hmcts.divorce.solicitor.service.CcdAccessService; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.NO; +import static uk.gov.hmcts.ccd.sdk.type.YesOrNo.YES; +import static uk.gov.hmcts.divorce.citizen.event.CitizenRespondToRequestForInformation.CITIZEN_NOT_VALID_FOR_PARTY_END_ERROR; +import static uk.gov.hmcts.divorce.citizen.event.CitizenRespondToRequestForInformation.CITIZEN_NOT_VALID_FOR_PARTY_MID_ERROR; +import static uk.gov.hmcts.divorce.citizen.event.CitizenRespondToRequestForInformation.CITIZEN_NOT_VALID_FOR_PARTY_START_ERROR; +import static uk.gov.hmcts.divorce.citizen.event.CitizenRespondToRequestForInformation.CITIZEN_RESPOND_TO_REQUEST_FOR_INFORMATION; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.APPLICANT; +import static uk.gov.hmcts.divorce.divorcecase.model.State.AwaitingRequestedInformation; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_TEXT; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addCannotUploadResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addDocumentToRequestForInformationResponseDraft; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationCaseDetails; + +@ExtendWith(MockitoExtension.class) +class CitizenRespondToRequestForInformationTest { + + @Mock + private CcdAccessService ccdAccessService; + + @Mock + private HttpServletRequest httpServletRequest; + + @Mock + private NotificationDispatcher notificationDispatcher; + + @Mock + private CitizenRequestForInformationResponseNotification citizenRequestForInformationResponseNotification; + + @Mock + private CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @InjectMocks + private CitizenRespondToRequestForInformation citizenRespondToRequestForInformation; + + @Test + void shouldAddConfigurationToConfigBuilder() { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + citizenRespondToRequestForInformation.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(CITIZEN_RESPOND_TO_REQUEST_FOR_INFORMATION); + } + + @Test + void shouldReturnErrorIfRfiSolePartyIsOther() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationSoleParties.OTHER, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(true); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).containsExactly( + CITIZEN_NOT_VALID_FOR_PARTY_START_ERROR + + CITIZEN_NOT_VALID_FOR_PARTY_MID_ERROR + + RequestForInformationSoleParties.OTHER + + CITIZEN_NOT_VALID_FOR_PARTY_END_ERROR + + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorIfRfiJointPartyIsOther() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.OTHER, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(true); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).containsExactly( + CITIZEN_NOT_VALID_FOR_PARTY_START_ERROR + + "1 " + + CITIZEN_NOT_VALID_FOR_PARTY_MID_ERROR + + RequestForInformationJointParties.OTHER + + CITIZEN_NOT_VALID_FOR_PARTY_END_ERROR + + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorIfRespondentAndSoleCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(false); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).containsExactly( + CITIZEN_NOT_VALID_FOR_PARTY_START_ERROR + + "2 " + + CITIZEN_NOT_VALID_FOR_PARTY_MID_ERROR + + APPLICANT + + CITIZEN_NOT_VALID_FOR_PARTY_END_ERROR + + TEST_CASE_ID + ); + } + + @Test + void shouldPopulateDataWithResponseFromApplicantOnSoleCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + draft.setRfiDraftResponseCannotUploadDocs(NO); + addDocumentToRequestForInformationResponseDraft(draft); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + Applicant applicant = caseData.getApplicant1(); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(true); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationResponseApplicant1()) + .isEqualTo(new RequestForInformationResponseDraft()); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(applicant.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(applicant.getFullName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldPopulateDataWithCannotUploadResponseFromApplicantOnSoleCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + draft.setRfiDraftResponseCannotUploadDocs(YES); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + Applicant applicant = caseData.getApplicant1(); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(true); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationResponseApplicant1()) + .isEqualTo(new RequestForInformationResponseDraft()); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(applicant.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(applicant.getFullName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).isNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isEqualTo(YES); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(AwaitingRequestedInformation); + } + + @Test + void shouldReturnErrorIfApplicant1AndRfiJointPartyIsApplicant2() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT2, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(true); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).containsExactly( + CITIZEN_NOT_VALID_FOR_PARTY_START_ERROR + + "1 " + + CITIZEN_NOT_VALID_FOR_PARTY_MID_ERROR + + RequestForInformationJointParties.APPLICANT2 + + CITIZEN_NOT_VALID_FOR_PARTY_END_ERROR + + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorIfApplicant2AndRfiJointPartyIsApplicant1() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT1, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant2(draft); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(false); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + assertThat(response.getErrors()).containsExactly( + CITIZEN_NOT_VALID_FOR_PARTY_START_ERROR + + "2 " + + CITIZEN_NOT_VALID_FOR_PARTY_MID_ERROR + + RequestForInformationJointParties.APPLICANT1 + + CITIZEN_NOT_VALID_FOR_PARTY_END_ERROR + + TEST_CASE_ID + ); + } + + @Test + void shouldPopulateDataWithResponseFromApplicant1OnJointCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT1, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + draft.setRfiDraftResponseCannotUploadDocs(NO); + addDocumentToRequestForInformationResponseDraft(draft); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + Applicant applicant = caseData.getApplicant1(); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(true); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationResponseApplicant1()) + .isEqualTo(new RequestForInformationResponseDraft()); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(applicant.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(applicant.getFullName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldPopulateDataWithResponseFromApplicant2OnJointCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT2, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + draft.setRfiDraftResponseCannotUploadDocs(NO); + addDocumentToRequestForInformationResponseDraft(draft); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant2(draft); + Applicant applicant = caseData.getApplicant2(); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(false); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationResponseApplicant2()) + .isEqualTo(new RequestForInformationResponseDraft()); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT2); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(applicant.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(applicant.getFullName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldPopulateDataWithResponseFromApplicant1OnJointCaseWhenRfiForBoth() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + draft.setRfiDraftResponseCannotUploadDocs(NO); + addDocumentToRequestForInformationResponseDraft(draft); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + Applicant applicant = caseData.getApplicant1(); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(true); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationResponseApplicant1()) + .isEqualTo(new RequestForInformationResponseDraft()); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(applicant.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(applicant.getFullName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldPopulateDataWithResponseFromApplicant2OnJointCaseWhenRfiForBoth() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + draft.setRfiDraftResponseCannotUploadDocs(NO); + addDocumentToRequestForInformationResponseDraft(draft); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant2(draft); + Applicant applicant = caseData.getApplicant2(); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(false); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationResponseApplicant2()) + .isEqualTo(new RequestForInformationResponseDraft()); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT2); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(applicant.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(applicant.getFullName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isNull(); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldPopulateDataWithCannotUploadResponseFromApplicant1OnJointCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT1, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + draft.setRfiDraftResponseCannotUploadDocs(YES); + addDocumentToRequestForInformationResponseDraft(draft); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant1(draft); + Applicant applicant = caseData.getApplicant1(); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(true); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationResponseApplicant1()) + .isEqualTo(new RequestForInformationResponseDraft()); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(applicant.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(applicant.getFullName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isEqualTo(YES); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(AwaitingRequestedInformation); + } + + @Test + void shouldPopulateDataWithCannotUploadResponseFromApplicant2OnJointCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT2, false, false); + final CaseData caseData = caseDetails.getData(); + RequestForInformationResponseDraft draft = new RequestForInformationResponseDraft(); + draft.setRfiDraftResponseDetails(TEST_TEXT); + draft.setRfiDraftResponseCannotUploadDocs(YES); + addDocumentToRequestForInformationResponseDraft(draft); + caseData.getRequestForInformationList().setRequestForInformationResponseApplicant2(draft); + Applicant applicant = caseData.getApplicant2(); + + when(ccdAccessService.isApplicant1(any(), any())).thenReturn(false); + + final AboutToStartOrSubmitResponse response = + citizenRespondToRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + assertThat(response.getData().getRequestForInformationList().getRequestForInformationResponseApplicant2()) + .isEqualTo(new RequestForInformationResponseDraft()); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT2); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(applicant.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(applicant.getFullName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseCannotUploadDocs()).isEqualTo(YES); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(AwaitingRequestedInformation); + } + + @Test + void shouldSendNotificationToRespondingPartyOnlyOnSoleCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationSoleParties.APPLICANT, false, false); + caseDetails.setId(TEST_CASE_ID); + + citizenRespondToRequestForInformation.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldSendNotificationToRespondingPartyOnlyOnJointCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT1, false, false); + caseDetails.setId(TEST_CASE_ID); + + citizenRespondToRequestForInformation.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldReturnErrorWhenSendNotificationToRespondingPartyFailsOnSoleCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationSoleParties.APPLICANT, false, false); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + citizenRespondToRequestForInformation.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldReturnErrorWhenSendNotificationToRespondingPartyFailsOnJointCase() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.APPLICANT1, false, false); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + citizenRespondToRequestForInformation.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + verifyNoMoreInteractions(notificationDispatcher); + } + + @Test + void shouldSendNotificationToRespondingPartyAndPartnerOnJointCaseWhenRFISentToBothAndAllDocsUploaded() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant1()); + caseDetails.setId(TEST_CASE_ID); + + citizenRespondToRequestForInformation.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verify(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + } + + @Test + void shouldSendNotificationToPartnerOnJointCaseWhenRFISentToBothAndNotAllDocsUploaded() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addCannotUploadResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant1()); + caseDetails.setId(TEST_CASE_ID); + + citizenRespondToRequestForInformation.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + verify(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorWhenSendNotificationToPartnerFails() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant1()); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + citizenRespondToRequestForInformation.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + } + + @Test + void shouldReturnErrorWhenSendNotificationToBothPartiesFails() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant1()); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + citizenRespondToRequestForInformation.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponseNotification( + citizenRequestForInformationResponseNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponseNotificationTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponseNotificationTest.java new file mode 100644 index 00000000000..a399c2d3da0 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponseNotificationTest.java @@ -0,0 +1,241 @@ +package uk.gov.hmcts.divorce.citizen.notification; + +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.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseJointParties; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.util.Map; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT2; +import static uk.gov.hmcts.divorce.notification.CommonContent.SMART_SURVEY; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_RESPONSE; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS; +import static uk.gov.hmcts.divorce.testutil.TestConstants.SMART_SURVEY_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addCannotUploadResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addOfflineResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationCaseDetails; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationTemplateVars; + +@ExtendWith(MockitoExtension.class) +class CitizenRequestForInformationResponseNotificationTest { + + @Mock + private CommonContent commonContent; + + @Mock + private NotificationService notificationService; + + @InjectMocks + private CitizenRequestForInformationResponseNotification citizenRequestForInformationResponseNotification; + + @Test + void shouldSendRequestForInformationResponseEmailToApplicant1() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, false).getData(); + addResponseToLatestRequestForInformation(caseData, caseData.getApplicant1()); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponseNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant1() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, false).getData(); + addCannotUploadResponseToLatestRequestForInformation(caseData, caseData.getApplicant1()); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponseNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseEmailToApplicant1AfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, false).getData(); + addOfflineResponseToLatestRequestForInformation(caseData, RequestForInformationOfflineResponseJointParties.APPLICANT1); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponseNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant1AfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, false).getData(); + addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation( + caseData, + RequestForInformationOfflineResponseJointParties.APPLICANT1 + ); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponseNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseEmailToApplicant2() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, false, false).getData(); + addResponseToLatestRequestForInformation(caseData, caseData.getApplicant2()); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponseNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant2() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, false, false).getData(); + addCannotUploadResponseToLatestRequestForInformation(caseData, caseData.getApplicant2()); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponseNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseEmailToApplicant2AfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, false, false).getData(); + addOfflineResponseToLatestRequestForInformation(caseData, RequestForInformationOfflineResponseJointParties.APPLICANT2); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponseNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant2AfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, false, false).getData(); + addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation( + caseData, + RequestForInformationOfflineResponseJointParties.APPLICANT2 + ); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponseNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + private Map getApplicantTemplateContent() { + Map templateVars = getRequestForInformationTemplateVars(); + templateVars.put(SMART_SURVEY, SMART_SURVEY_TEST_URL); + + return templateVars; + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponsePartnerNotificationTest.java b/src/test/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponsePartnerNotificationTest.java new file mode 100644 index 00000000000..32edc8368c0 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/citizen/notification/CitizenRequestForInformationResponsePartnerNotificationTest.java @@ -0,0 +1,476 @@ +package uk.gov.hmcts.divorce.citizen.notification; + +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.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationOfflineResponseJointParties; +import uk.gov.hmcts.divorce.notification.CommonContent; +import uk.gov.hmcts.divorce.notification.NotificationService; + +import java.util.Map; + +import static java.lang.String.join; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.divorce.divorcecase.model.LanguagePreference.ENGLISH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT2; +import static uk.gov.hmcts.divorce.notification.CommonContent.APPLICANT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.DATE_OF_ISSUE; +import static uk.gov.hmcts.divorce.notification.CommonContent.RESPONDENT_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SIGN_IN_URL; +import static uk.gov.hmcts.divorce.notification.CommonContent.SMART_SURVEY; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.notification.CommonContent.SOLICITOR_REFERENCE; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_RESPONSE_PARTNER; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY; +import static uk.gov.hmcts.divorce.notification.EmailTemplateName.REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD; +import static uk.gov.hmcts.divorce.testutil.TestConstants.PROFESSIONAL_USERS_SIGN_IN_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.SMART_SURVEY_TEST_URL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_FIRST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_LAST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SOLICITOR_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_USER_EMAIL; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addCannotUploadResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addOfflineResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getMainTemplateVars; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationCaseDetails; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationTemplateVars; + +@ExtendWith(MockitoExtension.class) +class CitizenRequestForInformationResponsePartnerNotificationTest { + + @Mock + private CommonContent commonContent; + + @Mock + private NotificationService notificationService; + + @InjectMocks + private CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @Test + void shouldSendRequestForInformationResponsePartnerEmailToApplicant1() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, false, false).getData(); + addResponseToLatestRequestForInformation(caseData, caseData.getApplicant2()); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponsePartnerEmailToApplicant1Solicitor() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, true, false).getData(); + addResponseToLatestRequestForInformation(caseData, caseData.getApplicant2()); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = solicitorTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant1() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, false, false).getData(); + addCannotUploadResponseToLatestRequestForInformation(caseData, caseData.getApplicant2()); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant1Solicitor() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, true, false).getData(); + addCannotUploadResponseToLatestRequestForInformation(caseData, caseData.getApplicant2()); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = solicitorTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponsePartnerEmailToApplicant1AfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, false, false).getData(); + addOfflineResponseToLatestRequestForInformation(caseData, RequestForInformationOfflineResponseJointParties.APPLICANT2); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponsePartnerEmailToApplicant1SolicitorAfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, true, false).getData(); + addOfflineResponseToLatestRequestForInformation(caseData, RequestForInformationOfflineResponseJointParties.APPLICANT2); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = solicitorTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant1AfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, false, false).getData(); + addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation( + caseData, + RequestForInformationOfflineResponseJointParties.APPLICANT2 + ); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant1(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant1SolicitorAfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT2, true, false).getData(); + addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation( + caseData, + RequestForInformationOfflineResponseJointParties.APPLICANT2 + ); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant1(), caseData.getApplicant2())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = solicitorTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant1Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseEmailToApplicant2() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, false).getData(); + addResponseToLatestRequestForInformation(caseData, caseData.getApplicant1()); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponsePartnerEmailToApplicant2Solicitor() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, true).getData(); + addResponseToLatestRequestForInformation(caseData, caseData.getApplicant1()); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = solicitorTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant2() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, false).getData(); + addCannotUploadResponseToLatestRequestForInformation(caseData, caseData.getApplicant1()); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant2Solicitor() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, true).getData(); + addCannotUploadResponseToLatestRequestForInformation(caseData, caseData.getApplicant1()); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = solicitorTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponsePartnerEmailToApplicant2AfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, false).getData(); + addOfflineResponseToLatestRequestForInformation(caseData, RequestForInformationOfflineResponseJointParties.APPLICANT1); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_PARTNER, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponsePartnerEmailToApplicant2SolicitorAfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, true).getData(); + addOfflineResponseToLatestRequestForInformation(caseData, RequestForInformationOfflineResponseJointParties.APPLICANT1); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = solicitorTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant2AfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, false).getData(); + addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation( + caseData, + RequestForInformationOfflineResponseJointParties.APPLICANT1 + ); + + when(commonContent.requestForInformationTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getRequestForInformationTemplateVars()); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = getApplicantTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant2(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_USER_EMAIL, + REQUEST_FOR_INFORMATION_RESPONSE_CANNOT_UPLOAD_DOCS, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + @Test + void shouldSendRequestForInformationResponseCannotUploadDocsEmailToApplicant2SolicitorAfterOfflineResponse() { + CaseData caseData = getRequestForInformationCaseDetails(APPLICANT1, false, true).getData(); + addNotAllDocsUploadedOfflineResponseToLatestRequestForInformation( + caseData, + RequestForInformationOfflineResponseJointParties.APPLICANT1 + ); + + when(commonContent.mainTemplateVars(caseData, TEST_CASE_ID, caseData.getApplicant2(), caseData.getApplicant1())) + .thenReturn(getMainTemplateVars()); + + when(commonContent.getProfessionalUsersSignInUrl(TEST_CASE_ID)).thenReturn(PROFESSIONAL_USERS_SIGN_IN_URL); + + when(commonContent.getSmartSurvey()).thenReturn(SMART_SURVEY_TEST_URL); + + Map templateContent = solicitorTemplateContent(); + + citizenRequestForInformationResponsePartnerNotification.sendToApplicant2Solicitor(caseData, TEST_CASE_ID); + + verify(notificationService).sendEmail( + TEST_SOLICITOR_EMAIL, + REQUEST_FOR_INFORMATION_SOLICITOR_OTHER_PARTY_COULD_NOT_UPLOAD, + templateContent, + ENGLISH, + TEST_CASE_ID + ); + } + + private Map getApplicantTemplateContent() { + Map templateVars = getRequestForInformationTemplateVars(); + templateVars.put(SMART_SURVEY, SMART_SURVEY_TEST_URL); + + return templateVars; + } + + private Map solicitorTemplateContent() { + Map templateVars = getMainTemplateVars(); + + templateVars.put(APPLICANT_NAME, join(" ", TEST_FIRST_NAME, TEST_LAST_NAME)); + templateVars.put(RESPONDENT_NAME, join(" ", TEST_FIRST_NAME, TEST_LAST_NAME)); + templateVars.put(DATE_OF_ISSUE, ""); + templateVars.put(SOLICITOR_REFERENCE, "not provided"); + templateVars.put(SOLICITOR_NAME, TEST_SOLICITOR_NAME); + templateVars.put(SIGN_IN_URL, PROFESSIONAL_USERS_SIGN_IN_URL); + templateVars.put(SMART_SURVEY, SMART_SURVEY_TEST_URL); + + return templateVars; + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/common/event/page/SolFinalOrderPaymentTest.java b/src/test/java/uk/gov/hmcts/divorce/common/event/page/SolFinalOrderPaymentTest.java new file mode 100644 index 00000000000..27e51992452 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/common/event/page/SolFinalOrderPaymentTest.java @@ -0,0 +1,59 @@ +package uk.gov.hmcts.divorce.common.event.page; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.SolicitorPaymentMethod; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; +import uk.gov.hmcts.divorce.solicitor.client.pba.PbaService; +import uk.gov.hmcts.divorce.solicitor.event.page.SolFinalOrderPayment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.divorce.testutil.TestConstants.APPLICANT_2_FIRST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.caseData; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.orderSummaryWithFee; + +@ExtendWith(MockitoExtension.class) +class SolFinalOrderPaymentTest { + @Mock + private PbaService pbaService; + + @Mock + private PaymentSetupService paymentSetupService; + + @InjectMocks + + private SolFinalOrderPayment page; + + @Test + void shouldCreateServiceRequestForPbaPayment() { + final CaseData caseData = caseData(); + var orderSummary = orderSummaryWithFee(); + caseData.getApplicant2().setFirstName(APPLICANT_2_FIRST_NAME); + caseData.getFinalOrder().setApplicant2SolFinalOrderFeeOrderSummary(orderSummary); + caseData.getFinalOrder().setApplicant2SolPaymentHowToPay(SolicitorPaymentMethod.FEE_PAY_BY_ACCOUNT); + + final CaseDetails beforeDetails = new CaseDetails<>(); + final CaseDetails details = new CaseDetails<>(); + details.setData(caseData); + details.setId(TEST_CASE_ID); + + when(paymentSetupService.createFinalOrderFeeServiceRequest( + caseData, TEST_CASE_ID, null, orderSummary + )).thenReturn(TEST_SERVICE_REFERENCE); + + AboutToStartOrSubmitResponse response = page.midEvent(details, beforeDetails); + + assertThat(response.getData().getFinalOrder().getApplicant2FinalOrderFeeServiceRequestReference()) + .isEqualTo(TEST_SERVICE_REFERENCE); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/payment/PaymentSetupServiceTest.java b/src/test/java/uk/gov/hmcts/divorce/payment/PaymentSetupServiceTest.java new file mode 100644 index 00000000000..b6c9bacf97d --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/payment/PaymentSetupServiceTest.java @@ -0,0 +1,173 @@ +package uk.gov.hmcts.divorce.payment; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.type.OrderSummary; +import uk.gov.hmcts.divorce.divorcecase.model.Applicant; +import uk.gov.hmcts.divorce.divorcecase.model.Application; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.FinalOrder; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_GENERAL; +import static uk.gov.hmcts.divorce.payment.PaymentService.EVENT_ISSUE; +import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_DIVORCE; +import static uk.gov.hmcts.divorce.payment.PaymentService.KEYWORD_NOTICE; +import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_DIVORCE; +import static uk.gov.hmcts.divorce.payment.PaymentService.SERVICE_OTHER; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_FIRST_NAME; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; + +@ExtendWith(MockitoExtension.class) +public class PaymentSetupServiceTest { + @Mock + private PaymentService paymentService; + + @InjectMocks + private PaymentSetupService paymentSetupService; + + @Test + public void shouldNotCreateApplicationFeeOrderSummaryIfItAlreadyExists() { + final CaseData caseData = new CaseData(); + final OrderSummary orderSummary = OrderSummary.builder().build(); + caseData.setApplication( + Application.builder() + .applicationFeeOrderSummary(orderSummary) + .build() + ); + + OrderSummary response = paymentSetupService.createApplicationFeeOrderSummary(caseData, TEST_CASE_ID); + + verify(paymentService, never()).getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE,KEYWORD_DIVORCE); + assertThat(response).isEqualTo(orderSummary); + } + + @Test + public void shouldCreateApplicationFeeOrderSummaryIfDoesNotAlreadyExist() { + final CaseData caseData = new CaseData(); + final OrderSummary orderSummary = OrderSummary.builder().build(); + caseData.setApplication(Application.builder().build()); + + when(paymentService.getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE,KEYWORD_DIVORCE)) + .thenReturn(orderSummary); + + OrderSummary response = paymentSetupService.createApplicationFeeOrderSummary(caseData, TEST_CASE_ID); + + verify(paymentService).getOrderSummaryByServiceEvent(SERVICE_DIVORCE, EVENT_ISSUE,KEYWORD_DIVORCE); + assertThat(response).isEqualTo(orderSummary); + } + + @Test + public void shouldNotCreateApplicationFeeServiceRequestIfItAlreadyExists() { + final CaseData caseData = new CaseData(); + caseData.setApplicant1(Applicant.builder().firstName(TEST_FIRST_NAME).build()); + final OrderSummary orderSummary = OrderSummary.builder().build(); + caseData.setApplication( + Application.builder() + .applicationFeeServiceRequestReference(TEST_SERVICE_REFERENCE) + .applicationFeeOrderSummary(orderSummary) + .build() + ); + + String response = paymentSetupService.createApplicationFeeServiceRequest(caseData, TEST_CASE_ID, null); + + verify(paymentService, never()).createServiceRequestReference(null, TEST_CASE_ID, TEST_FIRST_NAME, orderSummary); + assertThat(response).isEqualTo(TEST_SERVICE_REFERENCE); + } + + @Test + public void shouldCreateApplicationFeeServiceRequestIfDoesNotAlreadyExist() { + final CaseData caseData = new CaseData(); + caseData.setApplicant1(Applicant.builder().firstName(TEST_FIRST_NAME).build()); + final OrderSummary orderSummary = OrderSummary.builder().build(); + caseData.setApplication( + Application.builder() + .applicationFeeOrderSummary(orderSummary) + .build() + ); + + when(paymentService.createServiceRequestReference(null, TEST_CASE_ID, TEST_FIRST_NAME, orderSummary)) + .thenReturn(TEST_SERVICE_REFERENCE); + + String response = paymentSetupService.createApplicationFeeServiceRequest(caseData, TEST_CASE_ID, null); + + verify(paymentService).createServiceRequestReference(null, TEST_CASE_ID, TEST_FIRST_NAME, orderSummary); + assertThat(response).isEqualTo(TEST_SERVICE_REFERENCE); + } + + @Test + public void shouldNotCreateFinalOrderFeeOrderSummaryIfItAlreadyExists() { + final CaseData caseData = new CaseData(); + final OrderSummary orderSummary = OrderSummary.builder().build(); + caseData.setFinalOrder( + FinalOrder.builder() + .applicant2FinalOrderFeeOrderSummary(orderSummary) + .build() + ); + + OrderSummary response = paymentSetupService.createFinalOrderFeeOrderSummary(caseData, TEST_CASE_ID); + + verify(paymentService, never()).getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE); + assertThat(response).isEqualTo(orderSummary); + } + + @Test + public void shouldCreateFinalOrderFeeOrderSummaryIfDoesNotAlreadyExist() { + final CaseData caseData = new CaseData(); + final OrderSummary orderSummary = OrderSummary.builder().build(); + caseData.setFinalOrder(FinalOrder.builder().build()); + + when(paymentService.getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE)) + .thenReturn(orderSummary); + + OrderSummary response = paymentSetupService.createFinalOrderFeeOrderSummary(caseData, TEST_CASE_ID); + + verify(paymentService).getOrderSummaryByServiceEvent(SERVICE_OTHER, EVENT_GENERAL, KEYWORD_NOTICE); + assertThat(response).isEqualTo(orderSummary); + } + + @Test + public void shouldNotCreateFinalOrderFeeServiceRequestIfItAlreadyExists() { + final CaseData caseData = new CaseData(); + caseData.setApplicant2(Applicant.builder().firstName(TEST_FIRST_NAME).build()); + final OrderSummary orderSummary = OrderSummary.builder().build(); + caseData.setFinalOrder( + FinalOrder.builder() + .applicant2FinalOrderFeeServiceRequestReference(TEST_SERVICE_REFERENCE) + .applicant2FinalOrderFeeOrderSummary(orderSummary) + .build() + ); + + String response = paymentSetupService.createFinalOrderFeeServiceRequest(caseData, TEST_CASE_ID, null, orderSummary); + + verify(paymentService, never()).createServiceRequestReference(null, TEST_CASE_ID, TEST_FIRST_NAME, orderSummary); + assertThat(response).isEqualTo(TEST_SERVICE_REFERENCE); + } + + @Test + public void shouldCreateFinalOrderFeeServiceRequestIfDoesNotAlreadyExist() { + final CaseData caseData = new CaseData(); + caseData.setApplicant2(Applicant.builder().firstName(TEST_FIRST_NAME).build()); + final OrderSummary orderSummary = OrderSummary.builder().build(); + caseData.setFinalOrder( + FinalOrder.builder() + .applicant2FinalOrderFeeOrderSummary(orderSummary) + .build() + ); + + when(paymentService.createServiceRequestReference(null, TEST_CASE_ID, TEST_FIRST_NAME, orderSummary)) + .thenReturn(TEST_SERVICE_REFERENCE); + + String response = paymentSetupService.createFinalOrderFeeServiceRequest(caseData, TEST_CASE_ID, null, orderSummary); + + verify(paymentService).createServiceRequestReference(null, TEST_CASE_ID, TEST_FIRST_NAME, orderSummary); + assertThat(response).isEqualTo(TEST_SERVICE_REFERENCE); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/Applicant1SolicitorRespondRequestForInformationTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/Applicant1SolicitorRespondRequestForInformationTest.java new file mode 100644 index 00000000000..29d31fee660 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/Applicant1SolicitorRespondRequestForInformationTest.java @@ -0,0 +1,315 @@ +package uk.gov.hmcts.divorce.solicitor.event; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.Solicitor; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationSoleParties.OTHER; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.solicitor.event.Applicant1SolicitorRespondRequestForInformation.APP_1_SOLICITOR_RESPOND_REQUEST_INFO; +import static uk.gov.hmcts.divorce.solicitor.event.Applicant1SolicitorRespondRequestForInformation.MUST_ADD_DOCS_OR_DETAILS_ERROR; +import static uk.gov.hmcts.divorce.solicitor.event.Applicant1SolicitorRespondRequestForInformation.NOT_AUTHORISED_TO_RESPOND_ERROR; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_TEXT; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.buildDraft; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationCaseDetails; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationResponseDraft; + +@ExtendWith(MockitoExtension.class) +class Applicant1SolicitorRespondRequestForInformationTest { + + @Mock + private NotificationDispatcher notificationDispatcher; + + @Mock + private CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @InjectMocks + private Applicant1SolicitorRespondRequestForInformation applicant1SolicitorRespondRequestForInformation; + + @Test + void shouldAddConfigurationToConfigBuilder() { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + applicant1SolicitorRespondRequestForInformation.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(APP_1_SOLICITOR_RESPOND_REQUEST_INFO); + } + + @Test + void shouldReturnErrorIfRequestForOtherParty() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(OTHER, true, false); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NOT_AUTHORISED_TO_RESPOND_ERROR); + } + + @Test + void shouldReturnErrorIfRequestForApplicant2() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT2, true, false); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NOT_AUTHORISED_TO_RESPOND_ERROR); + } + + @Test + void shouldNotReturnErrorIfRequestForApplicant1() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getData()).isEqualTo(caseDetails.getData()); + } + + @Test + void shouldNotReturnErrorIfRequestForBoth() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(BOTH, true, false); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getData()).isEqualTo(caseDetails.getData()); + } + + @Test + void shouldReturnErrorIfNoDetailsOrDocuments() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant1(), false, false, false); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(MUST_ADD_DOCS_OR_DETAILS_ERROR); + } + + @Test + void shouldNotReturnErrorIfDocumentsButNoDetails() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant1(), false, true, false); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldNotReturnErrorIfDetailsButNoDocuments() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant1(), true, false, false); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldClearDefaultResponseObjectAfterAddingResponseToRequestObjectOnSoleCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant1(), true, true, false); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + final RequestForInformationResponseDraft draftResponse = + getRequestForInformationResponseDraft(response.getData(), response.getData().getApplicant1()); + assertThat(draftResponse.getRfiDraftResponseDetails()).isNull(); + assertThat(draftResponse.getRfiDraftResponseDocs()).isNull(); + } + + @Test + void shouldClearDefaultResponseObjectAfterAddingResponseToRequestObjectOnJointCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT1, true, false); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant1(), true, true, false); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + final RequestForInformationResponseDraft draftResponse = + getRequestForInformationResponseDraft(response.getData(), response.getData().getApplicant1()); + assertThat(draftResponse.getRfiDraftResponseDetails()).isNull(); + assertThat(draftResponse.getRfiDraftResponseDocs()).isNull(); + } + + @Test + void shouldPopulateDataWithResponseFromApplicantSolicitorOnSoleCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant1(), true, true, false); + final Solicitor solicitor = caseData.getApplicant1().getSolicitor(); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1SOLICITOR); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(solicitor.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(solicitor.getName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldPopulateDataWithResponseFromApplicant1SolicitorOnJointCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT1, true, false); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant1(), true, true, false); + final Solicitor solicitor = caseData.getApplicant1().getSolicitor(); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1SOLICITOR); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(solicitor.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(solicitor.getName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldPopulateDataWithResponsesFromApplicant1SolicitorAndApplicant2SolicitorOnJointCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(BOTH, true, true); + final CaseData caseData = caseDetails.getData(); + addResponseToLatestRequestForInformation(caseData, caseData.getApplicant2()); + buildDraft(caseData, caseData.getApplicant1(), true, true, false); + caseDetails.setState(RequestedInformationSubmitted); + final Solicitor app1Solicitor = caseData.getApplicant1().getSolicitor(); + final Solicitor app2Solicitor = caseData.getApplicant2().getSolicitor(); + + final AboutToStartOrSubmitResponse response = + applicant1SolicitorRespondRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(2); + + final RequestForInformationResponse responseRequestForInformationResponseApp1 = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1SOLICITOR); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseEmailAddress()) + .isEqualTo(app1Solicitor.getEmail()); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseName()) + .isEqualTo(app1Solicitor.getName()); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseDocs()).hasSize(1); + + final RequestForInformationResponse responseRequestForInformationResponseApp2 = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(1).getValue(); + + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseParties()).isEqualTo(APPLICANT2SOLICITOR); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseEmailAddress()) + .isEqualTo(app2Solicitor.getEmail()); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseName()) + .isEqualTo(app2Solicitor.getName()); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseDocs()).hasSize(1); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldSendNotificationToOtherPartyOnJointCaseWhenRFISentToBoth() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant1()); + caseDetails.setId(TEST_CASE_ID); + + applicant1SolicitorRespondRequestForInformation.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorWhenSendNotificationToOtherPartyFails() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant1()); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + applicant1SolicitorRespondRequestForInformation.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/event/Applicant2SolicitorRespondRequestForInformationTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/Applicant2SolicitorRespondRequestForInformationTest.java new file mode 100644 index 00000000000..a0da8a5c78d --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/event/Applicant2SolicitorRespondRequestForInformationTest.java @@ -0,0 +1,284 @@ +package uk.gov.hmcts.divorce.solicitor.event; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.ConfigBuilderImpl; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.api.Event; +import uk.gov.hmcts.ccd.sdk.api.callback.AboutToStartOrSubmitResponse; +import uk.gov.hmcts.divorce.citizen.notification.CitizenRequestForInformationResponsePartnerNotification; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponse; +import uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseDraft; +import uk.gov.hmcts.divorce.divorcecase.model.Solicitor; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.divorcecase.model.UserRole; +import uk.gov.hmcts.divorce.notification.NotificationDispatcher; +import uk.gov.hmcts.divorce.notification.exception.NotificationTemplateException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT1; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.APPLICANT2; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.BOTH; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationJointParties.OTHER; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT1SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.RequestForInformationResponseParties.APPLICANT2SOLICITOR; +import static uk.gov.hmcts.divorce.divorcecase.model.State.RequestedInformationSubmitted; +import static uk.gov.hmcts.divorce.solicitor.event.Applicant1SolicitorRespondRequestForInformation.NOT_AUTHORISED_TO_RESPOND_ERROR; +import static uk.gov.hmcts.divorce.solicitor.event.Applicant2SolicitorRespondRequestForInformation.APP_2_SOLICITOR_RESPOND_REQUEST_INFO; +import static uk.gov.hmcts.divorce.solicitor.event.Applicant2SolicitorRespondRequestForInformation.MUST_ADD_DOCS_OR_DETAILS_ERROR; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.createCaseDataConfigBuilder; +import static uk.gov.hmcts.divorce.testutil.ConfigTestUtil.getEventsFrom; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_TEXT; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.addResponseToLatestRequestForInformation; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.buildDraft; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationCaseDetails; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.getRequestForInformationResponseDraft; + +@ExtendWith(MockitoExtension.class) +class Applicant2SolicitorRespondRequestForInformationTest { + + @Mock + private NotificationDispatcher notificationDispatcher; + + @Mock + private CitizenRequestForInformationResponsePartnerNotification citizenRequestForInformationResponsePartnerNotification; + + @InjectMocks + private Applicant2SolicitorRespondRequestForInformation applicant2SolicitorRespondRequestForInformation; + + @Test + void shouldAddConfigurationToConfigBuilder() { + final ConfigBuilderImpl configBuilder = createCaseDataConfigBuilder(); + + applicant2SolicitorRespondRequestForInformation.configure(configBuilder); + + assertThat(getEventsFrom(configBuilder).values()) + .extracting(Event::getId) + .contains(APP_2_SOLICITOR_RESPOND_REQUEST_INFO); + } + + @Test + void shouldReturnErrorIfRequestForOtherParty() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(OTHER, true, false); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NOT_AUTHORISED_TO_RESPOND_ERROR); + } + + @Test + void shouldReturnErrorIfRequestForApplicant1() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT1, true, false); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NOT_AUTHORISED_TO_RESPOND_ERROR); + } + + @Test + void shouldReturnErrorIfSoleApplication() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(NOT_AUTHORISED_TO_RESPOND_ERROR); + } + + @Test + void shouldNotReturnErrorIfRequestForApplicant2() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT2, false, true); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getData()).isEqualTo(caseDetails.getData()); + } + + @Test + void shouldNotReturnErrorIfRequestForBoth() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(BOTH, false, true); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.aboutToStart(caseDetails); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getData()).isEqualTo(caseDetails.getData()); + } + + @Test + void shouldReturnErrorIfNoDetailsOrDocuments() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT2, false, true); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant2(), false, false, false); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors()).contains(MUST_ADD_DOCS_OR_DETAILS_ERROR); + } + + @Test + void shouldNotReturnErrorIfDocumentsButNoDetails() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT2, false, true); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant2(), false, true, false); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldNotReturnErrorIfDetailsButNoDocuments() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT2, false, true); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant2(), true, false, false); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.midEvent(caseDetails, caseDetails); + + assertThat(response.getErrors()).isNull(); + } + + @Test + void shouldClearDefaultResponseObjectAfterAddingResponseToRequestObjectOnJointCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT2, false, true); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant2(), true, true, false); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + + final RequestForInformationResponseDraft draftResponse = + getRequestForInformationResponseDraft(response.getData(), response.getData().getApplicant2()); + assertThat(draftResponse.getRfiDraftResponseDetails()).isNull(); + assertThat(draftResponse.getRfiDraftResponseDocs()).isNull(); + } + + @Test + void shouldPopulateDataWithResponseFromApplicant2SolicitorOnJointCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(APPLICANT2, false, true); + final CaseData caseData = caseDetails.getData(); + buildDraft(caseData, caseData.getApplicant2(), true, true, false); + final Solicitor solicitor = caseData.getApplicant2().getSolicitor(); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(1); + + final RequestForInformationResponse responseRequestForInformationResponse = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseParties()).isEqualTo(APPLICANT2SOLICITOR); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseEmailAddress()) + .isEqualTo(solicitor.getEmail()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseName()) + .isEqualTo(solicitor.getName()); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponse.getRequestForInformationResponseDocs()).hasSize(1); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldPopulateDataWithResponsesFromApplicant1SolicitorAndApplicant2SolicitorOnJointCase() { + final CaseDetails caseDetails = getRequestForInformationCaseDetails(BOTH, true, true); + final CaseData caseData = caseDetails.getData(); + addResponseToLatestRequestForInformation(caseData, caseData.getApplicant1()); + buildDraft(caseData, caseData.getApplicant2(), true, true, false); + caseDetails.setState(RequestedInformationSubmitted); + final Solicitor app1Solicitor = caseData.getApplicant1().getSolicitor(); + final Solicitor app2Solicitor = caseData.getApplicant2().getSolicitor(); + + final AboutToStartOrSubmitResponse response = + applicant2SolicitorRespondRequestForInformation.aboutToSubmit(caseDetails, caseDetails); + assertThat(response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses()).hasSize(2); + + final RequestForInformationResponse responseRequestForInformationResponseApp2 = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(0).getValue(); + + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseParties()).isEqualTo(APPLICANT2SOLICITOR); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseEmailAddress()) + .isEqualTo(app2Solicitor.getEmail()); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseName()) + .isEqualTo(app2Solicitor.getName()); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponseApp2.getRequestForInformationResponseDocs()).hasSize(1); + + final RequestForInformationResponse responseRequestForInformationResponseApp1 = + response.getData().getRequestForInformationList().getLatestRequest().getRequestForInformationResponses().get(1).getValue(); + + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseParties()).isEqualTo(APPLICANT1SOLICITOR); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseEmailAddress()) + .isEqualTo(app1Solicitor.getEmail()); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseName()) + .isEqualTo(app1Solicitor.getName()); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseDetails()).isEqualTo(TEST_TEXT); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseDateTime()).isNotNull(); + assertThat(responseRequestForInformationResponseApp1.getRequestForInformationResponseDocs()).hasSize(1); + + assertThat(response.getErrors()).isNull(); + assertThat(response.getState()).isEqualTo(RequestedInformationSubmitted); + } + + @Test + void shouldSendNotificationToOtherPartyOnJointCaseWhenRFISentToBoth() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant2()); + caseDetails.setId(TEST_CASE_ID); + + applicant2SolicitorRespondRequestForInformation.submitted(caseDetails, caseDetails); + + verify(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + } + + @Test + void shouldReturnErrorWhenSendNotificationToOtherPartyFails() { + final CaseDetails caseDetails = + getRequestForInformationCaseDetails(RequestForInformationJointParties.BOTH, false, false); + addResponseToLatestRequestForInformation(caseDetails.getData(), caseDetails.getData().getApplicant2()); + caseDetails.setId(TEST_CASE_ID); + + doThrow(NotificationTemplateException.class).when(notificationDispatcher).sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + + applicant2SolicitorRespondRequestForInformation.submitted(caseDetails, caseDetails); + + Assertions.assertThrows(NotificationTemplateException.class, () -> { + notificationDispatcher.sendRequestForInformationResponsePartnerNotification( + citizenRequestForInformationResponsePartnerNotification, + caseDetails.getData(), + TEST_CASE_ID + ); + }); + } +} diff --git a/src/test/java/uk/gov/hmcts/divorce/solicitor/service/task/SetApplicationFeeServiceRequestTest.java b/src/test/java/uk/gov/hmcts/divorce/solicitor/service/task/SetApplicationFeeServiceRequestTest.java new file mode 100644 index 00000000000..2c0bf0b15ce --- /dev/null +++ b/src/test/java/uk/gov/hmcts/divorce/solicitor/service/task/SetApplicationFeeServiceRequestTest.java @@ -0,0 +1,53 @@ +package uk.gov.hmcts.divorce.solicitor.service.task; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.ccd.sdk.api.CaseDetails; +import uk.gov.hmcts.ccd.sdk.type.OrderSummary; +import uk.gov.hmcts.divorce.divorcecase.model.Application; +import uk.gov.hmcts.divorce.divorcecase.model.CaseData; +import uk.gov.hmcts.divorce.divorcecase.model.State; +import uk.gov.hmcts.divorce.payment.PaymentSetupService; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_CASE_ID; +import static uk.gov.hmcts.divorce.testutil.TestConstants.TEST_SERVICE_REFERENCE; +import static uk.gov.hmcts.divorce.testutil.TestDataHelper.applicantRepresentedBySolicitor; + +@ExtendWith(MockitoExtension.class) +class SetApplicationFeeServiceRequestTest { + + @Mock + private PaymentSetupService paymentSetupService; + + @InjectMocks + private SetApplicationFeeServiceRequest setApplicationFeeServiceRequest; + + @Test + void shouldCreateOrderSummaryAndServiceRequestByDelegatingToPaymentSetupService() { + final var caseData = CaseData.builder() + .applicant1(applicantRepresentedBySolicitor()) + .build(); + final CaseDetails caseDetails = new CaseDetails<>(); + caseDetails.setData(caseData); + caseDetails.setId(TEST_CASE_ID); + final OrderSummary orderSummary = new OrderSummary(); + + when(paymentSetupService.createApplicationFeeOrderSummary(caseData, TEST_CASE_ID)) + .thenReturn(orderSummary); + when(paymentSetupService.createApplicationFeeServiceRequest(caseData, TEST_CASE_ID, null)) + .thenReturn(TEST_SERVICE_REFERENCE); + + final CaseDetails result = setApplicationFeeServiceRequest.apply(caseDetails); + Application resultApplication = result.getData().getApplication(); + + assertThat(resultApplication.getApplicationFeeOrderSummary()) + .isEqualTo(orderSummary); + assertThat(resultApplication.getApplicationFeeServiceRequestReference()) + .isEqualTo(TEST_SERVICE_REFERENCE); + } +}