Determine not only availability, but number of remaining tickets

This commit is contained in:
Raphael Michel
2015-02-12 10:37:29 +01:00
parent f6b4f726e1
commit 2ee7ddd9b2
4 changed files with 40 additions and 36 deletions

View File

@@ -781,7 +781,7 @@ class Item(Versionable):
if self.properties.count() > 0:
raise ValueError('Do not call this directly on items which have properties '
'but call this on their ItemVariation objects')
return max([q.availability() for q in self.quotas.all()])
return min([q.availability() for q in self.quotas.all()])
class ItemVariation(Versionable):
@@ -840,7 +840,7 @@ class ItemVariation(Versionable):
This method is used to determine whether this Item is currently available
for sale. It may return any of the return codes of Quota.availability()
"""
return max([q.availability() for q in self.quotas.all()])
return min([q.availability() for q in self.quotas.all()])
class VariationsField(VersionedManyToManyField):
@@ -929,7 +929,7 @@ class Quota(Versionable):
anything with quotas. This might confuse you otherwise.
http://docs.pretix.eu/en/latest/development/concepts.html#restriction-by-number
The AVAILABILITY_* constants represent varios states of an quota allowing
The AVAILABILITY_* constants represent various states of an quota allowing
its items/variations being for sale.
AVAILABILITY_OK
@@ -949,10 +949,10 @@ class Quota(Versionable):
This item is completely sold out.
"""
AVAILABILITY_GONE = 30
AVAILABILITY_ORDERED = 20
AVAILABILITY_RESERVED = 10
AVAILABILITY_OK = 0
AVAILABILITY_GONE = 0
AVAILABILITY_ORDERED = 10
AVAILABILITY_RESERVED = 20
AVAILABILITY_OK = 100
event = VersionedForeignKey(
Event,
@@ -1000,9 +1000,9 @@ class Quota(Versionable):
def availability(self):
"""
This method is used to determine whether Items or ItemVariations belonging
to this quota should currently be available for sale. It returns one of the
Quota.AVAILABILITY_ constants. 0 is returned if the item is available, a
positive number depending on the reason, if not.
to this quota should currently be available for sale. It returns a tuple where
the first entry is one of the Quota.AVAILABILITY_ constants and the second
is the number of available tickets.
"""
# TODO: These lookups are highly inefficient. However, we'll wait with optimizing
# until Django 1.8 is released, as the following feature might make it a
@@ -1024,7 +1024,7 @@ class Quota(Versionable):
& quotalookup
).count()
if paid_orders >= self.size:
return Quota.AVAILABILITY_GONE
return (Quota.AVAILABILITY_GONE, 0)
pending_valid_orders = OrderPosition.objects.filter(
Q(order__status=Order.STATUS_PENDING)
@@ -1032,16 +1032,16 @@ class Quota(Versionable):
& quotalookup
).count()
if (paid_orders + pending_valid_orders) >= self.size:
return Quota.AVAILABILITY_ORDERED
return (Quota.AVAILABILITY_ORDERED, 0)
valid_cart_positions = CartPosition.objects.filter(
Q(expires__gte=now())
& quotalookup
).count()
if (paid_orders + pending_valid_orders + valid_cart_positions) >= self.size:
return Quota.AVAILABILITY_RESERVED
return (Quota.AVAILABILITY_RESERVED, 0)
return Quota.AVAILABILITY_OK
return (Quota.AVAILABILITY_OK, self.size - paid_orders - pending_valid_orders - valid_cart_positions)
class Order(Versionable):

View File

@@ -202,7 +202,7 @@ class QuotaTestCase(TestCase):
def test_available(self):
self.quota.items.add(self.item1)
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
self.quota.items.add(self.item2)
self.quota.variations.add(self.var1)
try:
@@ -210,7 +210,7 @@ class QuotaTestCase(TestCase):
self.assertTrue(False)
except:
pass
self.assertEqual(self.var1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_OK, 2))
def test_sold_out(self):
self.quota.items.add(self.item1)
@@ -219,19 +219,19 @@ class QuotaTestCase(TestCase):
total=4)
OrderPosition.objects.create(order=order, item=self.item1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, price=2)
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_GONE)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_GONE, 0))
self.quota.items.add(self.item2)
self.quota.variations.add(self.var1)
self.quota.size = 3
self.quota.save()
self.assertEqual(self.var1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_OK, 1))
order = Order.objects.create(event=self.event, status=Order.STATUS_PAID,
expires=now() + timedelta(days=3),
total=4)
OrderPosition.objects.create(order=order, item=self.item2, variation=self.var1, price=2)
self.assertEqual(self.var1.availability(), Quota.AVAILABILITY_GONE)
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_GONE, 0))
def test_ordered(self):
self.quota.items.add(self.item1)
@@ -239,17 +239,17 @@ class QuotaTestCase(TestCase):
expires=now() + timedelta(days=3),
total=4)
OrderPosition.objects.create(order=order, item=self.item1, price=2)
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
expires=now() + timedelta(days=3),
total=4)
OrderPosition.objects.create(order=order, item=self.item1, price=2)
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_ORDERED)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_ORDERED, 0))
order.expires = now() - timedelta(days=3)
order.save()
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
def test_reserved(self):
self.quota.items.add(self.item1)
@@ -259,32 +259,36 @@ class QuotaTestCase(TestCase):
expires=now() + timedelta(days=3),
total=4)
OrderPosition.objects.create(order=order, item=self.item1, price=2)
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
expires=now() + timedelta(days=3),
total=4)
OrderPosition.objects.create(order=order, item=self.item1, price=2)
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
cp = CartPosition.objects.create(event=self.event, item=self.item1, price=2,
expires=now() + timedelta(days=3))
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_RESERVED)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_RESERVED, 0))
cp.expires = now() - timedelta(days=3)
cp.save()
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
self.quota.items.add(self.item2)
self.quota.variations.add(self.var1)
cp = CartPosition.objects.create(event=self.event, item=self.item2, variation=self.var1,
price=2, expires=now() + timedelta(days=3))
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_RESERVED)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_RESERVED, 0))
def test_multiple(self):
self.quota.items.add(self.item1)
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_OK)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
quota2 = Quota.objects.create(event=self.event, name="Test 2", size=0)
quota2 = Quota.objects.create(event=self.event, name="Test 2", size=1)
quota2.items.add(self.item1)
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_GONE)
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
quota2.size = 0
quota2.save()
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_GONE, 0))

View File

@@ -1,9 +1,9 @@
{% load i18n %}
{% if avail == 30 %}
{% if avail == 0 %}
<div class="col-md-2 col-xs-6 availability-box gone">
<strong>{% trans "SOLD OUT" %}</strong>
</div>
{% elif avail < 30 %}
{% elif avail < 100 %}
<div class="col-md-2 col-xs-6 availability-box unavailable">
<strong>{% trans "Unavailable" %}</strong><br />
<small>

View File

@@ -27,12 +27,12 @@
{% endblocktrans %}</small>
{% endif %}
</div>
{% if var.cached_availability == 0 %}
{% if var.cached_availability.0 == 100 %}
<div class="col-md-2 col-xs-6 availability-box available">
<input type="number" class="form-control input-item-count" placeholder="0" min="0">
</div>
{% else %}
{% include "pretixpresale/event/fragment_availability.html" with avail=var.cached_availability %}
{% include "pretixpresale/event/fragment_availability.html" with avail=var.cached_availability.0 %}
{% endif %}
<div class="clearfix"></div>
</div>
@@ -51,12 +51,12 @@
{% endblocktrans %}</small>
{% endif %}
</div>
{% if item.cached_availability == 0 %}
{% if item.cached_availability.0 == 100 %}
<div class="col-md-2 col-xs-6 availability-box available">
<input type="number" class="form-control input-item-count" placeholder="0" min="0">
</div>
{% else %}
{% include "pretixpresale/event/fragment_availability.html" with avail=item.cached_availability %}
{% include "pretixpresale/event/fragment_availability.html" with avail=item.cached_availability.0 %}
{% endif %}
<div class="clearfix"></div>
</div>