Resolved two more edge cases in quota handling

Scenario 1) Blocking voucher is used in a CartPosition. Previously
too much was subtracted from the quota.

Scenario 2) When two quotas are assigned to a product and one of them
is sold out, blocking vouchers for the other quota should not enable to
buy the product.
This commit is contained in:
Raphael Michel
2016-08-15 22:53:32 +02:00
parent fddd612a63
commit 63b683096b
5 changed files with 132 additions and 1 deletions

View File

@@ -554,6 +554,10 @@ class Quota(LoggedModel):
return CartPosition.objects.filter(
Q(expires__gte=now()) &
~Q(
Q(voucher__isnull=False) & Q(voucher__block_quota=True)
& Q(Q(voucher__valid_until__isnull=True) | Q(voucher__valid_until__gte=now()))
) &
self._position_lookup
).distinct().count()

View File

@@ -222,8 +222,13 @@ def _check_positions(event: Event, dt: datetime, positions: List[CartPosition]):
quota_ok = True
if not cp.voucher or not (cp.voucher.allow_ignore_quota or cp.voucher.block_quota):
ignore_all_quotas = cp.expires >= dt or (
cp.voucher and (cp.voucher.allow_ignore_quota or (cp.voucher.block_quota and cp.voucher.quota is None)))
if not ignore_all_quotas:
for quota in quotas:
if cp.voucher and cp.voucher.block_quota and cp.voucher.quota_id == quota.pk:
continue
avail = quota.availability()
if avail[0] != Quota.AVAILABILITY_OK:
# This quota is sold out/currently unavailable, so do not sell this at all

View File

@@ -240,6 +240,44 @@ class QuotaTestCase(BaseQuotaTestCase):
block_quota=True)
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
def test_blocking_voucher_in_cart(self):
self.quota.items.add(self.item1)
v = Voucher.objects.create(quota=self.quota, event=self.event, valid_until=now() + timedelta(days=5),
block_quota=True)
CartPosition.objects.create(event=self.event, item=self.item1, price=2,
expires=now() + timedelta(days=3), voucher=v)
self.assertEqual(self.quota.count_blocking_vouchers(), 1)
self.assertEqual(self.quota.count_in_cart(), 0)
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
def test_blocking_voucher_in_cart_inifinitely_valid(self):
self.quota.items.add(self.item1)
v = Voucher.objects.create(quota=self.quota, event=self.event, block_quota=True)
CartPosition.objects.create(event=self.event, item=self.item1, price=2,
expires=now() + timedelta(days=3), voucher=v)
self.assertEqual(self.quota.count_blocking_vouchers(), 1)
self.assertEqual(self.quota.count_in_cart(), 0)
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
def test_blocking_expired_voucher_in_cart(self):
self.quota.items.add(self.item1)
v = Voucher.objects.create(quota=self.quota, event=self.event, valid_until=now() - timedelta(days=5),
block_quota=True)
CartPosition.objects.create(event=self.event, item=self.item1, price=2,
expires=now() + timedelta(days=3), voucher=v)
self.assertEqual(self.quota.count_blocking_vouchers(), 0)
self.assertEqual(self.quota.count_in_cart(), 1)
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
def test_nonblocking_voucher_in_cart(self):
self.quota.items.add(self.item1)
v = Voucher.objects.create(quota=self.quota, event=self.event)
CartPosition.objects.create(event=self.event, item=self.item1, price=2,
expires=now() + timedelta(days=3), voucher=v)
self.assertEqual(self.quota.count_blocking_vouchers(), 0)
self.assertEqual(self.quota.count_in_cart(), 1)
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
class OrderTestCase(BaseQuotaTestCase):
def setUp(self):

View File

@@ -600,6 +600,15 @@ class CartTest(CartTestMixin, TestCase):
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_voucher_quota_other_quota_full(self):
quota2 = self.event.quotas.create(name='Test', size=0)
quota2.variations.add(self.shirt_red)
v = Voucher.objects.create(quota=self.quota_shirts, event=self.event)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'variation_%d_%d_voucher' % (self.shirt.id, self.shirt_red.id): v.code,
}, follow=True)
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count(), 0)
def test_hide_without_voucher(self):
v = Voucher.objects.create(item=self.shirt, event=self.event)
self.shirt.hide_without_voucher = True

View File

@@ -409,6 +409,24 @@ class CheckoutTestCase(TestCase):
self.assertEqual(Order.objects.count(), 1)
self.assertEqual(OrderPosition.objects.count(), 1)
def test_voucher_block_quota_other_quota_full(self):
self.quota_tickets.size = 0
self.quota_tickets.save()
q2 = self.event.quotas.create(name='Testquota', size=0)
q2.items.add(self.ticket)
v = Voucher.objects.create(quota=self.quota_tickets, price=Decimal('12.00'), event=self.event,
valid_until=now() - timedelta(days=2), block_quota=True)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=12, expires=now() + timedelta(minutes=10), voucher=v
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content)
self.assertEqual(len(doc.select(".alert-danger")), 1)
self.assertFalse(Order.objects.exists())
def test_voucher_double(self):
self.quota_tickets.size = 2
self.quota_tickets.save()
@@ -536,3 +554,60 @@ class CheckoutTestCase(TestCase):
doc = BeautifulSoup(response.rendered_content)
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
def test_confirm_expired_with_blocking_voucher_unavailable(self):
self.quota_tickets.size = 0
self.quota_tickets.save()
v = Voucher.objects.create(quota=self.quota_tickets, event=self.event, block_quota=True)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket, voucher=v,
price=23, expires=now() - timedelta(minutes=10)
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content)
self.assertEqual(len(doc.select(".thank-you")), 1)
def test_confirm_expired_with_non_blocking_voucher_unavailable(self):
self.quota_tickets.size = 0
self.quota_tickets.save()
v = Voucher.objects.create(quota=self.quota_tickets, event=self.event)
cr1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket, voucher=v,
price=23, expires=now() - timedelta(minutes=10)
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content)
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
def test_confirm_not_expired_with_blocking_voucher_unavailable(self):
self.quota_tickets.size = 0
self.quota_tickets.save()
v = Voucher.objects.create(quota=self.quota_tickets, event=self.event, block_quota=True)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket, voucher=v,
price=23, expires=now() + timedelta(minutes=10)
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content)
self.assertEqual(len(doc.select(".thank-you")), 1)
def test_confirm_not_expired_with_non_blocking_voucher_unavailable(self):
self.quota_tickets.size = 0
self.quota_tickets.save()
v = Voucher.objects.create(quota=self.quota_tickets, event=self.event)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket, voucher=v,
price=23, expires=now() + timedelta(minutes=10)
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content)
self.assertEqual(len(doc.select(".thank-you")), 1)