diff --git a/src/pretix/presale/templates/pretixpresale/event/index.html b/src/pretix/presale/templates/pretixpresale/event/index.html
index e5254c7b9e..e12d552c2a 100644
--- a/src/pretix/presale/templates/pretixpresale/event/index.html
+++ b/src/pretix/presale/templates/pretixpresale/event/index.html
@@ -101,6 +101,12 @@
{{ frontpage_text|rich_text }}
{% endif %}
+
+ {% if request.GET.voucher %}
+
+ {% trans "Please select a date to redeem your voucher." %}
+
+ {% endif %}
{% endif %}
{% if subevent and "year" not in request.GET %}
diff --git a/src/pretix/presale/templates/pretixpresale/event/voucher.html b/src/pretix/presale/templates/pretixpresale/event/voucher.html
index 5b800b7b45..e178e69e8b 100644
--- a/src/pretix/presale/templates/pretixpresale/event/voucher.html
+++ b/src/pretix/presale/templates/pretixpresale/event/voucher.html
@@ -10,7 +10,13 @@
{% block content %}
{% trans "Voucher redemption" %}
+
{% if subevent %}
+ {% if request.GET.subevent and subevent.pk|stringformat:"i" != request.GET.subevent %}
+
+ {% trans "This voucher is valid only for the following specific date and time." %}
+
+ {% endif %}
{{ subevent.name }}
{% with ev=subevent %}
diff --git a/src/pretix/presale/views/cart.py b/src/pretix/presale/views/cart.py
index f188a0df5e..f54286a0aa 100644
--- a/src/pretix/presale/views/cart.py
+++ b/src/pretix/presale/views/cart.py
@@ -518,7 +518,6 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, TemplateView):
kwargs={'cart_namespace': kwargs.get('cart_namespace') or ''})
if context['cart_redirect'].startswith('https:'):
context['cart_redirect'] = '/' + context['cart_redirect'].split('/', 3)[3]
-
return context
def dispatch(self, request, *args, **kwargs):
@@ -566,6 +565,10 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, TemplateView):
if hasattr(self, 'voucher') and self.voucher.subevent:
self.subevent = self.voucher.subevent
+
+ if not err and not self.subevent:
+ return redirect(eventreverse(self.request.event, 'presale:event.index',
+ kwargs={'cart_namespace': kwargs.get('cart_namespace') or ''}) + '?voucher=' + quote(self.voucher.code))
else:
pass
diff --git a/src/pretix/presale/views/event.py b/src/pretix/presale/views/event.py
index 420e7fbfea..5863052a01 100644
--- a/src/pretix/presale/views/event.py
+++ b/src/pretix/presale/views/event.py
@@ -23,7 +23,7 @@ from django.views.decorators.csrf import csrf_exempt
from django.views.generic import TemplateView
from pretix.base.channels import get_all_sales_channels
-from pretix.base.models import ItemVariation, Quota, SeatCategoryMapping
+from pretix.base.models import ItemVariation, Quota, SeatCategoryMapping, Voucher
from pretix.base.models.event import SubEvent
from pretix.base.models.items import (
ItemBundle, SubEventItem, SubEventItemVariation,
@@ -375,6 +375,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
if vouchers_exist is None:
vouchers_exist = self.request.event.vouchers.exists()
self.request.event.cache.set('vouchers_exist', vouchers_exist)
+ context['show_vouchers'] = context['vouchers_exist'] = vouchers_exist
if not self.request.event.has_subevents or self.subevent:
# Fetch all items
@@ -399,12 +400,6 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
context['items_by_category'] = item_group_by_category(items)
context['display_add_to_cart'] = display_add_to_cart
- context['show_vouchers'] = vouchers_exist
- context['vouchers_exist'] = vouchers_exist
- else:
- context['show_vouchers'] = False
- context['vouchers_exist'] = vouchers_exist
-
context['ev'] = self.subevent or self.request.event
context['subevent'] = self.subevent
context['cart'] = self.get_cart()
@@ -434,6 +429,13 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
return context
def _subevent_list_context(self):
+ voucher = None
+ if self.request.GET.get('voucher'):
+ try:
+ voucher = Voucher.objects.get(code__iexact=self.request.GET.get('voucher'), event=self.request.event)
+ except Voucher.DoesNotExist:
+ pass
+
context = {}
context['list_type'] = self.request.GET.get("style", self.request.event.settings.event_list_type)
if context['list_type'] not in ("calendar", "week") and self.request.event.subevents.filter(date_from__gt=now()).count() > 50:
@@ -456,7 +458,8 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request),
before, after, ebd, set(), self.request.event,
- self.kwargs.get('cart_namespace')
+ self.kwargs.get('cart_namespace'),
+ voucher,
)
context['show_names'] = ebd.get('_subevents_different_names', False) or sum(
@@ -484,7 +487,9 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request),
before, after, ebd, set(), self.request.event,
- self.kwargs.get('cart_namespace')
+ self.kwargs.get('cart_namespace'),
+ self.request.GET.get('voucher'),
+ voucher,
)
context['show_names'] = ebd.get('_subevents_different_names', False) or sum(
@@ -503,7 +508,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
context['subevent_list'] = self.request.event.subevents_sorted(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request)
)
- if self.request.event.settings.event_list_available_only:
+ if self.request.event.settings.event_list_available_only and not voucher:
context['subevent_list'] = [
se for se in context['subevent_list']
if not se.presale_has_ended and se.best_availability_state >= Quota.AVAILABILITY_RESERVED
diff --git a/src/pretix/presale/views/organizer.py b/src/pretix/presale/views/organizer.py
index 211d62960d..6ecac85835 100644
--- a/src/pretix/presale/views/organizer.py
+++ b/src/pretix/presale/views/organizer.py
@@ -1,6 +1,7 @@
import calendar
from collections import defaultdict
from datetime import date, datetime, time, timedelta
+from urllib.parse import quote
import isoweek
import pytz
@@ -357,7 +358,7 @@ def add_events_for_days(request, baseqs, before, after, ebd, timezones):
})
-def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_namespace=None):
+def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_namespace=None, voucher=None):
qs = qs.filter(active=True, is_public=True).filter(
Q(Q(date_to__gte=before) & Q(date_from__lte=after)) |
Q(Q(date_from__lte=after) & Q(date_to__gte=before)) |
@@ -365,6 +366,7 @@ def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_n
).order_by(
'date_from'
)
+
quotas_to_compute = []
for se in qs:
if se.presale_is_running:
@@ -388,7 +390,7 @@ def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_n
s = event.settings if event else se.event.settings
if s.event_list_available_only:
- if se.presale_has_ended or se.best_availability_state < Quota.AVAILABILITY_RESERVED:
+ if se.presale_has_ended or ((not voucher or not voucher.allow_ignore_quota) and se.best_availability_state < Quota.AVAILABILITY_RESERVED):
continue
timezones.add(s.timezones)
@@ -417,7 +419,12 @@ def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_n
else None
),
'event': se,
- 'url': eventreverse(se.event, 'presale:event.index', kwargs=kwargs)
+ 'url': (
+ eventreverse(se.event, 'presale:event.redeem',
+ kwargs={k: v for k, v in kwargs.items() if k != 'subevent'}) + f'?subevent={se.pk}&voucher={quote(voucher.code)}'
+ if voucher
+ else eventreverse(se.event, 'presale:event.index', kwargs=kwargs)
+ )
})
d += timedelta(days=1)
@@ -426,7 +433,12 @@ def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_n
'event': se,
'continued': False,
'time': datetime_from.time().replace(tzinfo=None) if s.show_times else None,
- 'url': eventreverse(se.event, 'presale:event.index', kwargs=kwargs),
+ 'url': (
+ eventreverse(se.event, 'presale:event.redeem',
+ kwargs={k: v for k, v in kwargs.items() if k != 'subevent'}) + f'?subevent={se.pk}&voucher={quote(voucher.code)}'
+ if voucher
+ else eventreverse(se.event, 'presale:event.index', kwargs=kwargs)
+ ),
'timezone': s.timezone,
})