def test_expense_wf(self): # Setup default workflow install_expense_workflow() ABR = Consultant.objects.get(trigramme="ABR") TCO = Consultant.objects.get(trigramme="TCO") tco = TCO.getUser() abr = ABR.getUser() fla = User.objects.get(username="******") category = ExpenseCategory.objects.create(name="repas") e = Expense.objects.create(user=tco, description="une grande bouffe", category=category, amount=123, creation_date=date.today(), expense_date=date.today()) self.assertEqual(wf.get_state(e), None) wf.set_initial_state(e) self.assertNotEqual(wf.get_state(e), None) # Now wf is setup # state = requested self.assertEqual(len(wf.get_allowed_transitions(e, tco)), 0) # No transition allowed for user self.assertEqual(len(wf.get_allowed_transitions(e, fla)), 0) # No transition allowed for paymaster self.assertEqual(len(wf.get_allowed_transitions(e, abr)), 2) # But for his manager accept/reject # Reject it reject = Transition.objects.get(name="reject") self.assertTrue(wf.do_transition(e, reject, abr)) for user in (tco, abr, fla): self.assertEqual(len(wf.get_allowed_transitions(e, user)), 0) # No transition allowed # Validate it wf.set_initial_state(e) # Returns to requested state validate = Transition.objects.get(name="validate") self.assertTrue(wf.do_transition(e, validate, abr)) for user in (tco, abr): self.assertEqual(len(wf.get_allowed_transitions(e, user)), 0) # No transition allowed self.assertEqual(len(wf.get_allowed_transitions(e, fla)), 2) # Except paymaster accept/ask info # Ask information ask = Transition.objects.get(name="ask information") self.assertTrue(wf.do_transition(e, ask, fla)) self.assertTrue(perm.has_permission(e, tco, "expense_edit")) wf.set_initial_state(e) # Returns to requested state self.assertEqual(len(wf.get_allowed_transitions(e, tco)), 0) # No transition allowed for user self.assertTrue(wf.do_transition(e, validate, abr)) # Validate it again # Check it control = Transition.objects.get(name="control") self.assertTrue(wf.do_transition(e, control, fla)) for user in (tco, abr, fla): self.assertEqual(len(wf.get_allowed_transitions(e, user)), 0) # No transition allowed # Create a payment for that expense expensePayment = ExpensePayment(payment_date=date.today()) expensePayment.save() e.expensePayment = expensePayment e.save() self.assertEqual(expensePayment.user(), tco) self.assertEqual(expensePayment.amount(), 123)
def state(self): """expense state according to expense workflow""" state = wf.get_state(self) if state: return state.name else: return _("unknown")
def get_results(self, request, term, page, context): """ Override standard method to filter according to workflow state. Cannot be done in a simple query set...""" qs = copy.deepcopy(self.get_queryset()) params = self.prepare_qs_params(request, term, self.search_fields) if self.max_results: min_ = (page - 1) * self.max_results max_ = min_ + self.max_results + 1 # fetching one extra row to check if it has more rows. res = list(qs.filter(*params['or'], **params['and'])[min_:max_]) has_more = len(res) == (max_ - min_) if has_more: res = res[:-1] else: res = list(qs.filter(*params['or'], **params['and'])) has_more = False res = [ expense for expense in res if wf.get_state(expense).transitions.count() == 0 ] res = [(getattr(obj, self.to_field_name), self.label_from_instance(obj), self.extra_data_from_instance(obj)) for obj in res] return ( NO_ERR_RESP, has_more, res, )
def get_queryset(self): expenses = Expense.objects.filter(workflow_in_progress=True, corporate_card=False, expensePayment=None) # Filter on expenses that really terminate their workflow. expenses_id = [expense.id for expense in expenses if wf.get_state(expense).transitions.count() == 0] # Recreate a queryset that match thoses expenses expenses = Expense.objects.filter(id__in=expenses_id) return expenses
def expense_payments(request, expense_payment_id=None): readOnly = False if not request.user.groups.filter(name="expense_paymaster").exists() and not request.user.is_superuser: readOnly = True try: if expense_payment_id: expensePayment = ExpensePayment.objects.get(id=expense_payment_id) except ExpensePayment.DoesNotExist: messages.add_message(request, messages.ERROR, _("Expense payment %s does not exist" % expense_payment_id)) expense_payment_id = None expensePayment = None if readOnly: expensesToPay = [] else: expensesToPay = Expense.objects.filter(workflow_in_progress=True, corporate_card=False, expensePayment=None) expensesToPay = [expense for expense in expensesToPay if wf.get_state(expense).transitions.count() == 0] if request.method == "POST": if readOnly: # A bad user is playing with urls... return HttpResponseRedirect(urlresolvers.reverse("forbiden")) form = ExpensePaymentForm(request.POST) if form.is_valid(): if expense_payment_id: expensePayment = ExpensePayment.objects.get(id=expense_payment_id) expensePayment.payment_date = form.cleaned_data["payment_date"] else: expensePayment = ExpensePayment(payment_date=form.cleaned_data["payment_date"]) expensePayment.save() for expense in Expense.objects.filter(expensePayment=expensePayment): expense.expensePayment = None # Remove any previous association expense.save() if form.cleaned_data["expenses"]: for expense in form.cleaned_data["expenses"]: expense.expensePayment = expensePayment expense.workflow_in_progress = False expense.save() return HttpResponseRedirect(urlresolvers.reverse("expense.views.expense_payments")) else: print "form is not valid" else: if expense_payment_id: expensePayment = ExpensePayment.objects.get(id=expense_payment_id) form = ExpensePaymentForm({"expenses": list(Expense.objects.filter(expensePayment=expensePayment).values_list("id", flat=True)), "payment_date": expensePayment.payment_date}) # A form that edit current expense payment else: form = ExpensePaymentForm(initial={"payment_date": date.today()}) # An unbound form return render(request, "expense/expense_payments.html", {"modify_expense_payment": bool(expense_payment_id), "data_url": urlresolvers.reverse('expense_payment_table_DT'), "data_options": ''' "pageLength": 25, "order": [[0, "desc"]], "columnDefs": [{ "orderable": false, "targets": [1, 2, 4] }]''', "expense_to_pay_table": ExpenseTable(expensesToPay), "read_only": readOnly, "form": form, "user": request.user})
def state(self): """ Get the state in workflow for the instance of the workflow managed model :return: the state of the managed instance :rtype: `workflows.models.State <http://packages.python.org/django-workflows/api.html#workflows.models.State>`_ """ return get_state(self)
def join_group_subscription(user, group): """ add to the group object all information about subscriptions """ group.pending_subscriptions = [] subscriptions = SubscriptionRequest.objects.filter(group=group.pk) for subscription in subscriptions: if get_state(subscription).name == 'Pending': group.pending_subscriptions.append(subscription) try: subscription = SubscriptionRequest.objects.get(group=group.pk, user=user) except ObjectDoesNotExist: subscription = None if subscription and get_state(subscription).name == 'Pending': group.is_subscription_pending = True elif subscription and get_state(subscription).name == 'Refused': group.is_subscription_refused = True
def set_workflow(request): workflow = Workflow.objects.get(name="Standard") name=request.REQUEST.get('name') from workflows.utils import set_workflow project=Project.objects.get(name=name) set_workflow(project, workflow) state=get_state(project) ts=state.get_allowed_transitions
def _filter_applications_on_state(self, applications, state): ''' This function will filter the list of applications, returning only those applications that are in the state specified. ''' if not isinstance(applications, list) or not isinstance(state, State): raise AttributeError filtered = [] for application in applications: if get_state(application) == state: filtered.append(application) return filtered
def get_results(self, request, term, page, context): """ Override standard method to filter according to workflow state. Cannot be done in a simple query set...""" qs = copy.deepcopy(self.get_queryset()) params = self.prepare_qs_params(request, term, self.search_fields) if self.max_results: min_ = (page - 1) * self.max_results max_ = min_ + self.max_results + 1 # fetching one extra row to check if it has more rows. res = list(qs.filter(*params['or'], **params['and'])[min_:max_]) has_more = len(res) == (max_ - min_) if has_more: res = res[:-1] else: res = list(qs.filter(*params['or'], **params['and'])) has_more = False res = [expense for expense in res if wf.get_state(expense).transitions.count() == 0] res = [(getattr(obj, self.to_field_name), self.label_from_instance(obj), self.extra_data_from_instance(obj)) for obj in res] return (NO_ERR_RESP, has_more, res,)
def update_expense_state(request, expense_id, transition_id): """Do workflow transition for that expense.""" error = False message = "" try: expense = Expense.objects.get(id=expense_id) if expense.user == request.user and not utils.has_role(request.user, "expense administrator"): message = _("You cannot manage your own expense !") error = True except Expense.DoesNotExist: message = _("Expense %s does not exist" % expense_id) error = True if not error: try: transition = Transition.objects.get(id=transition_id) except Transition.DoesNotExist: message = ("Transition %s does not exist" % transition_id) error = True if wf.do_transition(expense, transition, request.user): message = _("Successfully update expense") # Prune expense in terminal state (no more transition) and without payment (ie paid ith corporate card) # Expense that need to be paid are pruned during payment process. if expense.corporate_card and wf.get_state(expense).transitions.count() == 0: expense.workflow_in_progress = False expense.save() else: message = _("You cannot do this transition") error = True response = {"message": message, "expense_id": expense_id, "error": error} return HttpResponse(json.dumps(response), content_type="application/json")
def expense_payments(request, expense_payment_id=None): readOnly = False if not request.user.groups.filter(name="expense_paymaster").exists() and not request.user.is_superuser: readOnly = True try: if expense_payment_id: expensePayment = ExpensePayment.objects.get(id=expense_payment_id) except ExpensePayment.DoesNotExist: messages.add_message(request, messages.ERROR, _("Expense payment %s does not exist" % expense_payment_id)) expense_payment_id = None expensePayment = None if readOnly: expensesToPay = [] else: expensesToPay = Expense.objects.filter(workflow_in_progress=True, corporate_card=False, expensePayment=None) expensesToPay = [expense for expense in expensesToPay if wf.get_state(expense).transitions.count() == 0] try: consultant = Consultant.objects.get(trigramme__iexact=request.user.username) user_team = consultant.userTeam() except Consultant.DoesNotExist: user_team = [] expensePayments = ExpensePayment.objects.all() if not utils.has_role(request.user, "expense paymaster"): expensePayments = expensePayments.filter(Q(expense__user=request.user) | Q(expense__user__in=user_team)).distinct() if request.method == "POST": if readOnly: # A bad user is playing with urls... return HttpResponseRedirect(urlresolvers.reverse("forbiden")) form = ExpensePaymentForm(request.POST) if form.is_valid(): if expense_payment_id: expensePayment = ExpensePayment.objects.get(id=expense_payment_id) expensePayment.payment_date = form.cleaned_data["payment_date"] else: expensePayment = ExpensePayment(payment_date=form.cleaned_data["payment_date"]) expensePayment.save() for expense in Expense.objects.filter(expensePayment=expensePayment): expense.expensePayment = None # Remove any previous association expense.save() if form.cleaned_data["expenses"]: for expense in form.cleaned_data["expenses"]: expense.expensePayment = expensePayment expense.workflow_in_progress = False expense.save() return HttpResponseRedirect(urlresolvers.reverse("expense.views.expense_payments")) else: print "form is not valid" else: if expense_payment_id: expensePayment = ExpensePayment.objects.get(id=expense_payment_id) form = ExpensePaymentForm({"expenses": list(Expense.objects.filter(expensePayment=expensePayment).values_list("id", flat=True)), "payment_date": expensePayment.payment_date}) # A form that edit current expense payment else: form = ExpensePaymentForm(initial={"payment_date": date.today()}) # An unbound form return render(request, "expense/expense_payments.html", {"modify_expense_payment": bool(expense_payment_id), "expense_payment_table": ExpensePaymentTable(expensePayments), "expense_to_pay_table": ExpenseTable(expensesToPay), "read_only": readOnly, "form": form, "user": request.user})
def get_current_state(self): ''' This function returns the current state for this application. This will be useful to easily get the state of a given application in a template ''' return get_state(self)
def productionline_do_transition(user,productionline,status): # import pdb; pdb.set_trace() result = 0 #set current_operation_record status current_operation_record = productionline.current_operation_record current_operation_record.status = status current_operation_record.save() # logger.info(current_operation_record.status) transitions = get_allowed_transitions(productionline,user) # logger.info(transitions) if len(transitions) > 0: do_transition(productionline,transitions[0],user) #get current_operation_record current_state = get_state(productionline) for oper_group_record in productionline.oper_group_records.all(): for operation_record in oper_group_record.operation_records.all(): if operation_record.operation.state == current_state: current_operation_record = operation_record #set productionline current_operation_record productionline.current_operation_record = current_operation_record productionline.save() #set current_operation_record status productionline.current_operation_record.status = 2 productionline.current_operation_record.save() #set parent_operation_record status parent_operation_record = current_operation_record.parent_operation_record if parent_operation_record.status == 1: parent_operation_record.status = 2 parent_operation_record.save() #set manufacture_item.status for manufacture_item in productionline.manufacture_items.all(): manufacture_item.status = 2 manufacture_item.save() #parent_productionline do_transition parent_productionline = productionline.parent_productionline order_code = parent_productionline.current_operation_record.order_code can_do_parent_transition_tag = True for mig in parent_productionline.manu_item_groups.all(): for mi in mig.manufacture_items.all(): if mi.current_operation_record.order_code <= order_code: can_do_parent_transition_tag = False if can_do_parent_transition_tag: #set parent_current_operation_record status parent_current_operation_record = parent_productionline.current_operation_record parent_current_operation_record.status = status parent_current_operation_record.save() #do_transition parent_transitions = get_allowed_transitions(parent_productionline,user) if len(parent_transitions) > 0: do_transition(parent_productionline,parent_transitions[0],user) #get parent_current_operation_record current_state = get_state(parent_productionline) for oper_group_record in parent_productionline.oper_group_records.all(): for operation_record in oper_group_record.operation_records.all(): if operation_record.operation.state == current_state: parent_current_operation_record = operation_record #set parent_current_operation_record status parent_productionline.current_operation_record = parent_current_operation_record parent_productionline.save() #set parent_current_operation_record status parent_productionline.current_operation_record.status = 2 parent_productionline.current_operation_record.save() else: #do parent_productionline finish parent_productionline.state = 3 parent_productionline.save() return 0 else: #do parent_productionline finish productionline.state = 3 productionline.save() parent_productionline = productionline.parent_productionline can_do_parent_transition_tag = True for mig in parent_productionline.manu_item_groups.all(): for mi in mig.manufacture_items.all(): if mi.productionline.state != 3: can_do_parent_transition_tag = False if can_do_parent_transition_tag: #set parent_current_operation_record status parent_current_operation_record = parent_productionline.current_operation_record parent_current_operation_record.status = status parent_current_operation_record.save() #do parent_productionline finish parent_productionline.state = 3 parent_productionline.save() return 0 return 0 return result
def state(self): """expense state according to expense workflow""" return wf.get_state(self).name
def is_pending_institution(request, m): for institution in Institution.objects.all(): state = get_state(institution) if not(state is None or state.name == 'Accepted') and request.user.is_superuser: return True return False
def forward(self, user): """Apply default transition""" state = get_state(self) transition = DefaultTransition.objects.get(workflow=self.workflow, state=state).transition do_transition(self, transition, user)
def is_request_pending(r): state = get_state(r) if state.name == request_pending.name: return True
def get_state_for_draft(draft): if settings.USE_DB_REDESIGN_PROXY_CLASSES: return draft.get_state("draft-stream-%s" % draft.stream_id) return get_state(draft)
def get_query(self, q, request): """Only return expenses that can be paid""" expenses = super(PayableExpenseLookup, self).get_query(q, request) expenses = expenses.filter(workflow_in_progress=True, corporate_card=False, expensePayment=None) return [expense for expense in expenses if wf.get_state(expense).transitions.count() == 0]
def expenses(request, expense_id=None): """Display user expenses and expenses that he can validate""" if not request.user.groups.filter(name="expense_requester").exists(): return HttpResponseRedirect(urlresolvers.reverse("forbiden")) try: consultant = Consultant.objects.get(trigramme__iexact=request.user.username) user_team = consultant.userTeam(excludeSelf=False) except Consultant.DoesNotExist: user_team = [] try: if expense_id: expense = Expense.objects.get(id=expense_id) if not (perm.has_permission(expense, request.user, "expense_edit") and (expense.user == request.user or expense.user in user_team)): messages.add_message(request, messages.WARNING, _("You are not allowed to edit that expense")) expense_id = None expense = None except Expense.DoesNotExist: messages.add_message(request, messages.ERROR, _("Expense %s does not exist" % expense_id)) expense_id = None if request.method == "POST": if expense_id: form = ExpenseForm(request.POST, request.FILES, instance=expense) else: form = ExpenseForm(request.POST, request.FILES) if form.is_valid(): expense = form.save(commit=False) if not hasattr(expense, "user"): # Don't update user if defined (case of expense updated by manager or adminstrator) expense.user = request.user expense.creation_date = date.today() expense.save() wf.set_initial_state(expense) return HttpResponseRedirect(urlresolvers.reverse("expense.views.expenses")) else: if expense_id: form = ExpenseForm(instance=expense) # A form that edit current expense else: form = ExpenseForm(initial={"expense_date": date.today()}) # An unbound form # Get user expenses user_expenses = Expense.objects.filter(user=request.user, workflow_in_progress=True).select_related() if user_team: team_expenses = Expense.objects.filter(user__in=user_team, workflow_in_progress=True).select_related() else: team_expenses = [] # Paymaster manage all expenses if utils.has_role(request.user, "expense paymaster"): managed_expenses = Expense.objects.filter(workflow_in_progress=True).exclude(user=request.user).select_related() else: managed_expenses = team_expenses userExpenseTable = UserExpenseWorkflowTable(user_expenses) userExpenseTable.transitionsData = dict([(e.id, []) for e in user_expenses]) # Inject expense allowed transitions. Always empty for own expense userExpenseTable.expenseEditPerm = dict([(e.id, perm.has_permission(e, request.user, "expense_edit")) for e in user_expenses]) # Inject expense edit permissions RequestConfig(request, paginate={"per_page": 50}).configure(userExpenseTable) managedExpenseTable = ManagedExpenseWorkflowTable(managed_expenses) managedExpenseTable.transitionsData = dict([(e.id, e.transitions(request.user)) for e in managed_expenses]) # Inject expense allowed transitions managedExpenseTable.expenseEditPerm = dict([(e.id, perm.has_permission(e, request.user, "expense_edit")) for e in managed_expenses]) # Inject expense edit permissions RequestConfig(request, paginate={"per_page": 100}).configure(managedExpenseTable) # Prune every expense not updated since 60 days. For instance, rejected expense. for expense in Expense.objects.filter(workflow_in_progress=True, update_date__lt=(date.today() - timedelta(60))): if wf.get_state(expense).transitions.count() == 0: expense.workflow_in_progress = False expense.save() return render(request, "expense/expenses.html", {"user_expense_table": userExpenseTable, "managed_expense_table": managedExpenseTable, "modify_expense": bool(expense_id), "form": form, "user": request.user})
def group_details(request, idGroup=None, idStudy=None): #temp_fix_institution_managers() institutions = [] user_institutions = [] other_institutions = [] user_groups = [] other_groups = [] pending_institutions = [] selected_group = None studies = [] for institution in Institution.objects.all().order_by('name'): state = get_state(institution) if request.user.is_authenticated(): join_group_subscription(request.user, institution) if state is None or state.name == 'Accepted': institution.state = True institutions.append(institution) else: institution.state = False pending_institutions.append(institution) institution.studies = institution.study_set.all() institution.studies_actions = 0 if idGroup is not None and institution.pk == int(idGroup): selected_group = institution selected_group.selected_study = None selected_group.type = 'institution' try: selected_group.portal = institution.institutionportal except ObjectDoesNotExist: selected_group.portal = None for study in institution.studies: if request.user.is_authenticated(): join_group_subscription(request.user, study) institution.studies_actions += len(study.pending_subscriptions) study.studies_actions = len(study.pending_subscriptions) if idStudy is not None and study.pk == int(idStudy): selected_group.selected_study = study for dbstudy in Study.objects.all().order_by('name'): dbstudy.studies_actions = 0 if request.user.is_authenticated(): join_group_subscription(request.user, dbstudy) dbstudy.studies_actions += len(dbstudy.pending_subscriptions) studies.append(dbstudy) institutions_studies_pk = [i.pk for i in Institution.objects.all()] + [i.pk for i in Study.objects.all()] if not request.user.is_authenticated(): other_institutions = institutions other_groups = VPHShareSmartGroup.objects.filter(active=True).exclude(pk__in=institutions_studies_pk) else: for institution in institutions: if request.user in institution.user_set.all() or request.user in institution.managers.all(): user_institutions.append(institution) else: other_institutions.append(institution) for vphgroup in VPHShareSmartGroup.objects.filter(active=True).exclude(pk__in=institutions_studies_pk): if request.user.is_authenticated(): join_group_subscription(request.user, vphgroup) vphgroup.studies_actions = 0 vphgroup.state = True if idGroup is not None and vphgroup.pk == int(idGroup): selected_group = vphgroup selected_group.type = 'smart' #if request.user in vphgroup.user_set.all() or request.user in vphgroup.managers.all(): if request.user in vphgroup.managers.all(): user_groups.append(vphgroup) else: other_groups.append(vphgroup) if selected_group: exclude = [getattr(selected_group, 'pending_subscriptions', []), selected_group.user_set.all()] else: exclude = [] studyUserFinder = None if selected_group and getattr(selected_group, 'selected_study', False) : excludeFromStudy = [getattr(selected_group.selected_study, 'pending_subscriptions', []), selected_group.selected_study.user_set.all()] userList = selected_group.user_set.all() studyUserFinder = StudyUserFinder(list=userList,exclude=excludeFromStudy) return render_to_response( 'scs_groups/institutions.html', {'user_institutions': user_institutions, 'pending_institutions': pending_institutions, 'other_institutions': other_institutions, 'other_groups': other_groups, 'user_groups': user_groups, 'selected_group': selected_group, 'UserFinder': UserFinder(exclude=exclude), 'StudyUserFinder': studyUserFinder, 'Studies': studies}, RequestContext(request) )
def expense_payments(request, expense_payment_id=None): readOnly = False if not request.user.groups.filter(name="expense_paymaster").exists() and not request.user.is_superuser: readOnly = True try: if expense_payment_id: expensePayment = ExpensePayment.objects.get(id=expense_payment_id) except ExpensePayment.DoesNotExist: messages.add_message(request, messages.ERROR, _("Expense payment %s does not exist" % expense_payment_id)) expense_payment_id = None expensePayment = None if readOnly: expensesToPay = [] else: expensesToPay = Expense.objects.filter(workflow_in_progress=True, corporate_card=False, expensePayment=None) expensesToPay = [expense for expense in expensesToPay if wf.get_state(expense).transitions.count() == 0] try: consultant = Consultant.objects.get(trigramme__iexact=request.user.username) user_team = consultant.userTeam() except Consultant.DoesNotExist: user_team = [] expensePayments = ExpensePayment.objects.all() if not perm.has_role(request.user, "expense paymaster"): expensePayments = expensePayments.filter(Q(expense__user=request.user) | Q(expense__user__in=user_team)).distinct() if request.method == "POST": if readOnly: # A bad user is playing with urls... return HttpResponseRedirect(urlresolvers.reverse("forbiden")) form = ExpensePaymentForm(request.POST) if form.is_valid(): if expense_payment_id: expensePayment = ExpensePayment.objects.get(id=expense_payment_id) else: expensePayment = ExpensePayment(payment_date=form.cleaned_data["payment_date"]) expensePayment.save() for expense in Expense.objects.filter(expensePayment=expensePayment): expense.expensePayment = None # Remove any previous association expense.save() if form.cleaned_data["expenses"]: for expense_id in form.cleaned_data["expenses"]: expense = Expense.objects.get(id=expense_id) expense.expensePayment = expensePayment expense.workflow_in_progress = False expense.save() return HttpResponseRedirect(urlresolvers.reverse("expense.views.expense_payments")) else: if expense_payment_id: form = ExpensePaymentForm({"expenses": "|".join([str(e.id) for e in Expense.objects.filter(expensePayment=expensePayment)]), "payment_date": expensePayment.payment_date}) # A form that edit current expense payment else: form = ExpensePaymentForm(initial={"payment_date": date.today()}) # An unbound form return render(request, "expense/expense_payments.html", {"modify_expense_payment": bool(expense_payment_id), "expense_payment_table": ExpensePaymentTable(expensePayments), "expense_to_pay_table": ExpenseTable(expensesToPay), "read_only": readOnly, "form": form, "user": request.user})
def state_equals(obj, state): if type(state) != type([]): state = [state] return get_state(obj) in State.objects.filter(name__in=state)
def __get__(self, instance, cls): state = utils.get_state(instance) if state is None: self.__set__(instance, instance.workflow.initial_state) return instance.workflow.initial_state return state
def current_state(self): return get_state(self)