def test_F680_application(self): application = self.create_mod_clearance_application(self.organisation, CaseTypeEnum.F680) case_flag = self.create_flag("case flag", FlagLevels.CASE, self.team) self.create_flagging_rule( FlagLevels.CASE, self.team, flag=case_flag, matching_values=[application.case_type.reference] ) good = GoodOnApplication.objects.filter(application_id=application.id).first().good good_flag = self.create_flag("good flag", FlagLevels.GOOD, self.team) self.create_flagging_rule( FlagLevels.GOOD, self.team, flag=good_flag, matching_values=[good.control_list_entries.first().rating] ) party = PartyOnApplication.objects.filter(application_id=application.id).first().party destination_flag = self.create_flag("dest flag", FlagLevels.DESTINATION, self.team) self.create_flagging_rule( FlagLevels.DESTINATION, self.team, flag=destination_flag, matching_values=[party.country_id] ) self.submit_application(application) apply_flagging_rules_to_case(application) application.refresh_from_db() good.refresh_from_db() party.refresh_from_db() self.assertIn(case_flag, application.flags.all()) self.assertIn(good_flag, good.flags.all()) self.assertIn(destination_flag, party.flags.all())
def test_open_application_automation(self): application = self.create_draft_open_application(self.organisation) case_flag = self.create_flag("case flag", FlagLevels.CASE, self.team) self.create_flagging_rule( FlagLevels.CASE, self.team, flag=case_flag, matching_values=[application.case_type.reference] ) goods_type = GoodsType.objects.filter(application_id=application.id).first() good_flag = self.create_flag("good flag", FlagLevels.GOOD, self.team) self.create_flagging_rule( FlagLevels.GOOD, self.team, flag=good_flag, matching_values=[goods_type.control_list_entries.first().rating], is_for_verified_goods_only=False, ) country = CountryOnApplication.objects.filter(application_id=application.id).first().country destination_flag = self.create_flag("dest flag", FlagLevels.DESTINATION, self.team) dest_flagging_rule = self.create_flagging_rule( FlagLevels.DESTINATION, self.team, flag=destination_flag, matching_values=[country.id] ) apply_flagging_rule_to_all_open_cases(dest_flagging_rule) self.submit_application(application) apply_flagging_rules_to_case(application) application.refresh_from_db() goods_type.refresh_from_db() country.refresh_from_db() self.assertIn(case_flag, application.flags.all()) self.assertIn(good_flag, goods_type.flags.all()) self.assertIn(destination_flag, country.flags.all())
def test_hmrc_application(self): application = self.create_hmrc_query(self.organisation) case_flag = self.create_flag("case flag", FlagLevels.CASE, self.team) self.create_flagging_rule( FlagLevels.CASE, self.team, flag=case_flag, matching_values=[application.case_type.reference] ) goods_type = GoodsType.objects.filter(application_id=application.id).first() good_flag = self.create_flag("good flag", FlagLevels.GOOD, self.team) goods_type.control_list_entries.set([get_control_list_entry("ML1a")]) self.create_flagging_rule( FlagLevels.GOOD, self.team, flag=good_flag, matching_values=[goods_type.control_list_entries.first().rating] ) party = PartyOnApplication.objects.filter(application_id=application.id).first().party destination_flag = self.create_flag("dest flag", FlagLevels.DESTINATION, self.team) self.create_flagging_rule( FlagLevels.DESTINATION, self.team, flag=destination_flag, matching_values=[party.country_id] ) self.submit_application(application) apply_flagging_rules_to_case(application) application.refresh_from_db() goods_type.refresh_from_db() party.refresh_from_db() self.assertIn(case_flag, application.flags.all()) self.assertIn(good_flag, goods_type.flags.all()) self.assertIn(destination_flag, party.flags.all())
def submit_application(application: BaseApplication, user: ExporterUser = None): if not user: user = UserOrganisationRelationship.objects.filter( organisation_id=application.organisation_id).first().user application.submitted_at = timezone.localtime() application.sla_remaining_days = get_application_target_sla( application.case_type.sub_type) application.status = get_case_status_by_status( CaseStatusEnum.SUBMITTED) application.save() if application.case_type.sub_type in [ CaseTypeSubTypeEnum.STANDARD, CaseTypeSubTypeEnum.OPEN ]: set_case_flags_on_submitted_standard_or_open_application( application) add_goods_flags_to_submitted_application(application) apply_flagging_rules_to_case(application) audit_trail_service.create( actor=user.baseuser_ptr, verb=AuditType.UPDATED_STATUS, target=application.get_case(), payload={ "status": { "new": CaseStatusEnum.get_text(CaseStatusEnum.SUBMITTED), "old": CaseStatusEnum.get_text(CaseStatusEnum.DRAFT), } }, ) return application
def post(self, request): """ Create a new End User Advisory Enquiry query case instance """ data = JSONParser().parse(request) if not data.get("end_user"): data["end_user"] = {} organisation_id = get_request_user_organisation_id(request) data["organisation"] = organisation_id data["end_user"]["organisation"] = organisation_id data["end_user"]["type"] = PartyType.END_USER data["submitted_by"] = request.user serializer = EndUserAdvisoryViewSerializer(data=data) try: if serializer.is_valid(): if "validate_only" not in data or data["validate_only"] == "False": eua = serializer.save() audit_trail_service.create( actor=request.user, verb=AuditType.CREATED, action_object=eua.get_case(), payload={"status": {"new": eua.status.status}}, ) apply_flagging_rules_to_case(eua) return JsonResponse(data={"end_user_advisory": serializer.data}, status=status.HTTP_201_CREATED) else: return JsonResponse(data={}, status=status.HTTP_200_OK) return JsonResponse(data={"errors": serializer.errors}, status=status.HTTP_400_BAD_REQUEST) except serializers.ValidationError as e: return JsonResponse(data={"errors": e}, status=status.HTTP_400_BAD_REQUEST)
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)
def post(self, request): """ Create a new GoodsQuery case instance """ data = request.data good = get_good(data["good_id"]) data["organisation"] = get_request_user_organisation_id(request) is_clc_required = good.is_good_controlled is None is_pv_grading_required = good.is_pv_graded == GoodPvGraded.GRADING_REQUIRED errors = self._check_request_for_errors(good, is_clc_required, is_pv_grading_required) if errors: return JsonResponse(data={"errors": errors}, status=status.HTTP_400_BAD_REQUEST) good.status = GoodStatus.QUERY goods_query = GoodsQuery.objects.create( clc_control_list_entry=data.get("clc_control_list_entry"), clc_raised_reasons=data.get("clc_raised_reasons"), pv_grading_raised_reasons=data.get("pv_grading_raised_reasons"), good=good, organisation_id=data["organisation"], case_type_id=CaseTypeEnum.GOODS.id, status=get_starting_status(is_clc_required), submitted_at=django.utils.timezone.now(), submitted_by=request.user.exporteruser, ) # attach flags based on what's required if is_clc_required: flag = Flag.objects.get(id=SystemFlags.GOOD_CLC_QUERY_ID) goods_query.flags.add(flag) goods_query.clc_responded = False if is_pv_grading_required: flag = Flag.objects.get(id=SystemFlags.GOOD_PV_GRADING_QUERY_ID) goods_query.flags.add(flag) goods_query.pv_grading_responded = False good.save() goods_query.save() audit_trail_service.create( actor=request.user, verb=AuditType.CREATED, action_object=goods_query.get_case(), payload={"status": {"new": goods_query.status.status}}, ) apply_flagging_rules_to_case(goods_query) return JsonResponse(data={"id": goods_query.id}, status=status.HTTP_201_CREATED)
def reopen_closed_cases_for_organisation(organisation): """ Set the case status to 'Reopened due to org changes' for any cases in the organisation that have been granted a licence. """ reopened_due_to_org_changes_status = CaseStatus.objects.get( status="reopened_due_to_org_changes") applications = BaseApplication.objects.filter( organisation=organisation, status=get_case_status_by_status(CaseStatusEnum.FINALISED)) applications.update(status_id=reopened_due_to_org_changes_status) for application in applications: apply_flagging_rules_to_case(application)
def test_end_user_advisory_application(self): query = self.create_end_user_advisory("a", "v", self.organisation) case_flag = self.create_flag("case flag", FlagLevels.CASE, self.team) self.create_flagging_rule( FlagLevels.CASE, self.team, flag=case_flag, matching_values=[query.case_type.reference] ) party = query.end_user destination_flag = self.create_flag("dest flag", FlagLevels.DESTINATION, self.team) self.create_flagging_rule( FlagLevels.DESTINATION, self.team, flag=destination_flag, matching_values=[party.country_id] ) apply_flagging_rules_to_case(query) query.refresh_from_db() party.refresh_from_db() self.assertIn(case_flag, query.flags.all()) self.assertIn(destination_flag, party.flags.all())
def test_goods_query_application(self): query = self.create_clc_query("query", self.organisation) case_flag = self.create_flag("case flag", FlagLevels.CASE, self.team) self.create_flagging_rule( FlagLevels.CASE, self.team, flag=case_flag, matching_values=[query.case_type.reference] ) good = query.good good.control_list_entries.set([get_control_list_entry("ML1a")]) good_flag = self.create_flag("good flag", FlagLevels.GOOD, self.team) self.create_flagging_rule( FlagLevels.GOOD, self.team, flag=good_flag, matching_values=[good.control_list_entries.first().rating] ) apply_flagging_rules_to_case(query) query.refresh_from_db() good.refresh_from_db() self.assertIn(case_flag, query.flags.all()) self.assertIn(good_flag, good.flags.all())
def test_exhibition_application(self): application = self.create_mod_clearance_application(self.organisation, CaseTypeEnum.EXHIBITION) self.submit_application(application) case_flag = self.create_flag("case flag", FlagLevels.CASE, self.team) self.create_flagging_rule( FlagLevels.CASE, self.team, flag=case_flag, matching_values=[application.case_type.reference] ) good = GoodOnApplication.objects.filter(application_id=application.id).first().good good_flag = self.create_flag("good flag", FlagLevels.GOOD, self.team) self.create_flagging_rule( FlagLevels.GOOD, self.team, flag=good_flag, matching_values=[good.control_list_entries.first().rating] ) self.submit_application(application) apply_flagging_rules_to_case(application) application.refresh_from_db() good.refresh_from_db() self.assertIn(case_flag, application.flags.all()) self.assertIn(good_flag, good.flags.all())
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)
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, )
def put(self, request, pk): """ Submit a draft application which will set its submitted_at datetime and status before creating a case Depending on the application subtype, this will also submit the declaration of the licence """ application = get_application(pk) old_status = application.status.status if application.case_type.sub_type != CaseTypeSubTypeEnum.HMRC: assert_user_has_permission( request.user.exporteruser, ExporterPermissions.SUBMIT_LICENCE_APPLICATION, application.organisation) errors = validate_application_ready_for_submission(application) if errors: return JsonResponse(data={"errors": errors}, status=status.HTTP_400_BAD_REQUEST) # Queries are completed directly when submit is clicked on the task list # HMRC are completed when submit is clicked on the summary page (page after task list) # Applications are completed when submit is clicked on the declaration page (page after summary page) if application.case_type.sub_type in [ CaseTypeSubTypeEnum.EUA, CaseTypeSubTypeEnum.GOODS ] or (CaseTypeSubTypeEnum.HMRC and request.data.get("submit_hmrc")): application.submitted_by = request.user.exporteruser create_submitted_audit(request, application, old_status) submit_application(application) if request.data.get("submit_hmrc"): auto_generate_case_document( "application_form", application, AutoGeneratedDocuments.APPLICATION_FORM) elif application.case_type.sub_type in [ CaseTypeSubTypeEnum.STANDARD, CaseTypeSubTypeEnum.OPEN, CaseTypeSubTypeEnum.F680, CaseTypeSubTypeEnum.GIFTING, CaseTypeSubTypeEnum.EXHIBITION, ]: if request.data.get("submit_declaration"): errors = _validate_agree_to_declaration(request, errors) if errors: return JsonResponse(data={"errors": errors}, status=status.HTTP_400_BAD_REQUEST) # If a valid declaration is provided, save the application application.submitted_by = request.user.exporteruser application.agreed_to_foi = request.data.get("agreed_to_foi") application.foi_reason = request.data.get("foi_reason", "") submit_application(application) if application.case_type.sub_type in [ CaseTypeSubTypeEnum.STANDARD, CaseTypeSubTypeEnum.OPEN ]: set_case_flags_on_submitted_standard_or_open_application( application) add_goods_flags_to_submitted_application(application) apply_flagging_rules_to_case(application) create_submitted_audit(request, application, old_status) auto_generate_case_document( "application_form", application, AutoGeneratedDocuments.APPLICATION_FORM) run_routing_rules(application) # Set the sites on this application as used so their name/site records located at are no longer editable sites_on_application = SiteOnApplication.objects.filter( application=application) Site.objects.filter(id__in=sites_on_application.values_list( "site_id", flat=True)).update(is_used_on_application=True) if application.case_type.sub_type in [ CaseTypeSubTypeEnum.STANDARD, CaseTypeSubTypeEnum.OPEN, CaseTypeSubTypeEnum.HMRC, ]: if UUID(SystemFlags.ENFORCEMENT_CHECK_REQUIRED ) not in application.flags.values_list("id", flat=True): application.flags.add(SystemFlags.ENFORCEMENT_CHECK_REQUIRED) # If the user hasn't visited the optional goods to country mapping page, then no goods to country mappings will # have been saved before this point. So save mappings for all goods to all countries, which is the default if (application.case_type.sub_type == CaseTypeSubTypeEnum.OPEN and GoodsType.objects.filter(application=application, countries__isnull=True).exists()): countries_on_application = CountryOnApplication.objects.filter( application=application).values_list("country", flat=True) for goods_type in GoodsType.objects.filter(application=application, countries__isnull=True): goods_type.countries.set(countries_on_application) # Serialize for the response message serializer = get_application_view_serializer(application) serializer = serializer(application, context={"user_type": request.user.type}) application_data = serializer.data if application.case_type.sub_type == CaseTypeSubTypeEnum.OPEN: application_data["destinations"] = get_destinations( application.id, user_type=request.user.type) data = { "application": { "reference_code": application.reference_code, **application_data } } if application.reference_code: data["reference_code"] = application.reference_code return JsonResponse(data=data, status=status.HTTP_200_OK)