diff --git a/frontstage/controllers/party_controller.py b/frontstage/controllers/party_controller.py index f3f215add..c619a4f41 100644 --- a/frontstage/controllers/party_controller.py +++ b/frontstage/controllers/party_controller.py @@ -574,6 +574,22 @@ def register_pending_surveys(payload: json, party_id: str) -> requests.Response: return response +def get_existing_pending_surveys(party_id: str) -> dict: + """ + Returns a list of pending_surveys that already exist in the DB for this user + """ + url = f"{app.config['PARTY_URL']}/party-api/v1/pending-surveys/originator/{party_id}" + response = requests.get(url, auth=app.config["BASIC_AUTH"]) + try: + response.raise_for_status() + except requests.exceptions.HTTPError: + logger.error("Failed to get pending surveys for respondent", party_id=party_id) + raise ApiError(logger, response) + + logger.info("Successfully retrieved pending surveys for respondent", party_id=party_id) + return response.json() + + def get_pending_surveys_batch_number(batch_no): """ Gets batch number for the shared survey diff --git a/frontstage/views/account/account_transfer_survey.py b/frontstage/views/account/account_transfer_survey.py index a96f6aa9b..dbd49039d 100644 --- a/frontstage/views/account/account_transfer_survey.py +++ b/frontstage/views/account/account_transfer_survey.py @@ -12,6 +12,7 @@ from frontstage.controllers import party_controller, survey_controller from frontstage.controllers.party_controller import ( get_business_by_id, + get_existing_pending_surveys, get_respondent_enrolments, get_surveys_to_transfer_map, register_pending_surveys, @@ -92,11 +93,83 @@ def transfer_survey_email_entry(session): return render_template("surveys/surveys-transfer/recipient-email-address.html", form=form, errors=errors) flask_session["transfer_survey_recipient_email_address"] = form.data["email_address"] + + existing_pending_surveys = get_existing_pending_surveys(party_id) + + if existing_pending_surveys: + duplicate_transfers = _get_duplicate_transfers(existing_pending_surveys, form.data["email_address"]) + if duplicate_transfers: + business_survey_enrolments = get_respondent_enrolments(party_id) + errors = { + "email_address": [ + _build_duplicate_transfer_error_message(duplicate_transfers, business_survey_enrolments) + ] + } + return render_template( + "surveys/surveys-transfer/recipient-email-address.html", form=form, errors=errors + ) + return redirect(url_for("account_bp.send_transfer_instruction_get")) return render_template("surveys/surveys-transfer/recipient-email-address.html", form=form) +def _get_duplicate_transfers(existing_pending_surveys, email_address): + duplicate_transfers = [] + for existing_pending_survey in existing_pending_surveys: + if ( + _existing_pending_survey_is_in_selection( + flask_session["surveys_to_transfer_map"], + existing_pending_survey["business_id"], + existing_pending_survey["survey_id"], + ) + and existing_pending_survey["email_address"].lower() == email_address.lower() + ): + duplicate_transfers.append( + { + "business_id": existing_pending_survey["business_id"], + "survey_id": existing_pending_survey["survey_id"], + "email_address": existing_pending_survey["email_address"].lower(), + } + ) + return duplicate_transfers + + +def _build_duplicate_transfer_error_message(duplicate_transfers, business_survey_enrolments): + error_message = "You have already shared or transferred the following surveys to this email address. " + error_message += "They have 72 hours to accept your request. " + error_message += "

If you have made an error then wait for the share/transfer to expire or contact us." + error_message += "" + return error_message + + +def _existing_pending_survey_is_in_selection(surveys_to_transfer_map, business_id, survey_id): + if business_id in surveys_to_transfer_map: + return survey_id in surveys_to_transfer_map[business_id] + return False + + @account_bp.route("/transfer-surveys/send-instruction", methods=["GET"]) @jwt_authorization(request) def send_transfer_instruction_get(_): diff --git a/tests/integration/mocked_services.py b/tests/integration/mocked_services.py index 31c6e9417..a6a50aa0c 100644 --- a/tests/integration/mocked_services.py +++ b/tests/integration/mocked_services.py @@ -79,6 +79,9 @@ with open("tests/test_data/survey/bricks_survey.json") as fp: survey = json.load(fp) +with open("tests/test_data/party/pending_surveys.json") as fp: + pending_surveys = json.load(fp) + with open("tests/test_data/survey/qbs_survey.json") as fp: survey_eq = json.load(fp) @@ -108,6 +111,7 @@ url_get_business_party = f"{app.config['PARTY_URL']}/party-api/v1/businesses/id/{business_party['id']}" url_get_respondent_party = f"{app.config['PARTY_URL']}/party-api/v1/respondents/party_id/{party['id']}" url_get_respondent_enrolments = f"{app.config['PARTY_URL']}/party-api/v1/enrolments/respondent/{party['id']}" +url_get_existing_pending_surveys = f"{app.config['PARTY_URL']}/party-api/v1/pending-surveys/originator/{party['id']}" url_get_case = f"{app.config['CASE_URL']}/cases/{case['id']}" url_get_case_by_enrolment_code = f"{app.config['CASE_URL']}/cases/iac/{enrolment_code}" url_get_case_categories = f"{app.config['CASE_URL']}/categories" @@ -137,6 +141,7 @@ url_auth_token = f"{app.config['AUTH_URL']}/api/v1/tokens/" url_password_change = f"{app.config['PARTY_URL']}/party-api/v1/respondents/change_password" url_post_add_survey = f"{app.config['PARTY_URL']}/party-api/v1/respondents/add_survey" +url_get_post_add_survey = f"{app.config['PARTY_URL']}/party-api/v1/respondents/add_survey" url_post_case_event_uuid = f"{app.config['CASE_URL']}/cases/{case['id']}/events" url_reset_password_request = f"{app.config['PARTY_URL']}/party-api/v1/respondents/request_password_change" url_send_message = app.config["SECURE_MESSAGE_URL"] + "/messages" diff --git a/tests/integration/views/account/test_transfer_surveys.py b/tests/integration/views/account/test_transfer_surveys.py index e94a290cf..bceff5ac8 100644 --- a/tests/integration/views/account/test_transfer_surveys.py +++ b/tests/integration/views/account/test_transfer_surveys.py @@ -9,10 +9,13 @@ business_party, encoded_jwt_token, party, + pending_surveys, respondent_enrolments, respondent_party, survey, url_banner_api, + url_get_existing_pending_surveys, + url_get_respondent_enrolments, url_get_respondent_party, url_get_survey, ) @@ -153,12 +156,40 @@ def test_transfer_survey_recipient_email_invalid(self, mock_request): self.assertIn("Problem with the email address".encode(), response.data) self.assertIn("Invalid email address".encode(), response.data) + @requests_mock.mock() + def test_transfer_survey_transfer_duplicates(self, mock_request): + mock_request.get(url_banner_api, status_code=404) + mock_request.get(url_get_respondent_party, status_code=200, json=respondent_party) + mock_request.get(url_get_business_details, status_code=200, json=[business_party]) + mock_request.get(url_get_survey, status_code=200, json=survey) + mock_request.get(url_get_existing_pending_surveys, status_code=200, json=pending_surveys) + mock_request.get(url_get_respondent_enrolments, json=respondent_enrolments) + with self.app.session_transaction() as mock_session: + mock_session["surveys_to_transfer_map"] = {business_party["id"]: [survey["id"]]} + response = self.app.post( + "/my-account/transfer-surveys/recipient-email-address", + data={"email_address": "bob@here.com"}, + follow_redirects=True, + ) + self.assertEqual(response.status_code, 200) + self.assertIn( + "You have already shared or transferred the following surveys to this email address.".encode(), + response.data, + ) + self.assertIn("They have 72 hours to accept your request.".encode(), response.data) + self.assertIn( + "

If you have made an error then wait for the share/transfer to expire or contact us.".encode(), + response.data, + ) + self.assertIn("".encode(), response.data) + @requests_mock.mock() def test_transfer_survey_transfer_instruction(self, mock_request): mock_request.get(url_banner_api, status_code=404) mock_request.get(url_get_respondent_party, status_code=200, json=respondent_party) mock_request.get(url_get_business_details, status_code=200, json=[business_party]) mock_request.get(url_get_survey, status_code=200, json=survey) + mock_request.get(url_get_existing_pending_surveys, status_code=200, json=pending_surveys) with self.app.session_transaction() as mock_session: mock_session["surveys_to_transfer_map"] = {business_party["id"]: [survey["id"]]} response = self.app.post( diff --git a/tests/test_data/party/pending_surveys.json b/tests/test_data/party/pending_surveys.json new file mode 100644 index 000000000..41bb886a1 --- /dev/null +++ b/tests/test_data/party/pending_surveys.json @@ -0,0 +1,11 @@ +[ + { + "batch_no": "be34d7ca-1d06-4f6e-98e3-73b80c911d32", + "business_id": "be3483c3-f5c9-4b13-bdd7-244db78ff687", + "email_address": "bob@here.com", + "is_transfer": true, + "shared_by": "f956e8ae-6e0f-4414-b0cf-a07c1aa3e37b", + "survey_id": "cb8accda-6118-4d3b-85a3-149e28960c54", + "time_shared": "2025-02-05 16:08:58" + } +]