From a43c7da3022ec5b6bfcd7eda1ffe1613cfbe3f24 Mon Sep 17 00:00:00 2001 From: Yingtong Li Date: Thu, 20 Jun 2019 01:39:29 +1000 Subject: [PATCH] Committee approval of budgets --- sstreasury/jinja2/sstreasury/budget_view.html | 22 +++++- .../jinja2/sstreasury/email/approved.md | 3 + .../sstreasury/email/endorsed_secretary.md | 3 + .../sstreasury/email/returned_committee.md | 3 + sstreasury/views.py | 74 +++++++++++++++---- 5 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 sstreasury/jinja2/sstreasury/email/approved.md create mode 100644 sstreasury/jinja2/sstreasury/email/endorsed_secretary.md create mode 100644 sstreasury/jinja2/sstreasury/email/returned_committee.md diff --git a/sstreasury/jinja2/sstreasury/budget_view.html b/sstreasury/jinja2/sstreasury/budget_view.html index deb9106..16e9ec0 100644 --- a/sstreasury/jinja2/sstreasury/budget_view.html +++ b/sstreasury/jinja2/sstreasury/budget_view.html @@ -29,12 +29,20 @@ {% if revision.state == import('sstreasury.models').BudgetState.DRAFT.value or revision.state == import('sstreasury.models').BudgetState.RESUBMIT.value %} + + {% if request.user.groups.filter(name='Secretary').exists() %} + + + + {% endif %} {% elif revision.state == import('sstreasury.models').BudgetState.AWAIT_REVIEW.value and request.user.groups.filter(name='Treasury').exists() %} - {% elif revision.state == import('sstreasury.models').BudgetState.ENDORSED.value and request.user.groups.filter(name='Secretary').exists() %} - {# TODO #} + {% elif revision.state != import('sstreasury.models').BudgetState.APPROVED.value and request.user.groups.filter(name='Secretary').exists() %} + + + {% elif revision.state == import('sstreasury.models').BudgetState.APPROVED.value %} {# Blank #} {% else %} @@ -43,6 +51,8 @@ {% if revision.state == import('sstreasury.models').BudgetState.DRAFT.value or revision.state == import('sstreasury.models').BudgetState.RESUBMIT.value or (revision.state != import('sstreasury.models').BudgetState.APPROVED.value and (request.user.groups.filter(name='Treasury').exists() or request.user.groups.filter(name='Secretary').exists())) %} Edit + {% elif revision.state == import('sstreasury.models').BudgetState.APPROVED.value %} + {# Blank #} {% else %}

This budget has been submitted and is now awaiting approval. If you wish to edit this budget, you must first withdraw it. This will revert the budget to a draft.

@@ -143,9 +153,13 @@ {% if revision.state == import('sstreasury.models').BudgetState.AWAIT_REVIEW.value and request.user.groups.filter(name='Treasury').exists() %} - + - + + {% elif revision.state != import('sstreasury.models').BudgetState.APPROVED.value and request.user.groups.filter(name='Secretary').exists() %} + + + {% endif %} diff --git a/sstreasury/jinja2/sstreasury/email/approved.md b/sstreasury/jinja2/sstreasury/email/approved.md new file mode 100644 index 0000000..3a07900 --- /dev/null +++ b/sstreasury/jinja2/sstreasury/email/approved.md @@ -0,0 +1,3 @@ +Your budget titled *{{ revision.name }}* has been reviewed by the committee and **approved**. The expenditure shown in the budget may now commence. + +{{ baseurl }}{{ url('budget_view', kwargs={'id': revision.budget.id}) }} diff --git a/sstreasury/jinja2/sstreasury/email/endorsed_secretary.md b/sstreasury/jinja2/sstreasury/email/endorsed_secretary.md new file mode 100644 index 0000000..47c08fd --- /dev/null +++ b/sstreasury/jinja2/sstreasury/email/endorsed_secretary.md @@ -0,0 +1,3 @@ +A budget titled *{{ revision.name }}* has been endorsed by Treasury and referred to the committee for consideration at its next meeting. + +{{ baseurl }}{{ url('budget_view', kwargs={'id': revision.budget.id}) }} diff --git a/sstreasury/jinja2/sstreasury/email/returned_committee.md b/sstreasury/jinja2/sstreasury/email/returned_committee.md new file mode 100644 index 0000000..22866b0 --- /dev/null +++ b/sstreasury/jinja2/sstreasury/email/returned_committee.md @@ -0,0 +1,3 @@ +Your budget titled *{{ revision.name }}* has been reviewed by the committee and returned to you for re-drafting. You should make any requested changes and resubmit the budget. + +{{ baseurl }}{{ url('budget_view', kwargs={'id': revision.budget.id}) }} diff --git a/sstreasury/views.py b/sstreasury/views.py index 0b06ed4..c0153cc 100644 --- a/sstreasury/views.py +++ b/sstreasury/views.py @@ -46,23 +46,29 @@ def budget_list(request): revision = budget.budgetrevision_set.reverse()[0] state = models.BudgetState(revision.state) - if request.user.groups.filter(name='Treasury').exists(): - if state == models.BudgetState.AWAIT_REVIEW: - budgets_action.append(revision) - elif state != models.BudgetState.APPROVED: - budgets_open.append(revision) - else: - budgets_closed.append(revision) + group = None + + if request.user.groups.filter(name='Treasury').exists() and state == models.BudgetState.AWAIT_REVIEW: + group = budgets_action + elif request.user.groups.filter(name='Secretary').exists() and state == models.BudgetState.ENDORSED: + group = budgets_action else: - if request.user not in revision.contributors.all(): - continue - - if state in [models.BudgetState.DRAFT, models.BudgetState.RESUBMIT]: - budgets_action.append(revision) - elif state in [models.BudgetState.AWAIT_REVIEW, models.BudgetState.ENDORSED]: - budgets_open.append(revision) + if request.user in revision.contributors.all(): + if state in [models.BudgetState.DRAFT, models.BudgetState.RESUBMIT]: + group = budgets_action + elif state in [models.BudgetState.AWAIT_REVIEW, models.BudgetState.ENDORSED]: + group = budgets_open + else: + group = budgets_closed else: - budgets_closed.append(revision) + if request.user in revision.contributors.all() or request.user.groups.filter(name='Treasury').exists() or request.user.groups.filter(name='Secretary').exists(): + if state == models.BudgetState.APPROVED: + group = budgets_closed + else: + group = budgets_open + + if group is not None: + group.append(revision) return render(request, 'sstreasury/budget_list.html', { 'budgets_action': budgets_action, @@ -272,6 +278,8 @@ def budget_action(request, budget, revision): revision.save() emailer = Emailer() + for user in User.objects.filter(groups__name='Secretary'): + emailer.send_mail([user.email], 'Action required: Budget endorsed: {}'.format(revision.name), 'sstreasury/email/endorsed_secretary.md', {'revision': revision}) for user in revision.contributors.all(): emailer.send_mail([user.email], 'Budget endorsed, awaiting committee approval: {}'.format(revision.name), 'sstreasury/email/endorsed.md', {'revision': revision}) @@ -293,4 +301,40 @@ def budget_action(request, budget, revision): for user in revision.contributors.all(): emailer.send_mail([user.email], 'Action required: Budget returned for re-drafting: {}'.format(revision.name), 'sstreasury/email/returned.md', {'revision': revision}) + if 'Approve' in actions: + if revision.state == models.BudgetState.APPROVED.value: + raise PermissionDenied + if not request.user.groups.filter(name='Secretary').exists(): + raise PermissionDenied + + with transaction.atomic(): + revision.copy() + revision.author = request.user + revision.time = timezone.now() + revision.state = models.BudgetState.APPROVED.value + revision.action = models.BudgetAction.UPDATE_STATE.value + revision.save() + + emailer = Emailer() + for user in revision.contributors.all(): + emailer.send_mail([user.email], 'Budget approved: {}'.format(revision.name), 'sstreasury/email/approved.md', {'revision': revision}) + + if 'CmteReturn' in actions: + if revision.state == models.BudgetState.APPROVED.value: + raise PermissionDenied + if not request.user.groups.filter(name='Secretary').exists(): + raise PermissionDenied + + with transaction.atomic(): + revision.copy() + revision.author = request.user + revision.time = timezone.now() + revision.state = models.BudgetState.RESUBMIT.value + revision.action = models.BudgetAction.UPDATE_STATE.value + revision.save() + + emailer = Emailer() + for user in revision.contributors.all(): + emailer.send_mail([user.email], 'Action required: Budget returned for re-drafting: {}'.format(revision.name), 'sstreasury/email/returned_committee.md', {'revision': revision}) + return redirect(reverse('budget_view', kwargs={'id': budget.id}))