From aa148810c66f053c65c5759580143d0dfd03a44a 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 14:38:26 +0100
Subject: [PATCH 08/11] manager: simple desk management & time periods (#48924)

---
 chrono/manager/forms.py |  27 +++++++
 chrono/manager/views.py |  25 +++++-
 tests/test_manager.py   | 165 +++++++++++++++++++++++++++++++---------
 3 files changed, 181 insertions(+), 36 deletions(-)

diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py
index 8fd1974..4cf1ffb 100644
--- a/chrono/manager/forms.py
+++ b/chrono/manager/forms.py
@@ -266,11 +266,38 @@ class TimePeriodForm(forms.ModelForm):
         }
         exclude = ['agenda', 'desk']
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.old_weekday = self.instance.weekday
+        self.old_start_time = self.instance.start_time
+        self.old_end_time = self.instance.end_time
+
     def clean_end_time(self):
         if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
             raise ValidationError(_('End time must come after start time.'))
         return self.cleaned_data['end_time']
 
+    def save(self):
+        super().save()
+
+        if not self.instance.desk:
+            return self.instance
+
+        if not self.instance.desk.agenda.desk_simple_management:
+            return self.instance
+
+        for desk in self.instance.desk.agenda.desk_set.exclude(pk=self.instance.desk.pk):
+            timeperiod = desk.timeperiod_set.filter(
+                weekday=self.old_weekday, start_time=self.old_start_time, end_time=self.old_end_time
+            ).first()
+            if timeperiod is not None:
+                timeperiod.weekday = self.instance.weekday
+                timeperiod.start_time = self.instance.start_time
+                timeperiod.end_time = self.instance.end_time
+                timeperiod.save()
+
+        return self.instance
+
 
 class NewDeskForm(forms.ModelForm):
     copy_from = forms.ModelChoiceField(
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index 62318b6..eb90367 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -1882,7 +1882,11 @@ class AgendaAddTimePeriodView(ManagedDeskMixin, FormView):
         return super(FormView, self).get_form_kwargs()
 
     def form_valid(self, form):
-        process_time_period_add_form(form, desk=self.desk)
+        if self.desk.agenda.desk_simple_management:
+            for desk in self.desk.agenda.desk_set.all():
+                process_time_period_add_form(form, desk=desk)
+        else:
+            process_time_period_add_form(form, desk=self.desk)
         return super(AgendaAddTimePeriodView, self).form_valid(form)
 
 
@@ -1917,6 +1921,25 @@ class TimePeriodDeleteView(ManagedTimePeriodMixin, DeleteView):
     template_name = 'chrono/manager_confirm_delete.html'
     model = TimePeriod
 
+    def delete(self, request, *args, **kwargs):
+        time_period = self.get_object()
+        response = super().delete(request, *args, **kwargs)
+
+        if not time_period.desk:
+            return response
+
+        if not time_period.desk.agenda.desk_simple_management:
+            return response
+
+        for desk in time_period.desk.agenda.desk_set.exclude(pk=time_period.desk.pk):
+            tp = desk.timeperiod_set.filter(
+                weekday=time_period.weekday, start_time=time_period.start_time, end_time=time_period.end_time
+            ).first()
+            if tp is not None:
+                tp.delete()
+
+        return response
+
 
 time_period_delete = TimePeriodDeleteView.as_view()
 
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 0f198d4..4108d6a 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -1989,10 +1989,11 @@ def test_meetings_agenda_delete_meeting_type(app, admin_user):
 def test_meetings_agenda_add_time_period(app, admin_user):
     agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
     desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    desk2 = Desk.objects.create(agenda=agenda, label='Desk B')
     MeetingType.objects.create(agenda=agenda, label='Blah')
     app = login(app)
     resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
-    resp = resp.click('Add a time period$')
+    resp = resp.click('Add a time period', index=0)
     resp.form['weekdays-2'].checked = True
     resp.form['start_time'] = '10:00'
     resp.form['end_time'] = '17:00'
@@ -2002,6 +2003,7 @@ def test_meetings_agenda_add_time_period(app, admin_user):
     assert TimePeriod.objects.get(desk=desk).start_time.minute == 0
     assert TimePeriod.objects.get(desk=desk).end_time.hour == 17
     assert TimePeriod.objects.get(desk=desk).end_time.minute == 0
+    assert desk2.timeperiod_set.exists() is False
     resp = resp.follow()
 
     # add a second time period
@@ -2023,20 +2025,6 @@ def test_meetings_agenda_add_time_period(app, admin_user):
     resp2 = resp2.form.submit()
     assert 'End time must come after start time.' in resp2.text
 
-    # and edit
-    resp = resp.click(u'Wednesday / 10 a.m. → 5 p.m.')
-    assert 'Edit Time Period' in resp.text
-    resp.form['start_time'] = '9:00'
-    resp = resp.form.submit()
-    resp = resp.follow()
-    assert TimePeriod.objects.get(desk=desk, weekday=2).start_time.hour == 9
-
-    # and edit with inverted start/end
-    resp2 = resp.click(u'Wednesday / 9 a.m. → 5 p.m.')
-    resp2.form['start_time'] = '18:00'
-    resp2 = resp2.form.submit()
-    assert 'End time must come after start time.' in resp2.text
-
     # and add same time periods on multiple days
     resp = resp.click('Add a time period', index=0)
     resp.form['weekdays-4'].checked = True
@@ -2047,33 +2035,28 @@ def test_meetings_agenda_add_time_period(app, admin_user):
     assert TimePeriod.objects.filter(desk=desk).count() == 4
 
 
-def test_meetings_agenda_delete_time_period(app, admin_user):
-    agenda = Agenda(label=u'Foo bar', kind='meetings')
-    agenda.save()
-
-    MeetingType(agenda=agenda, label='Blah').save()
-
+def test_meetings_agenda_add_time_period_desk_simple_management(app, admin_user):
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings', desk_simple_management=True)
     desk = Desk.objects.create(agenda=agenda, label='Desk A')
-    time_period = TimePeriod(
-        desk=desk, weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
-    )
-    time_period.save()
+    desk2 = Desk.objects.create(agenda=agenda, label='Desk B')
+    assert agenda.is_available_for_simple_management() is True
 
     app = login(app)
-    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
-    resp = resp.click('Wednesday')
-    resp = resp.click('Delete')
-    resp = resp.form.submit()
-    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
-    assert TimePeriod.objects.count() == 0
+    resp = app.get('/manage/agendas/%s/desk/%s/add-time-period' % (agenda.pk, desk.pk))
+    resp.form['weekdays-0'].checked = True
+    resp.form['start_time'] = '10:00'
+    resp.form['end_time'] = '13:00'
+    resp.form.submit()
+
+    assert TimePeriod.objects.filter(desk=desk).count() == 1
+    assert TimePeriod.objects.filter(desk=desk2).count() == 1
+    assert agenda.is_available_for_simple_management() is True
 
 
 def test_meetings_agenda_add_time_period_on_missing_desk(app, admin_user):
     app = login(app)
-    agenda = Agenda(label=u'Foo bar', kind='meetings')
-    agenda.save()
-    MeetingType(agenda=agenda, label='Blah').save()
-    app.get('/manage/agendas/1/desk/777/add-time-period', status=404)
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
+    app.get('/manage/agendas/%s/desk/0/add-time-period' % agenda.pk, status=404)
 
 
 def test_meetings_agenda_add_time_period_as_manager(app, manager_user):
@@ -2107,6 +2090,118 @@ def test_meetings_agenda_add_time_period_as_manager(app, manager_user):
     app.get('/manage/timeperiods/%d/edit' % time_period.id, status=200)
 
 
+def test_meetings_agenda_edit_time_period(app, admin_user):
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    time_period = TimePeriod.objects.create(
+        desk=desk, weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
+    )
+    desk2 = desk.duplicate()
+    time_period2 = desk2.timeperiod_set.get()
+
+    app = login(app)
+    # edit
+    resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
+    resp = resp.click('Monday / 9 a.m. → noon', index=0)
+    assert 'Edit Time Period' in resp.text
+    resp.form['start_time'] = '10:00'
+    resp = resp.form.submit()
+    resp = resp.follow()
+    time_period.refresh_from_db()
+    assert time_period.start_time.hour == 10
+    time_period2.refresh_from_db()
+    assert time_period2.start_time.hour == 9
+
+    # edit with inverted start/end
+    resp2 = resp.click('Monday / 10 a.m. → noon')
+    resp2.form['start_time'] = '18:00'
+    resp2 = resp2.form.submit()
+    assert 'End time must come after start time.' in resp2.text
+
+
+def test_meetings_agenda_edit_time_period_desk_simple_management(app, admin_user):
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings', desk_simple_management=True)
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    time_period = TimePeriod.objects.create(
+        desk=desk, weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
+    )
+    desk2 = desk.duplicate()
+    time_period2 = desk2.timeperiod_set.get()
+    assert agenda.is_available_for_simple_management() is True
+
+    app = login(app)
+    resp = app.get('/manage/timeperiods/%s/edit' % time_period.pk)
+    resp.form['weekday'] = 3
+    resp.form['start_time'] = '10:00'
+    resp.form['end_time'] = '11:00'
+    resp.form.submit()
+    time_period.refresh_from_db()
+    time_period2.refresh_from_db()
+    assert time_period.weekday == 3
+    assert time_period.start_time.hour == 10
+    assert time_period.end_time.hour == 11
+    assert time_period2.weekday == 3
+    assert time_period2.start_time.hour == 10
+    assert time_period2.end_time.hour == 11
+    assert agenda.is_available_for_simple_management() is True
+
+    # should not happen: corresponding time period does not exist
+    time_period2.delete()
+    resp = app.get('/manage/timeperiods/%s/edit' % time_period.pk)
+    resp.form['weekday'] = 3
+    resp.form['start_time'] = '10:00'
+    resp.form['end_time'] = '11:00'
+    # no error
+    resp.form.submit()
+    time_period.refresh_from_db()
+    assert time_period.weekday == 3
+    assert time_period.start_time.hour == 10
+    assert time_period.end_time.hour == 11
+
+
+def test_meetings_agenda_delete_time_period(app, admin_user):
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    TimePeriod.objects.create(
+        desk=desk, weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
+    )
+    desk.duplicate()
+    assert TimePeriod.objects.count() == 2
+
+    app = login(app)
+    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
+    resp = resp.click('Wednesday', index=0)
+    resp = resp.click('Delete')
+    resp = resp.form.submit()
+    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
+    assert TimePeriod.objects.count() == 1
+
+
+def test_meetings_agenda_delete_time_period_desk_simple_management(app, admin_user):
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings', desk_simple_management=True)
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    time_period = TimePeriod.objects.create(
+        desk=desk, weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
+    )
+    desk.duplicate()
+    assert TimePeriod.objects.count() == 2
+    assert agenda.is_available_for_simple_management() is True
+
+    app = login(app)
+    resp = app.get('/manage/timeperiods/%s/delete' % time_period.pk)
+    resp.form.submit()
+    assert TimePeriod.objects.count() == 0
+    assert agenda.is_available_for_simple_management() is True
+
+    # should not happen: corresponding time period does not exist
+    time_period = TimePeriod.objects.create(
+        desk=desk, weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
+    )
+    resp = app.get('/manage/timeperiods/%s/delete' % time_period.pk)
+    resp.form.submit()
+    assert TimePeriod.objects.count() == 0
+
+
 def test_meetings_agenda_add_desk(app, admin_user):
     app = login(app)
     resp = app.get('/manage/', status=200)
-- 
2.29.2

