diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index 9719e76334..44e9593138 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -318,7 +318,7 @@ class CartManager: self._check_item_constraints(op) operations.append(op) - self._quota_diff += quota_diff + self._quota_diff.update(quota_diff) self._voucher_use_diff += voucher_use_diff self._operations += operations @@ -456,7 +456,7 @@ class CartManager: for k, v in al.items(): if k not in input_addons[cp.id]: if v.expires > self.now_dt: - quotas = list(cp.quotas) + quotas = list(v.quotas) for quota in quotas: quota_diff[quota] -= 1 @@ -464,12 +464,14 @@ class CartManager: op = self.RemoveOperation(position=v) operations.append(op) - self._quota_diff += quota_diff + self._quota_diff.update(quota_diff) self._operations += operations def _get_quota_availability(self): - quotas_ok = {} + quotas_ok = defaultdict(int) for quota, count in self._quota_diff.items(): + if count <= 0: + quotas_ok[quota] = 0 avail = quota.availability(self.now_dt) if avail[1] is not None and avail[1] < count: quotas_ok[quota] = min(count, avail[1]) @@ -541,6 +543,9 @@ class CartManager: for op in self._operations: if isinstance(op, self.RemoveOperation): + if op.position.expires > self.now_dt: + for q in op.position.quotas: + quotas_ok[q] += 1 op.position.delete() elif isinstance(op, self.AddOperation) or isinstance(op, self.ExtendOperation): diff --git a/src/tests/presale/test_cart.py b/src/tests/presale/test_cart.py index 91f3824a31..c559333927 100644 --- a/src/tests/presale/test_cart.py +++ b/src/tests/presale/test_cart.py @@ -1737,6 +1737,43 @@ class CartAddonTest(CartTestMixin, TestCase): ]) self.cm.commit() + def test_sold_out_swap_addons(self): + cp1 = CartPosition.objects.create( + expires=now() + timedelta(minutes=10), item=self.ticket, price=Decimal('23.00'), + event=self.event, cart_id=self.session_key + ) + CartPosition.objects.create( + expires=now() + timedelta(minutes=10), item=self.workshop1, price=Decimal('12.00'), + event=self.event, cart_id=self.session_key, addon_to=cp1 + ) + cp2 = CartPosition.objects.create( + expires=now() + timedelta(minutes=10), item=self.ticket, price=Decimal('23.00'), + event=self.event, cart_id=self.session_key + ) + CartPosition.objects.create( + expires=now() + timedelta(minutes=10), item=self.workshop2, price=Decimal('12.00'), + event=self.event, cart_id=self.session_key, addon_to=cp2 + ) + self.workshopquota.size = 0 + self.workshopquota.save() + self.cm.set_addons([ + { + 'addon_to': cp1.pk, + 'item': self.workshop2.pk, + 'variation': None + }, + { + 'addon_to': cp2.pk, + 'item': self.workshop1.pk, + 'variation': None + }, + ]) + self.cm.commit() + assert cp1.addons.count() == 1 + assert cp2.addons.count() == 1 + assert cp1.addons.first().item == self.workshop2 + assert cp2.addons.first().item == self.workshop1 + def test_expand_expired(self): cp1 = CartPosition.objects.create( expires=now() - timedelta(minutes=10), item=self.ticket, price=Decimal('23.00'),