Allows Product.limit_per_user to be blank and null. Adds Category.limit_per_user. Adds functionality and tests to verify that this is legal.
This commit is contained in:
parent
7c99750f3a
commit
0d458bea06
4 changed files with 132 additions and 14 deletions
|
@ -1,6 +1,7 @@
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.db.models import Sum
|
||||||
from registrasion import models as rego
|
from registrasion import models as rego
|
||||||
|
|
||||||
from conditions import ConditionController
|
from conditions import ConditionController
|
||||||
|
@ -42,17 +43,25 @@ class ProductController(object):
|
||||||
|
|
||||||
carts = rego.Cart.objects.filter(user=user)
|
carts = rego.Cart.objects.filter(user=user)
|
||||||
items = rego.ProductItem.objects.filter(
|
items = rego.ProductItem.objects.filter(
|
||||||
product=self.product,
|
cart=carts,
|
||||||
cart=carts)
|
)
|
||||||
|
|
||||||
count = 0
|
prod_items = items.filter(product=self.product)
|
||||||
for item in items:
|
cat_items = items.filter(product__category=self.product.category)
|
||||||
count += item.quantity
|
|
||||||
|
|
||||||
if quantity + count > self.product.limit_per_user:
|
prod_count = prod_items.aggregate(Sum("quantity"))["quantity__sum"]
|
||||||
return False
|
cat_count = cat_items.aggregate(Sum("quantity"))["quantity__sum"]
|
||||||
else:
|
|
||||||
|
prod_limit = self.product.limit_per_user
|
||||||
|
prod_met = prod_limit is None or quantity + prod_count <= prod_limit
|
||||||
|
|
||||||
|
cat_limit = self.product.category.limit_per_user
|
||||||
|
cat_met = cat_limit is None or quantity + cat_count <= cat_limit
|
||||||
|
|
||||||
|
if prod_met and cat_met:
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def can_add_with_enabling_conditions(self, user, quantity):
|
def can_add_with_enabling_conditions(self, user, quantity):
|
||||||
''' Returns true if the user is able to add _quantity_ to their count
|
''' Returns true if the user is able to add _quantity_ to their count
|
||||||
|
|
24
registrasion/migrations/0007_auto_20160326_2105.py
Normal file
24
registrasion/migrations/0007_auto_20160326_2105.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('registrasion', '0006_category_required'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='category',
|
||||||
|
name='limit_per_user',
|
||||||
|
field=models.PositiveIntegerField(null=True, verbose_name='Limit per user', blank=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='product',
|
||||||
|
name='limit_per_user',
|
||||||
|
field=models.PositiveIntegerField(null=True, verbose_name='Limit per user', blank=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -132,6 +132,10 @@ class Category(models.Model):
|
||||||
name = models.CharField(max_length=65, verbose_name=_("Name"))
|
name = models.CharField(max_length=65, verbose_name=_("Name"))
|
||||||
description = models.CharField(max_length=255,
|
description = models.CharField(max_length=255,
|
||||||
verbose_name=_("Description"))
|
verbose_name=_("Description"))
|
||||||
|
limit_per_user = models.PositiveIntegerField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("Limit per user"))
|
||||||
required = models.BooleanField(blank=True)
|
required = models.BooleanField(blank=True)
|
||||||
order = models.PositiveIntegerField(verbose_name=("Display order"))
|
order = models.PositiveIntegerField(verbose_name=("Display order"))
|
||||||
render_type = models.IntegerField(choices=CATEGORY_RENDER_TYPES,
|
render_type = models.IntegerField(choices=CATEGORY_RENDER_TYPES,
|
||||||
|
@ -153,6 +157,7 @@ class Product(models.Model):
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
verbose_name=_("Price"))
|
verbose_name=_("Price"))
|
||||||
limit_per_user = models.PositiveIntegerField(
|
limit_per_user = models.PositiveIntegerField(
|
||||||
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_("Limit per user"))
|
verbose_name=_("Limit per user"))
|
||||||
reservation_duration = models.DurationField(
|
reservation_duration = models.DurationField(
|
||||||
|
|
|
@ -35,7 +35,7 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
cls.RESERVATION = datetime.timedelta(hours=1)
|
cls.RESERVATION = datetime.timedelta(hours=1)
|
||||||
|
|
||||||
cls.categories = []
|
cls.categories = []
|
||||||
for i in xrange(3):
|
for i in xrange(2):
|
||||||
cat = rego.Category.objects.create(
|
cat = rego.Category.objects.create(
|
||||||
name="Category " + str(i + 1),
|
name="Category " + str(i + 1),
|
||||||
description="This is a test category",
|
description="This is a test category",
|
||||||
|
@ -48,13 +48,12 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
|
|
||||||
cls.CAT_1 = cls.categories[0]
|
cls.CAT_1 = cls.categories[0]
|
||||||
cls.CAT_2 = cls.categories[1]
|
cls.CAT_2 = cls.categories[1]
|
||||||
cls.CAT_3 = cls.categories[2]
|
|
||||||
|
|
||||||
cls.products = []
|
cls.products = []
|
||||||
for i in xrange(6):
|
for i in xrange(4):
|
||||||
prod = rego.Product.objects.create(
|
prod = rego.Product.objects.create(
|
||||||
name="Product 1",
|
name="Product 1",
|
||||||
description="This is a test product."
|
description="This is a test product.",
|
||||||
category=cls.categories[i / 2], # 2 products per category
|
category=cls.categories[i / 2], # 2 products per category
|
||||||
price=Decimal("10.00"),
|
price=Decimal("10.00"),
|
||||||
reservation_duration=cls.RESERVATION,
|
reservation_duration=cls.RESERVATION,
|
||||||
|
@ -68,8 +67,6 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
cls.PROD_2 = cls.products[1]
|
cls.PROD_2 = cls.products[1]
|
||||||
cls.PROD_3 = cls.products[2]
|
cls.PROD_3 = cls.products[2]
|
||||||
cls.PROD_4 = cls.products[3]
|
cls.PROD_4 = cls.products[3]
|
||||||
cls.PROD_5 = cls.products[4]
|
|
||||||
cls.PROD_6 = cls.products[5]
|
|
||||||
|
|
||||||
cls.PROD_4.price = Decimal("5.00")
|
cls.PROD_4.price = Decimal("5.00")
|
||||||
cls.PROD_4.save()
|
cls.PROD_4.save()
|
||||||
|
@ -208,3 +205,86 @@ class BasicCartTests(RegistrationCartTestCase):
|
||||||
# Second user should not be affected by first user's limits
|
# Second user should not be affected by first user's limits
|
||||||
second_user_cart = CartController.for_user(self.USER_2)
|
second_user_cart = CartController.for_user(self.USER_2)
|
||||||
second_user_cart.add_to_cart(self.PROD_1, 10)
|
second_user_cart.add_to_cart(self.PROD_1, 10)
|
||||||
|
|
||||||
|
def set_limits(self):
|
||||||
|
self.CAT_2.limit_per_user = 10
|
||||||
|
self.PROD_2.limit_per_user = None
|
||||||
|
self.PROD_3.limit_per_user = None
|
||||||
|
self.PROD_4.limit_per_user = 6
|
||||||
|
|
||||||
|
self.CAT_2.save()
|
||||||
|
self.PROD_2.save()
|
||||||
|
self.PROD_3.save()
|
||||||
|
self.PROD_4.save()
|
||||||
|
|
||||||
|
def test_per_user_product_limit_ignored_if_blank(self):
|
||||||
|
self.set_limits()
|
||||||
|
|
||||||
|
current_cart = CartController.for_user(self.USER_1)
|
||||||
|
# There is no product limit on PROD_2, and there is no cat limit
|
||||||
|
current_cart.add_to_cart(self.PROD_2, 1)
|
||||||
|
# There is no product limit on PROD_3, but there is a cat limit
|
||||||
|
current_cart.add_to_cart(self.PROD_3, 1)
|
||||||
|
|
||||||
|
def test_per_user_category_limit_ignored_if_blank(self):
|
||||||
|
self.set_limits()
|
||||||
|
current_cart = CartController.for_user(self.USER_1)
|
||||||
|
# There is no product limit on PROD_2, and there is no cat limit
|
||||||
|
current_cart.add_to_cart(self.PROD_2, 1)
|
||||||
|
# There is no cat limit on PROD_1, but there is a prod limit
|
||||||
|
current_cart.add_to_cart(self.PROD_1, 1)
|
||||||
|
|
||||||
|
def test_per_user_category_limit_only(self):
|
||||||
|
self.set_limits()
|
||||||
|
|
||||||
|
current_cart = CartController.for_user(self.USER_1)
|
||||||
|
|
||||||
|
# Cannot add to cart if category limit is filled by one product.
|
||||||
|
current_cart.set_quantity(self.PROD_3, 10)
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
current_cart.set_quantity(self.PROD_4, 1)
|
||||||
|
|
||||||
|
# Can add to cart if category limit is not filled by one product
|
||||||
|
current_cart.set_quantity(self.PROD_3, 5)
|
||||||
|
current_cart.set_quantity(self.PROD_4, 5)
|
||||||
|
# Cannot add to cart if category limit is filled by two products
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
current_cart.add_to_cart(self.PROD_3, 1)
|
||||||
|
|
||||||
|
current_cart.cart.active = False
|
||||||
|
current_cart.cart.save()
|
||||||
|
|
||||||
|
current_cart = CartController.for_user(self.USER_1)
|
||||||
|
# The category limit should extend across carts
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
current_cart.add_to_cart(self.PROD_3, 10)
|
||||||
|
|
||||||
|
def test_per_user_category_and_product_limits(self):
|
||||||
|
self.set_limits()
|
||||||
|
|
||||||
|
current_cart = CartController.for_user(self.USER_1)
|
||||||
|
|
||||||
|
# Hit both the product and category edges:
|
||||||
|
current_cart.set_quantity(self.PROD_3, 4)
|
||||||
|
current_cart.set_quantity(self.PROD_4, 6)
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
# There's unlimited PROD_3, but limited in the category
|
||||||
|
current_cart.add_to_cart(self.PROD_3, 1)
|
||||||
|
|
||||||
|
current_cart.set_quantity(self.PROD_3, 0)
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
# There's only 6 allowed of PROD_4
|
||||||
|
current_cart.add_to_cart(self.PROD_4, 1)
|
||||||
|
|
||||||
|
# The limits should extend across carts...
|
||||||
|
current_cart.cart.active = False
|
||||||
|
current_cart.cart.save()
|
||||||
|
|
||||||
|
current_cart = CartController.for_user(self.USER_1)
|
||||||
|
current_cart.set_quantity(self.PROD_3, 4)
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
current_cart.set_quantity(self.PROD_3, 5)
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
current_cart.set_quantity(self.PROD_4, 1)
|
||||||
|
|
Loading…
Reference in a new issue