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 test_standard_application_declaration_submit_success(self, upload_bytes_file_func, html_to_pdf_func): upload_bytes_file_func.return_value = None html_to_pdf_func.return_value = None self.draft.agreed_to_foi = True self.draft.save() self.assertEqual(self.draft.status.status, CaseStatusEnum.DRAFT) data = { "submit_declaration": "True", "agreed_to_declaration": "True", "agreed_to_foi": "False", "foi_reason": "Lorem ipsum", } url = reverse("applications:application_submit", kwargs={"pk": self.draft.id}) response = self.client.put(url, data, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) case = Case.objects.get(id=self.draft.id) self.assertIsNotNone(case.submitted_at) self.assertNotEqual(case.status.status, CaseStatusEnum.DRAFT) self.assertFalse(case.status.is_terminal) self.assertEqual(case.baseapplication.agreed_to_foi, False) self.assertEqual(case.baseapplication.foi_reason, "Lorem ipsum") self.assertEqual(case.submitted_by, self.exporter_user) self.assertTrue(UUID(SystemFlags.ENFORCEMENT_CHECK_REQUIRED) in case.flags.values_list("id", flat=True)) for good_on_application in GoodOnApplication.objects.filter(application=case): self.assertEqual(good_on_application.good.status, GoodStatus.SUBMITTED) case_status_audits = Audit.objects.filter(target_object_id=case.id, verb=AuditType.UPDATED_STATUS).values_list( "payload", flat=True ) self.assertIn( { "status": { "new": CaseStatusEnum.get_text(CaseStatusEnum.SUBMITTED), "old": CaseStatusEnum.get_text(CaseStatusEnum.DRAFT), } }, case_status_audits, ) # Asserting that the 'Application Form' has been autogenerated on submission of the application html_to_pdf_func.assert_called_once() upload_bytes_file_func.assert_called_once() self.assertEqual( CaseDocument.objects.filter( name__contains=AutoGeneratedDocuments.APPLICATION_FORM, type=CaseDocumentState.AUTO_GENERATED, safe=True, case=case, visible_to_exporter=False, ).count(), 1, )
def create_submitted_audit(request: Request, application: HmrcQuery, old_status: str) -> None: audit_trail_service.create( actor=request.user, verb=AuditType.UPDATED_STATUS, target=application.get_case(), payload={ "status": { "new": CaseStatusEnum.get_text(CaseStatusEnum.SUBMITTED), "old": CaseStatusEnum.get_text(old_status), } }, ignore_case_status=True, send_notification=False, )
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_rules_rerun_when_no_rules_are_applied_then_case_status_is_changed_and_audited( self): self.routing_rule_1.delete() self.case.queues.set([self.other_queue.id]) response = self.client.put(self.url, {}, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) self.case.refresh_from_db() # Case has been removed from queues self.assertEqual(self.case.queues.count(), 0) # Assert case status changed to all applicable statuses in correct order (ignoring initial submission status) exclude_payload = { "status": { "new": CaseStatusEnum.get_text(CaseStatusEnum.SUBMITTED), "old": CaseStatusEnum.get_text(CaseStatusEnum.DRAFT), } } actual_status_changes = (Audit.objects.filter( target_object_id=self.case.id, verb=AuditType.UPDATED_STATUS).exclude( payload=exclude_payload).order_by("created_at")) applicable_status_changes = CaseStatus.objects.filter( workflow_sequence__isnull=False, workflow_sequence__gt=CaseStatus.objects.get( status=CaseStatusEnum.SUBMITTED).workflow_sequence, workflow_sequence__lte=CaseStatus.objects.get( status=CaseStatusEnum.UNDER_FINAL_REVIEW).workflow_sequence, ).order_by("workflow_sequence") self.assertEqual(actual_status_changes.count(), applicable_status_changes.count()) for index in range(len(applicable_status_changes)): self.assertEqual( actual_status_changes[index].payload["status"]["new"], CaseStatusEnum.get_text( applicable_status_changes[index].status), ) # Assert the case status was finally set to Under Final Review (the last applicable status) self.assertEqual(self.case.status.status, CaseStatusEnum.UNDER_FINAL_REVIEW)
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 test_exp_set_application_status_to_submitted_when_previously_applicant_editing_success( self, upload_bytes_file_func, html_to_pdf_func ): upload_bytes_file_func.return_value = None html_to_pdf_func.return_value = None standard_application = self.create_draft_standard_application(self.organisation) self.submit_application(standard_application) standard_application.status = get_case_status_by_status(CaseStatusEnum.APPLICANT_EDITING) standard_application.save() previous_submitted_at = standard_application.submitted_at data = {"submit_declaration": True, "agreed_to_declaration": True, "agreed_to_foi": True, "foi_reason": ""} url = reverse("applications:application_submit", kwargs={"pk": standard_application.id}) response = self.client.put(url, data, **self.exporter_headers) standard_application.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertNotEqual( standard_application.status.status, CaseStatusEnum.APPLICANT_EDITING, ) self.assertFalse(standard_application.status.is_terminal) self.assertNotEqual(standard_application.submitted_at, previous_submitted_at) self.assertEqual(standard_application.agreed_to_foi, True) case_status_audits = Audit.objects.filter( target_object_id=standard_application.id, verb=AuditType.UPDATED_STATUS ).values_list("payload", flat=True) self.assertIn( { "status": { "new": CaseStatusEnum.get_text(CaseStatusEnum.SUBMITTED), "old": CaseStatusEnum.get_text(CaseStatusEnum.APPLICANT_EDITING), } }, case_status_audits, ) html_to_pdf_func.assert_called_once() upload_bytes_file_func.assert_called_once()
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, )
case=case).exists(): CaseAssignment( user=rule.user, queue=rule.queue, case=case).save(audit_user=system_user) team_rule_tier = rule.tier rules_have_been_applied = True break # If no rules have been applied, we wish to either move to the next status, or break loop if keep_status is True # or the next status is terminal if not rules_have_been_applied: next_status = get_next_status_in_workflow_sequence(case) if next_status and not next_status.is_terminal and not keep_status: old_status = case.status case.status = next_status case.save() audit_trail_service.create( actor=system_user, verb=AuditType.UPDATED_STATUS, target=case, payload={ "status": { "new": CaseStatusEnum.get_text(next_status.status), "old": CaseStatusEnum.get_text(old_status.status), } }, ) else: rules_have_been_applied = True
def get_status(self, instance): return { "key": instance.status.status, "value": CaseStatusEnum.get_text(instance.status.status) }