forked from CGM_Public/pretix_original
Determine not only availability, but number of remaining tickets
This commit is contained in:
+14
-14
@@ -781,7 +781,7 @@ class Item(Versionable):
|
|||||||
if self.properties.count() > 0:
|
if self.properties.count() > 0:
|
||||||
raise ValueError('Do not call this directly on items which have properties '
|
raise ValueError('Do not call this directly on items which have properties '
|
||||||
'but call this on their ItemVariation objects')
|
'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):
|
class ItemVariation(Versionable):
|
||||||
@@ -840,7 +840,7 @@ class ItemVariation(Versionable):
|
|||||||
This method is used to determine whether this Item is currently available
|
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()
|
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):
|
class VariationsField(VersionedManyToManyField):
|
||||||
@@ -929,7 +929,7 @@ class Quota(Versionable):
|
|||||||
anything with quotas. This might confuse you otherwise.
|
anything with quotas. This might confuse you otherwise.
|
||||||
http://docs.pretix.eu/en/latest/development/concepts.html#restriction-by-number
|
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.
|
its items/variations being for sale.
|
||||||
|
|
||||||
AVAILABILITY_OK
|
AVAILABILITY_OK
|
||||||
@@ -949,10 +949,10 @@ class Quota(Versionable):
|
|||||||
This item is completely sold out.
|
This item is completely sold out.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
AVAILABILITY_GONE = 30
|
AVAILABILITY_GONE = 0
|
||||||
AVAILABILITY_ORDERED = 20
|
AVAILABILITY_ORDERED = 10
|
||||||
AVAILABILITY_RESERVED = 10
|
AVAILABILITY_RESERVED = 20
|
||||||
AVAILABILITY_OK = 0
|
AVAILABILITY_OK = 100
|
||||||
|
|
||||||
event = VersionedForeignKey(
|
event = VersionedForeignKey(
|
||||||
Event,
|
Event,
|
||||||
@@ -1000,9 +1000,9 @@ class Quota(Versionable):
|
|||||||
def availability(self):
|
def availability(self):
|
||||||
"""
|
"""
|
||||||
This method is used to determine whether Items or ItemVariations belonging
|
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
|
to this quota should currently be available for sale. It returns a tuple where
|
||||||
Quota.AVAILABILITY_ constants. 0 is returned if the item is available, a
|
the first entry is one of the Quota.AVAILABILITY_ constants and the second
|
||||||
positive number depending on the reason, if not.
|
is the number of available tickets.
|
||||||
"""
|
"""
|
||||||
# TODO: These lookups are highly inefficient. However, we'll wait with optimizing
|
# 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
|
# until Django 1.8 is released, as the following feature might make it a
|
||||||
@@ -1024,7 +1024,7 @@ class Quota(Versionable):
|
|||||||
& quotalookup
|
& quotalookup
|
||||||
).count()
|
).count()
|
||||||
if paid_orders >= self.size:
|
if paid_orders >= self.size:
|
||||||
return Quota.AVAILABILITY_GONE
|
return (Quota.AVAILABILITY_GONE, 0)
|
||||||
|
|
||||||
pending_valid_orders = OrderPosition.objects.filter(
|
pending_valid_orders = OrderPosition.objects.filter(
|
||||||
Q(order__status=Order.STATUS_PENDING)
|
Q(order__status=Order.STATUS_PENDING)
|
||||||
@@ -1032,16 +1032,16 @@ class Quota(Versionable):
|
|||||||
& quotalookup
|
& quotalookup
|
||||||
).count()
|
).count()
|
||||||
if (paid_orders + pending_valid_orders) >= self.size:
|
if (paid_orders + pending_valid_orders) >= self.size:
|
||||||
return Quota.AVAILABILITY_ORDERED
|
return (Quota.AVAILABILITY_ORDERED, 0)
|
||||||
|
|
||||||
valid_cart_positions = CartPosition.objects.filter(
|
valid_cart_positions = CartPosition.objects.filter(
|
||||||
Q(expires__gte=now())
|
Q(expires__gte=now())
|
||||||
& quotalookup
|
& quotalookup
|
||||||
).count()
|
).count()
|
||||||
if (paid_orders + pending_valid_orders + valid_cart_positions) >= self.size:
|
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):
|
class Order(Versionable):
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ class QuotaTestCase(TestCase):
|
|||||||
|
|
||||||
def test_available(self):
|
def test_available(self):
|
||||||
self.quota.items.add(self.item1)
|
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.items.add(self.item2)
|
||||||
self.quota.variations.add(self.var1)
|
self.quota.variations.add(self.var1)
|
||||||
try:
|
try:
|
||||||
@@ -210,7 +210,7 @@ class QuotaTestCase(TestCase):
|
|||||||
self.assertTrue(False)
|
self.assertTrue(False)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
self.assertEqual(self.var1.availability(), Quota.AVAILABILITY_OK)
|
self.assertEqual(self.var1.availability(), (Quota.AVAILABILITY_OK, 2))
|
||||||
|
|
||||||
def test_sold_out(self):
|
def test_sold_out(self):
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
@@ -219,19 +219,19 @@ class QuotaTestCase(TestCase):
|
|||||||
total=4)
|
total=4)
|
||||||
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
OrderPosition.objects.create(order=order, item=self.item1, price=2)
|
||||||
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.items.add(self.item2)
|
||||||
self.quota.variations.add(self.var1)
|
self.quota.variations.add(self.var1)
|
||||||
self.quota.size = 3
|
self.quota.size = 3
|
||||||
self.quota.save()
|
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,
|
order = Order.objects.create(event=self.event, status=Order.STATUS_PAID,
|
||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
total=4)
|
||||||
OrderPosition.objects.create(order=order, item=self.item2, variation=self.var1, price=2)
|
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):
|
def test_ordered(self):
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
@@ -239,17 +239,17 @@ class QuotaTestCase(TestCase):
|
|||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
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_OK)
|
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
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_ORDERED)
|
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_ORDERED, 0))
|
||||||
|
|
||||||
order.expires = now() - timedelta(days=3)
|
order.expires = now() - timedelta(days=3)
|
||||||
order.save()
|
order.save()
|
||||||
self.assertEqual(self.item1.availability(), Quota.AVAILABILITY_OK)
|
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
def test_reserved(self):
|
def test_reserved(self):
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
@@ -259,32 +259,36 @@ class QuotaTestCase(TestCase):
|
|||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
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_OK)
|
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 2))
|
||||||
|
|
||||||
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
|
||||||
expires=now() + timedelta(days=3),
|
expires=now() + timedelta(days=3),
|
||||||
total=4)
|
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_OK)
|
self.assertEqual(self.item1.availability(), (Quota.AVAILABILITY_OK, 1))
|
||||||
|
|
||||||
cp = CartPosition.objects.create(event=self.event, item=self.item1, price=2,
|
cp = CartPosition.objects.create(event=self.event, item=self.item1, price=2,
|
||||||
expires=now() + timedelta(days=3))
|
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.expires = now() - timedelta(days=3)
|
||||||
cp.save()
|
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.items.add(self.item2)
|
||||||
self.quota.variations.add(self.var1)
|
self.quota.variations.add(self.var1)
|
||||||
cp = CartPosition.objects.create(event=self.event, item=self.item2, variation=self.var1,
|
cp = CartPosition.objects.create(event=self.event, item=self.item2, variation=self.var1,
|
||||||
price=2, expires=now() + timedelta(days=3))
|
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):
|
def test_multiple(self):
|
||||||
self.quota.items.add(self.item1)
|
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)
|
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))
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% if avail == 30 %}
|
{% if avail == 0 %}
|
||||||
<div class="col-md-2 col-xs-6 availability-box gone">
|
<div class="col-md-2 col-xs-6 availability-box gone">
|
||||||
<strong>{% trans "SOLD OUT" %}</strong>
|
<strong>{% trans "SOLD OUT" %}</strong>
|
||||||
</div>
|
</div>
|
||||||
{% elif avail < 30 %}
|
{% elif avail < 100 %}
|
||||||
<div class="col-md-2 col-xs-6 availability-box unavailable">
|
<div class="col-md-2 col-xs-6 availability-box unavailable">
|
||||||
<strong>{% trans "Unavailable" %}</strong><br />
|
<strong>{% trans "Unavailable" %}</strong><br />
|
||||||
<small>
|
<small>
|
||||||
|
|||||||
@@ -27,12 +27,12 @@
|
|||||||
{% endblocktrans %}</small>
|
{% endblocktrans %}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if var.cached_availability == 0 %}
|
{% if var.cached_availability.0 == 100 %}
|
||||||
<div class="col-md-2 col-xs-6 availability-box available">
|
<div class="col-md-2 col-xs-6 availability-box available">
|
||||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0">
|
<input type="number" class="form-control input-item-count" placeholder="0" min="0">
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,12 +51,12 @@
|
|||||||
{% endblocktrans %}</small>
|
{% endblocktrans %}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if item.cached_availability == 0 %}
|
{% if item.cached_availability.0 == 100 %}
|
||||||
<div class="col-md-2 col-xs-6 availability-box available">
|
<div class="col-md-2 col-xs-6 availability-box available">
|
||||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0">
|
<input type="number" class="form-control input-item-count" placeholder="0" min="0">
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user