From 7fe9e86ee74a2e0129ae219dd633b9b4a8ce8467 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 28 Jan 2021 16:41:58 +0100
Subject: [PATCH 09/11] manager: simple desk management & exceptions (#48924)

---
 chrono/manager/forms.py |  69 ++++++++++--
 chrono/manager/views.py |  53 +++++----
 tests/test_manager.py   | 243 ++++++++++++++++++++++++++++++----------
 3 files changed, 280 insertions(+), 85 deletions(-)

diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py
index 4cf1ffb..890c726 100644
--- a/chrono/manager/forms.py
+++ b/chrono/manager/forms.py
@@ -345,8 +345,6 @@ class DeskForm(forms.ModelForm):
 
 
 class TimePeriodExceptionForm(forms.ModelForm):
-    all_desks = forms.BooleanField(label=_('Apply exception on all desks of the agenda'), required=False)
-
     class Meta:
         model = TimePeriodException
         fields = ['start_datetime', 'end_datetime', 'label']
@@ -357,12 +355,9 @@ class TimePeriodExceptionForm(forms.ModelForm):
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        if self.instance.pk is not None:
-            del self.fields['all_desks']
-        elif self.instance.unavailability_calendar:
-            del self.fields['all_desks']
-        elif self.instance.desk_id and self.instance.desk.agenda.desk_set.count() == 1:
-            del self.fields['all_desks']
+        self.old_label = self.instance.label
+        self.old_start_datetime = self.instance.start_datetime
+        self.old_end_datetime = self.instance.end_datetime
 
     def clean(self):
         cleaned_data = super().clean()
@@ -373,6 +368,64 @@ class TimePeriodExceptionForm(forms.ModelForm):
 
         return cleaned_data
 
+    def save(self):
+        super().save()
+
+        self.exceptions = [self.instance]
+        desk_simple_management = False
+        if self.instance.desk_id:
+            desk_simple_management = self.instance.desk.agenda.desk_simple_management
+        if desk_simple_management:
+            for desk in self.instance.desk.agenda.desk_set.exclude(pk=self.instance.desk_id):
+                exception = desk.timeperiodexception_set.filter(
+                    source__isnull=True,
+                    label=self.old_label,
+                    start_datetime=self.old_start_datetime,
+                    end_datetime=self.old_end_datetime,
+                ).first()
+                if exception is not None:
+                    exception.label = self.instance.label
+                    exception.start_datetime = self.instance.start_datetime
+                    exception.end_datetime = self.instance.end_datetime
+                    exception.save()
+                    self.exceptions.append(exception)
+
+        return self.instance
+
+
+class NewTimePeriodExceptionForm(TimePeriodExceptionForm):
+    all_desks = forms.BooleanField(label=_('Apply exception on all desks of the agenda'), required=False)
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        if self.instance.unavailability_calendar:
+            del self.fields['all_desks']
+        elif self.instance.desk_id and self.instance.desk.agenda.desk_set.count() == 1:
+            del self.fields['all_desks']
+        elif self.instance.desk_id and self.instance.desk.agenda.desk_simple_management:
+            del self.fields['all_desks']
+
+    def save(self):
+        super().save()
+
+        self.exceptions = [self.instance]
+        all_desks = self.cleaned_data.get('all_desks')
+        desk_simple_management = False
+        if self.instance.desk_id:
+            desk_simple_management = self.instance.desk.agenda.desk_simple_management
+        if all_desks or desk_simple_management:
+            for desk in self.instance.desk.agenda.desk_set.exclude(pk=self.instance.desk_id):
+                self.exceptions.append(
+                    TimePeriodException.objects.create(
+                        desk=desk,
+                        label=self.instance.label,
+                        start_datetime=self.instance.start_datetime,
+                        end_datetime=self.instance.end_datetime,
+                    )
+                )
+
+        return self.instance
+
 
 class VirtualMemberForm(forms.ModelForm):
     class Meta:
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index 379a0a7..e9211eb 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -86,6 +86,7 @@ from .forms import (
     ImportEventsForm,
     NewDeskForm,
     DeskForm,
+    NewTimePeriodExceptionForm,
     TimePeriodExceptionForm,
     ExceptionsImportForm,
     AgendasImportForm,
@@ -2040,7 +2041,7 @@ virtual_member_delete = VirtualMemberDeleteView.as_view()
 class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView):
     template_name = 'chrono/manager_time_period_exception_form.html'
     model = TimePeriodException
-    form_class = TimePeriodExceptionForm
+    form_class = NewTimePeriodExceptionForm
 
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
@@ -2050,26 +2051,14 @@ class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView):
 
     def form_valid(self, form):
         result = super().form_valid(form)
-        exceptions = [self.object]
-        if form.cleaned_data.get('all_desks'):
-            for desk in Desk.objects.filter(agenda=self.object.desk.agenda_id):
-                if desk == self.object.desk:
-                    continue
-                exceptions.append(
-                    TimePeriodException.objects.create(
-                        desk=desk,
-                        label=self.object.label,
-                        start_datetime=self.object.start_datetime,
-                        end_datetime=self.object.end_datetime,
-                    )
-                )
+        all_desks = form.cleaned_data.get('all_desks')
         message = ungettext(
             'Exception added.',
             'Exceptions added.',
-            len(exceptions),
+            len(form.exceptions) if all_desks else 1,
         )
         messages.info(self.request, message)
-        for exception in exceptions:
+        for exception in form.exceptions:
             if exception.has_booking_within_time_slot():
                 messages.warning(self.request, _('One or several bookings exists within this time slot.'))
                 break
@@ -2101,9 +2090,11 @@ class TimePeriodExceptionEditView(ManagedTimePeriodExceptionMixin, UpdateView):
 
     def form_valid(self, form):
         result = super().form_valid(form)
-        if self.object.has_booking_within_time_slot():
-            messages.info(self.request, _('Exception updated.'))
-            messages.warning(self.request, _('One or several bookings exists within this time slot.'))
+        messages.info(self.request, _('Exception updated.'))
+        for exception in form.exceptions:
+            if exception.has_booking_within_time_slot():
+                messages.warning(self.request, _('One or several bookings exists within this time slot.'))
+                break
         return result
 
 
@@ -2159,6 +2150,28 @@ class TimePeriodExceptionDeleteView(ManagedTimePeriodExceptionMixin, DeleteView)
     def get_queryset(self):
         return super().get_queryset().filter(source__settings_slug__isnull=True)
 
+    def delete(self, request, *args, **kwargs):
+        exception = self.get_object()
+        response = super().delete(request, *args, **kwargs)
+
+        if not exception.desk_id:
+            return response
+
+        if not exception.desk.agenda.desk_simple_management:
+            return response
+
+        for desk in exception.desk.agenda.desk_set.exclude(pk=exception.desk.pk):
+            exc = desk.timeperiodexception_set.filter(
+                source__isnull=True,
+                label=exception.label,
+                start_datetime=exception.start_datetime,
+                end_datetime=exception.end_datetime,
+            ).first()
+            if exc is not None:
+                exc.delete()
+
+        return response
+
 
 time_period_exception_delete = TimePeriodExceptionDeleteView.as_view()
 
@@ -2565,7 +2578,7 @@ unavailability_calendar_export = UnavailabilityCalendarExport.as_view()
 
 class UnavailabilityCalendarAddUnavailabilityView(ManagedUnavailabilityCalendarMixin, CreateView):
     template_name = 'chrono/manager_time_period_exception_form.html'
-    form_class = TimePeriodExceptionForm
+    form_class = NewTimePeriodExceptionForm
     model = TimePeriodException
 
     def get_form_kwargs(self):
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 4108d6a..ad5f12f 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -2384,10 +2384,11 @@ def test_meetings_agenda_delete_desk(app, admin_user):
 def test_meetings_agenda_add_time_period_exception(app, admin_user):
     agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
     desk = Desk.objects.create(agenda=agenda, label='Desk A')
-    meeting_type = MeetingType.objects.create(agenda=agenda, label='Blah')
+    desk2 = Desk.objects.create(agenda=agenda, label='Desk B')
+
     app = login(app)
     resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
-    resp = resp.click('Add a time period exception')
+    resp = resp.click('Add a time period exception', index=0)
     today = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
     tomorrow = make_aware(today + datetime.timedelta(days=1))
     dt_format = '%Y-%m-%d %H:%M'
@@ -2398,6 +2399,7 @@ def test_meetings_agenda_add_time_period_exception(app, admin_user):
     resp.form['end_datetime_1'] = '16:00'
     resp = resp.form.submit().follow()
     assert TimePeriodException.objects.count() == 1
+    assert desk2.timeperiodexception_set.exists() is False
     time_period_exception = TimePeriodException.objects.first()
     assert localtime(time_period_exception.start_datetime).strftime(dt_format) == tomorrow.replace(
         hour=8
@@ -2421,28 +2423,61 @@ def test_meetings_agenda_add_time_period_exception(app, admin_user):
     assert 'Exception 1' in resp.text
     assert 'Exception 2' in resp.text
 
+
+def test_meetings_agenda_add_time_period_exception_booking_overlaps(app, admin_user):
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    meeting_type = MeetingType.objects.create(agenda=agenda, label='Blah')
+
+    # different type of overlap
+    # event.start_datetime <= exception.start_datetime < event.start_datetime + meeting_type.duration
+    event = Event.objects.create(
+        agenda=agenda,
+        places=1,
+        desk=desk,
+        meeting_type=meeting_type,
+        start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30)),
+    )
+    Booking.objects.create(event=event)
+    app = login(app)
+    resp = app.get('/manage/agendas/%s/desk/%s/add-time-period-exception' % (agenda.pk, desk.pk))
+    resp.form['label'] = 'Exception'
+    resp.form['start_datetime_0'] = '2017-05-22'
+    resp.form['start_datetime_1'] = '10:45'
+    resp.form['end_datetime_0'] = '2017-05-22'
+    resp.form['end_datetime_1'] = '17:30'
+    resp = resp.form.submit().follow()
+    assert TimePeriodException.objects.count() == 1
+    assert 'Exception added.' in resp.text
+    assert 'One or several bookings exists within this time slot.' in resp.text
+
+
+def test_meetings_agenda_add_time_period_exception_all_desks(app, admin_user):
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+
     # add global time exception
     # only one desk: no option to apply to all desks
+    app = login(app)
     resp = app.get('/manage/agendas/%s/desk/%s/add-time-period-exception' % (agenda.pk, desk.pk))
     assert 'all_desks' not in resp.context['form'].fields
     # more than one desk
+    Desk.objects.create(agenda=agenda, label='Desk B')
     agenda2 = Agenda.objects.create(label='Foo bar', kind='meetings')
     Desk.objects.create(agenda=agenda2, label='Other Desk')  # check exception is not created for this one
-    desk2 = Desk.objects.create(agenda=agenda, label='Desk B')
     event = Event.objects.create(
-        agenda=agenda, places=1, desk=desk2, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
+        agenda=agenda, places=1, desk=desk, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
     )
     Booking.objects.create(event=event)
     resp = app.get('/manage/agendas/%s/desk/%s/add-time-period-exception' % (agenda.pk, desk.pk))
-    future = tomorrow + datetime.timedelta(days=35)
-    resp.form['label'] = 'Exception 3'
+    resp.form['label'] = 'Exception'
     resp.form['start_datetime_0'] = '2017-05-22'
     resp.form['start_datetime_1'] = '08:00'
     resp.form['end_datetime_0'] = '2017-05-26'
     resp.form['end_datetime_1'] = '17:30'
     resp.form['all_desks'] = True
     resp = resp.form.submit().follow()
-    assert TimePeriodException.objects.count() == 4
+    assert TimePeriodException.objects.count() == 2
     assert 'Exceptions added.' in resp.text
     assert 'One or several bookings exists within this time slot.' in resp.text
 
@@ -2450,28 +2485,6 @@ def test_meetings_agenda_add_time_period_exception(app, admin_user):
     resp = app.get('/manage/time-period-exceptions/%s/edit' % exception.pk)
     assert 'all_desks' not in resp.context['form'].fields
 
-    # different type of overlap
-    # event.start_datetime <= exception.start_datetime < event.start_datetime + meeting_type.duration
-    event = Event.objects.create(
-        agenda=agenda,
-        places=1,
-        desk=desk2,
-        meeting_type=meeting_type,
-        start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30)),
-    )
-    Booking.objects.create(event=event)
-    resp = app.get('/manage/agendas/%s/desk/%s/add-time-period-exception' % (agenda.pk, desk2.pk))
-    resp.form['label'] = 'Exception 4'
-    resp.form['start_datetime_0'] = '2017-05-22'
-    resp.form['start_datetime_1'] = '10:45'
-    resp.form['end_datetime_0'] = '2017-05-22'
-    resp.form['end_datetime_1'] = '17:30'
-    resp.form['all_desks'] = False
-    resp = resp.form.submit().follow()
-    assert TimePeriodException.objects.count() == 5
-    assert 'Exception added.' in resp.text
-    assert 'One or several bookings exists within this time slot.' in resp.text
-
 
 def test_meetings_agenda_add_time_period_exception_when_booking_exists(app, admin_user):
     agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
@@ -2522,11 +2535,59 @@ def test_meetings_agenda_add_time_period_exception_when_cancelled_booking_exists
     resp.form['end_datetime_0'] = '2017-05-26'
     resp.form['end_datetime_1'] = '17:30'
     resp = resp.form.submit().follow()
-    assert 'Exception added. Note: one or several bookings exists within this time slot.' not in resp.text
+    assert 'Exception added' in resp.text
+    assert 'One or several bookings exists within this time slot.' not in resp.text
     assert TimePeriodException.objects.count() == 1
 
 
+def test_meetings_agenda_add_time_period_exception_desk_simple_management(app, admin_user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='meetings', desk_simple_management=True)
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    desk2 = Desk.objects.create(agenda=agenda, label='Desk B')
+    event = Event.objects.create(
+        agenda=agenda, places=1, desk=desk2, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
+    )
+    Booking.objects.create(event=event)
+    assert agenda.is_available_for_simple_management() is True
+
+    login(app)
+    resp = app.get('/manage/agendas/%s/desk/%s/add-time-period-exception' % (agenda.pk, desk.pk))
+    assert 'all_desks' not in resp.form.fields
+    resp.form['label'] = 'Exception'
+    resp.form['start_datetime_0'] = '2017-05-22'
+    resp.form['start_datetime_1'] = '8:00'
+    resp.form['end_datetime_0'] = '2017-05-22'
+    resp.form['end_datetime_1'] = '17:30'
+    resp = resp.form.submit().follow()
+    assert 'Exception added' in resp.text
+    assert 'One or several bookings exists within this time slot.' in resp.text
+    assert TimePeriodException.objects.count() == 2
+    assert agenda.is_available_for_simple_management() is True
+
+
 def test_meetings_agenda_edit_time_period_exception(app, admin_user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    exception = TimePeriodException.objects.create(
+        label='Exception',
+        desk=desk,
+        start_datetime=make_aware(datetime.datetime(2018, 12, 16, 5, 0)),
+        end_datetime=make_aware(datetime.datetime(2018, 12, 16, 23, 0)),
+    )
+    desk2 = desk.duplicate()
+    exception2 = desk2.timeperiodexception_set.get()
+
+    login(app)
+    resp = app.get('/manage/time-period-exceptions/%s/edit' % exception.pk)
+    resp.form['start_datetime_1'] = '8:00'
+    resp.form.submit()
+    exception.refresh_from_db()
+    assert exception.start_datetime == make_aware(datetime.datetime(2018, 12, 16, 8, 0))
+    exception2.refresh_from_db()
+    assert exception2.start_datetime == make_aware(datetime.datetime(2018, 12, 16, 5, 0))
+
+
+def test_meetings_agenda_edit_time_period_exception_overlaps(app, admin_user):
     agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
     desk = Desk.objects.create(agenda=agenda, label='Desk A')
     calendar = UnavailabilityCalendar.objects.create(label='foo')
@@ -2543,6 +2604,7 @@ def test_meetings_agenda_edit_time_period_exception(app, admin_user):
         start_datetime=make_aware(datetime.datetime(2018, 12, 16, 5, 0)),
         end_datetime=make_aware(datetime.datetime(2018, 12, 16, 23, 0)),
     )
+    login(app)
     for time_period_exception in (time_period_exception_desk, time_period_exception_calendar):
         event = Event.objects.create(
             agenda=agenda,
@@ -2551,28 +2613,76 @@ def test_meetings_agenda_edit_time_period_exception(app, admin_user):
             start_datetime=make_aware(datetime.datetime(2018, 12, 16, 10, 30)),
         )
 
-        login(app)
         resp = app.get('/manage/time-period-exceptions/%s/edit' % time_period_exception.pk)
         resp = resp.form.submit().follow()
-        assert 'Exception updated.' not in resp.text
         assert 'One or several bookings exists within this time slot.' not in resp.text
 
         booking = Booking.objects.create(event=event)
         resp = app.get('/manage/time-period-exceptions/%s/edit' % time_period_exception.pk)
         resp = resp.form.submit().follow()
-        assert 'Exception updated.' in resp.text
         assert 'One or several bookings exists within this time slot.' in resp.text
 
         booking.cancel()
         resp = app.get('/manage/time-period-exceptions/%s/edit' % time_period_exception.pk)
         resp = resp.form.submit().follow()
-        assert 'Exception updated.' not in resp.text
         assert 'One or several bookings exists within this time slot.' not in resp.text
 
         booking.delete()
         event.delete()
 
 
+def test_meetings_agenda_edit_time_period_exception_desk_simple_management(app, admin_user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='meetings', desk_simple_management=True)
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    exception = TimePeriodException.objects.create(
+        label='Exception',
+        desk=desk,
+        start_datetime=make_aware(datetime.datetime(2017, 5, 21, 5, 0)),
+        end_datetime=make_aware(datetime.datetime(2017, 5, 21, 23, 0)),
+    )
+    desk2 = desk.duplicate()
+    event = Event.objects.create(
+        agenda=agenda, places=1, desk=desk2, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
+    )
+    Booking.objects.create(event=event)
+    exception2 = desk2.timeperiodexception_set.get()
+    assert agenda.is_available_for_simple_management() is True
+
+    login(app)
+    resp = app.get('/manage/time-period-exceptions/%s/edit' % exception.pk)
+    resp.form['label'] = 'Exception foo bar'
+    resp.form['start_datetime_0'] = '2017-05-22'
+    resp.form['start_datetime_1'] = '8:00'
+    resp.form['end_datetime_0'] = '2017-05-22'
+    resp.form['end_datetime_1'] = '17:30'
+    resp = resp.form.submit().follow()
+    assert 'One or several bookings exists within this time slot.' in resp.text
+    exception.refresh_from_db()
+    exception2.refresh_from_db()
+    assert exception.label == 'Exception foo bar'
+    assert exception.start_datetime == make_aware(datetime.datetime(2017, 5, 22, 8, 0))
+    assert exception.end_datetime == make_aware(datetime.datetime(2017, 5, 22, 17, 30))
+    assert exception2.label == 'Exception foo bar'
+    assert exception2.start_datetime == make_aware(datetime.datetime(2017, 5, 22, 8, 0))
+    assert exception2.end_datetime == make_aware(datetime.datetime(2017, 5, 22, 17, 30))
+    assert agenda.is_available_for_simple_management() is True
+
+    # should not happen: corresponding exception does not exist
+    exception2.delete()
+    resp = app.get('/manage/time-period-exceptions/%s/edit' % exception.pk)
+    resp.form['label'] = 'Exception'
+    resp.form['start_datetime_0'] = '2017-05-21'
+    resp.form['start_datetime_1'] = '9:00'
+    resp.form['end_datetime_0'] = '2017-05-21'
+    resp.form['end_datetime_1'] = '18:30'
+    # no error
+    resp.form.submit()
+    exception.refresh_from_db()
+    assert exception.label == 'Exception'
+    assert exception.start_datetime == make_aware(datetime.datetime(2017, 5, 21, 9, 0))
+    assert exception.end_datetime == make_aware(datetime.datetime(2017, 5, 21, 18, 30))
+
+
 def test_meetings_agenda_add_invalid_time_period_exception():
     form = TimePeriodExceptionForm(
         data={
@@ -2611,34 +2721,21 @@ def test_meetings_agenda_add_invalid_time_period_exception():
 def test_meetings_agenda_delete_time_period_exception(app, admin_user):
     agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
     desk = Desk.objects.create(agenda=agenda, label='Desk A')
-    MeetingType(agenda=agenda, label='Blah').save()
-    TimePeriod.objects.create(
-        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
+    TimePeriodException.objects.create(
+        label='Exception Foo',
+        desk=desk,
+        start_datetime=now() + datetime.timedelta(days=1),
+        end_datetime=now() + datetime.timedelta(days=2),
     )
+    desk.duplicate()
+    assert TimePeriodException.objects.count() == 2
+
     login(app)
     resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
-    resp = resp.click('Add a time period exception')
-    today = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
-    tomorrow = make_aware(today + datetime.timedelta(days=15))
-    dt_format = '%Y-%m-%d %H:%M'
-    resp.form['label'] = 'Exception 1'
-    resp.form['start_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
-    resp.form['start_datetime_1'] = '08:00'
-    resp.form['end_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
-    resp.form['end_datetime_1'] = '16:00'
-    resp = resp.form.submit().follow()
-    assert TimePeriodException.objects.count() == 1
-    time_period_exception = TimePeriodException.objects.first()
-    assert localtime(time_period_exception.start_datetime).strftime(dt_format) == tomorrow.replace(
-        hour=8
-    ).strftime(dt_format)
-    assert localtime(time_period_exception.end_datetime).strftime(dt_format) == tomorrow.replace(
-        hour=16
-    ).strftime(dt_format)
-    resp = resp.click(href='/manage/time-period-exceptions/%d/edit' % time_period_exception.id)
+    resp = resp.click('Exception Foo', index=0)
     resp = resp.click('Delete')
     resp = resp.form.submit().follow()
-    assert TimePeriodException.objects.count() == 0
+    assert TimePeriodException.objects.count() == 1
     assert resp.request.url.endswith('/manage/agendas/%d/settings' % agenda.pk)
 
     # stay on exception list
@@ -2656,6 +2753,38 @@ def test_meetings_agenda_delete_time_period_exception(app, admin_user):
     assert resp.request.url.endswith('/manage/time-period-exceptions/%d/exception-list' % desk.pk)
 
 
+def test_meetings_agenda_delete_time_period_exception_desk_simple_management(app, admin_user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='meetings', desk_simple_management=True)
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    exception = TimePeriodException.objects.create(
+        label='Exception',
+        desk=desk,
+        start_datetime=make_aware(datetime.datetime(2017, 5, 21, 5, 0)),
+        end_datetime=make_aware(datetime.datetime(2017, 5, 21, 23, 0)),
+    )
+    desk.duplicate()
+    assert TimePeriodException.objects.count() == 2
+    assert agenda.is_available_for_simple_management() is True
+
+    login(app)
+    resp = app.get('/manage/time-period-exceptions/%d/delete' % exception.pk)
+    resp.form.submit()
+    assert TimePeriodException.objects.count() == 0
+    assert agenda.is_available_for_simple_management() is True
+
+    # should not happen: corresponding exception does not exist
+    exception = TimePeriodException.objects.create(
+        label='Exception',
+        desk=desk,
+        start_datetime=make_aware(datetime.datetime(2017, 5, 21, 5, 0)),
+        end_datetime=make_aware(datetime.datetime(2017, 5, 21, 23, 0)),
+    )
+    assert TimePeriodException.objects.count() == 1
+    resp = app.get('/manage/time-period-exceptions/%d/delete' % exception.pk)
+    resp.form.submit()
+    assert TimePeriodException.objects.count() == 0
+
+
 @override_settings(
     EXCEPTIONS_SOURCES={
         'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
-- 
2.29.2

