forked from CGM_Public/pretix_original
Reduce number of redundant SQL queries
This commit is contained in:
@@ -28,48 +28,51 @@ def tuplesum(tuples: Iterable[Tuple]) -> Tuple:
|
|||||||
return tuple(map(mysum, zip(*list(tuples))))
|
return tuple(map(mysum, zip(*list(tuples))))
|
||||||
|
|
||||||
|
|
||||||
|
def dictsum(*dicts) -> dict:
|
||||||
|
res = {}
|
||||||
|
keys = set()
|
||||||
|
for d in dicts:
|
||||||
|
keys |= set(d.keys())
|
||||||
|
for k in keys:
|
||||||
|
res[k] = (sum(d[k][0] for d in dicts if k in d), sum(d[k][1] for d in dicts if k in d))
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]:
|
def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]:
|
||||||
items = event.items.all().select_related(
|
items = event.items.all().select_related(
|
||||||
'category', # for re-grouping
|
'category', # for re-grouping
|
||||||
|
).prefetch_related(
|
||||||
|
'variations'
|
||||||
).order_by('category__position', 'category_id', 'name')
|
).order_by('category__position', 'category_id', 'name')
|
||||||
|
|
||||||
num_total = {
|
counters = OrderPosition.objects.filter(
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
order__event=event
|
||||||
for p in (OrderPosition.objects
|
).values(
|
||||||
.filter(order__event=event,
|
'item', 'variation', 'order__status'
|
||||||
order__status__in=[Order.STATUS_PENDING, Order.STATUS_EXPIRED, Order.STATUS_PAID])
|
).annotate(cnt=Count('id'), price=Sum('price')).order_by()
|
||||||
.values('item', 'variation')
|
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
|
||||||
}
|
|
||||||
num_canceled = {
|
num_canceled = {
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
for p in (OrderPosition.objects
|
for p in counters if p['order__status'] == Order.STATUS_CANCELED
|
||||||
.filter(order__event=event, order__status=Order.STATUS_CANCELED)
|
|
||||||
.values('item', 'variation')
|
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
|
||||||
}
|
}
|
||||||
num_refunded = {
|
num_refunded = {
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
for p in (OrderPosition.objects
|
for p in counters if p['order__status'] == Order.STATUS_REFUNDED
|
||||||
.filter(order__event=event, order__status=Order.STATUS_REFUNDED)
|
|
||||||
.values('item', 'variation')
|
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
|
||||||
}
|
|
||||||
num_pending = {
|
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
|
||||||
for p in (OrderPosition.objects
|
|
||||||
.filter(order__event=event,
|
|
||||||
order__status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
|
||||||
.values('item', 'variation')
|
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
|
||||||
}
|
}
|
||||||
num_paid = {
|
num_paid = {
|
||||||
(p['item'], p['variation']): (p['cnt'], p['price'])
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
for p in (OrderPosition.objects
|
for p in counters if p['order__status'] == Order.STATUS_PAID
|
||||||
.filter(order__event=event, order__status=Order.STATUS_PAID)
|
|
||||||
.values('item', 'variation')
|
|
||||||
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
|
|
||||||
}
|
}
|
||||||
|
num_s_pending = {
|
||||||
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
|
for p in counters if p['order__status'] == Order.STATUS_PENDING
|
||||||
|
}
|
||||||
|
num_expired = {
|
||||||
|
(p['item'], p['variation']): (p['cnt'], p['price'])
|
||||||
|
for p in counters if p['order__status'] == Order.STATUS_EXPIRED
|
||||||
|
}
|
||||||
|
num_pending = dictsum(num_s_pending, num_expired)
|
||||||
|
num_total = dictsum(num_pending, num_paid)
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
item.all_variations = list(item.variations.all())
|
item.all_variations = list(item.variations.all())
|
||||||
@@ -120,41 +123,33 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
|
|||||||
payment_cat_obj = DummyObject()
|
payment_cat_obj = DummyObject()
|
||||||
payment_cat_obj.name = _('Payment method fees')
|
payment_cat_obj.name = _('Payment method fees')
|
||||||
payment_items = []
|
payment_items = []
|
||||||
num_total = {
|
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
counters = event.orders.values('payment_provider', 'status').annotate(
|
||||||
for o in (Order.objects
|
cnt=Count('id'), payment_fee=Sum('payment_fee')
|
||||||
.filter(event=event)
|
).order_by()
|
||||||
.values('payment_provider')
|
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
|
||||||
}
|
|
||||||
num_canceled = {
|
num_canceled = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects
|
for o in counters if o['status'] == Order.STATUS_CANCELED
|
||||||
.filter(event=event, status=Order.STATUS_CANCELED)
|
|
||||||
.values('payment_provider')
|
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
|
||||||
}
|
}
|
||||||
num_refunded = {
|
num_refunded = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects
|
for o in counters if o['status'] == Order.STATUS_REFUNDED
|
||||||
.filter(event=event, status=Order.STATUS_REFUNDED)
|
|
||||||
.values('payment_provider')
|
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
|
||||||
}
|
}
|
||||||
num_pending = {
|
num_s_pending = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects
|
for o in counters if o['status'] == Order.STATUS_PENDING
|
||||||
.filter(event=event, status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
|
}
|
||||||
.values('payment_provider')
|
num_expired = {
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
|
for o in counters if o['status'] == Order.STATUS_EXPIRED
|
||||||
}
|
}
|
||||||
num_paid = {
|
num_paid = {
|
||||||
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
o['payment_provider']: (o['cnt'], o['payment_fee'])
|
||||||
for o in (Order.objects
|
for o in counters if o['status'] == Order.STATUS_PAID
|
||||||
.filter(event=event, status=Order.STATUS_PAID)
|
|
||||||
.values('payment_provider')
|
|
||||||
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
|
|
||||||
}
|
}
|
||||||
|
num_pending = dictsum(num_s_pending, num_expired)
|
||||||
|
num_total = dictsum(num_pending, num_paid)
|
||||||
|
|
||||||
provider_names = {}
|
provider_names = {}
|
||||||
responses = register_payment_providers.send(event)
|
responses = register_payment_providers.send(event)
|
||||||
|
|||||||
@@ -514,8 +514,15 @@ class SettingsSandbox:
|
|||||||
self._event.settings.set(self._convert_key(key), value)
|
self._event.settings.set(self._convert_key(key), value)
|
||||||
|
|
||||||
|
|
||||||
class GlobalSettingsObject:
|
class GlobalSettingsObject():
|
||||||
|
data_dict = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.settings = SettingsProxy(self, type=GlobalSetting)
|
# This is a singleton-like object. Multiple objects can exist, but they share their state
|
||||||
self.setting_objects = GlobalSetting.objects
|
if GlobalSettingsObject.data_dict:
|
||||||
self.slug = '_global'
|
self.__dict__ = GlobalSettingsObject.data_dict
|
||||||
|
else:
|
||||||
|
self.settings = SettingsProxy(self, type=GlobalSetting)
|
||||||
|
self.setting_objects = GlobalSetting.objects
|
||||||
|
self.slug = '_global'
|
||||||
|
GlobalSettingsObject.data_dict = self.__dict__
|
||||||
|
|||||||
@@ -31,26 +31,16 @@
|
|||||||
{% if quota.size == None %}{% trans "Infinite" %}{% else %}{{ quota.size }}{% endif %}
|
{% if quota.size == None %}{% trans "Infinite" %}{% else %}{{ quota.size }}{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
{% for row in quota_table_rows %}
|
||||||
<div class="col-xs-9">{% trans "Paid orders" %}</div>
|
<div class="row">
|
||||||
<div class="col-xs-3 text-right"> – {{ quota.count_paid_orders }}</div>
|
<div class="col-xs-9">{{ row.label }}</div>
|
||||||
</div>
|
<div class="col-xs-3 text-right"> – {{ row.value }}</div>
|
||||||
<div class="row">
|
</div>
|
||||||
<div class="col-xs-9">{% trans "Pending orders" %}</div>
|
{% endfor %}
|
||||||
<div class="col-xs-3 text-right"> – {{ quota.count_pending_orders }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-9">{% trans "Vouchers" %}</div>
|
|
||||||
<div class="col-xs-3 text-right"> – {{ quota.count_blocking_vouchers }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-9">{% trans "Current user's carts" %}</div>
|
|
||||||
<div class="col-xs-3 text-right"> – {{ quota.count_in_cart }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9"><strong>{% trans "Current availability" %}</strong></div>
|
<div class="col-xs-9"><strong>{% trans "Current availability" %}</strong></div>
|
||||||
<div class="col-xs-3 text-right">
|
<div class="col-xs-3 text-right">
|
||||||
{% if quota.size == None %}{% trans "Infinite" %}{% else %}{{ quota.availability.1 }}{% endif %}
|
{% if quota.size == None %}{% trans "Infinite" %}{% else %}{{ avail.1 }}{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -330,8 +330,8 @@ class QuestionMixin:
|
|||||||
can_order=False, can_delete=True, extra=0
|
can_order=False, can_delete=True, extra=0
|
||||||
)
|
)
|
||||||
return formsetclass(self.request.POST if self.request.method == "POST" else None,
|
return formsetclass(self.request.POST if self.request.method == "POST" else None,
|
||||||
queryset=(QuestionOption.objects.filter(question=self.get_object())
|
queryset=(QuestionOption.objects.filter(question=self.object)
|
||||||
if self.get_object() else QuestionOption.objects.none()),
|
if self.object else QuestionOption.objects.none()),
|
||||||
event=self.request.event)
|
event=self.request.event)
|
||||||
|
|
||||||
def save_formset(self, obj):
|
def save_formset(self, obj):
|
||||||
@@ -423,8 +423,9 @@ class QuestionView(EventPermissionRequiredMixin, QuestionMixin, ChartContainingV
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
ctx = super().get_context_data()
|
ctx = super().get_context_data()
|
||||||
ctx['items'] = self.object.items.all()
|
ctx['items'] = self.object.items.all()
|
||||||
ctx['stats'] = self.get_answer_statistics()
|
stats = self.get_answer_statistics()
|
||||||
ctx['stats_json'] = json.dumps(self.get_answer_statistics())
|
ctx['stats'] = stats
|
||||||
|
ctx['stats_json'] = json.dumps(stats)
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
def get_object(self, queryset=None) -> Question:
|
def get_object(self, queryset=None) -> Question:
|
||||||
@@ -599,6 +600,10 @@ class QuotaView(ChartContainingView, DetailView):
|
|||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
ctx = super().get_context_data()
|
ctx = super().get_context_data()
|
||||||
|
|
||||||
|
avail = self.object.availability()
|
||||||
|
ctx['avail'] = avail
|
||||||
|
|
||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
'label': ugettext('Paid orders'),
|
'label': ugettext('Paid orders'),
|
||||||
@@ -617,10 +622,12 @@ class QuotaView(ChartContainingView, DetailView):
|
|||||||
'value': self.object.count_in_cart()
|
'value': self.object.count_in_cart()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
ctx['quota_table_rows'] = list(data)
|
||||||
|
|
||||||
if self.object.size is not None:
|
if self.object.size is not None:
|
||||||
data.append({
|
data.append({
|
||||||
'label': ugettext('Current availability'),
|
'label': ugettext('Current availability'),
|
||||||
'value': self.object.availability()[1]
|
'value': avail[1]
|
||||||
})
|
})
|
||||||
ctx['quota_chart_data'] = json.dumps(data)
|
ctx['quota_chart_data'] = json.dumps(data)
|
||||||
return ctx
|
return ctx
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ class EventList(ListView):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Event.objects.filter(
|
return Event.objects.filter(
|
||||||
permitted__id__exact=self.request.user.pk
|
permitted__id__exact=self.request.user.pk
|
||||||
).prefetch_related(
|
).select_related("organizer").prefetch_related(
|
||||||
"organizer",
|
"setting_objects", "organizer__setting_objects"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,33 +15,24 @@ class CartMixin:
|
|||||||
"""
|
"""
|
||||||
A list of this users cart position
|
A list of this users cart position
|
||||||
"""
|
"""
|
||||||
return list(CartPosition.objects.filter(
|
return list(get_cart(self.request))
|
||||||
cart_id=self.request.session.session_key, event=self.request.event
|
|
||||||
).order_by(
|
|
||||||
'item', 'variation'
|
|
||||||
).select_related(
|
|
||||||
'item', 'variation'
|
|
||||||
).prefetch_related(
|
|
||||||
'item__questions', 'answers'
|
|
||||||
))
|
|
||||||
|
|
||||||
def get_cart(self, answers=False, queryset=None, payment_fee=None, payment_fee_tax_rate=None, downloads=False):
|
def get_cart(self, answers=False, queryset=None, payment_fee=None, payment_fee_tax_rate=None, downloads=False):
|
||||||
queryset = queryset or CartPosition.objects.filter(
|
if queryset:
|
||||||
cart_id=self.request.session.session_key, event=self.request.event
|
prefetch = []
|
||||||
)
|
if answers:
|
||||||
|
prefetch.append('item__questions')
|
||||||
|
prefetch.append('answers')
|
||||||
|
|
||||||
prefetch = []
|
cartpos = queryset.order_by(
|
||||||
if answers:
|
'item', 'variation'
|
||||||
prefetch.append('item__questions')
|
).select_related(
|
||||||
prefetch.append('answers')
|
'item', 'variation'
|
||||||
|
).prefetch_related(
|
||||||
cartpos = queryset.order_by(
|
*prefetch
|
||||||
'item', 'variation'
|
)
|
||||||
).select_related(
|
else:
|
||||||
'item', 'variation'
|
cartpos = self.positions
|
||||||
).prefetch_related(
|
|
||||||
*prefetch
|
|
||||||
)
|
|
||||||
|
|
||||||
# Group items of the same variation
|
# Group items of the same variation
|
||||||
# We do this by list manipulations instead of a GROUP BY query, as
|
# We do this by list manipulations instead of a GROUP BY query, as
|
||||||
@@ -101,6 +92,20 @@ class CartMixin:
|
|||||||
return payment_fee
|
return payment_fee
|
||||||
|
|
||||||
|
|
||||||
|
def get_cart(request):
|
||||||
|
if not hasattr(request, '_cart_cache'):
|
||||||
|
request._cart_cache = CartPosition.objects.filter(
|
||||||
|
cart_id=request.session.session_key, event=request.event
|
||||||
|
).order_by(
|
||||||
|
'item', 'variation'
|
||||||
|
).select_related(
|
||||||
|
'item', 'variation'
|
||||||
|
).prefetch_related(
|
||||||
|
'item__questions', 'answers'
|
||||||
|
)
|
||||||
|
return request._cart_cache
|
||||||
|
|
||||||
|
|
||||||
class EventViewMixin:
|
class EventViewMixin:
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ from django.shortcuts import redirect
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
|
from pretix.base.models import CartPosition
|
||||||
from pretix.multidomain.urlreverse import eventreverse
|
from pretix.multidomain.urlreverse import eventreverse
|
||||||
from pretix.presale.checkoutflow import get_checkout_flow
|
from pretix.presale.checkoutflow import get_checkout_flow
|
||||||
from pretix.presale.views import CartMixin
|
|
||||||
|
|
||||||
|
|
||||||
class CheckoutView(CartMixin, View):
|
class CheckoutView(View):
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.request = request
|
self.request = request
|
||||||
if not self.positions and "async_id" not in request.GET:
|
has_cart = CartPosition.objects.filter(
|
||||||
|
cart_id=self.request.session.session_key, event=self.request.event).exists()
|
||||||
|
if not has_cart and "async_id" not in request.GET:
|
||||||
messages.error(request, _("Your cart is empty"))
|
messages.error(request, _("Your cart is empty"))
|
||||||
return redirect(eventreverse(self.request.event, 'presale:event.index'))
|
return redirect(eventreverse(self.request.event, 'presale:event.index'))
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Prefetch, Q
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
from pretix.base.models import ItemVariation
|
||||||
|
|
||||||
from . import CartMixin, EventViewMixin
|
from . import CartMixin, EventViewMixin
|
||||||
|
|
||||||
|
|
||||||
@@ -21,48 +23,56 @@ def item_group_by_category(items):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_grouped_items(event):
|
||||||
|
items = event.items.all().filter(
|
||||||
|
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(hide_without_voucher=False)
|
||||||
|
).select_related(
|
||||||
|
'category', # for re-grouping
|
||||||
|
).prefetch_related(
|
||||||
|
'quotas', 'variations__quotas', 'quotas__event', # for .availability()
|
||||||
|
Prefetch('variations', to_attr='available_variations',
|
||||||
|
queryset=ItemVariation.objects.filter(active=True, quotas__isnull=False).distinct()),
|
||||||
|
).annotate(
|
||||||
|
quotac=Count('quotas'),
|
||||||
|
has_variations=Count('variations')
|
||||||
|
).filter(
|
||||||
|
quotac__gt=0
|
||||||
|
).order_by('category__position', 'category_id', 'position', 'name')
|
||||||
|
display_add_to_cart = False
|
||||||
|
for item in items:
|
||||||
|
if not item.has_variations:
|
||||||
|
item.cached_availability = list(item.check_quotas())
|
||||||
|
item.order_max = min(item.cached_availability[1]
|
||||||
|
if item.cached_availability[1] is not None else sys.maxsize,
|
||||||
|
int(event.settings.max_items_per_order))
|
||||||
|
item.price = item.default_price
|
||||||
|
display_add_to_cart = display_add_to_cart or item.order_max > 0
|
||||||
|
else:
|
||||||
|
for var in item.available_variations:
|
||||||
|
var.cached_availability = list(var.check_quotas())
|
||||||
|
var.order_max = min(var.cached_availability[1]
|
||||||
|
if var.cached_availability[1] is not None else sys.maxsize,
|
||||||
|
int(event.settings.max_items_per_order))
|
||||||
|
display_add_to_cart = display_add_to_cart or var.order_max > 0
|
||||||
|
var.price = var.default_price if var.default_price is not None else item.default_price
|
||||||
|
if len(item.available_variations) > 0:
|
||||||
|
item.min_price = min([v.price for v in item.available_variations])
|
||||||
|
item.max_price = max([v.price for v in item.available_variations])
|
||||||
|
|
||||||
|
items = [item for item in items if len(item.available_variations) > 0 or not item.has_variations]
|
||||||
|
return items, display_add_to_cart
|
||||||
|
|
||||||
|
|
||||||
class EventIndex(EventViewMixin, CartMixin, TemplateView):
|
class EventIndex(EventViewMixin, CartMixin, TemplateView):
|
||||||
template_name = "pretixpresale/event/index.html"
|
template_name = "pretixpresale/event/index.html"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
# Fetch all items
|
# Fetch all items
|
||||||
items = self.request.event.items.all().filter(
|
items, display_add_to_cart = get_grouped_items(self.request.event)
|
||||||
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(hide_without_voucher=False)
|
|
||||||
).select_related(
|
|
||||||
'category', # for re-grouping
|
|
||||||
).prefetch_related(
|
|
||||||
'quotas', 'variations__quotas', 'quotas__event' # for .availability()
|
|
||||||
).annotate(quotac=Count('quotas')).filter(
|
|
||||||
quotac__gt=0
|
|
||||||
).order_by('category__position', 'category_id', 'position', 'name')
|
|
||||||
display_add_to_cart = False
|
|
||||||
for item in items:
|
|
||||||
item.available_variations = list(item.variations.filter(active=True, quotas__isnull=False).distinct())
|
|
||||||
item.has_variations = item.variations.exists()
|
|
||||||
if not item.has_variations:
|
|
||||||
item.cached_availability = list(item.check_quotas())
|
|
||||||
item.order_max = min(item.cached_availability[1]
|
|
||||||
if item.cached_availability[1] is not None else sys.maxsize,
|
|
||||||
int(self.request.event.settings.max_items_per_order))
|
|
||||||
item.price = item.default_price
|
|
||||||
display_add_to_cart = display_add_to_cart or item.order_max > 0
|
|
||||||
else:
|
|
||||||
for var in item.available_variations:
|
|
||||||
var.cached_availability = list(var.check_quotas())
|
|
||||||
var.order_max = min(var.cached_availability[1]
|
|
||||||
if var.cached_availability[1] is not None else sys.maxsize,
|
|
||||||
int(self.request.event.settings.max_items_per_order))
|
|
||||||
display_add_to_cart = display_add_to_cart or var.order_max > 0
|
|
||||||
var.price = var.default_price if var.default_price is not None else item.default_price
|
|
||||||
if len(item.available_variations) > 0:
|
|
||||||
item.min_price = min([v.price for v in item.available_variations])
|
|
||||||
item.max_price = max([v.price for v in item.available_variations])
|
|
||||||
|
|
||||||
items = [item for item in items if len(item.available_variations) > 0 or not item.has_variations]
|
|
||||||
|
|
||||||
# Regroup those by category
|
# Regroup those by category
|
||||||
context['items_by_category'] = item_group_by_category(items)
|
context['items_by_category'] = item_group_by_category(items)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class OrderDetailMixin:
|
|||||||
@cached_property
|
@cached_property
|
||||||
def order(self):
|
def order(self):
|
||||||
try:
|
try:
|
||||||
order = Order.objects.get(event=self.request.event, code=self.kwargs['order'])
|
order = self.request.event.orders.get(code=self.kwargs['order'])
|
||||||
if order.secret.lower() == self.kwargs['secret'].lower():
|
if order.secret.lower() == self.kwargs['secret'].lower():
|
||||||
return order
|
return order
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user