From 2bdbf04204e8619c012fa6f04a85393c318550f4 Mon Sep 17 00:00:00 2001 From: Josue Kouka Date: Tue, 19 Dec 2017 14:50:38 +0100 Subject: [PATCH] manager: prevent time period changes when bookings exist (#20791) --- chrono/manager/forms.py | 34 +++++++++++++++++++++- tests/test_manager.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py index 00f5363..8e3b9ff 100644 --- a/chrono/manager/forms.py +++ b/chrono/manager/forms.py @@ -19,10 +19,11 @@ import datetime from django import forms from django.forms import ValidationError +from django.utils.timezone import localtime, make_aware, now from django.utils.translation import ugettext_lazy as _ from chrono.agendas.models import (Event, MeetingType, TimePeriod, Desk, - TimePeriodException) + TimePeriodException, Booking) from . import widgets @@ -76,6 +77,37 @@ class TimePeriodForm(forms.ModelForm): } exclude = [] + def clean(self): + cleaned_data = super(TimePeriodForm, self).clean() + if not self.are_changes_valid(): + raise ValidationError(_('One or several bookings exist within this time period.')) + return cleaned_data + + def are_changes_valid(self): + if not self.instance.desk_id: + return True + has_booking = self.has_booking_out_of_bounds() + if self.cleaned_data['start_time'] > self.instance.start_time: + return not(has_booking) + if self.cleaned_data['end_time'] < self.instance.end_time: + return not(has_booking) + return True + + def has_booking_out_of_bounds(self): + bookings = Booking.objects.filter( + event__start_datetime__gte=now(), + event__desk=self.instance.desk, cancellation_datetime__isnull=True).select_related('event') + for booking in bookings: + event_startdt = booking.event.start_datetime + event_enddt = booking.event.end_datetime + if event_startdt.weekday() != self.instance.weekday: + continue + start = make_aware(datetime.datetime.combine(event_startdt, self.cleaned_data['start_time'])) + end = make_aware(datetime.datetime.combine(event_enddt, self.cleaned_data['end_time'])) + if (event_startdt < start) or (event_enddt) > end: + return True + return False + class NewDeskForm(forms.ModelForm): class Meta: diff --git a/tests/test_manager.py b/tests/test_manager.py index 7283c4e..1e03365 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -1127,3 +1127,78 @@ def test_agenda_import_time_period_exception_from_remote_ics_with_ssl_error(mock mocked_get.side_effect = mocked_requests_http_ssl_error resp = resp.form.submit(status=200) assert 'Failed to retrieve remote calendar (SSL error).' in resp.content + + +@mock.patch('chrono.manager.forms.now', lambda: datetime.datetime(2017, 5, 20, 10, 23)) +def test_prevent_time_period_changes_when_bookings_exist(app, admin_user): + agenda = Agenda.objects.create(label='Foo bar', kind='meetings') + desk = Desk.objects.create(agenda=agenda, label='Desk A') + meeting_type = MeetingType(agenda=agenda, label='Blah', duration=30) + meeting_type.save() + timeperiod = TimePeriod.objects.create( + weekday=1, desk=desk, start_time=datetime.time(8, 00), end_time=datetime.time(11, 0)) + event = Event.objects.create(agenda=agenda, places=1, desk=desk, meeting_type=meeting_type, + start_datetime=make_aware(datetime.datetime(2017, 5, 22, 8, 30))) + Booking.objects.create(event=event) + event = Event.objects.create(agenda=agenda, places=1, desk=desk, meeting_type=meeting_type, + start_datetime=make_aware(datetime.datetime(2017, 5, 23, 8, 30))) + Booking.objects.create(event=event) + login(app) + timeperiod_edit_link = "/manage/timeperiods/%d/edit" % timeperiod.pk + # extend time period start + resp = app.get('/manage/agendas/%d/' % agenda.pk) + resp = resp.click(href=timeperiod_edit_link) + resp.form['start_time'] = '7:30' + resp = resp.form.submit().follow() + assert resp.html.find('a', {'href': timeperiod_edit_link}).text.strip() == u'Tuesday / 7:30 a.m. \u2192 11 a.m.' + # shorten time period start at booking's bound + resp = app.get('/manage/agendas/%d/' % agenda.pk) + resp = resp.click(href=timeperiod_edit_link) + resp.form['start_time'] = '8:30' + resp = resp.form.submit().follow() + assert resp.html.find('a', {'href': timeperiod_edit_link}).text.strip() == u'Tuesday / 8:30 a.m. \u2192 11 a.m.' + # shorten time period start + resp = app.get('/manage/agendas/%d/' % agenda.pk) + resp = resp.click(href=timeperiod_edit_link) + resp.form['start_time'] = '9:30' + resp = resp.form.submit() + error_message = resp.html.find('ul', {'class': 'errorlist'}).text.strip() + assert error_message == 'One or several bookings exist within this time period.' + # extend time period end + resp = app.get('/manage/agendas/%d/' % agenda.pk) + resp = resp.click(href=timeperiod_edit_link) + resp.form['end_time'] = '12:30' + resp = resp.form.submit().follow() + assert resp.html.find('a', {'href': timeperiod_edit_link}).text.strip() == u'Tuesday / 8:30 a.m. \u2192 12:30 p.m.' + event = Event.objects.create(agenda=agenda, places=1, desk=desk, meeting_type=meeting_type, + start_datetime=make_aware(datetime.datetime(2017, 5, 23, 11, 30))) + Booking.objects.create(event=event) + # shorten time period end at booking's bound + resp = app.get('/manage/agendas/%d/' % agenda.pk) + resp = resp.click(href=timeperiod_edit_link) + resp.form['end_time'] = '12:00' + resp = resp.form.submit().follow() + assert resp.html.find('a', {'href': timeperiod_edit_link}).text.strip() == u'Tuesday / 8:30 a.m. \u2192 noon' + # shorten time period end + resp = app.get('/manage/agendas/%d/' % agenda.pk) + resp = resp.click(href=timeperiod_edit_link) + resp.form['end_time'] = '11:00' + resp = resp.form.submit() + error_message = resp.html.find('ul', {'class': 'errorlist'}).text.strip() + assert error_message == 'One or several bookings exist within this time period.' + # extend both time period start and end + resp = app.get('/manage/agendas/%d/' % agenda.pk) + resp = resp.click(href=timeperiod_edit_link) + resp.form['start_time'] = '7:00' + resp.form['end_time'] = '12:30' + resp = resp.form.submit().follow() + assert resp.html.find('a', {'href': timeperiod_edit_link}).text.strip() == u'Tuesday / 7 a.m. \u2192 12:30 p.m.' + # shorten both time period start and end + Booking.objects.create(event=event) + resp = app.get('/manage/agendas/%d/' % agenda.pk) + resp = resp.click(href=timeperiod_edit_link) + resp.form['start_time'] = '8:00' + resp.form['end_time'] = '11:00' + resp = resp.form.submit() + error_message = resp.html.find('ul', {'class': 'errorlist'}).text.strip() + assert error_message == 'One or several bookings exist within this time period.' -- 2.11.0