Added the option of unlimited quotas

This commit is contained in:
Raphael Michel
2015-10-21 16:31:58 +02:00
parent 2fb90efa4d
commit a2af3db771
6 changed files with 55 additions and 10 deletions

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='quota',
name='size',
field=models.PositiveIntegerField(help_text='Leave empty for an unlimited number of tickets.', verbose_name='Total capacity', blank=True, null=True),
),
]

View File

@@ -1,4 +1,5 @@
from itertools import product from itertools import product
import sys
from django.db import models from django.db import models
from django.db.models import Q, Case, Count, Sum, When from django.db.models import Q, Case, Count, Sum, When
@@ -315,7 +316,8 @@ class Item(Versionable):
if self.properties.count() > 0: # NOQA if self.properties.count() > 0: # NOQA
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 min([q.availability() for q in self.quotas.all()]) return min([q.availability() for q in self.quotas.all()],
key=lambda s: (s[0], s[1] if s[1] is not None else sys.maxsize))
def check_restrictions(self): def check_restrictions(self):
""" """
@@ -513,7 +515,8 @@ class ItemVariation(Versionable):
:returns: any of the return codes of :py:meth:`Quota.availability()`. :returns: any of the return codes of :py:meth:`Quota.availability()`.
""" """
return min([q.availability() for q in self.quotas.all()]) return min([q.availability() for q in self.quotas.all()],
key=lambda s: (s[0], s[1] if s[1] is not None else sys.maxsize))
def to_variation_dict(self): def to_variation_dict(self):
""" """
@@ -769,7 +772,9 @@ class Quota(Versionable):
verbose_name=_("Name") verbose_name=_("Name")
) )
size = models.PositiveIntegerField( size = models.PositiveIntegerField(
verbose_name=_("Total capacity") verbose_name=_("Total capacity"),
null=True, blank=True,
help_text=_("Leave empty for an unlimited number of tickets.")
) )
items = VersionedManyToManyField( items = VersionedManyToManyField(
Item, Item,
@@ -810,6 +815,9 @@ class Quota(Versionable):
and the second is the number of available tickets. and the second is the number of available tickets.
""" """
size_left = self.size size_left = self.size
if size_left is None:
return Quota.AVAILABILITY_OK, None
# TODO: Test for interference with old versions of Item-Quota-relations, etc. # TODO: Test for interference with old versions of Item-Quota-relations, etc.
# TODO: Prevent corner-cases like people having ordered an item before it got # TODO: Prevent corner-cases like people having ordered an item before it got
# its first variationsadde # its first variationsadde

View File

@@ -228,6 +228,7 @@ class Order(Versionable):
else: else:
# Use cached version # Use cached version
quota = quota_cache[quota.identity] quota = quota_cache[quota.identity]
if quota.cached_availability is not None:
quota.cached_availability -= 1 quota.cached_availability -= 1
if quota.cached_availability < 0: if quota.cached_availability < 0:
# This quota is sold out/currently unavailable, so do not sell this at all # This quota is sold out/currently unavailable, so do not sell this at all

View File

@@ -104,7 +104,7 @@ def _add_items(event, items, session, expiry):
quota_ok = i[2] quota_ok = i[2]
for quota in quotas: for quota in quotas:
avail = quota.availability() avail = quota.availability()
if avail[1] < i[2]: if avail[1] is not None and avail[1] < i[2]:
# This quota is not available or less than i[2] items are left, so we have to # This quota is not available or less than i[2] items are left, so we have to
# reduce the number of bought items # reduce the number of bought items
if avail[0] != Quota.AVAILABILITY_OK: if avail[0] != Quota.AVAILABILITY_OK:

View File

@@ -2,8 +2,12 @@
{% if availability.0 == 10 %} {% if availability.0 == 10 %}
<span class="label label-warning">{% trans "Sold out (pending orders)" %}</span> <span class="label label-warning">{% trans "Sold out (pending orders)" %}</span>
{% elif availability.0 == 100 %} {% elif availability.0 == 100 %}
{% if availability.1 != None %}
<span class="label label-success">{% blocktrans trimmed with num=availability.1 %} <span class="label label-success">{% blocktrans trimmed with num=availability.1 %}
{{ num }} available{% endblocktrans %}</span> {{ num }} available{% endblocktrans %}</span>
{% else %}
<span class="label label-success">{% trans "Unlimited" %}</span>
{% endif %}
{% elif availability.0 == 20 %} {% elif availability.0 == 20 %}
<span class="label label-warning">{% trans "Sold out (or reserved)" %}</span> <span class="label label-warning">{% trans "Sold out (or reserved)" %}</span>
{% elif availability.0 == 0 %} {% elif availability.0 == 0 %}

View File

@@ -303,6 +303,19 @@ class QuotaTestCase(BaseQuotaTestCase):
quota2.save() quota2.save()
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_GONE, 0)) self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_GONE, 0))
def test_unlimited(self):
self.quota.items.add(self.item1)
order = Order.objects.create(event=self.event, status=Order.STATUS_PAID,
expires=now() + timedelta(days=3),
total=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.check_quotas(), (Quota.AVAILABILITY_GONE, 0))
self.quota.size = None
self.quota.save()
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, None))
class OrderTestCase(BaseQuotaTestCase): class OrderTestCase(BaseQuotaTestCase):
def setUp(self): def setUp(self):