diff --git a/registrasion/controllers/cart.py b/registrasion/controllers/cart.py index aec77c07..f3bcabf1 100644 --- a/registrasion/controllers/cart.py +++ b/registrasion/controllers/cart.py @@ -1,14 +1,16 @@ +import collections import datetime import discount from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ValidationError from django.db import transaction -from django.db.models import Max +from django.db.models import Max, Sum from django.utils import timezone from registrasion import models as rego +from category import CategoryController from conditions import ConditionController from product import ProductController @@ -71,12 +73,40 @@ class CartController(object): @transaction.atomic def set_quantities(self, product_quantities): + items_in_cart = rego.ProductItem.objects.filter(cart=self.cart) + # Remove all items that we're updating - rego.ProductItem.objects.filter( - cart=self.cart, + items_in_cart.filter( product__in=(i[0] for i in product_quantities), ).delete() + # Collect by category + by_cat = collections.defaultdict(list) + for product, quantity in product_quantities: + by_cat[product.category].append((product, quantity)) + + # Test each category limit here + for cat in by_cat: + ctrl = CategoryController(cat) + limit = ctrl.user_quantity_remaining(self.cart.user) + + # Get the amount so far in the cart + cat_items = items_in_cart.filter(product__category=cat) + so_far = cat_items.aggregate(Sum("quantity"))["quantity__sum"] or 0 + to_add = sum(i[1] for i in by_cat[cat]) + + if so_far + to_add > limit: + # TODO: batch errors + raise ValidationError( + "You may only have %d items in category: %s" % ( + limit, cat.name, + ) + ) + + # Test each product limit here + + # Test each enabling condition here + for product, quantity in product_quantities: self._set_quantity_old(product, quantity) @@ -86,7 +116,7 @@ class CartController(object): ''' Sets the _quantity_ of the given _product_ in the cart to the given _quantity_. ''' - self.set_quantities( ((product,quantity),) ) + self.set_quantities(((product, quantity),)) def _set_quantity_old(self, product, quantity): ''' Sets the _quantity_ of the given _product_ in the cart to the given diff --git a/registrasion/controllers/category.py b/registrasion/controllers/category.py index 5a6b4a68..7f244263 100644 --- a/registrasion/controllers/category.py +++ b/registrasion/controllers/category.py @@ -1,7 +1,7 @@ -from .product import ProductController - from registrasion import models as rego +from django.db.models import Sum + class AllProducts(object): pass @@ -9,12 +9,18 @@ class AllProducts(object): class CategoryController(object): + def __init__(self, category): + self.category = category + @classmethod def available_categories(cls, user, products=AllProducts): ''' Returns the categories available to the user. Specify `products` if you want to restrict to just the categories that hold the specified products, otherwise it'll do all. ''' + # STOPGAP -- this needs to be elsewhere tbqh + from product import ProductController + if products is AllProducts: products = rego.Product.objects.all() @@ -24,3 +30,33 @@ class CategoryController(object): ) return set(i.category for i in available) + + def user_quantity_remaining(self, user): + ''' Returns the number of items from this category that the user may + add in the current cart. ''' + + cat_limit = self.category.limit_per_user + + if cat_limit is None: + # We don't need to waste the following queries + return 99999999 + + carts = rego.Cart.objects.filter( + user=user, + active=False, + released=False, + ) + + items = rego.ProductItem.objects.filter( + cart__in=carts, + product__category=self.category, + ) + + cat_count = items.aggregate(Sum("quantity"))["quantity__sum"] or 0 + + cat_limit = self.category.limit_per_user + + if cat_limit is None: + return 999999 # We should probably work on this. + else: + return cat_limit - cat_count diff --git a/registrasion/controllers/product.py b/registrasion/controllers/product.py index abb5947d..f92ecb10 100644 --- a/registrasion/controllers/product.py +++ b/registrasion/controllers/product.py @@ -4,6 +4,7 @@ from django.db.models import Q from django.db.models import Sum from registrasion import models as rego +from category import CategoryController from conditions import ConditionController @@ -32,6 +33,7 @@ class ProductController(object): out = [ product for product in all_products + if CategoryController(product.category).user_quantity_remaining(user) > 0 if cls(product).user_can_add_within_limit(user, 1, past_carts=True) if cls(product).can_add_with_enabling_conditions(user, 0) ] @@ -54,23 +56,14 @@ class ProductController(object): ) prod_items = items.filter(product=self.product) - cat_items = items.filter(product__category=self.product.category) prod_count = prod_items.aggregate(Sum("quantity"))["quantity__sum"] - cat_count = cat_items.aggregate(Sum("quantity"))["quantity__sum"] - - if prod_count is None: - prod_count = 0 - if cat_count is None: - cat_count = 0 + prod_count = prod_count or 0 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: + if prod_met: return True else: return False