Exemplo n.º 1
0
    def change_status(self,
                      user,
                      status: CaseStatus,
                      note: Optional[str] = ""):
        """
        Sets the status for the case, runs validation on various parameters,
        creates audit entries and also runs flagging and automation rules
        """
        from api.cases.helpers import can_set_status
        from api.audit_trail import service as audit_trail_service
        from api.applications.libraries.application_helpers import can_status_be_set_by_gov_user
        from api.workflow.automation import run_routing_rules
        from api.workflow.flagging_rules_automation import apply_flagging_rules_to_case
        from api.licences.helpers import update_licence_status

        old_status = self.status.status

        # Only allow the final decision if the user has the MANAGE_FINAL_ADVICE permission
        if status.status == CaseStatusEnum.FINALISED:
            assert_user_has_permission(
                user.govuser, GovPermissions.MANAGE_LICENCE_FINAL_ADVICE)

        if not can_set_status(self, status.status):
            raise ValidationError({"status": [strings.Statuses.BAD_STATUS]})

        if not can_status_be_set_by_gov_user(
                user.govuser, old_status, status.status, is_mod=False):
            raise ValidationError({"status": ["Status cannot be set by user"]})

        self.status = status
        self.save()

        # Update licence status if applicable case status change
        update_licence_status(self, status.status)

        if CaseStatusEnum.is_terminal(
                old_status) and not CaseStatusEnum.is_terminal(
                    self.status.status):
            apply_flagging_rules_to_case(self)

        audit_trail_service.create(
            actor=user,
            verb=AuditType.UPDATED_STATUS,
            target=self,
            payload={
                "status": {
                    "new": CaseStatusEnum.get_text(self.status.status),
                    "old": old_status
                },
                "additional_text": note,
            },
        )

        if old_status != self.status.status:
            run_routing_rules(case=self, keep_status=True)
Exemplo n.º 2
0
    def put(self, request, pk):
        """ Respond to a control list classification."""
        assert_user_has_permission(request.user.govuser, constants.GovPermissions.RESPOND_PV_GRADING)

        query = get_exporter_query(pk)
        if CaseStatusEnum.is_terminal(query.status.status):
            return JsonResponse(
                data={"errors": [strings.Applications.Generic.TERMINAL_CASE_CANNOT_PERFORM_OPERATION_ERROR]},
                status=status.HTTP_400_BAD_REQUEST,
            )

        data = request.data

        pv_grading_good_serializer = PVGradingResponseSerializer(data=data)

        if pv_grading_good_serializer.is_valid():
            if not str_to_bool(data.get("validate_only")):
                pv_grading = pv_grading_good_serializer.save()
                self.update_query_and_good(query, data, pv_grading)
                self.generate_audit_trail(request.user, query)

                # Send a notification to the user
                for user_relationship in UserOrganisationRelationship.objects.filter(organisation=query.organisation):
                    user_relationship.send_notification(content_object=query, case=query)

                return JsonResponse(
                    data={"pv_grading_query": pv_grading_good_serializer.data}, status=status.HTTP_200_OK,
                )

            return JsonResponse(data={"pv_grading_query": data}, status=status.HTTP_200_OK)

        return JsonResponse(data={"errors": pv_grading_good_serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
Exemplo n.º 3
0
def can_status_be_set_by_gov_user(user: GovUser, original_status: str,
                                  new_status: str, is_mod: bool) -> bool:
    """
    Check that a status can be set by a gov user. Gov users can not set a case's status to
    `Applicant editing`. They also cannot set a case's status to `Finalised` or open a closed case
    without additional permissions.
    """
    if new_status == CaseStatusEnum.APPLICANT_EDITING:
        return False

    elif CaseStatusEnum.is_terminal(
            original_status) and not assert_user_has_permission(
                user, GovPermissions.REOPEN_CLOSED_CASES):
        return False

    if new_status == CaseStatusEnum.FINALISED:
        if is_mod:
            if not assert_user_has_permission(
                    user, GovPermissions.MANAGE_CLEARANCE_FINAL_ADVICE):
                return False
        else:
            if not assert_user_has_permission(
                    user, GovPermissions.MANAGE_LICENCE_FINAL_ADVICE):
                return False
    return True
    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}",
                },
            )
Exemplo n.º 5
0
    def save(self, *args, **kwargs):
        if CaseStatusEnum.is_terminal(self.status.status):
            self.case_officer = None
            if self.pk:
                self.queues.clear()
            CaseAssignment.objects.filter(case=self).delete()

        if not self.reference_code and self.status != get_case_status_by_status(
                CaseStatusEnum.DRAFT):
            self.reference_code = generate_reference_code(self)

        super(Case, self).save(*args, **kwargs)
Exemplo n.º 6
0
def can_status_be_set_by_exporter_user(original_status: str,
                                       new_status: str) -> bool:
    """ Check that a status can be set by an exporter user. Exporter users cannot withdraw an application
    that is already in a terminal state and they cannot set an application to `Applicant editing` if the
    application is read only.
    """
    if new_status == CaseStatusEnum.WITHDRAWN:
        if CaseStatusEnum.is_terminal(original_status):
            return False
    elif new_status == CaseStatusEnum.SURRENDERED:
        if original_status != CaseStatusEnum.FINALISED:
            return False
    elif CaseStatusEnum.is_read_only(
            original_status) or new_status != CaseStatusEnum.APPLICANT_EDITING:
        return False

    return True
Exemplo n.º 7
0
    def test_apply_flagging_rule_to_open_cases(self, case_status):
        if case_status == CaseStatusEnum.DRAFT:
            case = self.create_draft_standard_application(self.organisation)
        else:
            case = self.create_standard_application_case(self.organisation)
            case.status = get_case_status_by_status(case_status)
            case.save()

        flag = self.create_flag(case.case_type.reference, FlagLevels.CASE, self.team)
        flagging_rule = self.create_flagging_rule(FlagLevels.CASE, self.team, flag, [case.case_type.reference])

        apply_flagging_rule_to_all_open_cases(flagging_rule)

        case.refresh_from_db()

        if CaseStatusEnum.is_terminal(case_status) or case_status == CaseStatusEnum.DRAFT:
            self.assertNotIn(flag, case.flags.all())
        else:
            self.assertIn(flag, case.flags.all())

    def test_apply_verified_goods_only_flagging_rule_to_open_cases_failure(self):
        """ Test flag not applied to good when flagging rule is for verified goods only. """
        case = self.create_standard_application_case(self.organisation)

        flag = self.create_flag("good flag", FlagLevels.GOOD, self.team)
        flagging_rule = self.create_flagging_rule(
            FlagLevels.GOOD, self.team, flag, [case.case_type.reference], is_for_verified_goods_only=True
        )
        good = GoodOnApplication.objects.filter(application_id=case.id).first().good

        apply_flagging_rule_to_all_open_cases(flagging_rule)
Exemplo n.º 8
0
    def put(self, request, pk):
        """ Respond to a control list classification."""
        assert_user_has_permission(request.user.govuser, constants.GovPermissions.REVIEW_GOODS)

        query = get_exporter_query(pk)
        if CaseStatusEnum.is_terminal(query.status.status):
            return JsonResponse(
                data={"errors": [strings.Applications.Generic.TERMINAL_CASE_CANNOT_PERFORM_OPERATION_ERROR]},
                status=status.HTTP_400_BAD_REQUEST,
            )

        data = request.data

        clc_good_serializer = ClcControlGoodSerializer(query.good, data=data)

        if clc_good_serializer.is_valid():
            if not str_to_bool(data.get("validate_only")):
                previous_control_list_entries = list(
                    query.good.control_list_entries.values_list("rating", flat=True)
                ) or [strings.Goods.GOOD_NO_CONTROL_CODE]

                clc_good_serializer.save()
                query.clc_responded = True
                query.save()

                if clc_good_serializer.validated_data.get("control_list_entries"):
                    values = clc_good_serializer.validated_data["control_list_entries"]
                    new_control_list_entries = [clc.rating for clc in values]
                else:
                    new_control_list_entries = [strings.Goods.GOOD_NO_CONTROL_CODE]

                if new_control_list_entries != previous_control_list_entries:
                    audit_trail_service.create(
                        actor=request.user,
                        verb=AuditType.GOOD_REVIEWED,
                        action_object=query.good,
                        target=query.get_case(),
                        payload={
                            "good_name": query.good.description,
                            "old_control_list_entry": previous_control_list_entries,
                            "new_control_list_entry": new_control_list_entries,
                        },
                    )

                flag = Flag.objects.get(id=SystemFlags.GOOD_CLC_QUERY_ID)
                query.good.flags.remove(flag)
                query.good.status = GoodStatus.VERIFIED
                query.good.save()
                apply_flagging_rules_to_case(query)

                audit_trail_service.create(
                    actor=request.user, verb=AuditType.CLC_RESPONSE, action_object=query.good, target=query.get_case(),
                )

                # Send a notification to the user
                for user_relationship in UserOrganisationRelationship.objects.filter(organisation=query.organisation):
                    user_relationship.send_notification(content_object=query, case=query)

                return JsonResponse(
                    data={"control_list_classification_query": clc_good_serializer.data}, status=status.HTTP_200_OK
                )

            return JsonResponse(data={"control_list_classification_query": data}, status=status.HTTP_200_OK)

        return JsonResponse(data={"errors": clc_good_serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
Exemplo n.º 9
0
    def post(self, request, case_pk):
        if CaseStatusEnum.is_terminal(self.application.status.status):
            return JsonResponse(
                data={
                    "errors": {
                        "error": [
                            strings.Applications.Generic.
                            TERMINAL_CASE_CANNOT_PERFORM_OPERATION_ERROR
                        ]
                    }
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        case = get_case(case_pk)

        for good in self.get_queryset():
            serializer = self.get_serializer(good)
            serializer.is_valid(raise_exception=True)
            old_control_list_entries = list(
                good.control_list_entries.values_list("rating", flat=True))
            old_is_controlled = good.is_good_controlled
            serializer.save()
            if "control_list_entries" in serializer.data or "is_good_controlled" in serializer.data:
                new_control_list_entries = [
                    item.rating for item in
                    serializer.validated_data["control_list_entries"]
                ]
                new_is_controlled = serializer.validated_data[
                    "is_good_controlled"]
                if new_control_list_entries != old_control_list_entries or new_is_controlled != old_is_controlled:
                    if isinstance(good, GoodsType):
                        good.flags.clear()
                    else:
                        good.good.flags.clear()
                    default_control = [strings.Goods.GOOD_NO_CONTROL_CODE]
                    audit_trail_service.create(
                        actor=request.user,
                        verb=AuditType.GOOD_REVIEWED,
                        action_object=good,
                        target=case,
                        payload={
                            "good_name":
                            good.description,
                            "new_control_list_entry":
                            new_control_list_entries or default_control,
                            "old_control_list_entry":
                            old_control_list_entries or default_control,
                            "old_is_good_controlled":
                            "Yes" if old_is_controlled else "No",
                            "new_is_good_controlled":
                            "Yes" if new_is_controlled else "No",
                            "additional_text":
                            serializer.validated_data["comment"],
                            "is_precedent":
                            serializer.validated_data.get(
                                "is_precedent", False),
                        },
                    )
        apply_good_flagging_rules_for_case(case)
        return JsonResponse(data={}, status=status.HTTP_200_OK)
Exemplo n.º 10
0
    def put(self, request, pk):
        application = get_application(pk)
        is_licence_application = application.case_type.sub_type != CaseTypeSubTypeEnum.EXHIBITION

        data = deepcopy(request.data)

        if data["status"] == CaseStatusEnum.FINALISED:
            return JsonResponse(
                data={
                    "errors": [
                        strings.Applications.Generic.Finalise.Error.
                        SET_FINALISED
                    ]
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        if not can_set_status(application, data["status"]):
            raise ValidationError({"status": [strings.Statuses.BAD_STATUS]})

        if hasattr(request.user, "exporteruser"):
            if get_request_user_organisation_id(
                    request) != application.organisation.id:
                raise PermissionDenied()

            if not can_status_be_set_by_exporter_user(
                    application.status.status, data["status"]):
                return JsonResponse(
                    data={
                        "errors": [
                            strings.Applications.Generic.Finalise.Error.
                            EXPORTER_SET_STATUS
                        ]
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )
        else:
            if not can_status_be_set_by_gov_user(
                    request.user.govuser, application.status.status,
                    data["status"], is_licence_application):
                return JsonResponse(
                    data={
                        "errors": [
                            strings.Applications.Generic.Finalise.Error.
                            GOV_SET_STATUS
                        ]
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

        update_licence_status(application, data["status"])

        case_status = get_case_status_by_status(data["status"])
        data["status"] = str(case_status.pk)
        old_status = application.status

        serializer = get_application_update_serializer(application)
        serializer = serializer(application, data=data, partial=True)

        if not serializer.is_valid():
            return JsonResponse(data={"errors": serializer.errors},
                                status=status.HTTP_400_BAD_REQUEST)

        application = serializer.save()

        if CaseStatusEnum.is_terminal(
                old_status.status) and not CaseStatusEnum.is_terminal(
                    application.status.status):
            # we reapply flagging rules if the status is reopened from a terminal state
            apply_flagging_rules_to_case(application)

        audit_trail_service.create(
            actor=request.user,
            verb=AuditType.UPDATED_STATUS,
            target=application.get_case(),
            payload={
                "status": {
                    "new": CaseStatusEnum.get_text(case_status.status),
                    "old": CaseStatusEnum.get_text(old_status.status),
                },
                "additional_text": data.get("note"),
            },
        )

        # Case routing rules
        if old_status != application.status:
            run_routing_rules(case=application, keep_status=True)

        if CaseStatusEnum.is_terminal(application.status.status):
            gov_notify_service.send_email(
                email_address=application.submitted_by.email,
                template_type=TemplateType.APPLICATION_STATUS,
                data=ApplicationStatusEmailData(
                    case_reference=application.reference_code,
                    application_reference=application.name,
                    link=
                    f"{settings.EXPORTER_BASE_URL}/applications/{application.id}",
                ),
            )

        data = get_application_view_serializer(application)(
            application, context={
                "user_type": request.user.type
            }).data

        if application.case_type.sub_type == CaseTypeSubTypeEnum.OPEN:
            data["destinations"] = get_destinations(
                application.id, user_type=request.user.type)

        return JsonResponse(
            data={"data": data},
            status=status.HTTP_200_OK,
        )
Exemplo n.º 11
0

def can_set_status(case, status):
    """
    Returns true or false depending on different case conditions
    """
    from api.compliance.models import ComplianceVisitCase
    from api.compliance.helpers import compliance_visit_case_complete

    reference_type = case.case_type.reference

    if reference_type == CaseTypeReferenceEnum.COMP_SITE and status not in CaseStatusEnum.compliance_site_statuses:
        return False
    elif reference_type == CaseTypeReferenceEnum.COMP_VISIT and status not in CaseStatusEnum.compliance_visit_statuses:
        return False

    if case.case_type.reference == CaseTypeReferenceEnum.COMP_VISIT and CaseStatusEnum.is_terminal(
            status):
        comp_case = ComplianceVisitCase.objects.get(id=case.id)
        if not compliance_visit_case_complete(comp_case):
            return False

    if reference_type == CaseTypeReferenceEnum.CRE and status not in [
            CaseStatusEnum.CLOSED,
            CaseStatusEnum.SUBMITTED,
            CaseStatusEnum.RESUBMITTED,
    ]:
        return False

    return True