pricing: agenda management on agenda pricing page (#67196)

This commit is contained in:
Lauréline Guérin 2022-07-21 15:40:40 +02:00
parent 0c4ae4e39b
commit 3f9e689e1c
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 210 additions and 1 deletions

View File

@ -23,7 +23,7 @@ from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from lingo.agendas.chrono import ChronoError, get_event, get_subscriptions
from lingo.agendas.models import CheckType
from lingo.agendas.models import Agenda, CheckType
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, PricingError
@ -164,6 +164,25 @@ class AgendaPricingForm(NewAgendaPricingForm):
return slug
class AgendaPricingAgendaAddForm(forms.Form):
agenda = forms.ModelChoiceField(label=_('Agenda to add'), queryset=Agenda.objects.none(), required=True)
def __init__(self, *args, **kwargs):
self.agenda_pricing = kwargs.pop('agenda_pricing')
super().__init__(*args, **kwargs)
self.fields['agenda'].queryset = Agenda.objects.exclude(agendapricings=self.agenda_pricing)
def clean_agenda(self):
agenda = self.cleaned_data['agenda']
overlapping_qs = AgendaPricing.objects.filter(agendas=agenda).extra(
where=["(date_start, date_end) OVERLAPS (%s, %s)"],
params=[self.agenda_pricing.date_start, self.agenda_pricing.date_end],
)
if overlapping_qs.exists():
raise forms.ValidationError(_('This agendas has already a pricing overlapping this period.'))
return agenda
class PricingMatrixForm(forms.Form):
def __init__(self, *args, **kwargs):
matrix = kwargs.pop('matrix')

View File

@ -0,0 +1,22 @@
{% extends "lingo/pricing/manager_agenda_pricing_detail.html" %}
{% load i18n %}
{% block breadcrumb %}
{{ block.super }}
<a href="{% url 'lingo-manager-agenda-pricing-agenda-add' object.pk %}">{% trans "Add an agenda" %}</a>
{% endblock %}
{% block appbar %}
<h2>{% trans "Add an agenda" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Save" %}</button>
<a class="cancel" href="{% url 'lingo-manager-agenda-pricing-detail' object.pk %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -27,6 +27,7 @@
<div class="pk-tabs">
<div class="pk-tabs--tab-list" role="tablist">
<button aria-controls="panel-options" aria-selected="true" id="tab-options" role="tab" tabindex="0">{% trans "Options" %}</button>
<button aria-controls="panel-agendas" aria-selected="false" id="tab-agendas" role="tab" tabindex="0">{% trans "Agendas" %}</button>
<button aria-controls="panel-debug" aria-selected="false" id="tab-debug" role="tab" tabindex="0">{% trans "Test tool" %}</button>
{% for matrix in iter_matrix %}
<button aria-controls="panel-matrix-{{ matrix.criteria.slug }}" aria-selected="false" id="tab-matrix-{{ matrix.criteria.slug }}" role="tab" tabindex="-1">{% trans "Pricings" context 'amount' %}{% if matrix.criteria %} - {{ matrix.criteria.label }}{% endif %}</button>
@ -43,6 +44,22 @@
</ul>
</div>
<div aria-labelledby="tab-agendas" hidden id="panel-agendas" role="tabpanel" tabindex="0">
<ul class="objects-list single-links">
{% for agenda in object.agendas.all %}
<li>
<a href="{% url 'lingo-manager-agenda-detail' pk=agenda.pk %}">
{{ agenda.label }}
</a>
<a class="delete" rel="popup" href="{% url 'lingo-manager-agenda-pricing-agenda-delete' object.pk agenda.pk %}">{% trans "remove"%}</a>
</li>
{% endfor %}
</ul>
<div class="panel--buttons">
<a class="pk-button" rel="popup" href="{% url 'lingo-manager-agenda-pricing-agenda-add' object.pk %}">{% trans "Add an agenda" %}</a>
</div>
</div>
<div aria-labelledby="tab-debug" hidden id="panel-debug" role="tabpanel" tabindex="0">
<form method="get" enctype="multipart/form-data" action="{% url 'lingo-manager-agenda-pricing-test-tool' object.pk %}">
{{ test_tool_form.as_p }}

View File

@ -188,6 +188,16 @@ urlpatterns = [
views.agenda_pricing_test_tool,
name='lingo-manager-agenda-pricing-test-tool',
),
url(
r'^agenda-pricing/(?P<pk>\d+)/agenda/add/$',
views.agenda_pricing_agenda_add,
name='lingo-manager-agenda-pricing-agenda-add',
),
url(
r'^agenda-pricing/(?P<pk>\d+)/agenda/(?P<agenda_pk>\d+)/delete/$',
views.agenda_pricing_agenda_delete,
name='lingo-manager-agenda-pricing-agenda-delete',
),
url(
r'^agenda-pricing/(?P<pk>\d+)/matrix/edit/$',
views.agenda_pricing_matrix_edit,

View File

@ -44,6 +44,7 @@ from lingo.agendas.chrono import refresh_agendas
from lingo.agendas.models import Agenda, CheckType, CheckTypeGroup
from lingo.agendas.views import AgendaMixin
from lingo.pricing.forms import (
AgendaPricingAgendaAddForm,
AgendaPricingForm,
CheckTypeForm,
CriteriaForm,
@ -827,6 +828,61 @@ class AgendaPricingExport(DetailView):
agenda_pricing_export = AgendaPricingExport.as_view()
class AgendaPricingAgendaAddView(FormView):
template_name = 'lingo/pricing/manager_agenda_pricing_agenda_form.html'
model = AgendaPricing
form_class = AgendaPricingAgendaAddForm
def dispatch(self, request, *args, **kwargs):
self.object = get_object_or_404(AgendaPricing, pk=kwargs['pk'])
return super().dispatch(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['agenda_pricing'] = self.object
return kwargs
def get_context_data(self, **kwargs):
kwargs['object'] = self.object
return super().get_context_data(**kwargs)
def form_valid(self, form):
self.object.agendas.add(form.cleaned_data['agenda'])
return super().form_valid(form)
def get_success_url(self):
return '%s#open:agendas' % reverse('lingo-manager-agenda-pricing-detail', args=[self.object.pk])
agenda_pricing_agenda_add = AgendaPricingAgendaAddView.as_view()
class AgendaPricingAgendaDeleteView(DeleteView):
template_name = 'lingo/manager_confirm_delete.html'
model = Agenda
pk_url_kwarg = 'agenda_pk'
def dispatch(self, request, *args, **kwargs):
self.agenda_pricing = get_object_or_404(AgendaPricing, pk=kwargs['pk'])
return super().dispatch(request, *args, **kwargs)
def get_queryset(self):
return self.agenda_pricing.agendas.all()
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.agenda_pricing.agendas.remove(self.object)
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return '%s#open:agendas' % reverse(
'lingo-manager-agenda-pricing-detail', args=[self.agenda_pricing.pk]
)
agenda_pricing_agenda_delete = AgendaPricingAgendaDeleteView.as_view()
class AgendaPricingMatrixEdit(FormView):
template_name = 'lingo/pricing/manager_agenda_pricing_matrix_form.html'

View File

@ -117,6 +117,91 @@ def test_delete_agenda_pricing(app, admin_user):
assert AgendaPricing.objects.exists() is False
def test_agenda_pricing_add_agenda(app, admin_user):
pricing = Pricing.objects.create(label='Model')
agenda_pricing = AgendaPricing.objects.create(
pricing=pricing,
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
agenda1 = Agenda.objects.create(label='Foo bar 1')
agenda2 = Agenda.objects.create(label='Foo bar 2')
agenda3 = Agenda.objects.create(label='Foo bar 3')
app = login(app)
resp = app.get('/manage/pricing/agenda-pricing/%s/' % agenda_pricing.pk)
resp = resp.click(href='/manage/pricing/agenda-pricing/%s/agenda/add/' % agenda_pricing.pk)
assert list(resp.context['form'].fields['agenda'].queryset) == [
agenda1,
agenda2,
agenda3,
]
resp.form['agenda'] = agenda1.pk
resp = resp.form.submit()
assert resp.location.endswith('/manage/pricing/agenda-pricing/%s/#open:agendas' % agenda_pricing.pk)
resp = resp.follow()
assert list(agenda_pricing.agendas.all()) == [agenda1]
resp = app.get('/manage/pricing/agenda-pricing/%s/agenda/add/' % agenda_pricing.pk)
assert list(resp.context['form'].fields['agenda'].queryset) == [
agenda2,
agenda3,
]
resp.form['agenda'] = agenda2.pk
resp = resp.form.submit().follow()
assert list(agenda_pricing.agendas.all()) == [agenda1, agenda2]
agenda_pricing2 = AgendaPricing.objects.create(
pricing=pricing,
date_start=datetime.date(year=2021, month=11, day=1),
date_end=datetime.date(year=2022, month=11, day=1),
)
resp = app.get('/manage/pricing/agenda-pricing/%s/agenda/add/' % agenda_pricing2.pk)
assert list(resp.context['form'].fields['agenda'].queryset) == [
agenda1,
agenda2,
agenda3,
]
resp.form['agenda'] = agenda1.pk
resp = resp.form.submit()
assert resp.context['form'].errors['agenda'] == [
'This agendas has already a pricing overlapping this period.'
]
resp.form['agenda'] = agenda3.pk
resp = resp.form.submit().follow()
assert list(agenda_pricing2.agendas.all()) == [agenda3]
def test_agenda_pricing_delete_agenda(app, admin_user):
pricing = Pricing.objects.create(label='Model')
agenda_pricing = AgendaPricing.objects.create(
pricing=pricing,
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
agenda1 = Agenda.objects.create(label='Foo bar 1')
agenda2 = Agenda.objects.create(label='Foo bar 2')
Agenda.objects.create(label='Foo bar 3')
agenda_pricing.agendas.add(agenda1, agenda2)
app = login(app)
resp = app.get('/manage/pricing/agenda-pricing/%s/' % agenda_pricing.pk)
resp = resp.click(
href='/manage/pricing/agenda-pricing/%s/agenda/%s/delete/' % (agenda_pricing.pk, agenda1.pk)
)
resp = resp.form.submit()
assert resp.location.endswith('/manage/pricing/agenda-pricing/%s/#open:agendas' % agenda_pricing.pk)
resp = resp.follow()
assert list(agenda_pricing.agendas.all()) == [agenda2]
# not linked
app.get(
'/manage/pricing/agenda-pricing/%s/agenda/%s/delete/' % (agenda_pricing.pk, agenda1.pk), status=404
)
# unknown
app.get('/manage/pricing/agenda-pricing/%s/agenda/%s/delete/' % (agenda_pricing.pk, 0), status=404)
def test_detail_agenda_pricing_3_categories(app, admin_user):
category1 = CriteriaCategory.objects.create(label='Cat 1')
Criteria.objects.create(label='Crit 1-1', slug='crit-1-1', category=category1, order=1)