Committee approval of budgets

This commit is contained in:
Yingtong Li 2019-06-20 01:39:29 +10:00
parent 69a7c052c1
commit a43c7da302
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
5 changed files with 86 additions and 19 deletions

View File

@ -29,12 +29,20 @@
{% if revision.state == import('sstreasury.models').BudgetState.DRAFT.value or revision.state == import('sstreasury.models').BudgetState.RESUBMIT.value %} {% if revision.state == import('sstreasury.models').BudgetState.DRAFT.value or revision.state == import('sstreasury.models').BudgetState.RESUBMIT.value %}
<button class="ui mini labeled primary icon button" type="submit" name="action" value="Submit" style="margin-left: 1em;" onclick="return confirm('Are you sure you want to submit this budget for Treasury approval? You will not be able to make any additional changes without withdrawing the budget.');"><i class="paper plane icon"></i> Submit</button> <button class="ui mini labeled primary icon button" type="submit" name="action" value="Submit" style="margin-left: 1em;" onclick="return confirm('Are you sure you want to submit this budget for Treasury approval? You will not be able to make any additional changes without withdrawing the budget.');"><i class="paper plane icon"></i> Submit</button>
{% if request.user.groups.filter(name='Secretary').exists() %}
<button class="ui mini labeled positive icon button" type="submit" name="action" value="Approve" onclick="return confirm('Are you sure you want to mark this budget as committee-approved?');"><i class="check icon"></i> Approve</button>
<button class="ui mini labeled basic negative icon button" type="submit" name="action" value="CmteReturn" onclick="return confirm('Are you sure you want to refuse this budget committee approval and return it for re-drafting?');"><i class="undo icon"></i> Return for re-drafting</button>
{% 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.AWAIT_REVIEW.value and request.user.groups.filter(name='Treasury').exists() %}
<button class="ui mini labeled positive icon button" type="submit" name="action" value="Endorse" style="margin-left: 1em;" onclick="return confirm('Are you sure you want to give this budget Treasury endorsement?');"><i class="check icon"></i> Endorse</button> <button class="ui mini labeled positive icon button" type="submit" name="action" value="Endorse" style="margin-left: 1em;" onclick="return confirm('Are you sure you want to give this budget Treasury endorsement?');"><i class="check icon"></i> Endorse</button>
<button class="ui mini labeled basic negative icon button" type="submit" name="action" value="Return" onclick="return confirm('Are you sure you want to refuse this budget Treasury endorsement and return it for re-drafting?');"><i class="undo icon"></i> Return for re-drafting</button> <button class="ui mini labeled basic negative icon button" type="submit" name="action" value="Return" onclick="return confirm('Are you sure you want to refuse this budget Treasury endorsement and return it for re-drafting?');"><i class="undo icon"></i> Return for re-drafting</button>
{% elif revision.state == import('sstreasury.models').BudgetState.ENDORSED.value and request.user.groups.filter(name='Secretary').exists() %} {% elif revision.state != import('sstreasury.models').BudgetState.APPROVED.value and request.user.groups.filter(name='Secretary').exists() %}
{# TODO #} <button class="ui mini labeled positive icon button" type="submit" name="action" value="Approve" style="margin-left: 1em;" onclick="return confirm('Are you sure you want to mark this budget as committee-approved?');"><i class="check icon"></i> Approve</button>
<button class="ui mini labeled basic negative icon button" type="submit" name="action" value="CmteReturn" onclick="return confirm('Are you sure you want to refuse this budget committee approval and return it for re-drafting?');"><i class="undo icon"></i> Return for re-drafting</button>
{% elif revision.state == import('sstreasury.models').BudgetState.APPROVED.value %} {% elif revision.state == import('sstreasury.models').BudgetState.APPROVED.value %}
{# Blank #} {# Blank #}
{% else %} {% 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())) %} {% 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())) %}
<a class="ui mini labeled right floated icon button" href="{{ url('budget_edit', kwargs={'id': revision.budget.id}) }}"><i class="edit icon"></i> Edit</a> <a class="ui mini labeled right floated icon button" href="{{ url('budget_edit', kwargs={'id': revision.budget.id}) }}"><i class="edit icon"></i> Edit</a>
{% elif revision.state == import('sstreasury.models').BudgetState.APPROVED.value %}
{# Blank #}
{% else %} {% else %}
<div class="ui message"> <div class="ui message">
<p>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.</p> <p>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.</p>
@ -143,9 +153,13 @@
<input class="ui primary button" type="submit" name="action" value="Comment"> <input class="ui primary button" type="submit" name="action" value="Comment">
{% if revision.state == import('sstreasury.models').BudgetState.AWAIT_REVIEW.value and request.user.groups.filter(name='Treasury').exists() %} {% if revision.state == import('sstreasury.models').BudgetState.AWAIT_REVIEW.value and request.user.groups.filter(name='Treasury').exists() %}
<button class="ui labeled positive icon button" type="submit" name="action" value="Comment,Endorse" onclick="return confirm('Are you sure you want to give this budget Treasury endorsement?');"><i class="check icon"></i> Comment and endorse</button> <button class="ui right floated labeled basic negative icon button" type="submit" name="action" value="Comment,Return" onclick="return confirm('Are you sure you want to refuse this budget Treasury endorsement and return it for re-drafting?');"><i class="undo icon"></i> Comment and return for re-drafting</button>
<button class="ui labeled basic negative icon button" type="submit" name="action" value="Comment,Return" onclick="return confirm('Are you sure you want to refuse this budget Treasury endorsement and return it for re-drafting?');"><i class="undo icon"></i> Comment and return for re-drafting</button> <button class="ui right floated labeled positive icon button" type="submit" name="action" value="Comment,Endorse" onclick="return confirm('Are you sure you want to give this budget Treasury endorsement?');"><i class="check icon"></i> Comment and endorse</button>
{% elif revision.state != import('sstreasury.models').BudgetState.APPROVED.value and request.user.groups.filter(name='Secretary').exists() %}
<button class="ui right floated labeled basic negative icon button" type="submit" name="action" value="Comment,CmteReturn" onclick="return confirm('Are you sure you want to refuse this budget committee approval and return it for re-drafting?');"><i class="undo icon"></i> Comment and return for re-drafting</button>
<button class="ui right floated labeled positive icon button" type="submit" name="action" value="Comment,Approve" onclick="return confirm('Are you sure you want to mark this budget as committee-approved?');"><i class="check icon"></i> Comment and approve</button>
{% endif %} {% endif %}
</form> </form>

View File

@ -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}) }}

View File

@ -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}) }}

View File

@ -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}) }}

View File

@ -46,23 +46,29 @@ def budget_list(request):
revision = budget.budgetrevision_set.reverse()[0] revision = budget.budgetrevision_set.reverse()[0]
state = models.BudgetState(revision.state) state = models.BudgetState(revision.state)
if request.user.groups.filter(name='Treasury').exists(): group = None
if state == models.BudgetState.AWAIT_REVIEW:
budgets_action.append(revision)
elif state != models.BudgetState.APPROVED:
budgets_open.append(revision)
else:
budgets_closed.append(revision)
else:
if request.user not in revision.contributors.all():
continue
if state in [models.BudgetState.DRAFT, models.BudgetState.RESUBMIT]: if request.user.groups.filter(name='Treasury').exists() and state == models.BudgetState.AWAIT_REVIEW:
budgets_action.append(revision) group = budgets_action
elif state in [models.BudgetState.AWAIT_REVIEW, models.BudgetState.ENDORSED]: elif request.user.groups.filter(name='Secretary').exists() and state == models.BudgetState.ENDORSED:
budgets_open.append(revision) group = budgets_action
else:
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: 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', { return render(request, 'sstreasury/budget_list.html', {
'budgets_action': budgets_action, 'budgets_action': budgets_action,
@ -272,6 +278,8 @@ def budget_action(request, budget, revision):
revision.save() revision.save()
emailer = Emailer() 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(): 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}) 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(): 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}) 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})) return redirect(reverse('budget_view', kwargs={'id': budget.id}))