diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index ddd57df73b..16b836069c 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -553,6 +553,8 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio bprice = bundle.designated_price or 0 except ItemBundle.DoesNotExist: bprice = cp.price + except ItemBundle.MultipleObjectsReturned: + raise OrderError("Invalid product configuration (duplicate bundle)") price = get_price(cp.item, cp.variation, cp.voucher, bprice, cp.subevent, custom_price_is_net=False, invoice_address=address, force_custom_price=True, max_discount=max_discount) pbv = get_price(cp.item, cp.variation, None, bprice, cp.subevent, custom_price_is_net=False, diff --git a/src/pretix/control/forms/item.py b/src/pretix/control/forms/item.py index bc40589024..4d9fbe0a64 100644 --- a/src/pretix/control/forms/item.py +++ b/src/pretix/control/forms/item.py @@ -684,6 +684,27 @@ class ItemBundleFormSet(I18nFormSet): self.add_fields(form, None) return form + def clean(self): + super().clean() + ivs = set() + for i in range(0, self.total_form_count()): + form = self.forms[i] + if self.can_delete: + if self._should_delete_form(form): + # This form is going to be deleted so any of its errors + # should not cause the entire formset to be invalid. + try: + ivs.remove(form.cleaned_data['itemvar']) + except KeyError: + pass + continue + + if 'itemvar' in form.cleaned_data: + if form.cleaned_data['itemvar'] in ivs: + raise ValidationError(_('You added the same bundled product twice')) + + ivs.add(form.cleaned_data['itemvar']) + class ItemBundleForm(I18nModelForm): itemvar = forms.ChoiceField(label=_('Bundled product'))