2016-01-22 05:01:30 +00:00
|
|
|
import pytz
|
|
|
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
|
|
from registrasion import models as rego
|
2016-03-25 07:09:24 +00:00
|
|
|
from registrasion.controllers import discount
|
2016-01-22 05:21:23 +00:00
|
|
|
from registrasion.controllers.cart import CartController
|
2016-03-25 07:09:24 +00:00
|
|
|
from registrasion.controllers.invoice import InvoiceController
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
from test_cart import RegistrationCartTestCase
|
|
|
|
|
|
|
|
UTC = pytz.timezone('UTC')
|
|
|
|
|
2016-01-22 06:02:07 +00:00
|
|
|
|
2016-01-22 05:01:30 +00:00
|
|
|
class DiscountTestCase(RegistrationCartTestCase):
|
|
|
|
|
|
|
|
@classmethod
|
2016-03-25 07:09:24 +00:00
|
|
|
def add_discount_prod_1_includes_prod_2(
|
|
|
|
cls,
|
|
|
|
amount=Decimal(100),
|
|
|
|
quantity=2,
|
|
|
|
):
|
2016-01-22 05:01:30 +00:00
|
|
|
discount = rego.IncludedProductDiscount.objects.create(
|
|
|
|
description="PROD_1 includes PROD_2 " + str(amount) + "%",
|
|
|
|
)
|
|
|
|
discount.save()
|
|
|
|
discount.enabling_products.add(cls.PROD_1)
|
|
|
|
discount.save()
|
|
|
|
rego.DiscountForProduct.objects.create(
|
|
|
|
discount=discount,
|
|
|
|
product=cls.PROD_2,
|
|
|
|
percentage=amount,
|
2016-03-25 07:09:24 +00:00
|
|
|
quantity=quantity,
|
2016-01-22 05:01:30 +00:00
|
|
|
).save()
|
|
|
|
return discount
|
|
|
|
|
|
|
|
@classmethod
|
2016-03-04 21:07:45 +00:00
|
|
|
def add_discount_prod_1_includes_cat_2(
|
|
|
|
cls,
|
|
|
|
amount=Decimal(100),
|
2016-03-25 07:09:24 +00:00
|
|
|
quantity=2,
|
|
|
|
):
|
2016-01-22 05:01:30 +00:00
|
|
|
discount = rego.IncludedProductDiscount.objects.create(
|
|
|
|
description="PROD_1 includes CAT_2 " + str(amount) + "%",
|
|
|
|
)
|
|
|
|
discount.save()
|
|
|
|
discount.enabling_products.add(cls.PROD_1)
|
|
|
|
discount.save()
|
|
|
|
rego.DiscountForCategory.objects.create(
|
|
|
|
discount=discount,
|
|
|
|
category=cls.CAT_2,
|
|
|
|
percentage=amount,
|
2016-03-04 21:07:45 +00:00
|
|
|
quantity=quantity,
|
2016-01-22 05:01:30 +00:00
|
|
|
).save()
|
|
|
|
return discount
|
|
|
|
|
2016-03-25 07:09:24 +00:00
|
|
|
@classmethod
|
|
|
|
def add_discount_prod_1_includes_prod_3_and_prod_4(
|
|
|
|
cls,
|
|
|
|
amount=Decimal(100),
|
|
|
|
quantity=2,
|
|
|
|
):
|
|
|
|
discount = rego.IncludedProductDiscount.objects.create(
|
|
|
|
description="PROD_1 includes PROD_3 and PROD_4 " +
|
|
|
|
str(amount) + "%",
|
|
|
|
)
|
|
|
|
discount.save()
|
|
|
|
discount.enabling_products.add(cls.PROD_1)
|
|
|
|
discount.save()
|
|
|
|
rego.DiscountForProduct.objects.create(
|
|
|
|
discount=discount,
|
|
|
|
product=cls.PROD_3,
|
|
|
|
percentage=amount,
|
|
|
|
quantity=quantity,
|
|
|
|
).save()
|
|
|
|
rego.DiscountForProduct.objects.create(
|
|
|
|
discount=discount,
|
|
|
|
product=cls.PROD_4,
|
|
|
|
percentage=amount,
|
|
|
|
quantity=quantity,
|
|
|
|
).save()
|
|
|
|
return discount
|
|
|
|
|
2016-01-22 05:01:30 +00:00
|
|
|
def test_discount_is_applied(self):
|
2016-01-22 06:02:07 +00:00
|
|
|
self.add_discount_prod_1_includes_prod_2()
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 1)
|
|
|
|
|
|
|
|
# Discounts should be applied at this point...
|
|
|
|
self.assertEqual(1, len(cart.cart.discountitem_set.all()))
|
|
|
|
|
|
|
|
def test_discount_is_applied_for_category(self):
|
2016-01-22 06:02:07 +00:00
|
|
|
self.add_discount_prod_1_includes_cat_2()
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
cart.add_to_cart(self.PROD_3, 1)
|
|
|
|
|
|
|
|
# Discounts should be applied at this point...
|
|
|
|
self.assertEqual(1, len(cart.cart.discountitem_set.all()))
|
|
|
|
|
|
|
|
def test_discount_does_not_apply_if_not_met(self):
|
2016-01-22 06:02:07 +00:00
|
|
|
self.add_discount_prod_1_includes_prod_2()
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 1)
|
|
|
|
|
|
|
|
# No discount should be applied as the condition is not met
|
|
|
|
self.assertEqual(0, len(cart.cart.discountitem_set.all()))
|
|
|
|
|
|
|
|
def test_discount_applied_out_of_order(self):
|
2016-01-22 06:02:07 +00:00
|
|
|
self.add_discount_prod_1_includes_prod_2()
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
|
|
|
|
# No discount should be applied as the condition is not met
|
|
|
|
self.assertEqual(1, len(cart.cart.discountitem_set.all()))
|
|
|
|
|
|
|
|
def test_discounts_collapse(self):
|
2016-01-22 06:02:07 +00:00
|
|
|
self.add_discount_prod_1_includes_prod_2()
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 1)
|
|
|
|
|
|
|
|
# Discounts should be applied and collapsed at this point...
|
|
|
|
self.assertEqual(1, len(cart.cart.discountitem_set.all()))
|
|
|
|
|
|
|
|
def test_discounts_respect_quantity(self):
|
2016-01-22 06:02:07 +00:00
|
|
|
self.add_discount_prod_1_includes_prod_2()
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 3)
|
|
|
|
|
|
|
|
# There should be three items in the cart, but only two should
|
|
|
|
# attract a discount.
|
|
|
|
discount_items = list(cart.cart.discountitem_set.all())
|
|
|
|
self.assertEqual(2, discount_items[0].quantity)
|
|
|
|
|
|
|
|
def test_multiple_discounts_apply_in_order(self):
|
|
|
|
discount_full = self.add_discount_prod_1_includes_prod_2()
|
|
|
|
discount_half = self.add_discount_prod_1_includes_prod_2(Decimal(50))
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 3)
|
|
|
|
|
|
|
|
# There should be two discounts
|
|
|
|
discount_items = list(cart.cart.discountitem_set.all())
|
|
|
|
discount_items.sort(key=lambda item: item.quantity)
|
|
|
|
self.assertEqual(2, len(discount_items))
|
|
|
|
# The half discount should be applied only once
|
|
|
|
self.assertEqual(1, discount_items[0].quantity)
|
|
|
|
self.assertEqual(discount_half.pk, discount_items[0].discount.pk)
|
|
|
|
# The full discount should be applied twice
|
|
|
|
self.assertEqual(2, discount_items[1].quantity)
|
|
|
|
self.assertEqual(discount_full.pk, discount_items[1].discount.pk)
|
|
|
|
|
|
|
|
def test_discount_applies_across_carts(self):
|
2016-01-22 06:02:07 +00:00
|
|
|
self.add_discount_prod_1_includes_prod_2()
|
2016-01-22 05:01:30 +00:00
|
|
|
|
|
|
|
# Enable the discount during the first cart.
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
cart.cart.active = False
|
|
|
|
cart.cart.save()
|
|
|
|
|
|
|
|
# Use the discount in the second cart
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 1)
|
|
|
|
|
|
|
|
# The discount should be applied.
|
|
|
|
self.assertEqual(1, len(cart.cart.discountitem_set.all()))
|
|
|
|
cart.cart.active = False
|
|
|
|
cart.cart.save()
|
|
|
|
|
|
|
|
# The discount should respect the total quantity across all
|
|
|
|
# of the user's carts.
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 2)
|
|
|
|
|
|
|
|
# Having one item in the second cart leaves one more item where
|
|
|
|
# the discount is applicable. The discount should apply, but only for
|
|
|
|
# quantity=1
|
|
|
|
discount_items = list(cart.cart.discountitem_set.all())
|
|
|
|
self.assertEqual(1, discount_items[0].quantity)
|
|
|
|
|
|
|
|
def test_discount_applies_only_once_enabled(self):
|
|
|
|
# Enable the discount during the first cart.
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
2016-01-22 06:02:07 +00:00
|
|
|
# This would exhaust discount if present
|
|
|
|
cart.add_to_cart(self.PROD_2, 2)
|
2016-01-22 05:01:30 +00:00
|
|
|
cart.cart.active = False
|
|
|
|
cart.cart.save()
|
|
|
|
|
2016-01-22 06:02:07 +00:00
|
|
|
self.add_discount_prod_1_includes_prod_2()
|
2016-01-22 05:01:30 +00:00
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_2, 2)
|
|
|
|
|
|
|
|
discount_items = list(cart.cart.discountitem_set.all())
|
|
|
|
self.assertEqual(2, discount_items[0].quantity)
|
2016-03-04 21:07:45 +00:00
|
|
|
|
|
|
|
def test_category_discount_applies_once_per_category(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
|
|
|
|
# Add two items from category 2
|
|
|
|
cart.add_to_cart(self.PROD_3, 1)
|
|
|
|
cart.add_to_cart(self.PROD_4, 1)
|
|
|
|
|
|
|
|
discount_items = list(cart.cart.discountitem_set.all())
|
|
|
|
# There is one discount, and it should apply to one item.
|
|
|
|
self.assertEqual(1, len(discount_items))
|
|
|
|
self.assertEqual(1, discount_items[0].quantity)
|
|
|
|
|
|
|
|
def test_category_discount_applies_to_highest_value(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1)
|
|
|
|
|
|
|
|
# Add two items from category 2, add the less expensive one first
|
|
|
|
cart.add_to_cart(self.PROD_4, 1)
|
|
|
|
cart.add_to_cart(self.PROD_3, 1)
|
|
|
|
|
|
|
|
discount_items = list(cart.cart.discountitem_set.all())
|
|
|
|
# There is one discount, and it should apply to the more expensive.
|
|
|
|
self.assertEqual(1, len(discount_items))
|
|
|
|
self.assertEqual(self.PROD_3, discount_items[0].product)
|
2016-03-25 01:50:34 +00:00
|
|
|
|
|
|
|
def test_discount_quantity_is_per_user(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
|
|
|
|
|
|
|
# Both users should be able to apply the same discount
|
|
|
|
# in the same way
|
|
|
|
for user in (self.USER_1, self.USER_2):
|
|
|
|
cart = CartController.for_user(user)
|
2016-03-25 03:51:39 +00:00
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
2016-03-25 01:50:34 +00:00
|
|
|
cart.add_to_cart(self.PROD_3, 1)
|
|
|
|
|
|
|
|
discount_items = list(cart.cart.discountitem_set.all())
|
|
|
|
# The discount is applied.
|
|
|
|
self.assertEqual(1, len(discount_items))
|
2016-03-25 07:09:24 +00:00
|
|
|
|
|
|
|
# Tests for the discount.available_discounts enumerator
|
|
|
|
def test_enumerate_no_discounts_for_no_input(self):
|
|
|
|
discounts = discount.available_discounts(self.USER_1, [], [])
|
|
|
|
self.assertEqual(0, len(discounts))
|
|
|
|
|
|
|
|
def test_enumerate_no_discounts_if_condition_not_met(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(
|
|
|
|
self.USER_1,
|
|
|
|
[],
|
|
|
|
[self.PROD_3],
|
|
|
|
)
|
|
|
|
self.assertEqual(0, len(discounts))
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(self.USER_1, [self.CAT_2], [])
|
|
|
|
self.assertEqual(0, len(discounts))
|
|
|
|
|
|
|
|
def test_category_discount_appears_once_if_met_twice(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(
|
|
|
|
self.USER_1,
|
|
|
|
[self.CAT_2],
|
|
|
|
[self.PROD_3],
|
|
|
|
)
|
|
|
|
self.assertEqual(1, len(discounts))
|
|
|
|
|
|
|
|
def test_category_discount_appears_with_category(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(self.USER_1, [self.CAT_2], [])
|
|
|
|
self.assertEqual(1, len(discounts))
|
|
|
|
|
|
|
|
def test_category_discount_appears_with_product(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(
|
|
|
|
self.USER_1,
|
|
|
|
[],
|
|
|
|
[self.PROD_3],
|
|
|
|
)
|
|
|
|
self.assertEqual(1, len(discounts))
|
|
|
|
|
|
|
|
def test_category_discount_appears_once_with_two_valid_product(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=1)
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(
|
|
|
|
self.USER_1,
|
|
|
|
[],
|
|
|
|
[self.PROD_3, self.PROD_4]
|
|
|
|
)
|
|
|
|
self.assertEqual(1, len(discounts))
|
|
|
|
|
|
|
|
def test_product_discount_appears_with_product(self):
|
|
|
|
self.add_discount_prod_1_includes_prod_2(quantity=1)
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(
|
|
|
|
self.USER_1,
|
|
|
|
[],
|
|
|
|
[self.PROD_2],
|
|
|
|
)
|
|
|
|
self.assertEqual(1, len(discounts))
|
|
|
|
|
|
|
|
def test_product_discount_does_not_appear_with_category(self):
|
|
|
|
self.add_discount_prod_1_includes_prod_2(quantity=1)
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(self.USER_1, [self.CAT_1], [])
|
|
|
|
self.assertEqual(0, len(discounts))
|
|
|
|
|
|
|
|
def test_discount_quantity_is_correct_before_first_purchase(self):
|
|
|
|
self.add_discount_prod_1_includes_cat_2(quantity=2)
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
|
|
|
cart.add_to_cart(self.PROD_3, 1) # Exhaust the quantity
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(self.USER_1, [self.CAT_2], [])
|
|
|
|
self.assertEqual(2, discounts[0].quantity)
|
|
|
|
inv = InvoiceController.for_cart(cart.cart)
|
|
|
|
inv.pay("Dummy reference", inv.invoice.value)
|
|
|
|
self.assertTrue(inv.invoice.paid)
|
|
|
|
|
|
|
|
def test_discount_quantity_is_correct_after_first_purchase(self):
|
|
|
|
self.test_discount_quantity_is_correct_before_first_purchase()
|
|
|
|
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_3, 1) # Exhaust the quantity
|
|
|
|
|
|
|
|
discounts = discount.available_discounts(self.USER_1, [self.CAT_2], [])
|
|
|
|
self.assertEqual(1, discounts[0].quantity)
|
|
|
|
inv = InvoiceController.for_cart(cart.cart)
|
|
|
|
inv.pay("Dummy reference", inv.invoice.value)
|
|
|
|
self.assertTrue(inv.invoice.paid)
|
|
|
|
|
|
|
|
def test_discount_is_gone_after_quantity_exhausted(self):
|
|
|
|
self.test_discount_quantity_is_correct_after_first_purchase()
|
|
|
|
discounts = discount.available_discounts(self.USER_1, [self.CAT_2], [])
|
|
|
|
self.assertEqual(0, len(discounts))
|
|
|
|
|
|
|
|
def test_product_discount_enabled_twice_appears_twice(self):
|
|
|
|
self.add_discount_prod_1_includes_prod_3_and_prod_4(quantity=2)
|
|
|
|
cart = CartController.for_user(self.USER_1)
|
|
|
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
|
|
|
discounts = discount.available_discounts(
|
|
|
|
self.USER_1,
|
|
|
|
[],
|
|
|
|
[self.PROD_3, self.PROD_4],
|
|
|
|
)
|
|
|
|
self.assertEqual(2, len(discounts))
|