Validate budget input
This commit is contained in:
parent
0da9bebcae
commit
1975c48273
@ -24,6 +24,18 @@
|
||||
<h1>{% if request.resolver_match.url_name == 'budget_new' %}New{% else %}Edit{% endif %} budget</h1>
|
||||
|
||||
<form class="ui form" method="POST">
|
||||
{% if errors %}
|
||||
<div class="ui visible error message">
|
||||
<div class="header">Invalid input</div>
|
||||
<p>Unable to save the budget. Please correct the issues below and try again:</p>
|
||||
<ul>
|
||||
{% for error in errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="ui disabled inline grid field">
|
||||
<label class="three wide column">ID</label>
|
||||
<input class="eleven wide column" type="text" name="id" value="{{ 'BU-{}'.format(revision.budget.id) if revision.budget.id != None else '' }}">
|
||||
@ -62,7 +74,7 @@
|
||||
<div class="four wide column">Estimated no. of attendees</div>
|
||||
<input class="seven wide column" type="text" name="event_attendees" value="{{ revision.event_attendees or '' }}">
|
||||
</div>
|
||||
<div class="ui required inline grid field">
|
||||
<div class="ui inline grid field">
|
||||
<label class="three wide column">Contributors <span data-tooltip="To share this budget with other contributors, enter their email addresses, one per line"><i class="grey question circle icon"></i></span></label>
|
||||
<textarea class="eleven wide column" rows="2" name="contributors" style="font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;">{{ contributors }}</textarea>
|
||||
</div>
|
||||
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
if form['name']:
|
||||
if len(form['name']) > 100:
|
||||
errors.append('Title must be at most 100 characters')
|
||||
revision.name = form['name']
|
||||
revision.date = form['date'] if form['date'] else None
|
||||
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,6 +272,7 @@ def budget_print(request, budget, revision):
|
||||
@login_required
|
||||
def budget_new(request):
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
with transaction.atomic():
|
||||
budget = models.Budget()
|
||||
budget.save()
|
||||
@ -236,6 +282,12 @@ def budget_new(request):
|
||||
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,6 +313,7 @@ def budget_edit(request, budget, revision):
|
||||
budget.delete()
|
||||
return redirect(reverse('budget_list'))
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
new_revision = models.BudgetRevision()
|
||||
new_revision.author = request.user
|
||||
@ -267,6 +321,12 @@ def budget_edit(request, budget, revision):
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user