Changeset - c51be4d30aff
[Not reviewed]
0 2 0
Christopher Neugebauer - 8 years ago 2016-03-04 22:07:02
chrisjrn@gmail.com
Adds set_quantity as a method on CartController.

Refactors add_to_cart to be in terms of set_quantity
2 files changed with 84 insertions and 21 deletions:
0 comments (0 inline, 0 general)
registrasion/controllers/cart.py
Show inline comments
...
 
@@ -33,99 +33,129 @@ class CartController(object):
 
        return CartController(existing)
 

	
 
    def extend_reservation(self):
 
        ''' Updates the cart's time last updated value, which is used to
 
        determine whether the cart has reserved the items and discounts it
 
        holds. '''
 

	
 
        reservations = [datetime.timedelta()]
 

	
 
        # If we have vouchers, we're entitled to an hour at minimum.
 
        if len(self.cart.vouchers.all()) >= 1:
 
            reservations.append(rego.Voucher.RESERVATION_DURATION)
 

	
 
        # Else, it's the maximum of the included products
 
        items = rego.ProductItem.objects.filter(cart=self.cart)
 
        agg = items.aggregate(Max("product__reservation_duration"))
 
        product_max = agg["product__reservation_duration__max"]
 

	
 
        if product_max is not None:
 
            reservations.append(product_max)
 

	
 
        self.cart.time_last_updated = timezone.now()
 
        self.cart.reservation_duration = max(reservations)
 

	
 
    def add_to_cart(self, product, quantity):
 
        ''' Adds _quantity_ of the given _product_ to the cart. Raises
 
        ValidationError if constraints are violated.'''
 

	
 
        prod = ProductController(product)
 
    def end_batch(self):
 
        ''' Performs operations that occur occur at the end of a batch of
 
        product changes/voucher applications etc. '''
 
        self.recalculate_discounts()
 

	
 
        # TODO: Check enabling conditions for product for user
 
        self.extend_reservation()
 
        self.cart.revision += 1
 
        self.cart.save()
 

	
 
        if not prod.can_add_with_enabling_conditions(self.cart.user, quantity):
 
            raise ValidationError("Not enough of that product left (ec)")
 
    def set_quantity(self, product, quantity, batched=False):
 
        ''' Sets the _quantity_ of the given _product_ in the cart to the given
 
        _quantity_. '''
 

	
 
        if not prod.user_can_add_within_limit(self.cart.user, quantity):
 
            raise ValidationError("Not enough of that product left (user)")
 
        if quantity < 0:
 
            raise ValidationError("Cannot have fewer than 0 items in cart.")
 

	
 
        try:
 
            # Try to update an existing item within this cart if possible.
 
            product_item = rego.ProductItem.objects.get(
 
                cart=self.cart,
 
                product=product)
 
            product_item.quantity += quantity
 
            old_quantity = product_item.quantity
 

	
 
            if quantity == 0:
 
                product_item.delete()
 
                return
 
        except ObjectDoesNotExist:
 
            if quantity == 0:
 
                return
 

	
 
            product_item = rego.ProductItem.objects.create(
 
                cart=self.cart,
 
                product=product,
 
                quantity=quantity,
 
                quantity=0,
 
            )
 

	
 
            old_quantity = 0
 

	
 
        # Validate the addition to the cart
 
        adjustment = quantity - old_quantity
 
        prod = ProductController(product)
 

	
 
        if not prod.can_add_with_enabling_conditions(
 
                self.cart.user, adjustment):
 
            raise ValidationError("Not enough of that product left (ec)")
 

	
 
        if not prod.user_can_add_within_limit(self.cart.user, adjustment):
 
            raise ValidationError("Not enough of that product left (user)")
 

	
 
        product_item.quantity = quantity
 
        product_item.save()
 

	
 
        self.recalculate_discounts()
 
        if not batched:
 
            self.end_batch()
 

	
 
        self.extend_reservation()
 
        self.cart.revision += 1
 
        self.cart.save()
 
    def add_to_cart(self, product, quantity):
 
        ''' Adds _quantity_ of the given _product_ to the cart. Raises
 
        ValidationError if constraints are violated.'''
 

	
 
        try:
 
            product_item = rego.ProductItem.objects.get(
 
                cart=self.cart,
 
                product=product)
 
            old_quantity = product_item.quantity
 
        except ObjectDoesNotExist:
 
            old_quantity = 0
 
        self.set_quantity(product, old_quantity + quantity)
 

	
 
    def apply_voucher(self, voucher):
 
        ''' Applies the given voucher to this cart. '''
 

	
 
        # TODO: is it valid for a cart to re-add a voucher that they have?
 

	
 
        # Is voucher exhausted?
 
        active_carts = rego.Cart.reserved_carts()
 
        carts_with_voucher = active_carts.filter(vouchers=voucher)
 
        if len(carts_with_voucher) >= voucher.limit:
 
            raise ValidationError("This voucher is no longer available")
 

	
 
        # If successful...
 
        self.cart.vouchers.add(voucher)
 

	
 
        self.extend_reservation()
 
        self.cart.revision += 1
 
        self.cart.save()
 
        self.end_batch()
 

	
 
    def validate_cart(self):
 
        ''' Determines whether the status of the current cart is valid;
 
        this is normally called before generating or paying an invoice '''
 

	
 
        is_reserved = self.cart in rego.Cart.reserved_carts()
 

	
 
        # TODO: validate vouchers
 

	
 
        items = rego.ProductItem.objects.filter(cart=self.cart)
 
        for item in items:
 
            # NOTE: per-user limits are tested at add time
 
            # and are unliklely to change
 
            prod = ProductController(item.product)
 

	
 
            # If the cart is not reserved, we need to see if we can re-reserve
 
            quantity = 0 if is_reserved else item.quantity
 

	
 
            if not prod.can_add_with_enabling_conditions(
 
                    self.cart.user, quantity):
 
                raise ValidationError("Products are no longer available")
 

	
 
        # Validate the discounts
 
        discount_items = rego.DiscountItem.objects.filter(cart=self.cart)
registrasion/tests/test_cart.py
Show inline comments
 
import datetime
 
import pytz
 

	
 
from decimal import Decimal
 
from django.contrib.auth.models import User
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.core.exceptions import ValidationError
 
from django.test import TestCase
 

	
 
from registrasion import models as rego
 
from registrasion.controllers.cart import CartController
 

	
 
from patch_datetime import SetTimeMixin
 

	
 
UTC = pytz.timezone('UTC')
 

	
 

	
 
class RegistrationCartTestCase(SetTimeMixin, TestCase):
 

	
 
    def setUp(self):
 
        super(RegistrationCartTestCase, self).setUp()
 

	
 
    @classmethod
 
    def setUpTestData(cls):
 
        cls.USER_1 = User.objects.create_user(
 
            username='testuser',
 
            email='test@example.com',
 
            password='top_secret')
 

	
 
        cls.USER_2 = User.objects.create_user(
...
 
@@ -138,48 +139,80 @@ class BasicCartTests(RegistrationCartTestCase):
 

	
 
        old_cart = current_cart
 

	
 
        current_cart = CartController.for_user(self.USER_1)
 
        self.assertNotEqual(old_cart.cart, current_cart.cart)
 

	
 
        current_cart2 = CartController.for_user(self.USER_1)
 
        self.assertEqual(current_cart.cart, current_cart2.cart)
 

	
 
    def test_add_to_cart_collapses_product_items(self):
 
        current_cart = CartController.for_user(self.USER_1)
 

	
 
        # Add a product twice
 
        current_cart.add_to_cart(self.PROD_1, 1)
 
        current_cart.add_to_cart(self.PROD_1, 1)
 

	
 
        # Count of products for a given user should be collapsed.
 
        items = rego.ProductItem.objects.filter(
 
            cart=current_cart.cart,
 
            product=self.PROD_1)
 
        self.assertEqual(1, len(items))
 
        item = items[0]
 
        self.assertEquals(2, item.quantity)
 

	
 
    def test_set_quantity(self):
 
        current_cart = CartController.for_user(self.USER_1)
 

	
 
        def get_item():
 
            return rego.ProductItem.objects.get(
 
                cart=current_cart.cart,
 
                product=self.PROD_1)
 

	
 
        current_cart.set_quantity(self.PROD_1, 1)
 
        self.assertEqual(1, get_item().quantity)
 

	
 
        # Setting the quantity to zero should remove the entry from the cart.
 
        current_cart.set_quantity(self.PROD_1, 0)
 
        with self.assertRaises(ObjectDoesNotExist):
 
            get_item()
 

	
 
        current_cart.set_quantity(self.PROD_1, 9)
 
        self.assertEqual(9, get_item().quantity)
 

	
 
        with self.assertRaises(ValidationError):
 
            current_cart.set_quantity(self.PROD_1, 11)
 

	
 
        self.assertEqual(9, get_item().quantity)
 

	
 
        with self.assertRaises(ValidationError):
 
            current_cart.set_quantity(self.PROD_1, -1)
 

	
 
        self.assertEqual(9, get_item().quantity)
 

	
 
        current_cart.set_quantity(self.PROD_1, 2)
 
        self.assertEqual(2, get_item().quantity)
 

	
 
    def test_add_to_cart_per_user_limit(self):
 
        current_cart = CartController.for_user(self.USER_1)
 

	
 
        # User should be able to add 1 of PROD_1 to the current cart.
 
        current_cart.add_to_cart(self.PROD_1, 1)
 

	
 
        # User should be able to add 1 of PROD_1 to the current cart.
 
        current_cart.add_to_cart(self.PROD_1, 1)
 

	
 
        # User should not be able to add 10 of PROD_1 to the current cart now,
 
        # because they have a limit of 10.
 
        with self.assertRaises(ValidationError):
 
            current_cart.add_to_cart(self.PROD_1, 10)
 

	
 
        current_cart.cart.active = False
 
        current_cart.cart.save()
 

	
 
        current_cart = CartController.for_user(self.USER_1)
 
        # User should not be able to add 10 of PROD_1 to the current cart now,
 
        # even though it's a new cart.
 
        with self.assertRaises(ValidationError):
 
            current_cart.add_to_cart(self.PROD_1, 10)
 

	
 
        # Second user should not be affected by first user's limits
0 comments (0 inline, 0 general)