class CaseNotesExporterCreateTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_draft_standard_application( self.organisation) self.case = self.submit_application(self.standard_application) self.url = reverse("cases:case_notes", kwargs={"pk": self.case.id}) self.data = {"text": "Days of brutalism"} def test_create_case_note_successful(self): response = self.client.post(self.url, data=self.data, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(CaseNote.objects.count(), 1) self.assertEqual(CaseNote.objects.get().text, self.data.get("text")) self.assertEqual(CaseNote.objects.get().is_visible_to_exporter, True) @parameterized.expand([ [{}], # Empty data [{ "text": "" }], # Empty text field [{ "text": "🍌" }], # Less than two character minimum [{ "text": "🍌" * 2201 }], # More than two thousand, two hundred character maximum ]) def test_create_case_note_failure(self, data): response = self.client.post(self.url, data=data, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(CaseNote.objects.count(), 0) @parameterized.expand(CaseStatusEnum.terminal_statuses()) def test_create_case_note_case_terminal_state_failure_exporter_user( self, terminal_status): self.standard_application.status = get_case_status_by_status( terminal_status) self.standard_application.save() response = self.client.post(self.url, data=self.data, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def apply_flagging_rule_to_all_open_cases(flagging_rule: FlaggingRule): """ Takes a flagging rule and creates a relationship between it's flag and objects that meet match conditions """ if flagging_rule.status == FlagStatuses.ACTIVE and flagging_rule.flag.status == FlagStatuses.ACTIVE: # Flagging rules should only be applied to open cases draft_and_terminal_statuses = [ CaseStatusEnum.DRAFT, *CaseStatusEnum.terminal_statuses() ] open_cases = Case.objects.exclude( status__status__in=draft_and_terminal_statuses) # Apply the flagging rule to different entities depending on the rule's level if flagging_rule.level == FlagLevels.CASE: # Add flag to all open Cases open_cases = open_cases.filter( case_type__reference__in=flagging_rule.matching_values ).values_list("id", flat=True) flagging_rule.flag.cases.add(*open_cases) elif flagging_rule.level == FlagLevels.GOOD: clc_entries_of_groups = [] for group in flagging_rule.matching_groups: child_entries = get_clc_child_nodes(group) clc_entries_of_groups.extend(child_entries) matching_values = flagging_rule.matching_values + clc_entries_of_groups # excluded_values contain individual entries and groups excluded_values = [] for rating in flagging_rule.excluded_values: entries = get_clc_child_nodes(rating) excluded_values.extend(entries) # Add flag to all Goods on open Goods Queries goods_in_query = GoodsQuery.objects.filter( good__control_list_entries__rating__in=matching_values ).exclude(status__status__in=draft_and_terminal_statuses) if excluded_values: # exclusion entries - goods that doesn't contain given control list entries goods_in_query = goods_in_query.exclude( good__control_list_entries__rating__in=excluded_values) if flagging_rule.is_for_verified_goods_only: goods_in_query = goods_in_query.filter( good__status=GoodStatus.VERIFIED) goods_in_query = goods_in_query.values_list("good_id", flat=True) flagging_rule.flag.goods.add(*goods_in_query) # Add flag to all Goods Types goods_types = GoodsType.objects.filter( application_id__in=open_cases, control_list_entries__rating__in=matching_values) if excluded_values: goods_types = goods_types.exclude( application_id__in=open_cases, control_list_entries__rating__in=excluded_values) goods_types = goods_types.values_list("id", flat=True) flagging_rule.flag.goods_type.add(*goods_types) # Add flag to all open Applications goods = GoodOnApplication.objects.filter( application_id__in=open_cases, good__control_list_entries__rating__in=matching_values) if excluded_values: goods = goods.exclude( application_id__in=open_cases, good__control_list_entries__rating__in=excluded_values) if flagging_rule.is_for_verified_goods_only: goods = goods.filter(good__status=GoodStatus.VERIFIED) goods = goods.values_list("good_id", flat=True) flagging_rule.flag.goods.add(*goods) elif flagging_rule.level == FlagLevels.DESTINATION: # Add flag to all End Users on open End User Advisory Queries end_users = (EndUserAdvisoryQuery.objects.filter( end_user__country_id__in=flagging_rule.matching_values ).exclude( status__status__in=draft_and_terminal_statuses).values_list( "end_user_id", flat=True)) flagging_rule.flag.parties.add(*end_users) # Add flag to all Parties on open Applications parties = PartyOnApplication.objects.filter( application_id__in=open_cases, party__country_id__in=flagging_rule.matching_values ).values_list("party_id", flat=True) flagging_rule.flag.parties.add(*parties) countries = Country.objects.filter( id__in=flagging_rule.matching_values).values_list("id", flat=True) flagging_rule.flag.countries.add(*countries)
class GoodsVerifiedTestsStandardApplication(DataTestClient): def setUp(self): super().setUp() self.report_summary = self.create_picklist_item( "Report Summary", self.team, PicklistType.REPORT_SUMMARY, PickListStatus.ACTIVE) self.good_1 = GoodFactory( organisation=self.organisation, flags=[FlagFactory(level=FlagLevels.GOOD, team=self.team)]) self.good_2 = GoodFactory(organisation=self.organisation) role = Role(name="review_goods") role.permissions.set([constants.GovPermissions.REVIEW_GOODS.name]) role.save() self.gov_user.role = role self.gov_user.save() self.application = self.create_draft_standard_application( organisation=self.organisation) self.good_on_application_1 = GoodOnApplication.objects.create( good=self.good_1, application=self.application, quantity=10, unit=Units.NAR, value=500) self.good_on_application_2 = GoodOnApplication.objects.create( good=self.good_2, application=self.application, quantity=10, unit=Units.NAR, value=500) self.case = self.submit_application(self.application) self.url = reverse_lazy("goods:control_list_entries", kwargs={"case_pk": self.case.id}) def test_verify_multiple_goods(self): """ Post multiple goods to the endpoint, and check that the control code is updated for both """ data = { "objects": [self.good_1.pk, self.good_2.pk], "comment": "I Am Easy to Find", "report_summary": self.report_summary.pk, "control_list_entries": ["ML1a"], "is_good_controlled": True, } response = self.client.post(self.url, data, **self.gov_headers) self.assertEquals(response.status_code, status.HTTP_200_OK) verified_good_1 = Good.objects.get(pk=self.good_1.pk) verified_good_2 = Good.objects.get(pk=self.good_2.pk) self.assertEqual(verified_good_1.control_list_entries.get().rating, "ML1a") self.assertEqual(verified_good_2.control_list_entries.get().rating, "ML1a") def test_verify_multiple_goods_NLR(self): """ Post multiple goods to the endpoint, and check that the control code is not set if good is not controlled """ data = { "objects": [self.good_1.pk, self.good_2.pk], "comment": "I Am Easy to Find", "report_summary": self.report_summary.pk, "control_list_entries": ["ML1a"], "is_good_controlled": False, } response = self.client.post(self.url, data, **self.gov_headers) self.assertEquals(response.status_code, status.HTTP_200_OK) self.good_1.refresh_from_db() self.good_2.refresh_from_db() self.assertEqual(self.good_1.control_list_entries.count(), 1) self.assertEqual(self.good_2.control_list_entries.count(), 1) def test_invalid_good_pk(self): # given one of the good pk is invalid data = { "objects": [self.team.pk, self.good_1.pk], "comment": "I Am Easy to Find", "report_summary": self.report_summary.pk, "is_good_controlled": False, "control_list_entries": ["ML1b"], } # when I review the goods response = self.client.post(self.url, data, **self.gov_headers) self.assertEquals(response.status_code, 200) # then the valid good is updated verified_good = Good.objects.get(pk=self.good_1.pk) self.assertEqual(verified_good.control_list_entries.count(), 1) @parameterized.expand([ ( # legacy where frontend doesn't send is_precedent { "comment": "I Am Easy to Find", "is_good_controlled": False, "control_list_entries": [], }, False, ), ( # precedent = False { "comment": "I Am Easy to Find", "is_good_controlled": False, "control_list_entries": [], "is_precedent": False, }, False, ), ( # precedent = True { "comment": "I Am Easy to Find", "is_good_controlled": False, "control_list_entries": [], "is_precedent": True, }, True, ), ]) def test_is_precedent_is_set(self, input, expected_is_precedent): defaults = { "objects": [self.good_1.pk], "report_summary": self.report_summary.pk, } data = {**defaults, **input} # when I review the goods response = self.client.post(self.url, data, **self.gov_headers) self.assertEquals(response.status_code, 200) # then the good_on_application is updated verified_good_on_application = GoodOnApplication.objects.get( pk=self.good_on_application_1.pk) self.assertEqual(verified_good_on_application.is_precedent, expected_is_precedent) def test_standard_invalid_control_list_entries(self): """ Post multiple goods to the endpoint, and that a bad request is returned, and that flags is not updated """ data = { "objects": [self.good_1.pk, self.good_2.pk], "comment": "I Am Easy to Find", "report_summary": self.report_summary.pk, "is_good_controlled": True, "control_list_entries": ["invalid"], } response = self.client.post(self.url, data, **self.gov_headers) self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) # since it has an invalid control code, flags should not be removed verified_good = Good.objects.get(pk=self.good_1.pk) self.assertTrue(is_not_verified_flag_set_on_good(verified_good)) def test_standard_controlled_good_empty_control_list_entries(self): """ Post multiple goods, with an blank control_list_entries and is controlled, for a 400 response, and no update of goods """ data = { "objects": [self.good_1.pk, self.good_2.pk], "comment": "I Am Easy to Find", "report_summary": self.report_summary.pk, "is_good_controlled": True, "control_list_entries": [], } response = self.client.post(self.url, data, **self.gov_headers) self.assertEquals(response.status_code, 200) def test_user_cannot_review_good_without_permissions(self): """ Tests that the right level of permissions are required by a gov user to review a good. """ # create a second user to adopt the super user role as it will # overwritten otherwise if we try and remove the role from the first valid_user = GovUserFactory( baseuser_ptr__email="*****@*****.**", baseuser_ptr__first_name="John", baseuser_ptr__last_name="Smith", team=self.team, role=self.super_user_role, ) valid_user.save() self.gov_user.role = self.default_role self.gov_user.save() response = self.client.post(self.url, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) @parameterized.expand(CaseStatusEnum.terminal_statuses()) def test_cannot_set_control_list_entries_when_application_in_terminal_state( self, terminal_status): self.application.status = get_case_status_by_status(terminal_status) self.application.save() data = { "objects": self.good_1.pk, "comment": "I Am Easy to Find", "report_summary": self.report_summary.pk, "control_list_entries": "ML1a", "is_good_controlled": "yes", } response = self.client.post(self.url, data, **self.gov_headers) self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
class CreateCaseAdviceTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_draft_standard_application( self.organisation) self.good = self.standard_application.goods.first().good self.standard_case = self.submit_application(self.standard_application) team_2 = Team(name="2") team_3 = Team(name="3") team_2.save() team_3.save() role = Role(name="team_level") role.permissions.set([ constants.GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name, constants.GovPermissions.MANAGE_TEAM_ADVICE.name, constants.GovPermissions.MANAGE_TEAM_CONFIRM_OWN_ADVICE.name, constants.GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name, constants.GovPermissions.MANAGE_TEAM_ADVICE.name, ]) role.save() self.gov_user.role = role self.gov_user.save() self.gov_user_2 = GovUserFactory(baseuser_ptr__email="*****@*****.**", team=team_2, role=role) self.gov_user_3 = GovUserFactory(baseuser_ptr__email="*****@*****.**", team=team_3, role=role) self.standard_case_url = reverse("cases:case_final_advice", kwargs={"pk": self.standard_case.id}) def test_advice_is_concatenated_when_final_advice_first_created(self): """ Final advice is created on first call """ self.create_advice(self.gov_user, self.standard_case, "end_user", AdviceType.PROVISO, AdviceLevel.TEAM) self.create_advice(self.gov_user_2, self.standard_case, "end_user", AdviceType.PROVISO, AdviceLevel.TEAM) self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.TEAM) self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.TEAM) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response_data), 6) end_user, good = None, None for data in response_data: if data.get("end_user"): end_user = data.get("type").get("key") elif data.get("good"): good = data.get("type").get("key") self.assertEqual(end_user, AdviceType.PROVISO) self.assertEqual(good, AdviceType.NO_LICENCE_REQUIRED) def test_create_conflicting_final_advice_shows_all_fields(self): """ The type should show conflicting if there are conflicting types in the advice on a single object """ self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.TEAM) self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.REFUSE, AdviceLevel.TEAM) self.create_advice(self.gov_user_3, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.TEAM) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"][0] self.assertEqual(response_data.get("type").get("key"), "conflicting") self.assertEqual(response_data.get("proviso"), "I am easy to proviso") self.assertCountEqual(["1a", "1b", "1c"], response_data["denial_reasons"]) def test_create_final_advice_same_advice_type_different_pv_gradings(self): """ Same advice types, different pv gradings """ inputs = [ (self.gov_user, PvGrading.UK_OFFICIAL), (self.gov_user_2, PvGrading.UK_OFFICIAL_SENSITIVE), (self.gov_user_3, PvGrading.NATO_CONFIDENTIAL), ] for user, pv_grading in inputs: self.create_advice(user, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.TEAM, pv_grading) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertEqual(response_data[0].get("type").get("key"), "proviso") self.assertEqual(response_data[0].get("proviso"), "I am easy to proviso") pv_gradings = Advice.objects.get( id=response_data[0]["id"]).collated_pv_grading self.assertIn("\n-------\n", pv_gradings) for _, pv_grading in inputs: self.assertIn(PvGrading.to_str(pv_grading), pv_gradings) def test_create_final_advice_same_advice_type_same_pv_gradings(self): """ Same advice types, same pv gradings """ pv_grading = PvGrading.OCCAR_CONFIDENTIAL inputs = [self.gov_user, self.gov_user_2, self.gov_user_3] for user in inputs: self.create_advice(user, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.TEAM, pv_grading) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertEqual(response_data[0].get("type").get("key"), "proviso") self.assertEqual(response_data[0].get("proviso"), "I am easy to proviso") pv_gradings = Advice.objects.get( id=response_data[0]["id"]).collated_pv_grading self.assertNotIn("\n-------\n", pv_gradings) self.assertIn(PvGrading.to_str(pv_grading), pv_gradings) def test_create_conflicting_final_advice_different_advice_type_same_pv_gradings( self): """ Different advice types, same pv gradings """ pv_grading = PvGrading.UK_OFFICIAL inputs = [ (self.gov_user, AdviceType.PROVISO), (self.gov_user_2, AdviceType.REFUSE), (self.gov_user_3, AdviceType.APPROVE), ] for user, advice_type in inputs: self.create_advice(user, self.standard_case, "good", advice_type, AdviceLevel.TEAM, pv_grading) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertEqual(response_data[0].get("type").get("key"), "conflicting") pv_gradings = Advice.objects.get( id=response_data[0]["id"]).collated_pv_grading self.assertNotIn("\n-------\n", pv_gradings) self.assertIn(PvGrading.to_str(pv_grading), pv_gradings) def test_create_conflicting_final_advice_different_advice_type_different_pv_gradings( self): """ Different advice types, different pv gradings """ inputs = [ (self.gov_user, AdviceType.PROVISO, PvGrading.UK_OFFICIAL), (self.gov_user_2, AdviceType.REFUSE, PvGrading.UK_OFFICIAL_SENSITIVE), (self.gov_user_3, AdviceType.APPROVE, PvGrading.NATO_CONFIDENTIAL), ] for user, advice_type, pv_grading in inputs: self.create_advice(user, self.standard_case, "good", advice_type, AdviceLevel.TEAM, pv_grading) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertEqual(response_data[0].get("type").get("key"), "conflicting") pv_gradings = Advice.objects.get( id=response_data[0]["id"]).collated_pv_grading self.assertIn("\n-------\n", pv_gradings) for _, _, pv_grading in inputs: self.assertIn(PvGrading.to_str(pv_grading), pv_gradings) # Normal restrictions on team advice items @parameterized.expand([ [AdviceType.APPROVE], [AdviceType.PROVISO], [AdviceType.REFUSE], [AdviceType.NO_LICENCE_REQUIRED], [AdviceType.NOT_APPLICABLE], ]) def test_create_end_user_case_final_advice(self, advice_type): """ Tests that a gov user can create an approval/proviso/refuse/nlr/not_applicable piece of team level advice for an end user """ data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": advice_type, "end_user": str(self.standard_application.end_user.party.id), } if advice_type == AdviceType.PROVISO: data["proviso"] = "I am easy to proviso" if advice_type == AdviceType.REFUSE: data["denial_reasons"] = ["1a", "1b", "1c"] response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertIsNotNone(Advice.objects.get()) def test_user_cannot_create_final_advice_without_permissions(self): """ Tests that the permissions are required to perform final level actions """ self.gov_user.role.permissions.set([]) self.gov_user.save() response = self.client.get(self.standard_case_url, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) response = self.client.post(self.standard_case_url, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) response = self.client.delete(self.standard_case_url, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_cannot_submit_user_level_advice_if_final_advice_exists_on_that_case( self): """ Logically blocks the submission of lower tier advice if higher tier advice exists """ FinalAdviceFactory(user=self.gov_user_2, case=self.standard_case, good=self.good) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post(reverse( "cases:user_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_can_submit_user_level_advice_if_final_advice_has_been_cleared_for_that_team_on_that_case( self, ): """ No residual data is left to block lower tier advice being submitted after a clear """ self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) self.client.delete(self.standard_case_url, **self.gov_headers) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post(reverse( "cases:user_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_cannot_submit_team_level_advice_if_final_advice_exists_for_that_team_on_that_case( self, ): """ Logically blocks the submission of lower tier advice if higher tier advice exists """ self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.FINAL) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post(reverse( "cases:team_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_can_submit_team_level_advice_if_final_advice_has_been_cleared_for_that_team_on_that_case( self, ): """ No residual data is left to block lower tier advice being submitted after a clear """ self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.TEAM) self.client.delete(self.standard_case_url, **self.gov_headers) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post(reverse( "cases:team_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_create_and_delete_audit_trail_is_created_when_the_appropriate_actions_take_place( self, ): """ Audit trail is created when clearing or combining advice """ self.create_advice(self.gov_user, self.standard_case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.TEAM) self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.REFUSE, AdviceLevel.TEAM) self.create_advice(self.gov_user_3, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.TEAM) self.client.get(self.standard_case_url, **self.gov_headers) self.client.delete(self.standard_case_url, **self.gov_headers) response = self.client.get( reverse("cases:activity", kwargs={"pk": self.standard_case.id}), **self.gov_headers) self.assertEqual(len(response.json()["activity"]), 3) def test_creating_final_advice_does_not_overwrite_user_level_advice_or_team_level_advice( self, ): """ Because of the shared parent class, make sure the parent class "save" method is overridden by the child class """ self.create_advice(self.gov_user, self.standard_case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.USER) self.create_advice(self.gov_user, self.standard_case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.TEAM) self.create_advice(self.gov_user, self.standard_case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.FINAL) self.client.get(self.standard_case_url, **self.gov_headers) self.assertEqual(Advice.objects.count(), 3) @parameterized.expand(CaseStatusEnum.terminal_statuses()) def test_cannot_create_final_advice_on_case_in_terminal_state( self, terminal_status): self.standard_application.status = get_case_status_by_status( terminal_status) self.standard_application.save() data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_get_entity_from_final_advice_model(self): data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) advice_object = Advice.objects.get( entity_id=self.standard_application.end_user.party.id) self.assertEqual(str(advice_object.end_user.id), data["end_user"]) self.assertEqual(advice_object.entity, advice_object.end_user) entity_field = Advice.ENTITY_FIELDS.copy() entity_field.remove("end_user") for field in entity_field: self.assertIsNone(getattr(advice_object, field, None)) def test_updating_final_advice_removes_draft_decision_documents(self): good = self.standard_application.goods.first().good FinalAdviceFactory( user=self.gov_user, team=self.team, case=self.standard_case, good=good, type=AdviceType.APPROVE, ) template = self.create_letter_template( case_types=[self.standard_case.case_type], decisions=[Decision.objects.get(name=AdviceType.APPROVE)], ) self.create_generated_case_document(self.standard_case, template, advice_type=AdviceType.APPROVE, visible_to_exporter=False) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "good": str(good.id), } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertFalse( GeneratedCaseDocument.objects.filter( case=self.standard_case).exists())
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)) def test_exporter_set_application_status_applicant_editing_when_in_editable_status_success(self): self.submit_application(self.standard_application) 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)) @mock.patch("gov_notify.service.client") def test_exporter_set_application_status_withdrawn_when_application_not_terminal_success(self, mock_notify_client): 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)) mock_notify_client.send_email.assert_called_with( email_address=self.standard_application.submitted_by.email, template_id=TemplateType.APPLICATION_STATUS.template_id, data={ "case_reference": self.standard_application.reference_code, "application_reference": self.standard_application.name, "link": f"{settings.EXPORTER_BASE_URL}/applications/{self.standard_application.pk}", }, ) @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( self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) ) data = {"status": case_status} with mock.patch("gov_notify.service.client") as mock_notify_client: 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) mock_notify_client.send_email.assert_called_with( email_address=self.standard_application.submitted_by.email, template_id=TemplateType.APPLICATION_STATUS.template_id, data={ "case_reference": self.standard_application.reference_code, "application_reference": self.standard_application.name, "link": f"{settings.EXPORTER_BASE_URL}/applications/{self.standard_application.pk}", }, ) @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 = self.create_licence(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.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)) @mock.patch("gov_notify.service.client") def test_exporter_set_application_status_surrendered_success(self, mock_notify_client): self.standard_application.status = get_case_status_by_status(CaseStatusEnum.FINALISED) self.standard_application.save() self.create_licence(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) 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)) mock_notify_client.send_email.assert_called_with( email_address=self.standard_application.submitted_by.email, template_id=TemplateType.APPLICATION_STATUS.template_id, data={ "case_reference": self.standard_application.reference_code, "application_reference": self.standard_application.name, "link": f"{settings.EXPORTER_BASE_URL}/applications/{self.standard_application.pk}", }, ) 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.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( self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) ) data = {"status": case_status} with mock.patch("gov_notify.service.client") as mock_notify_client: 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)) if CaseStatusEnum.is_terminal(case_status): mock_notify_client.send_email.assert_called_with( email_address=self.standard_application.submitted_by.email, template_id=TemplateType.APPLICATION_STATUS.template_id, data={ "case_reference": self.standard_application.reference_code, "application_reference": self.standard_application.name, "link": f"{settings.EXPORTER_BASE_URL}/applications/{self.standard_application.pk}", }, ) @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(), 1) self.assertEqual(self.standard_application.queues.first().id, routing_queue.id) def test_gov_user_set_hmrc_status_closed_success(self): self.hmrc_query = self.create_hmrc_query(self.organisation) self.submit_application(self.hmrc_query) data = {"status": CaseStatusEnum.CLOSED} url = reverse("applications:manage_status", kwargs={"pk": self.hmrc_query.id}) response = self.client.put(url, data=data, **self.gov_headers) self.hmrc_query.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(self.hmrc_query.status, get_case_status_by_status(CaseStatusEnum.CLOSED)) def test_gov_user_set_hmrc_invalid_status_failure(self): self.hmrc_query = self.create_hmrc_query(self.organisation) self.submit_application(self.hmrc_query) # HMRC case status can only be CLOSED, SUBMITTED or RESUBMITTED data = {"status": CaseStatusEnum.WITHDRAWN} url = reverse("applications:manage_status", kwargs={"pk": self.hmrc_query.id}) response = self.client.put(url, data=data, **self.gov_headers) self.hmrc_query.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.json().get("errors")["status"][0], strings.Statuses.BAD_STATUS) self.assertEqual( self.standard_application.status, get_case_status_by_status(CaseStatusEnum.SUBMITTED), )
class CreateCaseTeamAdviceTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_draft_standard_application(self.organisation) self.good = self.standard_application.goods.first().good self.standard_case = self.submit_application(self.standard_application) self.role = Role(name="team_level") self.role.permissions.set( [ constants.GovPermissions.MANAGE_TEAM_ADVICE.name, constants.GovPermissions.MANAGE_TEAM_CONFIRM_OWN_ADVICE.name, ] ) self.role.save() self.gov_user.role = self.role self.gov_user.save() self.gov_user_2 = GovUserFactory(baseuser_ptr__email="*****@*****.**", team=self.team, role=self.role) self.gov_user_3 = GovUserFactory(baseuser_ptr__email="*****@*****.**", team=self.team, role=self.role) self.open_application = self.create_draft_open_application(self.organisation) self.open_case = self.submit_application(self.open_application) self.standard_case_url = reverse("cases:team_advice", kwargs={"pk": self.standard_case.id}) self.open_case_url = reverse("cases:team_advice", kwargs={"pk": self.open_case.id}) def test_advice_is_concatenated_when_team_advice_first_created(self): """ Team advice is created on first call """ self.create_advice(self.gov_user, self.standard_case, "end_user", AdviceType.PROVISO, AdviceLevel.TEAM) self.create_advice(self.gov_user_2, self.standard_case, "end_user", AdviceType.PROVISO, AdviceLevel.TEAM) self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.TEAM) self.create_advice( self.gov_user_2, self.standard_case, "good", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.TEAM ) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response_data), 2) end_user, good = None, None for data in response_data: if data.get("end_user"): end_user = data.get("type").get("key") elif data.get("good"): good = data.get("type").get("key") self.assertEqual(end_user, AdviceType.PROVISO) self.assertEqual(good, AdviceType.NO_LICENCE_REQUIRED) def test_create_conflicting_team_advice_shows_all_fields(self): """ The type should show conflicting if there are conflicting types in the advice on a single object """ self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.USER) self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.REFUSE, AdviceLevel.USER) self.create_advice(self.gov_user_3, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"][0] self.assertEqual(response_data.get("type").get("key"), "conflicting") self.assertEqual(response_data.get("proviso"), "I am easy to proviso") self.assertCountEqual(["1a", "1b", "1c"], response_data["denial_reasons"]) # Normal restrictions on team advice items @parameterized.expand( [ [AdviceType.APPROVE], [AdviceType.PROVISO], [AdviceType.REFUSE], [AdviceType.NO_LICENCE_REQUIRED], [AdviceType.NOT_APPLICABLE], ] ) def test_create_end_user_case_team_advice(self, advice_type): """ Tests that a gov user can create an approval/proviso/refuse/nlr/not_applicable piece of team level advice for an end user """ data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": advice_type, "end_user": str(self.standard_application.end_user.party.id), } if advice_type == AdviceType.PROVISO: data["proviso"] = "I am easy to proviso" if advice_type == AdviceType.REFUSE: data["denial_reasons"] = ["1a", "1b", "1c"] response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) response_data = response.json()["advice"][0] self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response_data["text"], data["text"]) self.assertEqual(response_data["note"], data["note"]) self.assertEqual(response_data["type"]["key"], data["type"]) self.assertEqual(response_data["end_user"], data["end_user"]) advice_object = Advice.objects.get() # Ensure that proviso details aren't added unless the type sent is PROVISO if advice_type != AdviceType.PROVISO: self.assertEqual(response_data["proviso"], None) self.assertEqual(advice_object.proviso, None) else: self.assertEqual(response_data["proviso"], data["proviso"]) self.assertEqual(advice_object.proviso, data["proviso"]) # Ensure that refusal details aren't added unless the type sent is REFUSE if advice_type != AdviceType.REFUSE: self.assertEqual(response_data["denial_reasons"], []) self.assertEqual(advice_object.denial_reasons.count(), 0) else: self.assertCountEqual(response_data["denial_reasons"], data["denial_reasons"]) self.assertCountEqual( convert_queryset_to_str(advice_object.denial_reasons.values_list("id", flat=True)), data["denial_reasons"], ) # User must have permission to create team advice def test_user_cannot_create_team_level_advice_without_permissions(self): """ Tests that the right level of permissions are required """ self.gov_user.role.permissions.set([]) self.gov_user.save() response = self.client.get(self.standard_case_url, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) response = self.client.post(self.standard_case_url, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) response = self.client.delete(self.standard_case_url, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_advice_from_another_team_not_collated(self): """ When collating advice, only the user's team's advice should be collated """ TeamAdviceFactory(user=self.gov_user, team=self.team, case=self.standard_case, good=self.good) team_2 = TeamFactory() self.gov_user_2.team = team_2 self.gov_user_2.save() TeamAdviceFactory(user=self.gov_user_2, team=team_2, case=self.standard_case, good=self.good) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] # Team 2's advice would conflict with team 1's if both were brought in self.assertEqual(response.status_code, status.HTTP_200_OK) def test_cannot_submit_user_level_advice_if_team_advice_exists_for_that_team_on_that_case(self,): """ Logically blocks the submission of lower tier advice if higher tier advice exists """ TeamAdviceFactory(user=self.gov_user_2, team=self.team, case=self.standard_case, good=self.good) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post( reverse("cases:user_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers, data=[data] ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_can_submit_user_level_advice_if_team_advice_has_been_cleared_for_that_team_on_that_case(self,): """ No residual data is left to block lower tier advice being submitted after a clear """ self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) self.client.delete(self.standard_case_url, **self.gov_headers) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post( reverse("cases:user_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers, data=[data] ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_create_and_delete_audit_trail_is_created_when_the_appropriate_actions_take_place(self,): """ Audit trail is created when clearing or combining advice """ self.create_advice( self.gov_user, self.standard_case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.USER ) self.create_advice(self.gov_user_2, self.standard_case, "good", AdviceType.REFUSE, AdviceLevel.USER) self.create_advice(self.gov_user_3, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) self.client.get(self.standard_case_url, **self.gov_headers) self.client.delete(self.standard_case_url, **self.gov_headers) response = self.client.get(reverse("cases:activity", kwargs={"pk": self.standard_case.id}), **self.gov_headers) self.assertEqual(len(response.json()["activity"]), 3) # def test_creating_team_advice_does_not_overwrite_user_level_advice(self): # """ # Because of the shared parent class, make sure the parent class "save" method is overridden by the child class # """ # self.create_advice(self.gov_user, self.standard_case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.USER) # self.create_advice(self.gov_user, self.standard_case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.TEAM) # # self.client.get(self.standard_case_url, **self.gov_headers) # # self.assertEqual(Advice.objects.count(), 2) @parameterized.expand( [ [AdviceType.APPROVE], [AdviceType.PROVISO], [AdviceType.REFUSE], [AdviceType.NO_LICENCE_REQUIRED], [AdviceType.NOT_APPLICABLE], ] ) def test_coalesce_merges_duplicate_advice_instead_of_appending_it_simple(self, advice_type): """ Makes sure we strip out duplicates of advice on the same object """ self.create_advice(self.gov_user_2, self.standard_case, "good", advice_type, AdviceLevel.USER) self.create_advice(self.gov_user_3, self.standard_case, "good", advice_type, AdviceLevel.USER) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertNotIn("\n-------\n", response_data[0]["text"]) def test_merge_user_advice_same_advice_type_same_pv_gradings(self): """ Same advice type, same pv grading """ pv_grading = PvGrading.UK_OFFICIAL self.create_advice( self.gov_user_2, self.standard_case, "good", AdviceType.APPROVE, AdviceLevel.USER, pv_grading ) self.create_advice( self.gov_user_3, self.standard_case, "good", AdviceType.APPROVE, AdviceLevel.USER, pv_grading ) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertNotIn("\n-------\n", response_data[0]["text"]) self.assertEquals( PvGrading.to_str(pv_grading), Advice.objects.get(id=response_data[0]["id"]).collated_pv_grading ) def test_merge_user_advice_same_advice_type_different_pv_gradings(self): """ Same advice types, different pv gradings """ pv_grading = PvGrading.UK_OFFICIAL pv_grading_2 = PvGrading.UK_OFFICIAL_SENSITIVE self.create_advice( self.gov_user_2, self.standard_case, "good", AdviceType.APPROVE, AdviceLevel.USER, pv_grading ) self.create_advice( self.gov_user_3, self.standard_case, "good", AdviceType.APPROVE, AdviceLevel.USER, pv_grading_2 ) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertNotIn("\n-------\n", response_data[0]["text"]) self.assertIn("\n-------\n", Advice.objects.get(id=response_data[0]["id"]).collated_pv_grading) def test_merge_user_advice_different_advice_type_different_pv_gradings(self): """ Different advice type, different pv gradings """ pv_grading = PvGrading.UK_OFFICIAL pv_grading_2 = PvGrading.UK_OFFICIAL_SENSITIVE self.create_advice( self.gov_user_2, self.standard_case, "good", AdviceType.APPROVE, AdviceLevel.USER, pv_grading ) self.create_advice( self.gov_user_3, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER, pv_grading_2 ) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertNotIn("\n-------\n", response_data[0]["text"]) self.assertIn("\n-------\n", Advice.objects.get(id=response_data[0]["id"]).collated_pv_grading) def test_merge_user_advice_different_advice_type_same_pv_gradings(self): """ Different advice type, same pv gradings """ pv_grading = PvGrading.UK_OFFICIAL self.create_advice( self.gov_user_2, self.standard_case, "good", AdviceType.APPROVE, AdviceLevel.USER, pv_grading ) self.create_advice( self.gov_user_3, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER, pv_grading ) response = self.client.get(self.standard_case_url, **self.gov_headers) response_data = response.json()["advice"] self.assertNotIn("\n-------\n", response_data[0]["text"]) self.assertEquals( PvGrading.to_str(pv_grading), Advice.objects.get(id=response_data[0]["id"]).collated_pv_grading ) def test_when_user_advice_exists_combine_team_advice_with_confirm_own_advice_success(self,): self.role.permissions.set([constants.GovPermissions.MANAGE_TEAM_CONFIRM_OWN_ADVICE.name]) self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) response = self.client.get( reverse("cases:team_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers ) self.assertEqual(response.status_code, status.HTTP_200_OK) def test_when_user_advice_exists_combine_team_advice_without_confirm_own_advice_failure(self,): self.role.permissions.set([constants.GovPermissions.MANAGE_TEAM_ADVICE.name]) self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) response = self.client.get( reverse("cases:team_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_when_user_advice_exists_clear_team_advice_with_confirm_own_advice_success(self,): self.role.permissions.set([constants.GovPermissions.MANAGE_TEAM_CONFIRM_OWN_ADVICE.name]) self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) response = self.client.delete( reverse("cases:team_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers ) self.assertEqual(response.status_code, status.HTTP_200_OK) def test_when_user_advice_exists_clear_team_advice_without_confirm_own_advice_failure(self,): self.role.permissions.set([constants.GovPermissions.MANAGE_TEAM_ADVICE.name]) self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) response = self.client.delete( reverse("cases:team_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_when_user_advice_exists_create_team_advice_with_confirm_own_advice_success(self,): self.role.permissions.set([constants.GovPermissions.MANAGE_TEAM_CONFIRM_OWN_ADVICE.name]) self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post( reverse("cases:team_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers, data=[data] ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_when_user_advice_exists_create_team_advice_without_confirm_own_advice_failure(self,): self.role.permissions.set([constants.GovPermissions.MANAGE_TEAM_ADVICE.name]) self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.PROVISO, AdviceLevel.USER) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } response = self.client.post( reverse("cases:team_advice", kwargs={"pk": self.standard_case.id}), **self.gov_headers, data=[data] ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) @parameterized.expand(CaseStatusEnum.terminal_statuses()) def test_cannot_create_team_advice_when_case_in_terminal_state(self, terminal_status): data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.standard_application.end_user.party.id), } self.standard_application.status = get_case_status_by_status(terminal_status) self.standard_application.save() response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
class CreateCaseAdviceTests(DataTestClient): def setUp(self): super().setUp() self.application = self.create_draft_standard_application( self.organisation) self.case = self.submit_application(self.application) self.standard_case_url = reverse("cases:user_advice", kwargs={"pk": self.case.id}) @parameterized.expand([ [AdviceType.APPROVE], [AdviceType.PROVISO], [AdviceType.REFUSE], [AdviceType.NO_LICENCE_REQUIRED], [AdviceType.NOT_APPLICABLE], ]) def test_create_end_user_case_advice(self, advice_type): """ Tests that a gov user can create an approval/proviso/refuse/nlr/not_applicable piece of advice for an end user """ data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": advice_type, "end_user": str(self.application.end_user.party.id), } if advice_type == AdviceType.PROVISO: data["proviso"] = "I am easy to proviso" if advice_type == AdviceType.REFUSE: data["denial_reasons"] = ["1a", "1b", "1c"] response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertIsNotNone(Advice.objects.get()) self.assertTrue( Audit.objects.filter(verb=AuditType.CREATED_USER_ADVICE).exists()) def test_cannot_create_advice_for_two_items(self): """ Tests that a gov user cannot create a piece of advice for more than one item """ data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.application.end_user.party.id), "good": str(self.application.goods.first().id), } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(Advice.objects.count(), 0) @parameterized.expand(CaseStatusEnum.terminal_statuses()) def test_cannot_create_advice_when_case_in_terminal_state( self, terminal_status): self.application.status = get_case_status_by_status(terminal_status) self.application.save() data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.application.end_user.party.id), "good": str(self.application.goods.first().id), } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_cannot_add_footnote_without_permission(self): self.gov_user.role.permissions.remove( GovPermissions.MAINTAIN_FOOTNOTES.name) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.application.end_user.party.id), "footnote_required": "True", "footnote": "footnote", } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) response_data = response.json()["advice"][0] self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response_data["footnote"], None) self.assertEqual( Advice.objects.filter(footnote_required=None, footnote=None).count(), 1) def test_cannot_create_advice_without_footnote_and_having_permission(self): self.gov_user.role.permissions.add( GovPermissions.MAINTAIN_FOOTNOTES.name) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.application.end_user.party.id), } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_can_create_advice_with_footnote_not_required(self): self.gov_user.role.permissions.add( GovPermissions.MAINTAIN_FOOTNOTES.name) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.application.end_user.party.id), "footnote_required": "False", } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) response_data = response.json()["advice"][0] self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response_data["footnote"], None) self.assertEqual( Advice.objects.filter(footnote_required=False, footnote=None).count(), 1) def test_cannot_create_advice_with_footnote_required_and_no_footnote(self): self.gov_user.role.permissions.add( GovPermissions.MAINTAIN_FOOTNOTES.name) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.application.end_user.party.id), "footnote_required": "True", } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_can_create_advice_with_footnote_required(self): self.gov_user.role.permissions.add( GovPermissions.MAINTAIN_FOOTNOTES.name) data = { "text": "I Am Easy to Find", "note": "I Am Easy to Find", "type": AdviceType.APPROVE, "end_user": str(self.application.end_user.party.id), "footnote_required": "True", "footnote": "footnote", } response = self.client.post(self.standard_case_url, **self.gov_headers, data=[data]) response_data = response.json()["advice"][0] self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response_data["footnote"], "footnote") self.assertEqual( Advice.objects.filter(footnote_required=True, footnote=data["footnote"]).count(), 1)