From 452093fd5066b54a59d546d62a731fe6cae73eb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Mon, 21 Nov 2022 16:32:01 +0100
Subject: [PATCH] invoicing: link between Regie and Agenda (#71074)

---
 lingo/agendas/migrations/0004_regie.py        | 24 +++++++++
 lingo/agendas/models.py                       |  7 +++
 .../lingo/invoicing/manager_regie_detail.html | 51 +++++++++++++++----
 lingo/invoicing/views.py                      |  7 +--
 .../lingo/pricing/manager_agenda_detail.html  | 10 ++++
 lingo/pricing/urls.py                         |  5 ++
 lingo/pricing/views.py                        | 16 ++++++
 tests/invoicing/test_manager.py               | 23 ++++++---
 tests/pricing/manager/test_agenda.py          | 15 ++++++
 9 files changed, 140 insertions(+), 18 deletions(-)
 create mode 100644 lingo/agendas/migrations/0004_regie.py

diff --git a/lingo/agendas/migrations/0004_regie.py b/lingo/agendas/migrations/0004_regie.py
new file mode 100644
index 0000000..0ad16cc
--- /dev/null
+++ b/lingo/agendas/migrations/0004_regie.py
@@ -0,0 +1,24 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('invoicing', '0001_initial'),
+        ('agendas', '0003_check_type_group'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='agenda',
+            name='regie',
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                to='invoicing.Regie',
+                verbose_name='Regie',
+            ),
+        ),
+    ]
diff --git a/lingo/agendas/models.py b/lingo/agendas/models.py
index 5ef9534..ee71bd8 100644
--- a/lingo/agendas/models.py
+++ b/lingo/agendas/models.py
@@ -36,6 +36,13 @@ class Agenda(models.Model):
         null=True,
         on_delete=models.SET_NULL,
     )
+    regie = models.ForeignKey(
+        'invoicing.Regie',
+        verbose_name=_('Regie'),
+        blank=True,
+        null=True,
+        on_delete=models.SET_NULL,
+    )
 
     def __str__(self):
         return self.label
diff --git a/lingo/invoicing/templates/lingo/invoicing/manager_regie_detail.html b/lingo/invoicing/templates/lingo/invoicing/manager_regie_detail.html
index f59af8a..3aa5488 100644
--- a/lingo/invoicing/templates/lingo/invoicing/manager_regie_detail.html
+++ b/lingo/invoicing/templates/lingo/invoicing/manager_regie_detail.html
@@ -15,14 +15,47 @@
 {% endblock %}
 
 {% block content %}
-  {% if regie.description %}
-    <div class="bo-block">{{regie.description}}</div>
-  {% endif %}
-  <div class="bo-block">
-    <h3>{% trans "Parameters" %}</h3>
-    <ul>
-      <li>{% trans "slug" %}&nbsp;: {{regie.slug}}</li>
-      <li>{% trans "cashier role" %}&nbsp;: {{regie.cashier_role}}</li>
-    </ul>
+  <div class="section">
+    <div class="pk-tabs">
+      <div class="pk-tabs--tab-list" role="tablist">
+        <button aria-controls="panel-settings" aria-selected="true" id="tab-settings" role="tab" tabindex="0">{% trans "Settings" %}</button>
+        <button aria-controls="panel-usage" aria-selected="false" id="tab-usage" role="tab" tabindex="-1">{% trans "Used in agendas" %}</button>
+      </div>
+      <div class="pk-tabs--container">
+
+        <div aria-labelledby="tab-settings" id="panel-settings" role="tabpanel" tabindex="0">
+          {% if regie.description %}
+            <h3>{% trans "Description" %}</h3>
+            <p>{{ regie.description|linebreaksbr }}</p>
+          {% endif %}
+          <h3>{% trans "Parameters" %}</h3>
+          <ul>
+            <li>{% trans "Identifier:" %} {{ regie.slug }}</li>
+            <li>{% trans "Cashier role:" %} {{ regie.cashier_role }}</li>
+          </ul>
+        </div>
+
+        <div aria-labelledby="tab-usage" hidden="" id="panel-usage" role="tabpanel" tabindex="0">
+          {% if agendas %}
+            <ul class="objects-list single-links">
+              {% for agenda in agendas %}
+                <li>
+                  <a href="{% url 'lingo-manager-agenda-detail' pk=agenda.pk %}">
+                    {{ agenda.label }}
+                  </a>
+                </li>
+              {% endfor %}
+            </ul>
+          {% else %}
+            <div class="big-msg-info">
+              {% blocktrans trimmed %}
+                This Regie is not used yet.
+              {% endblocktrans %}
+            </div>
+          {% endif %}
+        </div>
+
+      </div>
+    </div>
   </div>
 {% endblock %}
diff --git a/lingo/invoicing/views.py b/lingo/invoicing/views.py
index e12cc9b..fa75e66 100644
--- a/lingo/invoicing/views.py
+++ b/lingo/invoicing/views.py
@@ -34,6 +34,7 @@ from django.views.generic import (
     UpdateView,
 )
 
+from lingo.agendas.models import Agenda
 from lingo.invoicing.models import Regie, RegieImportError
 from lingo.pricing.forms import ImportForm
 
@@ -83,9 +84,9 @@ class RegieDetailView(DetailView):
     model = Regie
 
     def get_context_data(self, **kwargs):
-        context = super().get_context_data(**kwargs)
-        context['regie'] = self.object
-        return context
+        kwargs['regie'] = self.object
+        kwargs['agendas'] = Agenda.objects.filter(regie=self.object)
+        return super().get_context_data(**kwargs)
 
 
 regie_detail = RegieDetailView.as_view()
diff --git a/lingo/pricing/templates/lingo/pricing/manager_agenda_detail.html b/lingo/pricing/templates/lingo/pricing/manager_agenda_detail.html
index 79e6afa..fd5d9af 100644
--- a/lingo/pricing/templates/lingo/pricing/manager_agenda_detail.html
+++ b/lingo/pricing/templates/lingo/pricing/manager_agenda_detail.html
@@ -22,6 +22,7 @@
       <div class="pk-tabs--tab-list" role="tablist">
         <button aria-controls="panel-pricings" aria-selected="true" id="tab-pricings" role="tab" tabindex="0">{% trans "Pricings" context 'agenda pricing' %}</button>
         <button aria-controls="panel-check" aria-selected="false" id="tab-check" role="tab" tabindex="-1">{% trans "Booking check options" %}</button>
+        <button aria-controls="panel-invoicing" aria-selected="false" id="tab-invoicing" role="tab" tabindex="-1">{% trans "Invoicing options" %}</button>
       </div>
       <div class="pk-tabs--container">
 
@@ -96,6 +97,15 @@
           </div>
         </div>
 
+        <div aria-labelledby="tab-invoicing" hidden="" id="panel-invoicing" role="tabpanel" tabindex="0">
+          <ul>
+            <li>{% trans "Regie:" %} {{ object.regie|default:"" }}</li>
+          </ul>
+          <div class="panel--buttons">
+            <a rel="popup" class="button" href="{% url 'lingo-manager-agenda-invoicing-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
+          </div>
+        </div>
+
       </div>
     </div>
   </div>
diff --git a/lingo/pricing/urls.py b/lingo/pricing/urls.py
index 9f5d3d9..f4d4b0e 100644
--- a/lingo/pricing/urls.py
+++ b/lingo/pricing/urls.py
@@ -153,6 +153,11 @@ urlpatterns = [
         views.agenda_booking_check_settings,
         name='lingo-manager-agenda-booking-check-settings',
     ),
+    path(
+        'agenda/<int:pk>/invoicing-options/',
+        views.agenda_invoicing_settings,
+        name='lingo-manager-agenda-invoicing-settings',
+    ),
     path(
         'agenda-pricings/',
         views.agenda_pricing_list,
diff --git a/lingo/pricing/views.py b/lingo/pricing/views.py
index b111554..fe3ca42 100644
--- a/lingo/pricing/views.py
+++ b/lingo/pricing/views.py
@@ -747,6 +747,22 @@ class AgendaBookingCheckSettingsView(AgendaMixin, UpdateView):
 agenda_booking_check_settings = AgendaBookingCheckSettingsView.as_view()
 
 
+class AgendaInvoicingSettingsView(AgendaMixin, UpdateView):
+    template_name = 'lingo/pricing/manager_agenda_form.html'
+    model = Agenda
+    fields = ['regie']
+    tab_anchor = 'invoicing'
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['form_url'] = reverse('lingo-manager-agenda-invoicing-settings', args=[self.agenda.pk])
+        context['title'] = _("Configure invoicing options")
+        return context
+
+
+agenda_invoicing_settings = AgendaInvoicingSettingsView.as_view()
+
+
 class AgendaPricingListView(ListView):
     template_name = 'lingo/pricing/manager_agenda_pricing_list.html'
     model = AgendaPricing
diff --git a/tests/invoicing/test_manager.py b/tests/invoicing/test_manager.py
index 62fbe8e..09f3d52 100644
--- a/tests/invoicing/test_manager.py
+++ b/tests/invoicing/test_manager.py
@@ -6,6 +6,7 @@ from django.contrib.auth.models import Group
 from django.urls import reverse
 from webtest import Upload
 
+from lingo.agendas.models import Agenda
 from lingo.invoicing.models import Regie
 from tests.utils import login
 
@@ -95,12 +96,14 @@ def test_manager_invoicing_regie_detail(app, admin_user):
     resp = app.get(reverse('lingo-manager-invoicing-regie-detail', kwargs={'pk': regie.pk}))
     h2 = resp.pyquery('div#appbar h2')
     assert h2.text() == 'Regie - Foo'
-    descr = resp.pyquery('div#content div.bo-block')[0]
+    descr = resp.pyquery('div#panel-settings p')[0]
     assert descr.text == 'foo description'
-    slug = resp.pyquery('div#content div.bo-block ul li')[0]
-    assert slug.text == 'slug\xa0: foo'
-    cashier_role = resp.pyquery('div#content div.bo-block ul li')[1]
-    assert cashier_role.text == 'cashier role\xa0: role-foo'
+    slug = resp.pyquery('div#panel-settings ul li')[0]
+    assert slug.text == 'Identifier: foo'
+    cashier_role = resp.pyquery('div#panel-settings ul li')[1]
+    assert cashier_role.text == 'Cashier role: role-foo'
+    usage = resp.pyquery('div#panel-usage div')[0]
+    assert 'This Regie is not used yet.' in usage.text
     edit_button = resp.pyquery(
         'span.actions a[href="%s"]' % reverse('lingo-manager-invoicing-regie-edit', kwargs={'pk': regie.pk})
     )
@@ -110,6 +113,14 @@ def test_manager_invoicing_regie_detail(app, admin_user):
     )
     assert delete_button.text() == 'Delete'
 
+    agenda1 = Agenda.objects.create(label='Foo Bar', regie=regie)
+    agenda2 = Agenda.objects.create(label='Foo Bar 2', regie=regie)
+    agenda3 = Agenda.objects.create(label='Foo Bar 3')
+    resp = app.get(reverse('lingo-manager-invoicing-regie-detail', kwargs={'pk': regie.pk}))
+    assert '/manage/pricing/agenda/%s/' % agenda1.pk in resp
+    assert '/manage/pricing/agenda/%s/' % agenda2.pk in resp
+    assert '/manage/pricing/agenda/%s/' % agenda3.pk not in resp
+
 
 def test_manager_invoicing_regie_edit(app, admin_user):
     app = login(app)
@@ -151,7 +162,7 @@ def test_manager_invoicing_regie_import_export(app, admin_user, freezer):
     app = login(app)
     group = Group.objects.create(name='role-foo')
     regie1 = Regie.objects.create(label='Foo', description='foo description', cashier_role=group)
-    regie2 = Regie.objects.create(label='Bar', description='bar description', cashier_role=group)
+    Regie.objects.create(label='Bar', description='bar description', cashier_role=group)
     response = app.get(reverse('lingo-manager-invoicing-regie-export'))
     assert response.headers['content-type'] == 'application/json'
     assert response.headers['content-disposition'] == 'attachment; filename="export_regies_20200615.json"'
diff --git a/tests/pricing/manager/test_agenda.py b/tests/pricing/manager/test_agenda.py
index 7a19f32..8be59a0 100644
--- a/tests/pricing/manager/test_agenda.py
+++ b/tests/pricing/manager/test_agenda.py
@@ -3,6 +3,7 @@ from unittest import mock
 import pytest
 
 from lingo.agendas.models import Agenda, CheckTypeGroup
+from lingo.invoicing.models import Regie
 from tests.utils import login
 
 pytestmark = pytest.mark.django_db
@@ -59,3 +60,17 @@ def test_edit_agenda_check_type_group(app, admin_user):
     agenda.refresh_from_db()
     assert agenda.check_type_group == group
     assert 'Check type group: Foo bar' in resp
+
+
+def test_edit_agenda_invoicing_settings(app, admin_user):
+    agenda = Agenda.objects.create(label='Foo bar')
+    regie = Regie.objects.create(label='Foo bar')
+
+    app = login(app)
+    resp = app.get('/manage/pricing/agenda/%s/' % agenda.pk)
+    resp = resp.click(href='/manage/pricing/agenda/%s/invoicing-options' % agenda.pk)
+    resp.form['regie'] = regie.pk
+    resp = resp.form.submit().follow()
+    agenda.refresh_from_db()
+    assert agenda.regie == regie
+    assert 'Regie: Foo bar' in resp
-- 
2.35.1

