Improvements around the waiting list (#2219)

* Waiting list: Support for seated events, pre-fill customer email address

* Allow people to remove themselves

* Update src/pretix/base/settings.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/presale/views/waiting.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Resolve a review note

* Review notes

* Fix linter issues

* Fix import

Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
Raphael Michel
2021-09-27 20:48:02 +02:00
committed by GitHub
parent a9a4cf6fca
commit 9f2ffc3276
15 changed files with 359 additions and 62 deletions

View File

@@ -180,7 +180,7 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
).order_by('category__position', 'category_id', 'position', 'name')
if require_seat:
items = items.filter(requires_seat__gt=0)
else:
elif require_seat is not None:
items = items.filter(requires_seat=0)
if filter_items:
@@ -427,14 +427,38 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
self.request.event.cache.set('vouchers_exist', vouchers_exist)
context['show_vouchers'] = context['vouchers_exist'] = vouchers_exist
context['ev'] = self.subevent or self.request.event
context['subevent'] = self.subevent
context['allow_waitinglist'] = self.request.event.settings.waiting_list_enabled and context['ev'].presale_is_running
if not self.request.event.has_subevents or self.subevent:
# Fetch all items
items, display_add_to_cart = get_grouped_items(
self.request.event, self.subevent,
filter_items=self.request.GET.getlist('item'),
filter_categories=self.request.GET.getlist('category'),
require_seat=None,
channel=self.request.sales_channel.identifier
)
context['waitinglist_seated'] = False
if context['allow_waitinglist']:
for i in items:
if not i.allow_waitinglist or not i.requires_seat:
continue
if i.has_variations:
for v in i.available_variations:
if v.cached_availability[0] != Quota.AVAILABILITY_OK:
context['waitinglist_seated'] = True
break
else:
if i.cached_availability[0] != Quota.AVAILABILITY_OK:
context['waitinglist_seated'] = True
break
items = [i for i in items if not i.requires_seat]
context['itemnum'] = len(items)
context['allfree'] = all(
item.display_price.gross == Decimal('0.00') for item in items if not item.has_variations
@@ -450,11 +474,8 @@ 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['ev'] = self.subevent or self.request.event
context['subevent'] = self.subevent
context['cart'] = self.get_cart()
context['has_addon_choices'] = any(cp.has_addon_choices for cp in get_cart(self.request))
context['allow_waitinglist'] = self.request.event.settings.waiting_list_enabled and context['ev'].presale_is_running
if self.subevent:
context['frontpage_text'] = str(self.subevent.frontpage_text)

View File

@@ -19,21 +19,24 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from datetime import timedelta
from django.conf import settings
from django.contrib import messages
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django.views.generic import FormView
from django.views.generic import FormView, TemplateView
from pretix.base.models.event import SubEvent
from pretix.base.models import Quota, SubEvent
from pretix.base.templatetags.urlreplace import url_replace
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.views import EventViewMixin
from ...base.i18n import get_language_without_region
from ...base.models import Item, ItemVariation, WaitingListEntry
from ...base.models import Voucher, WaitingListEntry
from ..forms.waitinglist import WaitingListForm
from . import allow_frame_if_namespaced
@@ -47,17 +50,23 @@ class WaitingView(EventViewMixin, FormView):
kwargs = super().get_form_kwargs()
kwargs['event'] = self.request.event
kwargs['instance'] = WaitingListEntry(
item=self.item_and_variation[0], variation=self.item_and_variation[1],
event=self.request.event, locale=get_language_without_region(),
subevent=self.subevent
)
kwargs['channel'] = self.request.sales_channel.identifier
kwargs.setdefault('initial', {})
if 'var' in self.request.GET:
kwargs['initial']['itemvar'] = f'{self.request.GET.get("item")}-{self.request.GET.get("var")}'
else:
kwargs['initial']['itemvar'] = self.request.GET.get("item")
if getattr(self.request, 'customer', None):
kwargs['initial']['email'] = self.request.customer.email
return kwargs
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['event'] = self.request.event
ctx['subevent'] = self.subevent
ctx['item'], ctx['variation'] = self.item_and_variation
return ctx
def get(self, request, *args, **kwargs):
@@ -77,20 +86,6 @@ class WaitingView(EventViewMixin, FormView):
return super().get(request, *args, **kwargs)
@cached_property
def item_and_variation(self):
try:
item = self.request.event.items.get(pk=self.request.GET.get('item'))
if 'var' in self.request.GET:
var = item.variations.get(pk=self.request.GET['var'])
elif item.has_variations:
return None
else:
var = None
return item, var
except (Item.DoesNotExist, ItemVariation.DoesNotExist, ValueError):
return None
def dispatch(self, request, *args, **kwargs):
self.request = request
@@ -106,14 +101,6 @@ class WaitingView(EventViewMixin, FormView):
messages.error(request, _("The presale for this event has not yet started."))
return redirect(self.get_index_url())
if not self.item_and_variation:
messages.error(request, _("We could not identify the product you selected."))
return redirect(self.get_index_url())
if not self.item_and_variation[0].allow_waitinglist:
messages.error(request, _("The waiting list is disabled for this product."))
return redirect(self.get_index_url())
self.subevent = None
if request.event.has_subevents:
if 'subevent' in request.GET:
@@ -127,11 +114,11 @@ class WaitingView(EventViewMixin, FormView):
def form_valid(self, form):
availability = (
self.item_and_variation[1].check_quotas(count_waitinglist=True, subevent=self.subevent)
if self.item_and_variation[1]
else self.item_and_variation[0].check_quotas(count_waitinglist=True, subevent=self.subevent)
form.instance.variation.check_quotas(count_waitinglist=True, subevent=self.subevent)
if form.instance.variation
else form.instance.item.check_quotas(count_waitinglist=True, subevent=self.subevent)
)
if availability[0] == 100:
if availability[0] == Quota.AVAILABILITY_OK:
messages.error(self.request, _("You cannot add yourself to the waiting list as this product is currently "
"available."))
return redirect(self.get_index_url())
@@ -143,3 +130,43 @@ class WaitingView(EventViewMixin, FormView):
def get_success_url(self):
return self.get_index_url()
@method_decorator(allow_frame_if_namespaced, 'dispatch')
class WaitingRemoveView(EventViewMixin, TemplateView):
template_name = 'pretixpresale/event/waitinglist_remove.html'
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['event'] = self.request.event
ctx['voucher'] = self.voucher
return ctx
def dispatch(self, request, *args, **kwargs):
self.request = request
try:
self.voucher = self.request.event.vouchers.get(
code=request.GET.get("voucher", ""),
waitinglistentries__isnull=False,
)
except Voucher.DoesNotExist:
messages.error(request, _("We could not find you on our waiting list."))
return redirect(self.get_index_url())
if not self.voucher.is_active():
messages.error(request, _("Your waiting list spot is no longer valid or already used. There's nothing more to do here."))
return redirect(self.get_index_url())
return super().dispatch(request, *args, **kwargs)
@transaction.atomic()
def post(self, request, *args, **kwargs):
self.voucher.valid_until = now() - timedelta(seconds=1)
self.voucher.save(update_fields=['valid_until'])
self.voucher.log_action('pretix.voucher.expired.waitinglist')
messages.success(request, _("Thank you very much! We will assign your spot on the waiting list to someone else."))
return redirect(self.get_index_url())
def get_success_url(self):
return self.get_index_url()