diff --git a/doc/development/api/restriction.rst b/doc/development/api/restriction.rst index 46bb287b7..8d3973317 100644 --- a/doc/development/api/restriction.rst +++ b/doc/development/api/restriction.rst @@ -181,7 +181,7 @@ In our example, the implementation could look like this:: # Only take this restriction into consideration if it # is directly applied to this variation or if the item # has no variations - if len(v) != 0 and ('variation' not in v or v['variation'] not in applied_to): + if not v.empty() and ('variation' not in v or v['variation'] not in applied_to): continue if restriction.timeframe_from <= now() <= restriction.timeframe_to: diff --git a/src/pretixbase/models.py b/src/pretixbase/models.py index 8e10689a3..1f67e8f49 100644 --- a/src/pretixbase/models.py +++ b/src/pretixbase/models.py @@ -12,7 +12,7 @@ import six from versions.models import Versionable as BaseVersionable from versions.models import VersionedForeignKey, VersionedManyToManyField, get_utc_now -from pretixbase.types import VariationDict +from .types import VariationDict class Versionable(BaseVersionable): @@ -703,6 +703,35 @@ class Item(Versionable): self._get_all_variations_cache = result return result + def get_all_available_variations(self): + from .signals import determine_availability + + variations = self.get_all_variations() + responses = determine_availability.send( + self.event, item=self, + variations=variations, context=None, + cache=self.event.get_cache() + ) + + for i, var in enumerate(variations): + var['available'] = var['variation'].active if 'variation' in var else True + if 'variation' in var: + if var['variation'].default_price: + var['price'] = var['variation'].default_price + else: + var['price'] = self.default_price + else: + var['price'] = self.default_price + + for receiver, response in responses: + if 'available' in response[i]: + var['available'] &= response[i]['available'] + if 'price' in response[i] and response[i]['price'] \ + and response[i]['price'] < var['price']: + var['price'] = response[i]['price'] + + return variations + class ItemVariation(Versionable): """ diff --git a/src/pretixbase/signals.py b/src/pretixbase/signals.py index e1c1dd95f..99860e5c8 100644 --- a/src/pretixbase/signals.py +++ b/src/pretixbase/signals.py @@ -2,7 +2,7 @@ import django.dispatch from django.apps import apps from django.dispatch.dispatcher import NO_RECEIVERS -from pretixbase.models import Event +from .models import Event class EventPluginSignal(django.dispatch.Signal): diff --git a/src/pretixbase/types.py b/src/pretixbase/types.py index c52f67dac..34e2b6a3e 100644 --- a/src/pretixbase/types.py +++ b/src/pretixbase/types.py @@ -5,7 +5,7 @@ class VariationDict(dict): returned by ``Item.get_all_variations()`` to avoid duplicate code in the code calling this method. """ - IGNORE_KEYS = ('variation', 'key') + IGNORE_KEYS = ('variation', 'key', 'available', 'price') def relevant_items(self) -> "list[(str, PropertyValue)]": """ @@ -60,6 +60,13 @@ class VariationDict(dict): else: return super().__eq__(other) + def empty(self): + """ + Returns true, if this VariationDict does not contain any "real" data like + references to PropertyValues, but only "metadata". + """ + return not next(self.relevant_items(), False) + def ordered_values(self) -> "list[ItemVariation]": """ Returns a list of values ordered by their keys @@ -72,6 +79,9 @@ class VariationDict(dict): ) ] + def __str__(self): + return " – ".join([v.value for v in self.ordered_values()]) + def copy(self) -> "VariationDict": """ Return a one-level deep copy of this object (create a new diff --git a/src/pretixplugins/timerestriction/signals.py b/src/pretixplugins/timerestriction/signals.py index 355d1b20e..27568ae7f 100644 --- a/src/pretixplugins/timerestriction/signals.py +++ b/src/pretixplugins/timerestriction/signals.py @@ -84,7 +84,7 @@ def availability_handler(sender, **kwargs): # Only take this restriction into consideration if it # is directly applied to this variation or if the item # has no variations - if len(v) != 0 and ('variation' not in v or v['variation'] not in applied_to): + if not v.empty() and ('variation' not in v or v['variation'] not in applied_to): continue if restriction.timeframe_from <= now() <= restriction.timeframe_to: diff --git a/src/pretixpresale/templates/pretixpresale/event/index.html b/src/pretixpresale/templates/pretixpresale/event/index.html index 82f7dc7b6..a3823c4ac 100644 --- a/src/pretixpresale/templates/pretixpresale/event/index.html +++ b/src/pretixpresale/templates/pretixpresale/event/index.html @@ -4,16 +4,37 @@ {% for tup in items_by_category %}

{{ tup.0.name }}

{% for item in tup.1 %} -
-
- {{ item.name }} -

{{ item.short_description }}

+ {% if item.has_variations %} +
+
+ {{ item.name }} +

{{ item.short_description }}

+
-
- + {% for var in item.available_variations %} +
+
+ {{ var }} +
+
+ +
+
+
+ {% endfor %} + {% else %} +
+
+ {{ item.name }} +

{{ item.short_description }}

+
+
+ +
+
-
+ {% endif %} {% endfor %} {% endfor %} {% endblock %} \ No newline at end of file diff --git a/src/pretixpresale/views/event.py b/src/pretixpresale/views/event.py index 3eca58cd5..dca6ebf88 100644 --- a/src/pretixpresale/views/event.py +++ b/src/pretixpresale/views/event.py @@ -1,3 +1,4 @@ +from django.db.models import Count from django.views.generic import TemplateView @@ -13,12 +14,21 @@ class EventIndex(EventViewMixin, TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + # Fetch all items items = self.request.event.items.all().select_related( - 'category' + 'category', + ).annotate(quotac=Count('quotas')).filter( + quotac__gt=0 ).order_by('category__position', 'category_id', 'name') + + for item in items: + item.available_variations = item.get_all_available_variations() + item.has_variations = (len(item.available_variations) != 1 + or not item.available_variations[0].empty()) + # Regroup those by category - context['items_by_category'] = [ + context['items_by_category'] = sorted([ (cat, [i for i in items if i.category_id == cat.identity]) for cat in set([i.category for i in items]) - ] + ], key=lambda group: group[0].position) return context