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)
Beispiel #2
0
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),
        )
Beispiel #6
0
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)