+ {% trans "Quotas" %}
+
+
+
+ {% trans "Save" %}
+
+
+
+{% endblock %}
diff --git a/src/pretix/control/templates/pretixcontrol/subevents/index.html b/src/pretix/control/templates/pretixcontrol/subevents/index.html
index 52ac83fdf..f73d44b92 100644
--- a/src/pretix/control/templates/pretixcontrol/subevents/index.html
+++ b/src/pretix/control/templates/pretixcontrol/subevents/index.html
@@ -182,7 +182,7 @@
{% endif %}
{% if "event.subevents:write" in request.eventpermset %}
-
+
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py
index 35c659fd3..7b329d593 100644
--- a/src/pretix/control/urls.py
+++ b/src/pretix/control/urls.py
@@ -308,7 +308,8 @@ urlpatterns = [
re_path(r'^pdf/editor/(?P[^/]+).pdf$', pdf.PdfView.as_view(), name='pdf.background'),
re_path(r'^subevents/$', subevents.SubEventList.as_view(), name='event.subevents'),
re_path(r'^subevents/select2$', typeahead.subevent_select2, name='event.subevents.select2'),
- re_path(r'^subevents/(?P\d+)/$', subevents.SubEventUpdate.as_view(), name='event.subevent'),
+ re_path(r'^subevents/(?P\d+)/$', subevents.SubEventDetail.as_view(), name='event.subevent'),
+ re_path(r'^subevents/(?P\d+)/edit$', subevents.SubEventUpdate.as_view(), name='event.subevent.edit'),
re_path(r'^subevents/(?P\d+)/delete$', subevents.SubEventDelete.as_view(),
name='event.subevent.delete'),
re_path(r'^subevents/add$', subevents.SubEventCreate.as_view(), name='event.subevents.add'),
diff --git a/src/pretix/control/views/subevents.py b/src/pretix/control/views/subevents.py
index 4d5fbbc0c..cf0ae69c9 100644
--- a/src/pretix/control/views/subevents.py
+++ b/src/pretix/control/views/subevents.py
@@ -41,7 +41,9 @@ from django.contrib import messages
from django.core.exceptions import ValidationError
from django.core.files import File
from django.db import transaction
-from django.db.models import Count, F, Prefetch, ProtectedError
+from django.db.models import (
+ Count, Exists, F, OuterRef, Prefetch, ProtectedError, Subquery,
+)
from django.db.models.functions import Coalesce, TruncDate, TruncTime
from django.forms import inlineformset_factory
from django.http import Http404, HttpResponse, HttpResponseRedirect
@@ -52,14 +54,17 @@ from django.utils.functional import cached_property
from django.utils.timezone import make_aware, now
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django.views import View
-from django.views.generic import CreateView, FormView, ListView, UpdateView
+from django.views.generic import (
+ CreateView, DetailView, FormView, ListView, UpdateView,
+)
-from pretix.base.models import CartPosition, LogEntry
+from pretix.base.models import CartPosition, LogEntry, OrderPosition
from pretix.base.models.checkin import CheckinList
from pretix.base.models.event import SubEvent, SubEventMetaValue
from pretix.base.models.items import (
- ItemVariation, Quota, SubEventItem, SubEventItemVariation,
+ Item, ItemVariation, Quota, SubEventItem, SubEventItemVariation,
)
+from pretix.base.models.orders import CancellationRequest
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services import tickets
from pretix.base.services.quotas import QuotaAvailability
@@ -505,9 +510,67 @@ class SubEventEditorMixin(MetaDataEditorMixin):
) and self.cl_formset.is_valid() and all(f.is_valid() for f in self.plugin_forms)
-class SubEventUpdate(EventPermissionRequiredMixin, SubEventEditorMixin, UpdateView):
+class SubEventDetail(EventPermissionRequiredMixin, DetailView):
model = SubEvent
template_name = 'pretixcontrol/subevents/detail.html'
+ permission = None
+ context_object_name = 'subevent'
+
+ def get_object(self, queryset=None) -> SubEvent:
+ try:
+ return self.request.event.subevents.get(
+ id=self.kwargs['subevent']
+ )
+ except SubEvent.DoesNotExist:
+ raise Http404(pgettext_lazy("subevent", "The requested date does not exist."))
+
+ def get_context_data(self, **kwargs):
+ oqs = self.request.event.orders.filter(
+ Exists(
+ OrderPosition.all.filter(
+ subevent=self.object,
+ )
+ )
+ ).annotate(
+ pcnt=Subquery(
+ OrderPosition.objects.filter(
+ subevent=self.object,
+ ).values("subevent").annotate(c=Count("*")).values("c")
+ ),
+ has_cancellation_request=Exists(CancellationRequest.objects.filter(order=OuterRef("pk"))),
+ ).select_related("invoice_address").prefetch_related("sales_channel")
+ ctx = {
+ "quotas": self.object.quotas.prefetch_related(
+ Prefetch(
+ "items",
+ queryset=Item.objects.annotate(
+ has_variations=Exists(ItemVariation.objects.filter(item=OuterRef("pk")))
+ ),
+ to_attr="cached_items"
+ ),
+ "variations",
+ "variations__item",
+ ).order_by("name", "pk"),
+ "checkinlists": self.object.checkinlist_set.prefetch_related("limit_products"),
+ "orders": oqs[:11],
+ "order_count": oqs.count(),
+ }
+
+ qa = QuotaAvailability()
+ qa.queue(*ctx["quotas"])
+ qa.compute()
+ for quota in ctx["quotas"]:
+ quota.cached_avail = qa.results[quota]
+
+ return super().get_context_data(
+ **kwargs,
+ **ctx,
+ )
+
+
+class SubEventUpdate(EventPermissionRequiredMixin, SubEventEditorMixin, UpdateView):
+ model = SubEvent
+ template_name = 'pretixcontrol/subevents/edit.html'
permission = 'event.subevents:write'
context_object_name = 'subevent'
form_class = SubEventForm
@@ -585,7 +648,7 @@ class SubEventUpdate(EventPermissionRequiredMixin, SubEventEditorMixin, UpdateVi
class SubEventCreate(SubEventEditorMixin, EventPermissionRequiredMixin, CreateView):
model = SubEvent
- template_name = 'pretixcontrol/subevents/detail.html'
+ template_name = 'pretixcontrol/subevents/edit.html'
permission = 'event.subevents:write'
context_object_name = 'subevent'
form_class = SubEventForm
diff --git a/src/tests/control/test_permissions.py b/src/tests/control/test_permissions.py
index aaff20d9e..83f68da87 100644
--- a/src/tests/control/test_permissions.py
+++ b/src/tests/control/test_permissions.py
@@ -137,6 +137,7 @@ event_urls = [
"subevents/select2",
"subevents/add",
"subevents/2/delete",
+ "subevents/2/edit",
"subevents/2/",
"quotas/",
"quotas/2/delete",
@@ -360,8 +361,9 @@ event_permission_urls = [
("event.items:write", "discounts/reorder", 400, HTTP_POST),
("event.items:write", "discounts/add", 200, HTTP_GET),
(None, "subevents/", 200, HTTP_GET),
- ("event.subevents:write", "subevents/2/", 404, HTTP_GET),
- ("event.subevents:write", "subevents/2/", 404, HTTP_POST),
+ (None, "subevents/2/", 404, HTTP_GET),
+ ("event.subevents:write", "subevents/2/edit", 404, HTTP_GET),
+ ("event.subevents:write", "subevents/2/edit", 404, HTTP_POST),
("event.subevents:write", "subevents/2/delete", 404, HTTP_GET),
("event.subevents:write", "subevents/add", 200, HTTP_GET),
("event.subevents:write", "subevents/bulk_add", 200, HTTP_GET),
diff --git a/src/tests/control/test_subevents.py b/src/tests/control/test_subevents.py
index 5e58e0ebf..1089e9891 100644
--- a/src/tests/control/test_subevents.py
+++ b/src/tests/control/test_subevents.py
@@ -110,9 +110,9 @@ class SubEventsTest(SoupTest):
assert se.checkinlist_set.count() == 1
def test_modify(self):
- doc = self.get_doc('/control/event/ccc/30c3/subevents/%d/' % self.subevent1.pk)
+ doc = self.get_doc('/control/event/ccc/30c3/subevents/%d/edit' % self.subevent1.pk)
assert doc.select("input[name=quotas-TOTAL_FORMS]")
- doc = self.post_doc('/control/event/ccc/30c3/subevents/%d/' % self.subevent1.pk, {
+ doc = self.post_doc('/control/event/ccc/30c3/subevents/%d/edit' % self.subevent1.pk, {
'name_0': 'SE2',
'active': 'on',
'date_from_0': '2017-07-01',