From 68b5d8ba88b9569bc578bc973fb8e90c7fc99cdf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 26 Jan 2021 15:50:34 +0100
Subject: [PATCH 07/11] manager: desk display with desk_simple_management flag
 (#48924)

---
 chrono/manager/forms.py                       |  16 +-
 .../manager_meetings_agenda_settings.html     |  20 ++-
 chrono/manager/views.py                       |  19 ++-
 tests/test_manager.py                         | 148 +++++++++++++-----
 4 files changed, 158 insertions(+), 45 deletions(-)

diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py
index 675cf6a..8fd1974 100644
--- a/chrono/manager/forms.py
+++ b/chrono/manager/forms.py
@@ -285,12 +285,22 @@ class NewDeskForm(forms.ModelForm):
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        self.fields['copy_from'].queryset = Desk.objects.filter(agenda=self.instance.agenda)
+        if self.instance.agenda.desk_simple_management:
+            del self.fields['copy_from']
+        else:
+            self.fields['copy_from'].queryset = Desk.objects.filter(agenda=self.instance.agenda)
 
     def save(self):
-        if self.cleaned_data['copy_from']:
+        if self.instance.agenda.desk_simple_management:
+            desk = self.instance.agenda.desk_set.first()
+            if desk is not None:
+                return desk.duplicate(label=self.cleaned_data['label'])
+        elif self.cleaned_data['copy_from']:
             return self.cleaned_data['copy_from'].duplicate(label=self.cleaned_data['label'])
-        return super().save()
+
+        super().save()
+        self.instance.import_timeperiod_exceptions_from_settings(enable=True)
+        return self.instance
 
 
 class DeskForm(forms.ModelForm):
diff --git a/chrono/manager/templates/chrono/manager_meetings_agenda_settings.html b/chrono/manager/templates/chrono/manager_meetings_agenda_settings.html
index eeb3d5b..d258c57 100644
--- a/chrono/manager/templates/chrono/manager_meetings_agenda_settings.html
+++ b/chrono/manager/templates/chrono/manager_meetings_agenda_settings.html
@@ -54,16 +54,33 @@
 </div>
 </div>
 
+{% if object.desk_simple_management %}
+<div class="section">
+<h3>{% trans 'Desks' %}</h3>
+<div>
+    <ul class="objects-list single-links">
+        {% for desk in object.prefetched_desks %}
+            <li>
+                <a rel="popup" href="{% url 'chrono-manager-desk-edit' pk=desk.pk %}">{{ desk.label }}</a>
+                <a rel="popup" class="delete" href="{% url 'chrono-manager-desk-delete' pk=desk.pk %}">{% trans "remove" %}</a>
+            </li>
+        {% endfor %}
+    </ul>
+</div>
+</div>
+{% endif %}
+
 <div class="section">
 <h3>{% trans 'Time Periods' %}</h3>
 <div>
     {% if object.prefetched_desks %}
     <div class="timeperiods">
         {% for desk in object.prefetched_desks %}
+        {% if not object.desk_simple_management or object.desk_simple_management and forloop.counter == 1 %}
         <div class="timeperiod">
             {% url 'chrono-manager-agenda-add-time-period' agenda_pk=object.pk pk=desk.pk as add_time_period_url %}
             <ul class="objects-list single-links">
-                {% if forloop.counter != 1 or forloop.revcounter != 1 %}
+                {% if not object.desk_simple_management and object.prefetched_desks|length > 1 %}
                 <li><a rel="popup" href="{% url 'chrono-manager-desk-edit' pk=desk.id %}">
                     <strong>{{ desk.label }}</strong>
                     </a>
@@ -93,6 +110,7 @@
                 <li><a class="add" rel="popup" href="{{add_time_period_exception_url}}">{% trans 'Add a time period exception' %}</a></li>
             </ul>
         </div>
+        {% endif %}
         {% endfor %}
     </div>
     {% else %}
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index 63fca93..62318b6 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -1925,11 +1925,10 @@ class AgendaAddDesk(ManagedAgendaMixin, CreateView):
     template_name = 'chrono/manager_desk_form.html'
     model = Desk
     form_class = NewDeskForm
+    agenda = None
 
-    def form_valid(self, form):
-        self.object = form.save()
-        self.object.import_timeperiod_exceptions_from_settings(enable=True)
-        return HttpResponseRedirect(self.get_success_url())
+    def set_agenda(self, **kwargs):
+        self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='meetings')
 
 
 agenda_add_desk = AgendaAddDesk.as_view()
@@ -1940,6 +1939,9 @@ class DeskEditView(ManagedAgendaSubobjectMixin, UpdateView):
     model = Desk
     form_class = DeskForm
 
+    def get_queryset(self):
+        return super().get_queryset().filter(agenda__kind='meetings')
+
 
 desk_edit = DeskEditView.as_view()
 
@@ -1948,6 +1950,15 @@ class DeskDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
     template_name = 'chrono/manager_confirm_delete.html'
     model = Desk
 
+    def dispatch(self, request, *args, **kwargs):
+        agenda = self.get_object().agenda
+        if agenda.desk_set.count() == 1:
+            raise Http404
+        return super().dispatch(request, *args, **kwargs)
+
+    def get_queryset(self):
+        return super().get_queryset().filter(agenda__kind='meetings')
+
     def get_context_data(self, **kwargs):
         context = super(DeskDeleteView, self).get_context_data(**kwargs)
         context['cannot_delete'] = Booking.objects.filter(
diff --git a/tests/test_manager.py b/tests/test_manager.py
index ec77755..0f198d4 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -926,7 +926,9 @@ def test_add_agenda_exceptions_from_settings(app, admin_user):
     source = default_desk.timeperiodexceptionsource_set.first()
     assert source.enabled
 
-    resp = resp.click('New Desk')
+    agenda.desk_simple_management = False
+    agenda.save()
+    resp = app.get('/manage/agendas/%s/add-desk' % agenda.pk)
     resp.form['label'] = 'Desk A'
     resp = resp.form.submit().follow()
 
@@ -2115,7 +2117,8 @@ def test_meetings_agenda_add_desk(app, admin_user):
     assert Desk.objects.count() == 1
     assert str(Desk.objects.first()) == 'Desk 1'
     agenda = Agenda.objects.get(slug='foo-bar')
-    MeetingType(agenda=agenda, label='Blah').save()
+    agenda.desk_simple_management = False
+    agenda.save()
 
     resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
     resp = resp.click('New Desk')
@@ -2135,11 +2138,83 @@ def test_meetings_agenda_add_desk(app, admin_user):
     new_desk = Desk.objects.latest('pk')
     assert new_desk.timeperiod_set.count() == 0
 
-    resp = resp.click('Desk A', index=1)
+    # unknown pk
+    app.get('/manage/agendas/0/add-desk', status=404)
+
+    # wrong kind
+    agenda.kind = 'virtual'
+    agenda.save()
+    app.get('/manage/agendas/%s/add-desk' % agenda.pk, status=404)
+    agenda.kind = 'events'
+    agenda.save()
+    app.get('/manage/agendas/%s/add-desk' % agenda.pk, status=404)
+
+
+@override_settings(
+    EXCEPTIONS_SOURCES={
+        'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
+    }
+)
+def test_meetings_agenda_add_desk_from_another(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(
+        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
+    )
+    assert Desk.objects.count() == 1
+    assert desk.timeperiodexceptionsource_set.count() == 0
+
+    app = login(app)
+    resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
+    resp = resp.click('New Desk')
     resp.form['label'] = 'Desk B'
+    resp.form['copy_from'] = desk.pk
     resp = resp.form.submit().follow()
-    assert 'Desk A' in resp.text
-    assert 'Desk B' in resp.text
+    assert Desk.objects.count() == 2
+    new_desk = Desk.objects.latest('pk')
+    assert new_desk.label == 'Desk B'
+    assert new_desk.timeperiod_set.count() == 1
+    assert (
+        new_desk.timeperiodexceptionsource_set.count() == 0
+    )  # holidays not automatically added via duplication
+
+
+@override_settings(
+    EXCEPTIONS_SOURCES={
+        'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
+    }
+)
+def test_meetings_agenda_add_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')
+    TimePeriod.objects.create(
+        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
+    )
+    assert Desk.objects.count() == 1
+    assert desk.timeperiodexceptionsource_set.count() == 0
+
+    app = login(app)
+    resp = app.get('/manage/agendas/%s/add-desk' % agenda.pk)
+    resp.form['label'] = 'Desk B'
+    assert 'copy_from' not in resp.context['form'].fields
+    resp = resp.form.submit().follow()
+    assert Desk.objects.count() == 2
+    new_desk = Desk.objects.latest('pk')
+    assert new_desk.label == 'Desk B'
+    assert new_desk.timeperiod_set.count() == 1
+    assert (
+        new_desk.timeperiodexceptionsource_set.count() == 0
+    )  # holidays not automatically added via duplication
+
+    # ok if no desks (should not happen)
+    Desk.objects.all().delete()
+    resp = app.get('/manage/agendas/%s/add-desk' % agenda.pk)
+    resp.form['label'] = 'Desk'
+    assert 'copy_from' not in resp.context['form'].fields
+    resp = resp.form.submit().follow()
+    assert Desk.objects.count() == 1
+    new_desk = Desk.objects.latest('pk')
+    assert new_desk.timeperiodexceptionsource_set.count() == 1
 
 
 def test_meetings_agenda_edit_desk(app, admin_user):
@@ -2168,49 +2243,48 @@ def test_meetings_agenda_edit_desk(app, admin_user):
     resp = resp.form.submit()
     assert resp.context['form'].errors['slug'] == ['Another desk exists with the same identifier.']
 
+    # unknown pk
+    app.get('/manage/desks/0/edit', status=404)
 
-def test_meetings_agenda_add_desk_from_another(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(
-        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
-    )
-    assert Desk.objects.count() == 1
-
-    app = login(app)
-    resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
-    resp = resp.click('New Desk')
-    resp.form['label'] = 'Desk B'
-    resp.form['copy_from'] = desk.pk
-    resp = resp.form.submit().follow()
-    assert Desk.objects.count() == 2
-    new_desk = Desk.objects.latest('pk')
-    assert new_desk.label == 'Desk B'
-    assert new_desk.timeperiod_set.count() == 1
+    # wrong kind
+    agenda.kind = 'virtual'
+    agenda.save()
+    app.get('/manage/desks/%s/edit' % desk.pk, status=404)
+    agenda.kind = 'events'
+    agenda.save()
+    app.get('/manage/desks/%s/edit' % desk.pk, status=404)
 
 
 def test_meetings_agenda_delete_desk(app, admin_user):
+    agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
+    Desk.objects.create(agenda=agenda, label='Desk A')
+    desk_b = Desk.objects.create(agenda=agenda, label='Desk B')
+
     app = login(app)
-    resp = app.get('/manage/', status=200)
-    resp = resp.click('New')
-    resp.form['label'] = 'Foo bar'
-    resp.form['kind'] = 'meetings'
-    resp = resp.form.submit()
-    assert Desk.objects.count() == 1
-    agenda = Agenda.objects.get(slug='foo-bar')
-    MeetingType(agenda=agenda, label='Blah').save()
 
     resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
-    resp = resp.click('New Desk')
-    resp.form['label'] = 'Desk A'
-    resp = resp.form.submit().follow()
-    assert Desk.objects.count() == 2
-    resp = resp.click('Desk A', index=0)
+    resp = resp.click('Desk A')
     resp = resp.click('Delete')
     resp = resp.form.submit()
-    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
+    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk)
     assert Desk.objects.count() == 1
 
+    # only one desk
+    app.get('/manage/desks/%s/delete' % desk_b.pk, status=404)
+
+    desk_a = Desk.objects.create(agenda=agenda, label='Desk A')
+
+    # unknown pk
+    app.get('/manage/desks/0/delete', status=404)
+
+    # wrong kind
+    agenda.kind = 'virtual'
+    agenda.save()
+    app.get('/manage/desks/%s/delete' % desk_a.pk, status=404)
+    agenda.kind = 'events'
+    agenda.save()
+    app.get('/manage/desks/%s/delete' % desk_a.pk, status=404)
+
 
 def test_meetings_agenda_add_time_period_exception(app, admin_user):
     agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings')
-- 
2.29.2

