diff --git a/src/pretix/control/templates/pretixcontrol/vouchers/detail.html b/src/pretix/control/templates/pretixcontrol/vouchers/detail.html index f8e0456785..e8262f8ef8 100644 --- a/src/pretix/control/templates/pretixcontrol/vouchers/detail.html +++ b/src/pretix/control/templates/pretixcontrol/vouchers/detail.html @@ -32,7 +32,7 @@
diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html b/src/pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html index a9455d239e..7cfa42e89c 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html @@ -1,7 +1,7 @@ {% load i18n %} {% load eventurl %} {% for subev in subevent_list %} -
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, })