From eb4b648cb1000484a8a557318541fdfdf3e288ab Mon Sep 17 00:00:00 2001 From: Brendan Smith Date: Mon, 12 Aug 2024 15:43:03 +0100 Subject: [PATCH 1/2] Prevent caseworkers setting superseded by exporter edit status --- api/applications/caseworker/permissions.py | 3 +++ api/applications/caseworker/tests/test_permissions.py | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/api/applications/caseworker/permissions.py b/api/applications/caseworker/permissions.py index aabdb30ac..88ee92173 100644 --- a/api/applications/caseworker/permissions.py +++ b/api/applications/caseworker/permissions.py @@ -21,6 +21,9 @@ def has_object_permission(self, request, view, application): if new_status == CaseStatusEnum.APPLICANT_EDITING: return False + if new_status == CaseStatusEnum.SUPERSEDED_BY_EXPORTER_EDIT: + return False + if CaseStatusEnum.is_terminal(original_status) and not user.has_permission(GovPermissions.REOPEN_CLOSED_CASES): return False diff --git a/api/applications/caseworker/tests/test_permissions.py b/api/applications/caseworker/tests/test_permissions.py index de8f9725d..9e2febf1a 100644 --- a/api/applications/caseworker/tests/test_permissions.py +++ b/api/applications/caseworker/tests/test_permissions.py @@ -62,10 +62,11 @@ def test_has_object_permission_original_status_terminal_user_permitted(self, ori mock_request.data = {"status": CaseStatusEnum.OGD_ADVICE} assert self.permission_obj.has_object_permission(mock_request, None, self.application) is True - def test_has_object_permission_new_status_applicant_editing(self): + @parameterized.expand([CaseStatusEnum.APPLICANT_EDITING, CaseStatusEnum.SUPERSEDED_BY_EXPORTER_EDIT]) + def test_has_object_permission_new_status_disallowed(self, new_status): mock_request = mock.Mock() mock_request.user = self.gov_user.baseuser_ptr - mock_request.data = {"status": CaseStatusEnum.APPLICANT_EDITING} + mock_request.data = {"status": new_status} assert self.permission_obj.has_object_permission(mock_request, None, self.application) is False def test_has_object_permission_new_status_finalised_user_permitted(self): From b2d6c2d7ed634cfc377ce32fad4a6b08771e11d1 Mon Sep 17 00:00:00 2001 From: Brendan Smith Date: Mon, 12 Aug 2024 16:17:39 +0100 Subject: [PATCH 2/2] Remove legacy manage status API endpoint --- .../libraries/application_helpers.py | 78 ---- .../tests/test_application_status.py | 436 ------------------ api/applications/urls.py | 1 - api/applications/views/applications.py | 27 +- api/cases/tests/test_finalise_advice.py | 7 +- api/cases/tests/test_grant_licence.py | 8 +- api/cases/tests/test_is_recently_updated.py | 8 +- .../tests/test_gov_user_notifications.py | 4 +- .../tests/test_api_to_hmrc_integration.py | 8 +- 9 files changed, 19 insertions(+), 558 deletions(-) delete mode 100644 api/applications/tests/test_application_status.py diff --git a/api/applications/libraries/application_helpers.py b/api/applications/libraries/application_helpers.py index eb1de6f2f..b6ff56296 100644 --- a/api/applications/libraries/application_helpers.py +++ b/api/applications/libraries/application_helpers.py @@ -1,18 +1,9 @@ -from django.http import JsonResponse - -from rest_framework import status -from rest_framework.exceptions import PermissionDenied - from api.audit_trail import service as audit_trail_service from api.audit_trail.enums import AuditType -from api.cases.enums import CaseTypeSubTypeEnum from api.core.constants import GovPermissions from api.core.permissions import assert_user_has_permission from api.staticdata.statuses.enums import CaseStatusEnum -from api.organisations.libraries.get_organisation import get_request_user_organisation_id from api.users.models import GovUser -from lite_content.lite_api import strings -from lite_routing.routing_rules_internal.enums import TeamIdEnum def optional_str_to_bool(optional_string: str): @@ -26,27 +17,6 @@ def optional_str_to_bool(optional_string: str): raise ValueError("You provided " + optional_string + ', while the allowed values are None, "true" or "false"') -# TODO: After release of LTD-5225, remove this function alongside legacy ApplicationManageStatus view -def can_status_be_set_by_exporter_user(original_status: str, new_status: str) -> bool: - """Check that a status can be set by an exporter user. Exporter users cannot withdraw an application - that is already in a terminal state and they cannot set an application to `Applicant editing` if the - application is read only. - """ - if new_status == CaseStatusEnum.WITHDRAWN: - if CaseStatusEnum.is_terminal(original_status): - return False - elif new_status == CaseStatusEnum.SURRENDERED: - if original_status != CaseStatusEnum.FINALISED: - return False - elif CaseStatusEnum.can_invoke_major_edit(original_status) and new_status == CaseStatusEnum.APPLICANT_EDITING: - return True - elif CaseStatusEnum.is_read_only(original_status) or new_status != CaseStatusEnum.APPLICANT_EDITING: - return False - - return True - - -# TODO: After release of LTD-5225, remove this function alongside legacy ApplicationManageStatus view def can_status_be_set_by_gov_user(user: GovUser, original_status: str, new_status: str, is_mod: bool) -> bool: """ Check that a status can be set by a gov user. Gov users can not set a case's status to @@ -91,51 +61,3 @@ def create_submitted_audit(user, application, old_status: str, additional_payloa ignore_case_status=True, send_notification=False, ) - - -# TODO: After release of LTD-5225, remove this function alongside legacy ApplicationManageStatus view -def check_user_can_set_status(request, application, data): - """ - Checks whether an user (internal/exporter) can set the requested status - Returns error response if user cannot set the status, None otherwise - """ - if hasattr(request.user, "exporteruser"): - if get_request_user_organisation_id(request) != application.organisation.id: - raise PermissionDenied() - - if data["status"] == CaseStatusEnum.FINALISED: - return JsonResponse( - data={"errors": [strings.Applications.Generic.Finalise.Error.SET_FINALISED]}, - status=status.HTTP_400_BAD_REQUEST, - ) - - if not can_status_be_set_by_exporter_user(application.status.status, data["status"]): - return JsonResponse( - data={"errors": [strings.Applications.Generic.Finalise.Error.EXPORTER_SET_STATUS]}, - status=status.HTTP_400_BAD_REQUEST, - ) - elif hasattr(request.user, "govuser"): - gov_user = request.user.govuser - - if data["status"] == CaseStatusEnum.FINALISED: - lu_user = str(gov_user.team.id) == TeamIdEnum.LICENSING_UNIT - if lu_user and assert_user_has_permission(gov_user, GovPermissions.MANAGE_LICENCE_FINAL_ADVICE): - return None - - return JsonResponse( - data={"errors": [strings.Applications.Generic.Finalise.Error.SET_FINALISED]}, - status=status.HTTP_400_BAD_REQUEST, - ) - - is_licence_application = application.case_type.sub_type != CaseTypeSubTypeEnum.EXHIBITION - if not can_status_be_set_by_gov_user( - gov_user, application.status.status, data["status"], is_licence_application - ): - return JsonResponse( - data={"errors": [strings.Applications.Generic.Finalise.Error.GOV_SET_STATUS]}, - status=status.HTTP_400_BAD_REQUEST, - ) - else: - return JsonResponse(data={"errors": ["Invalid user type"]}, status=status.HTTP_401_UNAUTHORIZED) - - return None diff --git a/api/applications/tests/test_application_status.py b/api/applications/tests/test_application_status.py deleted file mode 100644 index cb73d6230..000000000 --- a/api/applications/tests/test_application_status.py +++ /dev/null @@ -1,436 +0,0 @@ -from unittest import mock - -from django.conf import settings -from django.test import override_settings -from django.urls import reverse -from parameterized import parameterized -from rest_framework import status - -from api.audit_trail.models import AuditType, Audit -from api.core.authentication import SharedAuthentication -from api.core.constants import GovPermissions -from api.cases.models import CaseAssignment -from api.licences.enums import LicenceStatus -from api.licences.tests.factories import StandardLicenceFactory -from api.teams.models import Team -from api.users.models import UserOrganisationRelationship, Permission -from api.staticdata.statuses.enums import CaseStatusEnum -from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status -from test_helpers.clients import DataTestClient -from api.users.enums import UserType -from api.users.models import BaseUser -from api.users.libraries.user_to_token import user_to_token - -from lite_content.lite_api import strings -from lite_routing.routing_rules_internal.enums import TeamIdEnum - - -class ApplicationManageStatusTests(DataTestClient): - def setUp(self): - super().setUp() - self.standard_application = self.create_draft_standard_application(self.organisation) - self.submit_application(self.standard_application) - self.url = reverse("applications:manage_status", kwargs={"pk": self.standard_application.id}) - - def test_gov_set_application_status_to_applicant_editing_failure(self): - data = {"status": CaseStatusEnum.APPLICANT_EDITING} - response = self.client.put(self.url, data=data, **self.gov_headers) - - self.standard_application.refresh_from_db() - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json().get("errors")[0], strings.Applications.Generic.Finalise.Error.GOV_SET_STATUS) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.SUBMITTED)) - - def test_set_application_status_on_application_not_in_users_organisation_failure(self): - self.submit_application(self.standard_application) - other_organisation, _ = self.create_organisation_with_exporter_user() - data = {"status": "Invalid status"} - permission_denied_user = UserOrganisationRelationship.objects.get(organisation=other_organisation).user - permission_denied_user_headers = { - "HTTP_EXPORTER_USER_TOKEN": user_to_token(permission_denied_user.baseuser_ptr), - "HTTP_ORGANISATION_ID": str(other_organisation.id), - } - - response = self.client.put(self.url, data=data, **permission_denied_user_headers) - - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.SUBMITTED)) - - @mock.patch("api.applications.notify.notify_exporter_case_opened_for_editing") - def test_exporter_set_application_status_applicant_editing_when_in_editable_status_success(self, mock_notify): - self.submit_application(self.standard_application) - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.REOPENED_FOR_CHANGES) - self.standard_application.save() - - data = {"status": CaseStatusEnum.APPLICANT_EDITING} - response = self.client.put(self.url, data=data, **self.exporter_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.APPLICANT_EDITING)) - audit_event = Audit.objects.first() - self.assertEqual(audit_event.verb, AuditType.UPDATED_STATUS) - self.assertEqual( - audit_event.payload, - {"status": {"new": CaseStatusEnum.APPLICANT_EDITING, "old": CaseStatusEnum.REOPENED_FOR_CHANGES}}, - ) - mock_notify.assert_called_with(self.standard_application) - - def test_exporter_set_application_status_withdrawn_when_application_not_terminal_success(self): - self.submit_application(self.standard_application) - - data = {"status": CaseStatusEnum.WITHDRAWN} - response = self.client.put(self.url, data=data, **self.exporter_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.WITHDRAWN)) - - @parameterized.expand( - [ - case_status - for case_status in CaseStatusEnum.terminal_statuses() - if case_status not in [CaseStatusEnum.FINALISED, CaseStatusEnum.SURRENDERED] - ] - ) - def test_gov_user_set_application_to_terminal_status_removes_case_from_queues_users_success(self, case_status): - """ - When a case is set to a terminal status, its assigned users, case officer and queues should be removed - """ - self.submit_application(self.standard_application) - self.standard_application.case_officer = self.gov_user - self.standard_application.save() - self.standard_application.queues.set([self.queue]) - case_assignment = CaseAssignment.objects.create( - case=self.standard_application, queue=self.queue, user=self.gov_user - ) - if case_status == CaseStatusEnum.REVOKED: - self.standard_application.licences.add( - StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) - ) - - data = {"status": case_status} - - response = self.client.put(self.url, data, **self.gov_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(self.standard_application.status.status, case_status) - self.assertEqual(self.standard_application.queues.count(), 0) - self.assertEqual(self.standard_application.case_officer, None) - self.assertEqual(CaseAssignment.objects.filter(case=self.standard_application).count(), 0) - - @parameterized.expand( - [ - ( - CaseStatusEnum.SUSPENDED, - LicenceStatus.SUSPENDED, - ), - ( - CaseStatusEnum.SURRENDERED, - LicenceStatus.SURRENDERED, - ), - ( - CaseStatusEnum.REVOKED, - LicenceStatus.REVOKED, - ), - ] - ) - def test_certain_case_statuses_changes_licence_status(self, case_status, licence_status): - licence = StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) - - data = {"status": case_status} - response = self.client.put(self.url, data=data, **self.gov_headers) - - self.standard_application.refresh_from_db() - licence.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()["data"]["status"]["key"], case_status) - self.assertEqual(self.standard_application.status.status, case_status) - self.assertEqual(licence.status, licence_status) - - def test_exporter_set_application_status_withdrawn_when_application_terminal_failure(self): - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.FINALISED) - self.standard_application.save() - - data = {"status": CaseStatusEnum.WITHDRAWN} - response = self.client.put(self.url, data=data, **self.exporter_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.FINALISED)) - - def test_exporter_set_application_status_applicant_editing_when_in_read_only_status_failure(self): - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.UNDER_FINAL_REVIEW) - self.standard_application.save() - - data = {"status": CaseStatusEnum.APPLICANT_EDITING} - response = self.client.put(self.url, data=data, **self.exporter_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.UNDER_FINAL_REVIEW)) - - @parameterized.expand( - [ - status - for status, value in CaseStatusEnum.get_choices() - if status not in [CaseStatusEnum.APPLICANT_EDITING, CaseStatusEnum.FINALISED, CaseStatusEnum.WITHDRAWN] - ] - ) - def test_exporter_set_application_status_failure(self, new_status): - """Test failure in setting application status to any status other than 'Applicant Editing' and 'Withdrawn' - as an exporter user. - """ - self.submit_application(self.standard_application) - - data = {"status": new_status} - response = self.client.put(self.url, data=data, **self.exporter_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.SUBMITTED)) - - def test_exporter_set_application_status_surrendered_success(self): - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.FINALISED) - self.standard_application.save() - StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) - surrendered_status = get_case_status_by_status("surrendered") - - data = {"status": CaseStatusEnum.SURRENDERED} - response = self.client.put(self.url, data=data, **self.exporter_headers) - response_data = response.json()["data"] - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_200_OK) - response_data["status"].pop("id", None) - self.assertEqual( - response_data["status"], - {"key": surrendered_status.status, "value": CaseStatusEnum.get_text(surrendered_status.status)}, - ) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.SURRENDERED)) - - def test_exporter_set_application_status_surrendered_no_licence_failure(self): - """Test failure in exporter user setting a case status to surrendered when the case - does not have a licence duration - """ - self.standard_application.licences.update(duration=None) - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.FINALISED) - self.standard_application.save() - - data = {"status": CaseStatusEnum.SURRENDERED} - response = self.client.put(self.url, data=data, **self.exporter_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual( - response.json(), {"errors": {"status": [strings.Applications.Generic.Finalise.Error.SURRENDER]}} - ) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.FINALISED)) - - def test_exporter_set_application_status_surrendered_not_finalised_failure(self): - """Test failure in exporter user setting a case status to surrendered when the case was not - previously finalised. - """ - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.SUBMITTED) - self.standard_application.save() - - data = {"status": CaseStatusEnum.SURRENDERED} - response = self.client.put(self.url, data=data, **self.exporter_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json(), {"errors": [strings.Applications.Generic.Finalise.Error.EXPORTER_SET_STATUS]}) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.SUBMITTED)) - - def test_exporter_cannot_set_status_to_finalised(self): - data = {"status": CaseStatusEnum.FINALISED} - response = self.client.put(self.url, data=data, **self.exporter_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json().get("errors")[0], strings.Applications.Generic.Finalise.Error.SET_FINALISED) - - def test_gov_set_status_to_applicant_editing_failure(self): - data = {"status": CaseStatusEnum.APPLICANT_EDITING} - response = self.client.put(self.url, data=data, **self.gov_headers) - - self.standard_application.refresh_from_db() - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json().get("errors")[0], strings.Applications.Generic.Finalise.Error.GOV_SET_STATUS) - self.assertEqual( - self.standard_application.status, - get_case_status_by_status(CaseStatusEnum.SUBMITTED), - ) - - @parameterized.expand( - [ - status - for status, value in CaseStatusEnum.get_choices() - if status - not in [ - CaseStatusEnum.APPLICANT_EDITING, - CaseStatusEnum.FINALISED, - CaseStatusEnum.SURRENDERED, - CaseStatusEnum.SUSPENDED, - CaseStatusEnum.REOPENED_FOR_CHANGES, - ] - ] - ) - def test_gov_set_status_for_all_except_applicant_editing_and_finalised_success(self, case_status): - if case_status == CaseStatusEnum.REVOKED: - self.standard_application.licences.add( - StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) - ) - - data = {"status": case_status} - - response = self.client.put(self.url, data=data, **self.gov_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(self.standard_application.status, get_case_status_by_status(case_status)) - - @parameterized.expand([CaseStatusEnum.REOPENED_FOR_CHANGES, CaseStatusEnum.REOPENED_DUE_TO_ORG_CHANGES]) - def test_gov_set_status_when_they_have_do_not_permission_to_reopen_closed_cases_failure(self, reopened_status): - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.WITHDRAWN) - self.standard_application.save() - - data = {"status": reopened_status} - response = self.client.put(self.url, data=data, **self.gov_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.WITHDRAWN)) - - def test_gov_set_status_when_they_have_permission_to_reopen_closed_cases_success(self): - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.WITHDRAWN) - self.standard_application.save() - - # Give gov user super used role, to include reopen closed cases permission - self.gov_user.role = self.super_user_role - self.gov_user.save() - - data = {"status": CaseStatusEnum.REOPENED_FOR_CHANGES} - - response = self.client.put(self.url, data=data, **self.gov_headers) - - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - self.standard_application.status, get_case_status_by_status(CaseStatusEnum.REOPENED_FOR_CHANGES) - ) - - def test_case_routing_automation(self): - routing_queue = self.create_queue("new queue", self.team) - self.create_routing_rule( - team_id=self.team.id, - queue_id=routing_queue.id, - tier=3, - status_id=get_case_status_by_status(CaseStatusEnum.UNDER_REVIEW).id, - additional_rules=[], - ) - self.assertNotEqual(self.standard_application.status.status, CaseStatusEnum.UNDER_REVIEW) - - data = {"status": CaseStatusEnum.UNDER_REVIEW} - - response = self.client.put(self.url, data, **self.gov_headers) - self.standard_application.refresh_from_db() - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(self.standard_application.status.status, CaseStatusEnum.UNDER_REVIEW) - self.assertEqual(self.standard_application.queues.count(), 2) - self.assertEqual( - sorted([queue.name for queue in self.standard_application.queues.all()]), - ["Licensing Unit Pre-circulation Cases to Review", "new queue"], - ) - - @parameterized.expand( - [ - ( - TeamIdEnum.TECHNICAL_ASSESSMENT_UNIT, - [], - CaseStatusEnum.INITIAL_CHECKS, - CaseStatusEnum.INITIAL_CHECKS, - status.HTTP_400_BAD_REQUEST, - ), - ( - TeamIdEnum.TECHNICAL_ASSESSMENT_UNIT, - [GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name], - CaseStatusEnum.INITIAL_CHECKS, - CaseStatusEnum.INITIAL_CHECKS, - status.HTTP_400_BAD_REQUEST, - ), - ( - TeamIdEnum.LICENSING_UNIT, - [], - CaseStatusEnum.UNDER_FINAL_REVIEW, - CaseStatusEnum.UNDER_FINAL_REVIEW, - status.HTTP_403_FORBIDDEN, - ), - ( - TeamIdEnum.LICENSING_UNIT, - [GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name], - CaseStatusEnum.UNDER_FINAL_REVIEW, - CaseStatusEnum.FINALISED, - status.HTTP_200_OK, - ), - ] - ) - def test_gov_user_set_status_to_finalised( - self, team_id, permissions, initial_status, expected_status, expected_status_code - ): - self.standard_application.status = get_case_status_by_status(initial_status) - self.standard_application.save() - self.assertEqual(self.standard_application.status, get_case_status_by_status(initial_status)) - - self.gov_user.team = Team.objects.get(id=team_id) - self.gov_user.save() - - self.gov_user.role.permissions.add(*Permission.objects.filter(id__in=permissions)) - - data = {"status": CaseStatusEnum.FINALISED} - - response = self.client.put(self.url, data=data, **self.gov_headers) - self.assertEqual(response.status_code, expected_status_code) - - self.standard_application.refresh_from_db() - self.assertEqual(self.standard_application.status, get_case_status_by_status(expected_status)) - - @override_settings( - MIDDLEWARE=[item for item in settings.MIDDLEWARE if item != "reversion.middleware.RevisionMiddleware"] - ) - @mock.patch.object(SharedAuthentication, "authenticate") - def test_hmrc_user_set_status_to_finalised_fail(self, mock_authenticate): - # Skip reversion middleware as we are mocking the authenticate method - # Reversion sets the user once authenticated which fails in this case - mock_authenticate.return_value = ["token", ""] - self.standard_application.status = get_case_status_by_status(CaseStatusEnum.UNDER_FINAL_REVIEW) - self.standard_application.save() - self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.UNDER_FINAL_REVIEW)) - - base_user = BaseUser( - email="test@mail.com", first_name="John", last_name="Smith", type=UserType.SYSTEM # /PS-IGNORE - ) - base_user.team = Team.objects.get(id=TeamIdEnum.LICENSING_UNIT) - base_user.save() - - headers = {"HTTP_GOV_USER_TOKEN": user_to_token(base_user)} - data = {"status": CaseStatusEnum.FINALISED} - response = self.client.put(self.url, data=data, **headers) - - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) diff --git a/api/applications/urls.py b/api/applications/urls.py index 338e4b07f..ea968fcba 100644 --- a/api/applications/urls.py +++ b/api/applications/urls.py @@ -38,7 +38,6 @@ ), path("/final-decision/", applications.ApplicationFinaliseView.as_view(), name="finalise"), path("/duration/", applications.ApplicationDurationView.as_view(), name="duration"), - path("/status/", applications.ApplicationManageStatus.as_view(), name="manage_status"), path("/sub-status/", applications.ApplicationManageSubStatus.as_view(), name="manage_sub_status"), path("/sub-statuses/", applications.ApplicationSubStatuses.as_view(), name="application_sub_statuses"), path( diff --git a/api/applications/views/applications.py b/api/applications/views/applications.py index 0bf984fb4..7af74cba3 100644 --- a/api/applications/views/applications.py +++ b/api/applications/views/applications.py @@ -33,7 +33,6 @@ from api.applications.libraries.application_helpers import ( can_status_be_set_by_gov_user, create_submitted_audit, - check_user_can_set_status, ) from api.applications.libraries.case_status_helpers import submit_application from api.applications.libraries.edit_applications import ( @@ -65,7 +64,7 @@ from api.cases.notify import notify_exporter_appeal_acknowledgement from api.cases.serializers import ApplicationManageSubStatusSerializer from api.cases.celery_tasks import get_application_target_sla -from api.core.authentication import ExporterAuthentication, SharedAuthentication, GovAuthentication +from api.core.authentication import ExporterAuthentication, GovAuthentication from api.core.constants import ExporterPermissions, GovPermissions, AutoGeneratedDocuments from api.core.decorators import ( application_is_editable, @@ -378,30 +377,6 @@ def put(self, request, pk): return JsonResponse(data=data, status=status.HTTP_200_OK) -# TODO: After release of LTD-5225, remove this endpoint completely -class ApplicationManageStatus(APIView): - authentication_classes = (SharedAuthentication,) - - @transaction.atomic - def put(self, request, pk): - application = get_application(pk) - data = deepcopy(request.data) - - error_response = check_user_can_set_status(request, application, data) - if error_response: - return error_response - - new_status = data["status"] - note = data.get("note", "") - user = request.user - - application.change_status(user, get_case_status_by_status(new_status), note) - - data = get_application_view_serializer(application)(application, context={"user_type": request.user.type}).data - - return JsonResponse(data={"data": data}, status=status.HTTP_200_OK) - - class ApplicationManageSubStatus(UpdateAPIView): authentication_classes = (GovAuthentication,) queryset = StandardApplication.objects.all() diff --git a/api/cases/tests/test_finalise_advice.py b/api/cases/tests/test_finalise_advice.py index 7d6d4593e..2f9a533af 100644 --- a/api/cases/tests/test_finalise_advice.py +++ b/api/cases/tests/test_finalise_advice.py @@ -1,4 +1,3 @@ -import pytest from unittest import mock from django.urls import reverse from api.audit_trail.enums import AuditType @@ -137,7 +136,7 @@ def setUp(self): self.test_flag.save() self.application.flags.add(self.test_flag) - user = BaseUser(email="test@mail.com", first_name="John", last_name="Smith", type=UserType.SYSTEM) + user = BaseUser(email="test@mail.com", first_name="John", last_name="Smith", type=UserType.SYSTEM) # /PS-IGNORE case = self.application.get_case() @@ -212,12 +211,12 @@ def test_finalised_standard_application_with_flags_removed( @parameterized.expand(case_status for case_status in [CaseStatusEnum.WITHDRAWN, CaseStatusEnum.CLOSED]) def test_standard_application_remove_audit_and_flag_with_statuses(self, case_status): - url = reverse("applications:manage_status", kwargs={"pk": self.application.id}) + url = reverse("caseworker_applications:change_status", kwargs={"pk": self.application.id}) data = {"status": case_status} self.assertEqual(self.application.flags.count(), 2) - response = self.client.put(url, data=data, **self.gov_headers) + response = self.client.post(url, data=data, **self.gov_headers) self.application.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/api/cases/tests/test_grant_licence.py b/api/cases/tests/test_grant_licence.py index e4a6cc3f5..9ac100ac9 100644 --- a/api/cases/tests/test_grant_licence.py +++ b/api/cases/tests/test_grant_licence.py @@ -125,9 +125,9 @@ def test_grant_standard_application_licence_and_revoke( send_exporter_notifications_func.assert_called() mock_notify_licence_issue.assert_called_with(self.standard_case.get_case()) - self.change_status_url = reverse("applications:manage_status", kwargs={"pk": self.standard_case.id}) + self.change_status_url = reverse("caseworker_applications:change_status", kwargs={"pk": self.standard_case.id}) data = {"status": CaseStatusEnum.REVOKED} - response = self.client.put(self.change_status_url, data=data, **self.gov_headers) + response = self.client.post(self.change_status_url, data=data, **self.gov_headers) self.standard_case.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -168,9 +168,9 @@ def test_grant_standard_application_licence_and_suspend( send_exporter_notifications_func.assert_called() mock_notify_licence_issue.assert_called_with(self.standard_case.get_case()) - self.change_status_url = reverse("applications:manage_status", kwargs={"pk": self.standard_case.id}) + self.change_status_url = reverse("caseworker_applications:change_status", kwargs={"pk": self.standard_case.id}) data = {"status": CaseStatusEnum.SUSPENDED} - response = self.client.put(self.change_status_url, data=data, **self.gov_headers) + response = self.client.post(self.change_status_url, data=data, **self.gov_headers) self.standard_case.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/api/cases/tests/test_is_recently_updated.py b/api/cases/tests/test_is_recently_updated.py index 181080de4..d18eb5aa7 100644 --- a/api/cases/tests/test_is_recently_updated.py +++ b/api/cases/tests/test_is_recently_updated.py @@ -33,7 +33,9 @@ def test_recently_updated(self): # GOV user updates the case (changes the status of it) data = {"status": CaseStatusEnum.RESUBMITTED} - self.client.put(reverse("applications:manage_status", kwargs={"pk": case.id}), data=data, **self.gov_headers) + self.client.post( + reverse("caseworker_applications:change_status", kwargs={"pk": case.id}), data=data, **self.gov_headers + ) response = self.client.get(self.url, **self.gov_headers) response_data = response.json()["results"]["cases"][0] @@ -47,8 +49,8 @@ def test_exporter_user_cannot_trigger_recently_updated(self): # Exporter user updates the case (changes the status of it) data = {"status": CaseStatusEnum.APPLICANT_EDITING} - self.client.put( - reverse("applications:manage_status", kwargs={"pk": case.id}), data=data, **self.exporter_headers + self.client.post( + reverse("caseworker_applications:change_status", kwargs={"pk": case.id}), data=data, **self.exporter_headers ) response = self.client.get(self.url, **self.gov_headers) diff --git a/api/gov_users/tests/test_gov_user_notifications.py b/api/gov_users/tests/test_gov_user_notifications.py index c6ada47e9..bd9ed17dc 100644 --- a/api/gov_users/tests/test_gov_user_notifications.py +++ b/api/gov_users/tests/test_gov_user_notifications.py @@ -63,10 +63,10 @@ def test_edit_application_as_gov_user_does_not_create_an_audit_notification_succ prev_case_audit_notification_count = GovNotification.objects.filter( user=self.gov_user.baseuser_ptr, content_type=self.audit_content_type, case=self.case ).count() - url = reverse("applications:manage_status", kwargs={"pk": self.case.id}) + url = reverse("caseworker_applications:change_status", kwargs={"pk": self.case.id}) data = {"status": "under_review"} - response = self.client.put(url, data, **self.gov_headers) + response = self.client.post(url, data, **self.gov_headers) self.case.refresh_from_db() case_audit_notification_count = GovNotification.objects.filter( user=self.gov_user.baseuser_ptr, content_type=self.audit_content_type, case=self.case diff --git a/api/licences/tests/test_api_to_hmrc_integration.py b/api/licences/tests/test_api_to_hmrc_integration.py index ff7676bad..c47e29207 100644 --- a/api/licences/tests/test_api_to_hmrc_integration.py +++ b/api/licences/tests/test_api_to_hmrc_integration.py @@ -570,8 +570,8 @@ def test_surrender_licence_success(self, request): expected_insert_json = HMRCIntegrationLicenceSerializer(standard_licence).data expected_insert_json["action"] = HMRCIntegrationActionEnum.CANCEL - url = reverse("applications:manage_status", kwargs={"pk": standard_application.id}) - response = self.client.put(url, data={"status": CaseStatusEnum.SURRENDERED}, **self.exporter_headers) + url = reverse("exporter_applications:change_status", kwargs={"pk": standard_application.id}) + response = self.client.post(url, data={"status": CaseStatusEnum.SURRENDERED}, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) request.assert_called_once_with( @@ -599,8 +599,8 @@ def test_revoke_licence_success(self, request): expected_insert_json = HMRCIntegrationLicenceSerializer(standard_licence).data expected_insert_json["action"] = HMRCIntegrationActionEnum.CANCEL - url = reverse("applications:manage_status", kwargs={"pk": standard_application.id}) - response = self.client.put(url, data={"status": CaseStatusEnum.REVOKED}, **self.gov_headers) + url = reverse("caseworker_applications:change_status", kwargs={"pk": standard_application.id}) + response = self.client.post(url, data={"status": CaseStatusEnum.REVOKED}, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) request.assert_called_once_with(