Created
February 20, 2017 17:31
-
-
Save chrischambers/06f471b3d60c1ff78489d217d64847d2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Essentially, the reason you're finding this hard to test is because the | |
# business logic is baked into the save_model method - this is effectively a | |
# "fat controller". You want to pull that logic out and give it names. This has | |
# several outcomes: | |
# * it makes your code easier to unit test because you're | |
# not coupled to the form and the admin, and in fact you don't even | |
# need a real expense object here, it could be just a stub. | |
# * it makes code reuse easier - it's rarely a good idea to have | |
# business logic living *only* in an admin method. | |
# * it's more legible - if you want to know what happens when the state | |
# changes for an expense, it lives in one place. | |
# in finance/utils.py | |
class ExpenseHandler: | |
""" | |
You give me an expense and a new state, I tell you what happens. | |
""" | |
def __init__(self, expense, new_state): | |
# dispatch on state | |
mapping = { | |
ExpenseStatuses.DRAFT: 'handle_draft_expense_creation', | |
ExpenseStatuses.SUBMITTED: 'handle_expense_submitted', | |
ExpenseStatuses.REJECT: 'handle_expense_rejection', | |
ExpenseStatuses.PAID: 'handle_expense_success', | |
} | |
return getattr(self, mapping[new_state])(expense) | |
def handle_draft_expense_creation(self, expense): | |
expense.remove_all_authorisations() | |
return expense | |
def handle_expense_submitted(self, expense): | |
expense.notify_next_authoriser() | |
return expense | |
def handle_expense_rejection(self, expense): | |
expense.send_rejection_email() | |
return expense | |
def handle_expense_success(self, expense): | |
expense.status = ExpenseStatuses.PAID | |
expense.reimbursed_by = editor | |
expense.reimbursed_on = datetime.now() | |
expense.send_reimbursed_email() | |
return expense | |
# Usage: | |
def save_model(self, request, obj, form, change): | |
# This bit should just handle the form parsing and basically defer all the | |
# business logic to something more clever: | |
expense = obj | |
if change: | |
if 'reimbursed' in form.changed_data and form.cleaned_data['reimbursed']: | |
expense = ExpenseHandler(expense, new_state=ExpenseStatuses.PAID) | |
expense.save() | |
if phase: | |
# Re-save all expense items to ensure the details text contains | |
# the correct phase ID: | |
for ei in expense.items.all(): | |
ei.save() | |
if 'advance_status_to' in form.changed_data: | |
advance_status_to = form.cleaned_data['advance_status_to'] | |
expense = ExpenseHandler(expense, new_state=advance_status_to) | |
expense.save() | |
else: | |
# more stuff |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment