''' NEEDS TESTS ''' import operator from registrasion.models import commerce from registrasion.models import inventory from collections import Iterable from collections import namedtuple from django.db.models import Case from django.db.models import Q from django.db.models import Sum from django.db.models import When from django.db.models import Value _ProductAndQuantity = namedtuple("ProductAndQuantity", ["product", "quantity"]) class ProductAndQuantity(_ProductAndQuantity): ''' Class that holds a product and a quantity. Attributes: product (models.inventory.Product) quantity (int) ''' pass class ItemController(object): def __init__(self, user): self.user = user def _items(self, cart_status, category=None): ''' Aggregates the items that this user has purchased. Arguments: cart_status (int or Iterable(int)): etc category (Optional[models.inventory.Category]): the category of items to restrict to. Returns: [ProductAndQuantity, ...]: A list of product-quantity pairs, aggregating like products from across multiple invoices. ''' if not isinstance(cart_status, Iterable): cart_status = [cart_status] status_query = ( Q(productitem__cart__status=status) for status in cart_status ) in_cart = Q(productitem__cart__user=self.user) in_cart = in_cart & reduce(operator.__or__, status_query) quantities_in_cart = When( in_cart, then="productitem__quantity", ) quantities_or_zero = Case( quantities_in_cart, default=Value(0), ) products = inventory.Product.objects if category: products = products.filter(category=category) products = products.select_related("category") products = products.annotate(quantity=Sum(quantities_or_zero)) products = products.filter(quantity__gt=0) out = [] for prod in products: out.append(ProductAndQuantity(prod, prod.quantity)) return out def items_pending_or_purchased(self): ''' Returns the items that this user has purchased or has pending. ''' status = [commerce.Cart.STATUS_PAID, commerce.Cart.STATUS_ACTIVE] return self._items(status) def items_purchased(self, category=None): ''' Aggregates the items that this user has purchased. Arguments: category (Optional[models.inventory.Category]): the category of items to restrict to. Returns: [ProductAndQuantity, ...]: A list of product-quantity pairs, aggregating like products from across multiple invoices. ''' return self._items(commerce.Cart.STATUS_PAID, category=category) def items_pending(self): ''' Gets all of the items that the user has reserved, but has not yet paid for. Returns: [ProductAndQuantity, ...]: A list of product-quantity pairs for the items that the user has not yet paid for. ''' return self._items(commerce.Cart.STATUS_ACTIVE) def items_released(self): ''' Gets all of the items that the user previously paid for, but has since refunded. Returns: [ProductAndQuantity, ...]: A list of product-quantity pairs for the items that the user has not yet paid for. ''' return self._items(commerce.Cart.STATUS_RELEASED)