forked from CGM_Public/pretix_original
Improve add-on products
This commit is contained in:
@@ -29,35 +29,45 @@ class CartMixin:
|
||||
cartpos = queryset.order_by(
|
||||
'item', 'variation'
|
||||
).select_related(
|
||||
'item', 'variation'
|
||||
'item', 'variation', 'addon_to'
|
||||
).prefetch_related(
|
||||
*prefetch
|
||||
)
|
||||
else:
|
||||
cartpos = self.positions
|
||||
|
||||
lcp = list(cartpos)
|
||||
has_addons = {cp.addon_to.pk for cp in lcp if cp.addon_to}
|
||||
|
||||
# Group items of the same variation
|
||||
# We do this by list manipulations instead of a GROUP BY query, as
|
||||
# Django is unable to join related models in a .values() query
|
||||
def keyfunc(pos):
|
||||
if isinstance(pos, OrderPosition):
|
||||
i = pos.positionid
|
||||
if pos.addon_to:
|
||||
i = pos.addon_to.positionid
|
||||
else:
|
||||
i = pos.positionid
|
||||
else:
|
||||
i = pos.pk
|
||||
if downloads:
|
||||
return i, pos.pk, 0, 0, 0, 0,
|
||||
if pos.addon_to:
|
||||
i = pos.addon_to.pk
|
||||
else:
|
||||
i = pos.pk
|
||||
|
||||
has_attendee_data = pos.item.admission and (
|
||||
self.request.event.settings.attendee_names_asked
|
||||
or self.request.event.settings.attendee_emails_asked
|
||||
)
|
||||
|
||||
addon_penalty = 1 if pos.addon_to else 0
|
||||
if downloads or pos.pk in has_addons or pos.addon_to:
|
||||
return i, addon_penalty, pos.pk, 0, 0, 0, 0,
|
||||
if answers and (has_attendee_data or pos.item.questions.all()):
|
||||
return i, pos.pk, 0, 0, 0, 0,
|
||||
return 0, 0, pos.item_id, pos.variation_id, pos.price, (pos.voucher_id or 0)
|
||||
return i, addon_penalty, pos.pk, 0, 0, 0, 0,
|
||||
|
||||
return 0, addon_penalty, 0, pos.item_id, pos.variation_id, pos.price, (pos.voucher_id or 0)
|
||||
|
||||
positions = []
|
||||
for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc):
|
||||
for k, g in groupby(sorted(lcp, key=keyfunc), key=keyfunc):
|
||||
g = list(g)
|
||||
group = g[0]
|
||||
group.count = len(g)
|
||||
|
||||
@@ -171,6 +171,7 @@ class RedeemView(EventViewMixin, TemplateView):
|
||||
Q(active=True)
|
||||
& Q(Q(available_from__isnull=True) | Q(available_from__lte=now()))
|
||||
& Q(Q(available_until__isnull=True) | Q(available_until__gte=now()))
|
||||
& ~Q(category__is_addon=True)
|
||||
)
|
||||
|
||||
vouchq = Q(hide_without_voucher=False)
|
||||
|
||||
@@ -46,6 +46,7 @@ def get_grouped_items(event):
|
||||
& Q(Q(available_from__isnull=True) | Q(available_from__lte=now()))
|
||||
& Q(Q(available_until__isnull=True) | Q(available_until__gte=now()))
|
||||
& Q(hide_without_voucher=False)
|
||||
& ~Q(category__is_addon=True)
|
||||
).select_related(
|
||||
'category', # for re-grouping
|
||||
).prefetch_related(
|
||||
|
||||
@@ -3,7 +3,7 @@ from datetime import timedelta
|
||||
from django.contrib import messages
|
||||
from django.db import transaction
|
||||
from django.db.models import Sum
|
||||
from django.http import FileResponse, Http404, HttpResponse
|
||||
from django.http import FileResponse, Http404, JsonResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
@@ -392,9 +392,7 @@ class OrderModify(EventViewMixin, OrderDetailMixin, QuestionsViewMixin, Template
|
||||
|
||||
@cached_property
|
||||
def positions(self):
|
||||
return list(self.order.positions.order_by(
|
||||
'item', 'variation'
|
||||
).select_related(
|
||||
return list(self.order.positions.select_related(
|
||||
'item', 'variation'
|
||||
).prefetch_related(
|
||||
'variation', 'item__questions', 'answers'
|
||||
@@ -445,7 +443,7 @@ class OrderModify(EventViewMixin, OrderDetailMixin, QuestionsViewMixin, Template
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['order'] = self.order
|
||||
ctx['forms'] = self.forms
|
||||
ctx['formgroups'] = self.formdict.items()
|
||||
ctx['invoice_form'] = self.invoice_form
|
||||
return ctx
|
||||
|
||||
@@ -501,6 +499,12 @@ class OrderCancelDo(EventViewMixin, OrderDetailMixin, AsyncAction, View):
|
||||
|
||||
class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
||||
|
||||
def get_self_url(self):
|
||||
return eventreverse(self.request.event,
|
||||
'presale:event.order.download' if 'position' in self.kwargs
|
||||
else 'presale:event.order.download.combined',
|
||||
kwargs=self.kwargs)
|
||||
|
||||
@cached_property
|
||||
def output(self):
|
||||
responses = register_ticket_outputs.send(self.request.event)
|
||||
@@ -516,20 +520,30 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
||||
except OrderPosition.DoesNotExist:
|
||||
return None
|
||||
|
||||
def error(self, msg):
|
||||
messages.error(self.request, msg)
|
||||
if "ajax" in self.request.POST or "ajax" in self.request.GET:
|
||||
return JsonResponse({
|
||||
'ready': True,
|
||||
'success': False,
|
||||
'redirect': self.get_order_url(),
|
||||
'message': msg,
|
||||
})
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if not self.output or not self.output.is_enabled:
|
||||
messages.error(request, _('You requested an invalid ticket output type.'))
|
||||
return redirect(self.get_order_url())
|
||||
return self.error(_('You requested an invalid ticket output type.'))
|
||||
if not self.order or ('position' in kwargs and not self.order_position):
|
||||
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
||||
if self.order.status != Order.STATUS_PAID:
|
||||
messages.error(request, _('Order is not paid.'))
|
||||
return redirect(self.get_order_url())
|
||||
return self.error(_('Order is not paid.'))
|
||||
if (not self.request.event.settings.ticket_download
|
||||
or (self.request.event.settings.ticket_download_date is not None
|
||||
and now() < self.request.event.settings.ticket_download_date)):
|
||||
messages.error(request, _('Ticket download is not (yet) enabled.'))
|
||||
return redirect(self.get_order_url())
|
||||
return self.error(_('Ticket download is not (yet) enabled.'))
|
||||
if 'position' in kwargs and (self.order_position.addon_to and not self.request.event.settings.ticket_download_addons):
|
||||
return self.error(_('Ticket download is not enabled for add-on products.'))
|
||||
|
||||
if 'position' in kwargs:
|
||||
return self._download_position()
|
||||
@@ -555,7 +569,11 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
||||
generate_order.apply_async(args=(self.order.id, self.output.identifier))
|
||||
|
||||
if 'ajax' in self.request.GET:
|
||||
return HttpResponse('1' if ct and ct.file else '0')
|
||||
return JsonResponse({
|
||||
'ready': bool(ct and ct.file),
|
||||
'success': False,
|
||||
'redirect': self.get_self_url()
|
||||
})
|
||||
elif not ct.file:
|
||||
return render(self.request, "pretixbase/cachedfiles/pending.html", {})
|
||||
else:
|
||||
@@ -584,7 +602,11 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
||||
generate.apply_async(args=(self.order_position.id, self.output.identifier))
|
||||
|
||||
if 'ajax' in self.request.GET:
|
||||
return HttpResponse('1' if ct and ct.file else '0')
|
||||
return JsonResponse({
|
||||
'ready': bool(ct and ct.file),
|
||||
'success': False,
|
||||
'redirect': self.get_self_url()
|
||||
})
|
||||
elif not ct.file:
|
||||
return render(self.request, "pretixbase/cachedfiles/pending.html", {})
|
||||
else:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from django import forms
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
@@ -8,8 +10,18 @@ from pretix.presale.views import get_cart
|
||||
|
||||
class QuestionsViewMixin:
|
||||
|
||||
@staticmethod
|
||||
def _keyfunc(pos):
|
||||
# Sort addons after the item they are an addon to
|
||||
if isinstance(pos, OrderPosition):
|
||||
i = pos.addon_to.positionid if pos.addon_to else pos.positionid
|
||||
else:
|
||||
i = pos.addon_to.pk if pos.addon_to else pos.pk
|
||||
addon_penalty = 1 if pos.addon_to else 0
|
||||
return i, addon_penalty, pos.pk
|
||||
|
||||
def _positions_for_questions(self):
|
||||
return get_cart(self.request)
|
||||
return sorted(get_cart(self.request), key=self._keyfunc)
|
||||
|
||||
@cached_property
|
||||
def forms(self):
|
||||
@@ -32,6 +44,17 @@ class QuestionsViewMixin:
|
||||
formlist.append(form)
|
||||
return formlist
|
||||
|
||||
@cached_property
|
||||
def formdict(self):
|
||||
storage = defaultdict(list)
|
||||
for f in self.forms:
|
||||
pos = f.cartpos or f.orderpos
|
||||
if pos.addon_to_id:
|
||||
storage[pos.addon_to].append(f)
|
||||
else:
|
||||
storage[pos].append(f)
|
||||
return storage
|
||||
|
||||
def save(self):
|
||||
failed = False
|
||||
for form in self.forms:
|
||||
|
||||
Reference in New Issue
Block a user