Add sub-events and relative date settings (#503)

* Data model

* little crud

* SubEventItemForm etc

* Drop SubEventItem.active, quota editor

* Fix failing tests

* First frontend stuff

* Addons form stuff

* Quota calculation

* net price display on EventIndex

* Add tests, solve some bugs

* Correct quota selection in more places, consolidate pricing logic

* Fix failing quota tests

* Fix TypeError

* Add tests for checkout

* Fixed a bug in QuotaForm

* Prevent immutable cart if a quota was removed from an item

* Add tests for pricing

* Handle waiting list

* Filter in check-in list

* Fixed import lost in rebase

* Fix waiting list widget

* Voucher management

* Voucher redemption

* Fix broken tests

* Add subevents to OrderChangeManager

* Create a subevent during event creation

* Fix bulk voucher creation

* Introduce subevent.active

* Copy from for subevents

* Show active in list

* ICal download for subevents

* Check start and end of presale

* Failing tests / show cart logic

* Test

* Rebase migrations

* REST API integration of sub-events

* Integrate quota calculation into the traditional quota form

* Make subevent argument to add_position optional

* Log-display foo

* pretixdroid and subevents

* Filter by subevent

* Add more tests

* Some mor tests

* Rebase fixes

* More tests

* Relative dates

* Restrict selection in relative datetime widgets

* Filter subevent list

* Re-label has_subevents

* Rebase fixes, subevents in calendar view

* Performance and caching issues

* Refactor calendar templates

* Permission tests

* Calendar fixes and month selection

* subevent selection

* Rename subevents to dates

* Add tests for calendar views
This commit is contained in:
Raphael Michel
2017-07-11 13:56:00 +02:00
committed by GitHub
parent 554800c06f
commit 8123effa65
141 changed files with 5920 additions and 1012 deletions

View File

@@ -10,6 +10,7 @@ from django.utils.formats import number_format
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from pretix.base.decimal import round_decimal
from pretix.base.models import ItemVariation, Question
from pretix.base.models.orders import InvoiceAddress, OrderPosition
from pretix.base.templatetags.rich_text import rich_text
@@ -274,7 +275,7 @@ class AddOnsForm(forms.Form):
This form class is responsible for selecting add-ons to a product in the cart.
"""
def _label(self, event, item_or_variation, avail):
def _label(self, event, item_or_variation, avail, override_price=None):
if isinstance(item_or_variation, ItemVariation):
variation = item_or_variation
item = item_or_variation.item
@@ -287,6 +288,11 @@ class AddOnsForm(forms.Form):
price_net = item.default_price_net
label = item.name
if override_price:
price = override_price
tax_value = round_decimal(price * (1 - 100 / (100 + item.tax_rate)))
price_net = price - tax_value
if not price:
n = '{name}'.format(
name=label
@@ -319,19 +325,29 @@ class AddOnsForm(forms.Form):
:param category: The category to choose from
:param event: The event this belongs to
:param subevent: The event the parent cart position belongs to
:param initial: The current set of add-ons
:param quota_cache: A shared dictionary for quota caching
:param item_cache: A shared dictionary for item/category caching
"""
category = kwargs.pop('category')
event = kwargs.pop('event')
subevent = kwargs.pop('subevent')
current_addons = kwargs.pop('initial')
quota_cache = kwargs.pop('quota_cache')
item_cache = kwargs.pop('item_cache')
super().__init__(*args, **kwargs)
if category.pk not in item_cache:
if subevent:
item_price_override = subevent.item_price_overrides
var_price_override = subevent.var_price_overrides
else:
item_price_override = {}
var_price_override = {}
ckey = '{}-{}'.format(subevent.pk if subevent else 0, category.pk)
if ckey not in item_cache:
# Get all items to possibly show
items = category.items.filter(
Q(active=True)
@@ -339,26 +355,37 @@ class AddOnsForm(forms.Form):
& Q(Q(available_until__isnull=True) | Q(available_until__gte=now()))
& Q(hide_without_voucher=False)
).prefetch_related(
'variations__quotas', # for .availability()
Prefetch('quotas', queryset=event.quotas.all()),
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).distinct()),
queryset=ItemVariation.objects.filter(active=True, quotas__isnull=False).prefetch_related(
Prefetch('quotas',
to_attr='_subevent_quotas',
queryset=event.quotas.filter(subevent=subevent))
).distinct()),
).annotate(
quotac=Count('quotas'),
has_variations=Count('variations')
).filter(
quotac__gt=0
).order_by('category__position', 'category_id', 'position', 'name')
item_cache[category.pk] = items
item_cache[ckey] = items
else:
items = item_cache[category.pk]
items = item_cache[ckey]
for i in items:
if i.has_variations:
choices = [('', _('no selection'), '')]
for v in i.available_variations:
cached_availability = v.check_quotas(_cache=quota_cache)
choices.append((v.pk, self._label(event, v, cached_availability), v.description))
cached_availability = v.check_quotas(subevent=subevent, _cache=quota_cache)
if v._subevent_quotas:
choices.append(
(v.pk,
self._label(event, v, cached_availability,
override_price=var_price_override.get(v.pk)),
v.description)
)
field = AddOnVariationField(
choices=choices,
@@ -368,13 +395,17 @@ class AddOnsForm(forms.Form):
help_text=rich_text(str(i.description)),
initial=current_addons.get(i.pk),
)
if len(choices) > 1:
self.fields['item_%s' % i.pk] = field
else:
cached_availability = i.check_quotas(_cache=quota_cache)
if not i._subevent_quotas:
continue
cached_availability = i.check_quotas(subevent=subevent, _cache=quota_cache)
field = forms.BooleanField(
label=self._label(event, i, cached_availability),
label=self._label(event, i, cached_availability,
override_price=item_price_override.get(i.pk)),
required=False,
initial=i.pk in current_addons,
help_text=rich_text(str(i.description)),
)
self.fields['item_%s' % i.pk] = field
self.fields['item_%s' % i.pk] = field