diff --git a/doc/development/api/restriction.rst b/doc/development/api/restriction.rst index 34569eda7..a9ea721a3 100644 --- a/doc/development/api/restriction.rst +++ b/doc/development/api/restriction.rst @@ -175,8 +175,9 @@ In our example, the implementation could look like this:: continue # Walk through all restriction objects applied to this item + prices = [] for restriction in restrictions: - applied_to = list(restriction.variations.all()) + applied_to = list(restriction.variations.current.all()) # Only take this restriction into consideration if it # is directly applied to this variation or if the item @@ -187,11 +188,9 @@ In our example, the implementation could look like this:: if restriction.timeframe_from <= now() <= restriction.timeframe_to: # Selling this item is currently possible available = True - # If multiple time frames are currently active, make sure to - # get the cheapest price: - if (restriction.price is not None - and (price is None or restriction.price < price)): - price = restriction.price + prices.append(restriction.price) + + price = min([p for p in prices if p is not None]) v['available'] = available v['price'] = price diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py index c426efc5b..4bda0b2e0 100644 --- a/src/pretix/base/models.py +++ b/src/pretix/base/models.py @@ -935,6 +935,32 @@ class Item(Versionable): self._get_all_variations_cache = result return result + def _get_all_generated_variations(self): + propids = set([p.identity for p in self.properties.all()]) + if len(propids) == 0: + variations = [VariationDict()] + else: + all_variations = list( + self.variations.annotate( + qc=Count('quotas') + ).filter(qc__gt=0).prefetch_related( + "values", "values__prop", "quotas__event" + ) + ) + variations = [] + for var in all_variations: + values = list(var.values.all()) + # Make sure we don't expose stale ItemVariation objects which are + # still around altough they have an old set of properties + if set([v.prop.identity for v in values]) != propids: + continue + vardict = VariationDict() + for v in values: + vardict[v.prop.identity] = v + vardict['variation'] = var + variations.append(vardict) + return variations + def get_all_available_variations(self, use_cache: bool=False): """ This method returns a list of all variations which are theoretically @@ -959,29 +985,7 @@ class Item(Versionable): from .signals import determine_availability - propids = set([p.identity for p in self.properties.all()]) - if len(propids) == 0: - variations = [VariationDict()] - else: - all_variations = list( - self.variations.annotate( - qc=Count('quotas') - ).filter(qc__gt=0).prefetch_related( - "values", "values__prop", "quotas__event" - ) - ) - variations = [] - for var in all_variations: - values = list(var.values.all()) - # Make sure we don't expose stale ItemVariation objects which are - # still around altough they have an old set of properties - if set([v.prop.identity for v in values]) != propids: - continue - vardict = VariationDict() - for v in values: - vardict[v.prop.identity] = v - vardict['variation'] = var - variations.append(vardict) + variations = self._get_all_generated_variations() responses = determine_availability.send( self.event, item=self, variations=variations, context=None, diff --git a/src/pretix/control/forms/__init__.py b/src/pretix/control/forms/__init__.py index bf8b98dd8..bc452df98 100644 --- a/src/pretix/control/forms/__init__.py +++ b/src/pretix/control/forms/__init__.py @@ -333,6 +333,26 @@ class VariationsField(forms.ModelMultipleChoiceField): if not isinstance(value, (list, tuple)): raise ValidationError(self.error_messages['list'], code='list') + cleaned_value = self._clean_value(value) + + qs = self.item.variations.current.filter(identity__in=cleaned_value) + + # Re-check for consistency + pks = set(force_text(getattr(o, "identity")) for o in qs) + for val in cleaned_value: + if force_text(val) not in pks: + raise ValidationError( + self.error_messages['invalid_choice'], + code='invalid_choice', + params={'value': val}, + ) + + # Since this overrides the inherited ModelChoiceField.clean + # we run custom validators here + self.run_validators(cleaned_value) + return qs + + def _clean_value(self, value): # Build up a cache of variations having an ItemVariation object # For implementation details, see ItemVariation.get_all_variations() # which uses a very similar method @@ -379,22 +399,6 @@ class VariationsField(forms.ModelMultipleChoiceField): else: # An ItemVariation id was given cleaned_value.append(pk) - - qs = self.item.variations.current.filter(identity__in=cleaned_value) - - # Re-check for consistency - pks = set(force_text(getattr(o, "identity")) for o in qs) - for val in cleaned_value: - if force_text(val) not in pks: - raise ValidationError( - self.error_messages['invalid_choice'], - code='invalid_choice', - params={'value': val}, - ) - - # Since this overrides the inherited ModelChoiceField.clean - # we run custom validators here - self.run_validators(cleaned_value) - return qs + return cleaned_value choices = property(_get_choices, forms.ChoiceField._set_choices) diff --git a/src/pretix/plugins/timerestriction/signals.py b/src/pretix/plugins/timerestriction/signals.py index 760aa095e..94d47bca5 100644 --- a/src/pretix/plugins/timerestriction/signals.py +++ b/src/pretix/plugins/timerestriction/signals.py @@ -79,6 +79,7 @@ def availability_handler(sender, **kwargs): continue # Walk through all restriction objects applied to this item + prices = [] for restriction in restrictions: applied_to = list(restriction.variations.current.all()) @@ -91,12 +92,9 @@ def availability_handler(sender, **kwargs): if restriction.timeframe_from <= now() <= restriction.timeframe_to: # Selling this item is currently possible available = True - # If multiple time frames are currently active, make sure to - # get the cheapest price: - if (restriction.price is not None - and (price is None or restriction.price < price)): - price = restriction.price + prices.append(restriction.price) + price = min([p for p in prices if p is not None]) v['available'] = available v['price'] = price cache.set( diff --git a/src/setup.cfg b/src/setup.cfg index 1ad6acf53..7a50a192b 100644 --- a/src/setup.cfg +++ b/src/setup.cfg @@ -2,4 +2,4 @@ ignore = N802,W503 max-line-length = 160 exclude = migrations,.ropeproject,static,mt940.py,_static -max-complexity = 12 +max-complexity = 11