def update_expense_state(request, expense_id, target_state): """Do workflow transition for that expense.""" error = False message = "" try: expense = Expense.objects.get(id=expense_id) except Expense.DoesNotExist: message = _("Expense %s does not exist" % expense_id) error = True if not error: next_states = expense_next_states(expense, request.user) if target_state in next_states: expense.state = target_state if in_terminal_state(expense): expense.workflow_in_progress = False expense.save() message = _("Successfully update expense") else: message = ("Transition %s is not allowed" % target_state) error = True response = {"message": message, "expense_id": expense_id, "error": error} return HttpResponse(json.dumps(response), content_type="application/json")
def test_expense_swf(self): """Test expense simple & stupid workflow""" cache.clear() # avoid user team cache tco = User.objects.get(username="******") abr = User.objects.get(username="******") fla = User.objects.get(username="******") sre = User.objects.get(username="******") gba = User.objects.get(username="******") abo = User.objects.get(username="******") category = ExpenseCategory.objects.create(name="repas") e = Expense.objects.create(user=tco, description="une grande bouffe", category=category, amount=123, chargeable=False, creation_date=date.today(), expense_date=date.today()) # current (starting) state is requested self.assertEqual(e.state, "REQUESTED") self.assertEqual(len(expense_next_states(e, tco)), 0) # No transition allowed for user self.assertEqual(len(expense_next_states(e, fla)), 0) # No transition allowed for paymaster self.assertEqual( len(expense_next_states(e, gba)), 0) # No transition allowed for administrator of other subsidiary # But for his manager or administrator for user in (abr, sre): states = expense_next_states(e, user) self.assertIn("VALIDATED", states) self.assertIn("NEEDS_INFORMATION", states) self.assertIn("REJECTED", states) # Not yet validated, so user and his manager can edit it self.assertTrue(can_edit_expense(e, tco)) self.assertTrue(can_edit_expense(e, abr)) # Reject it e.state = "REJECT" e.save() for user in (tco, abr, fla, gba): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed self.assertTrue(can_edit_expense(e, sre)) # Except admin # Validate it e.state = "VALIDATED" e.save() for user in (tco, abr): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed # Except paymaster for control/ask info states = expense_next_states(e, fla) self.assertIn("NEEDS_INFORMATION", states) self.assertIn("CONTROLLED", states) self.assertTrue(can_edit_expense(e, sre)) # Ask information e.state = "NEEDS_INFORMATION" e.save() self.assertTrue(can_edit_expense(e, tco)) self.assertTrue(can_edit_expense(e, abr)) # Control it e.state = "CONTROLLED" e.save() for user in (tco, abr, gba): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed self.assertTrue(can_edit_expense(e, sre)) # Except admin e.corporate_card = True e.save() self.assertEqual(len(expense_next_states(e, fla)), 0) # No payment if corporate card was used # Create a payment for that expense expensePayment = ExpensePayment(payment_date=date.today()) expensePayment.save() e.expensePayment = expensePayment e.state = "PAID" e.save() self.assertEqual(expensePayment.user(), tco) self.assertEqual(expensePayment.amount(), 123) for user in (tco, abr, fla): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed self.assertTrue(can_edit_expense(e, sre)) # Except admin # Create a new one in other subsidiary e = Expense.objects.create( user=abo, description="une belle quille de vin nature", category=category, amount=123, chargeable=False, creation_date=date.today(), expense_date=date.today()) # gba is not his manager the subsidiary manager, so he should be able to manage its expense states = expense_next_states(e, gba) self.assertIn("VALIDATED", states) self.assertIn("NEEDS_INFORMATION", states) self.assertIn("REJECTED", states) self.assertTrue(can_edit_expense(e, gba)) e.state = "VALIDATED" for user in (abo, gba): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed
def expenses(request, expense_id=None, clone_from=None): """Display user expenses and expenses that he can validate""" expense_administrator, expense_manager, expense_paymaster, expense_requester = user_expense_perm( request.user) if not expense_requester: return HttpResponseRedirect(reverse("core:forbiden")) user_team = user_expense_team(request.user) consultant = Consultant.objects.get( trigramme__iexact=request.user.username) subcontractor = None if consultant.subcontractor: subcontractor = consultant try: if expense_id: expense = Expense.objects.get(id=expense_id) if not can_edit_expense(expense, request.user): 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, subcontractor=subcontractor) else: form = ExpenseForm(request.POST, request.FILES, subcontractor=subcontractor) 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 administrator) expense.user = request.user expense.state = "REQUESTED" expense.workflow_in_progress = True expense.save() return HttpResponseRedirect(reverse("expense:expenses")) else: if expense_id: form = ExpenseForm(instance=expense, subcontractor=subcontractor ) # A form that edit current expense elif clone_from: try: expense = Expense.objects.get(id=clone_from) expense.pk = None # Null pk so it will generate a new fresh object during form submit expense.receipt = None # Never duplicate the receipt, a new one need to be provided form = ExpenseForm( instance=expense, subcontractor=subcontractor ) # A form with the new cloned expense (not saved) except Expense.DoesNotExist: form = ExpenseForm(initial={"expense_date": date.today()}) # An unbound form else: form = ExpenseForm(initial={"expense_date": date.today()}, subcontractor=subcontractor) # 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 = [] if expense_administrator: # Admin manage all expenses managed_expenses = Expense.objects.filter( workflow_in_progress=True).select_related() elif expense_paymaster: # Paymaster manage all expenses except his own 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, can_edit_expense(e, request.user)) 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, expense_next_states(e, request.user)) for e in managed_expenses ]) # Inject expense allowed transitions managedExpenseTable.expenseEditPerm = dict([ (e.id, can_edit_expense(e, request.user)) for e in managed_expenses ]) # Inject expense edit permissions RequestConfig(request, paginate={ "per_page": 100 }).configure(managedExpenseTable) 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 expenses(request, expense_id=None, clone_from=None): """Display user expenses and expenses that he can validate""" expense_administrator, expense_manager, expense_paymaster, expense_requester = user_expense_perm(request.user) if not expense_requester: return HttpResponseRedirect(reverse("core:forbiden")) user_team = user_expense_team(request.user) try: if expense_id: expense = Expense.objects.get(id=expense_id) if not can_edit_expense(expense, request.user): 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 administrator) expense.user = request.user expense.state = "REQUESTED" expense.workflow_in_progress = True expense.save() return HttpResponseRedirect(reverse("expense:expenses")) else: if expense_id: form = ExpenseForm(instance=expense) # A form that edit current expense elif clone_from: try: expense = Expense.objects.get(id=clone_from) expense.pk = None # Null pk so it will generate a new fresh object during form submit expense.receipt = None # Never duplicate the receipt, a new one need to be provided form = ExpenseForm(instance=expense) # A form with the new cloned expense (not saved) except Expense.DoesNotExist: form = ExpenseForm(initial={"expense_date": date.today()}) # An unbound form 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 = [] if expense_administrator: # Admin manage all expenses managed_expenses = Expense.objects.filter(workflow_in_progress=True).select_related() elif expense_paymaster: # Paymaster manage all expenses except his own 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, can_edit_expense(e, request.user)) 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, expense_next_states(e, request.user)) for e in managed_expenses]) # Inject expense allowed transitions managedExpenseTable.expenseEditPerm = dict([(e.id, can_edit_expense(e, request.user)) for e in managed_expenses]) # Inject expense edit permissions RequestConfig(request, paginate={"per_page": 100}).configure(managedExpenseTable) 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 test_expense_swf(self): """Test expense simple & stupid workflow""" tco = User.objects.get(username="******") abr = User.objects.get(username="******") fla = User.objects.get(username="******") sre = User.objects.get(username="******") category = ExpenseCategory.objects.create(name="repas") e = Expense.objects.create(user=tco, description="une grande bouffe", category=category, amount=123, chargeable=False, creation_date=date.today(), expense_date=date.today()) # current (starting) state is requested self.assertEqual(e.state, "REQUESTED") self.assertEqual(len(expense_next_states(e, tco)), 0) # No transition allowed for user self.assertEqual(len(expense_next_states(e, fla)), 0) # No transition allowed for paymaster # But for his manager... states = expense_next_states(e, abr) self.assertIn("VALIDATED", states) self.assertIn("NEEDS_INFORMATION", states) self.assertIn("REJECTED", states) self.assertTrue(can_edit_expense(e, tco)) # Reject it e.state = "REJECT" e.save() for user in (tco, abr, fla): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed self.assertTrue(can_edit_expense(e, sre)) # Except admin # Validate it e.state = "VALIDATED" e.save() for user in (tco, abr): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed # Except paymaster for control/ask info states = expense_next_states(e, fla) self.assertIn("NEEDS_INFORMATION", states) self.assertIn("CONTROLLED", states) self.assertTrue(can_edit_expense(e, sre)) # Ask information e.state = "NEEDS_INFORMATION" e.save() self.assertTrue(can_edit_expense(e, tco)) self.assertTrue(can_edit_expense(e, abr)) # Control it e.state = "CONTROLLED" e.save() for user in (tco, abr): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed self.assertTrue(can_edit_expense(e, sre)) # Except admin e.corporate_card = True e.save() self.assertEqual(len(expense_next_states(e, fla)), 0) # No payment if corporate card was used # Create a payment for that expense expensePayment = ExpensePayment(payment_date=date.today()) expensePayment.save() e.expensePayment = expensePayment e.state = "PAID" e.save() self.assertEqual(expensePayment.user(), tco) self.assertEqual(expensePayment.amount(), 123) for user in (tco, abr, fla): self.assertEqual(len(expense_next_states(e, user)), 0) # No transition allowed self.assertFalse(can_edit_expense(e, user)) # No edition allowed self.assertTrue(can_edit_expense(e, sre)) # Except admin