Changeset - 941caa30d9f4
[Not reviewed]
0 2 0
Christopher Neugebauer - 8 years ago 2016-04-30 10:30:21
chrisjrn@gmail.com
Replaces ProductController.attach_user_remainders with ProductController.user_remainders
2 files changed with 9 insertions and 22 deletions:
0 comments (0 inline, 0 general)
registrasion/controllers/cart.py
Show inline comments
...
 
@@ -172,106 +172,104 @@ class CartController(object):
 
        items_in_cart = commerce.ProductItem.objects.filter(cart=self.cart)
 
        items_in_cart = items_in_cart.select_related(
 
            "product",
 
            "product__category",
 
        )
 

	
 
        product_quantities = list(product_quantities)
 

	
 
        # n.b need to add have the existing items first so that the new
 
        # items override the old ones.
 
        all_product_quantities = dict(itertools.chain(
 
            ((i.product, i.quantity) for i in items_in_cart.all()),
 
            product_quantities,
 
        )).items()
 

	
 
        # Validate that the limits we're adding are OK
 
        self._test_limits(all_product_quantities)
 

	
 
        new_items = []
 
        products = []
 
        for product, quantity in product_quantities:
 
            products.append(product)
 

	
 
            if quantity == 0:
 
                continue
 

	
 
            item = commerce.ProductItem(
 
                cart=self.cart,
 
                product=product,
 
                quantity=quantity,
 
            )
 
            new_items.append(item)
 

	
 
        to_delete = (
 
            Q(quantity=0) |
 
            Q(product__in=products)
 
        )
 

	
 
        items_in_cart.filter(to_delete).delete()
 
        commerce.ProductItem.objects.bulk_create(new_items)
 

	
 
    def _test_limits(self, product_quantities):
 
        ''' Tests that the quantity changes we intend to make do not violate
 
        the limits and flag conditions imposed on the products. '''
 

	
 
        errors = []
 

	
 
        # Pre-annotate products
 
        products = [p for (p, q) in product_quantities]
 
        r = ProductController.attach_user_remainders(self.cart.user, products)
 
        with_remainders = dict((p, p) for p in r)
 
        remainders = ProductController.user_remainders(self.cart.user)
 

	
 
        # Test each product limit here
 
        for product, quantity in product_quantities:
 
            if quantity < 0:
 
                errors.append((product, "Value must be zero or greater."))
 

	
 
            limit = with_remainders[product].remainder
 
            limit = remainders[product.id]
 

	
 
            if quantity > limit:
 
                errors.append((
 
                    product,
 
                    "You may only have %d of product: %s" % (
 
                        limit, product,
 
                    )
 
                ))
 

	
 
        # Collect by category
 
        by_cat = collections.defaultdict(list)
 
        for product, quantity in product_quantities:
 
            by_cat[product.category].append((product, quantity))
 

	
 
        # Pre-annotate categories
 
        r = CategoryController.attach_user_remainders(self.cart.user, by_cat)
 
        with_remainders = dict((cat, cat) for cat in r)
 

	
 
        # Test each category limit here
 
        for category in by_cat:
 
            limit = with_remainders[category].remainder
 

	
 
            # Get the amount so far in the cart
 
            to_add = sum(i[1] for i in by_cat[category])
 

	
 
            if to_add > limit:
 
                errors.append((
 
                    category,
 
                    "You may only have %d items in category: %s" % (
 
                        limit, category.name,
 
                    )
 
                ))
 

	
 
        # Test the flag conditions
 
        errs = FlagController.test_flags(
 
            self.cart.user,
 
            product_quantities=product_quantities,
 
        )
 

	
 
        if errs:
 
            for error in errs:
 
                errors.append(error)
 

	
 
        if errors:
 
            raise CartValidationError(errors)
 

	
 
    @_modifies_cart
 
    def apply_voucher(self, voucher_code):
registrasion/controllers/product.py
Show inline comments
 
import itertools
 

	
 
from django.db.models import Case
 
from django.db.models import F, Q
 
from django.db.models import Sum
 
from django.db.models import When
 
from django.db.models import Value
 

	
 
from registrasion.models import commerce
 
from registrasion.models import inventory
 

	
 
from .category import CategoryController
 
from .flag import FlagController
 

	
 

	
 
class ProductController(object):
 

	
 
    def __init__(self, product):
 
        self.product = product
 

	
 
    @classmethod
 
    def available_products(cls, user, category=None, products=None):
 
        ''' Returns a list of all of the products that are available per
 
        flag conditions from the given categories. '''
 
        if category is None and products is None:
 
            raise ValueError("You must provide products or a category")
 

	
 
        if category is not None:
 
            all_products = inventory.Product.objects.filter(category=category)
 
            all_products = all_products.select_related("category")
 
        else:
 
            all_products = []
 

	
 
        if products is not None:
 
            all_products = set(itertools.chain(all_products, products))
 

	
 
        categories = set(product.category for product in all_products)
 
        r = CategoryController.attach_user_remainders(user, categories)
 
        cat_quants = dict((c, c) for c in r)
 

	
 
        r = ProductController.attach_user_remainders(user, all_products)
 
        prod_quants = dict((p, p) for p in r)
 
        product_remainders = ProductController.user_remainders(user)
 

	
 
        passed_limits = set(
 
            product
 
            for product in all_products
 
            if cat_quants[product.category].remainder > 0
 
            if prod_quants[product].remainder > 0
 
            if product_remainders[product.id] > 0
 
        )
 

	
 
        failed_and_messages = FlagController.test_flags(
 
            user, products=passed_limits
 
        )
 
        failed_conditions = set(i[0] for i in failed_and_messages)
 

	
 
        out = list(passed_limits - failed_conditions)
 
        out.sort(key=lambda product: product.order)
 

	
 
        return out
 

	
 
    @classmethod
 
    def attach_user_remainders(cls, user, products):
 
    def user_remainders(cls, user):
 
        '''
 

	
 
        Return:
 
            queryset(inventory.Product): A queryset containing items from
 
            ``product``, with an extra attribute -- remainder = the amount of
 
            this item that is remaining.
 
            Mapping[int->int]: A dictionary that maps the product ID to the
 
            user's remainder for that product.
 
        '''
 

	
 
        ids = [product.id for product in products]
 
        products = inventory.Product.objects.filter(id__in=ids)
 
        products = inventory.Product.objects.all()
 

	
 
        cart_filter = (
 
            Q(productitem__cart__user=user) &
 
            Q(productitem__cart__status=commerce.Cart.STATUS_PAID)
 
        )
 

	
 
        quantity = When(
 
            cart_filter,
 
            then='productitem__quantity'
 
        )
 

	
 
        quantity_or_zero = Case(
 
            quantity,
 
            default=Value(0),
 
        )
 

	
 
        remainder = Case(
 
            When(limit_per_user=None, then=Value(99999999)),
 
            default=F('limit_per_user') - Sum(quantity_or_zero),
 
        )
 

	
 
        products = products.annotate(remainder=remainder)
 

	
 
        return products
 

	
 
    def user_quantity_remaining(self, user):
 
        ''' Returns the quantity of this product that the user add in the
 
        current cart. '''
 

	
 
        with_remainders = self.attach_user_remainders(user, [self.product])
 

	
 
        return with_remainders[0].remainder
 
        return dict((product.id, product.remainder) for product in products)
0 comments (0 inline, 0 general)