Fix #1001 -- Add product bundles (#1041)

* Data model + Editor

* Cart and order management

* Rebase migrations

* Fix typos, add tests on cart handling

* Add tests for checkout and quotas

* Add API endpoints

* Validation of settings

* Front page tax display

* Voucher handling

* Widget foo

* Show correct net pricing

* Front page tests

* reverse charge foo

* Allow to require bundling

* Fix test failure on postgres
This commit is contained in:
Raphael Michel
2019-03-22 14:48:48 +00:00
committed by GitHub
parent c4b18a4c81
commit 90f881c48e
34 changed files with 2747 additions and 153 deletions

View File

@@ -19,6 +19,7 @@ from django.views.generic import TemplateView
from pretix.base.models import ItemVariation, Quota
from pretix.base.models.event import SubEvent
from pretix.base.models.items import ItemBundle
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.ical import get_ical
from pretix.presale.views.organizer import (
@@ -54,6 +55,21 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
Prefetch('quotas',
to_attr='_subevent_quotas',
queryset=event.quotas.filter(subevent=subevent)),
Prefetch('bundles',
queryset=ItemBundle.objects.prefetch_related(
Prefetch('bundled_item',
queryset=event.items.select_related('tax_rule').prefetch_related(
Prefetch('quotas',
to_attr='_subevent_quotas',
queryset=event.quotas.filter(subevent=subevent)),
)),
Prefetch('bundled_variation',
queryset=ItemVariation.objects.select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
Prefetch('quotas',
to_attr='_subevent_quotas',
queryset=event.quotas.filter(subevent=subevent)),
)),
)),
Prefetch('variations', to_attr='available_variations',
queryset=ItemVariation.objects.filter(active=True, quotas__isnull=False).prefetch_related(
Prefetch('quotas',
@@ -94,7 +110,7 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
)
else:
item.cached_availability = list(
item.check_quotas(subevent=subevent, _cache=quota_cache)
item.check_quotas(subevent=subevent, _cache=quota_cache, include_bundled=True)
)
item.order_max = min(
@@ -106,7 +122,7 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
price = item_price_override.get(item.pk, item.default_price)
if voucher:
price = voucher.calculate_price(price)
item.display_price = item.tax(price)
item.display_price = item.tax(price, currency=event.currency, include_bundled=True)
display_add_to_cart = display_add_to_cart or item.order_max > 0
else:
@@ -117,7 +133,7 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
)
else:
var.cached_availability = list(
var.check_quotas(subevent=subevent, _cache=quota_cache)
var.check_quotas(subevent=subevent, _cache=quota_cache, include_bundled=True)
)
var.order_max = min(
@@ -129,7 +145,7 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
price = var_price_override.get(var.pk, var.price)
if voucher:
price = voucher.calculate_price(price)
var.display_price = var.tax(price)
var.display_price = var.tax(price, currency=event.currency, include_bundled=True)
display_add_to_cart = display_add_to_cart or var.order_max > 0

View File

@@ -149,13 +149,14 @@ def widget_js(request, lang, **kwargs):
return resp
def price_dict(price):
def price_dict(item, price):
return {
'gross': price.gross,
'net': price.net,
'tax': price.tax,
'rate': price.rate,
'name': str(price.name)
'name': str(price.name),
'includes_mixed_tax_rate': item.includes_mixed_tax_rate,
}
@@ -185,7 +186,7 @@ class WidgetAPIProductList(EventListMixin, View):
'require_voucher': item.require_voucher,
'order_min': item.min_per_order,
'order_max': item.order_max if not item.has_variations else None,
'price': price_dict(item.display_price) if not item.has_variations else None,
'price': price_dict(item, item.display_price) if not item.has_variations else None,
'min_price': item.min_price if item.has_variations else None,
'max_price': item.max_price if item.has_variations else None,
'free_price': item.free_price,
@@ -200,7 +201,7 @@ class WidgetAPIProductList(EventListMixin, View):
'value': str(var.value),
'order_max': var.order_max,
'description': str(rich_text(var.description, safelinks=False)) if var.description else None,
'price': price_dict(var.display_price),
'price': price_dict(item, var.display_price),
'avail': [
var.cached_availability[0],
var.cached_availability[1] if self.request.event.settings.show_quota_left else None