+
@@ -178,10 +190,6 @@
$('.ui.form').form({
on: 'blur',
keyboardShortcuts: false,
- fields: {
- name: 'empty',
- contributors: 'empty'
- },
onSuccess: function(event, fields) {
var revenue_data = [];
$('#revenue_grid .jsgrid-grid-body tr:not(.totalrow):not(.jsgrid-nodata-row)').each(function(i, el) {
diff --git a/sstreasury/views.py b/sstreasury/views.py
index bc55abc..65d0e0a 100644
--- a/sstreasury/views.py
+++ b/sstreasury/views.py
@@ -16,7 +16,7 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, ValidationError
from django.core.validators import validate_email
from django.conf import settings
@@ -32,6 +32,7 @@ from . import models
from . import xero
from ssmain.email import Emailer
+from datetime import datetime
import functools
import io
import itertools
@@ -88,14 +89,58 @@ def claim_editable(viewfunc):
# HELPER FUNCTIONS
+class FormValidationError(Exception):
+ def __init__(self, data, errors):
+ super().__init__(self)
+ self.data = data
+ self.errors = errors
+
def revision_from_form(budget, revision, form):
+ errors = []
+
revision.budget = budget
- revision.name = form['name']
- revision.date = form['date'] if form['date'] else None
+ if form['name']:
+ if len(form['name']) > 100:
+ errors.append('Title must be at most 100 characters')
+ revision.name = form['name']
+ else:
+ errors.append('A title must be specified')
- revision.event_dt = form['event_dt'] if form['event_dt'] else None
- revision.event_attendees = form['event_attendees'] if form['event_attendees'] else None
+ if form['date']:
+ try:
+ form_date = datetime.strptime(form['date'], '%Y-%m-%d')
+ except ValueError:
+ errors.append('Due date is not a valid date')
+ revision.date = form['date']
+ else:
+ errors.append('A due date must be specified')
+
+ if form['event_dt']:
+ try:
+ form_event_dt = datetime.strptime(form['event_dt'], '%Y-%m-%d %H:%M')
+ except ValueError:
+ errors.append('Event date/time is not a valid date-time')
+ revision.event_dt = form['event_dt']
+ else:
+ revision.event_dt = None
+
+ if form['event_attendees']:
+ if len(form['event_attendees']) > 20:
+ errors.append('Event attendees must be at most 20 characters')
+ revision.event_attendees = form['event_attendees']
+ else:
+ revision.event_attendees = None
+
+ if form['contributors']:
+ contributors = form['contributors'].split('\n')
+ try:
+ for contributor in contributors:
+ validate_email(contributor.strip())
+ except ValidationError:
+ errors.append('Contributors contains invalid data – type only valid email addresses, one per line')
+ else:
+ contributors = []
revision.comments = form['comments']
revision.revenue = json.loads(form['revenue'])
@@ -104,11 +149,11 @@ def revision_from_form(budget, revision, form):
revision.expense_comments = form['expense_comments']
revision.expense_no_emergency_fund = True if form.get('expense_no_emergency_fund', False) else False
+ if errors:
+ raise FormValidationError(revision, errors)
+
revision.save()
- contributors = form['contributors'].split('\n')
- for contributor in contributors:
- validate_email(contributor.strip())
for contributor in contributors:
try:
user = User.objects.get(email=contributor.strip())
@@ -227,15 +272,22 @@ def budget_print(request, budget, revision):
@login_required
def budget_new(request):
if request.method == 'POST':
- with transaction.atomic():
- budget = models.Budget()
- budget.save()
- revision = models.BudgetRevision()
- revision.author = request.user
- revision.time = timezone.now()
- revision.action = models.BudgetAction.CREATE.value
- revision.state = models.BudgetState.DRAFT.value
- revision = revision_from_form(budget, revision, request.POST)
+ try:
+ with transaction.atomic():
+ budget = models.Budget()
+ budget.save()
+ revision = models.BudgetRevision()
+ revision.author = request.user
+ revision.time = timezone.now()
+ revision.action = models.BudgetAction.CREATE.value
+ revision.state = models.BudgetState.DRAFT.value
+ revision = revision_from_form(budget, revision, request.POST)
+ except FormValidationError as form_error:
+ return render(request, 'sstreasury/budget_edit.html', {
+ 'revision': form_error.data,
+ 'contributors': request.POST['contributors'],
+ 'errors': form_error.errors
+ })
if request.POST['submit'] == 'Save':
return redirect(reverse('budget_view', kwargs={'id': budget.id}))
@@ -248,7 +300,8 @@ def budget_new(request):
return render(request, 'sstreasury/budget_edit.html', {
'revision': revision,
- 'contributors': request.user.email
+ 'contributors': request.user.email,
+ 'errors': []
})
@login_required
@@ -260,13 +313,20 @@ def budget_edit(request, budget, revision):
budget.delete()
return redirect(reverse('budget_list'))
- with transaction.atomic():
- new_revision = models.BudgetRevision()
- new_revision.author = request.user
- new_revision.time = timezone.now()
- new_revision.action = models.BudgetAction.EDIT.value
- new_revision.state = revision.state
- new_revision = revision_from_form(budget, new_revision, request.POST)
+ try:
+ with transaction.atomic():
+ new_revision = models.BudgetRevision()
+ new_revision.author = request.user
+ new_revision.time = timezone.now()
+ new_revision.action = models.BudgetAction.EDIT.value
+ new_revision.state = revision.state
+ new_revision = revision_from_form(budget, new_revision, request.POST)
+ except FormValidationError as form_error:
+ return render(request, 'sstreasury/budget_edit.html', {
+ 'revision': form_error.data,
+ 'contributors': request.POST['contributors'],
+ 'errors': form_error.errors
+ })
if request.POST['submit'] == 'Save':
return redirect(reverse('budget_view', kwargs={'id': budget.id}))
@@ -275,7 +335,8 @@ def budget_edit(request, budget, revision):
else:
return render(request, 'sstreasury/budget_edit.html', {
'revision': revision,
- 'contributors': '\n'.join(revision.contributors.all().values_list('email', flat=True))
+ 'contributors': '\n'.join(revision.contributors.all().values_list('email', flat=True)),
+ 'errors': []
})
@login_required