Tests and fixes for a bug where discount quantities did not respect per-line item quantities.
This commit is contained in:
parent
6d52a4c18f
commit
02fe88a4e4
2 changed files with 52 additions and 15 deletions
|
@ -5,7 +5,7 @@ from registrasion.models import commerce
|
||||||
from registrasion.models import conditions
|
from registrasion.models import conditions
|
||||||
|
|
||||||
from django.db.models import Case
|
from django.db.models import Case
|
||||||
from django.db.models import Q
|
from django.db.models import F, Q
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.db.models import Value
|
from django.db.models import Value
|
||||||
from django.db.models import When
|
from django.db.models import When
|
||||||
|
@ -64,9 +64,7 @@ class DiscountController(object):
|
||||||
discount = clause.discount
|
discount = clause.discount
|
||||||
cond = ConditionController.for_condition(discount)
|
cond = ConditionController.for_condition(discount)
|
||||||
|
|
||||||
past_use_count = discount.past_use_count
|
past_use_count = clause.past_use_count
|
||||||
|
|
||||||
|
|
||||||
if past_use_count >= clause.quantity:
|
if past_use_count >= clause.quantity:
|
||||||
# This clause has exceeded its use count
|
# This clause has exceeded its use count
|
||||||
pass
|
pass
|
||||||
|
@ -139,7 +137,6 @@ class DiscountController(object):
|
||||||
discounts = discounttype.objects.filter(id__in=valid_discounts)
|
discounts = discounttype.objects.filter(id__in=valid_discounts)
|
||||||
ctrl = ConditionController.for_type(discounttype)
|
ctrl = ConditionController.for_type(discounttype)
|
||||||
discounts = ctrl.pre_filter(discounts, user)
|
discounts = ctrl.pre_filter(discounts, user)
|
||||||
discounts = cls._annotate_with_past_uses(discounts, user)
|
|
||||||
all_subsets.append(discounts)
|
all_subsets.append(discounts)
|
||||||
|
|
||||||
filtered_discounts = list(itertools.chain(*all_subsets))
|
filtered_discounts = list(itertools.chain(*all_subsets))
|
||||||
|
@ -148,11 +145,17 @@ class DiscountController(object):
|
||||||
# (contains annotations needed in the future)
|
# (contains annotations needed in the future)
|
||||||
from_filter = dict((i.id, i) for i in filtered_discounts)
|
from_filter = dict((i.id, i) for i in filtered_discounts)
|
||||||
|
|
||||||
# The set of all potential discounts
|
clause_sets = (
|
||||||
discount_clauses = set(itertools.chain(
|
|
||||||
product_discounts.filter(discount__in=filtered_discounts),
|
product_discounts.filter(discount__in=filtered_discounts),
|
||||||
all_category_discounts.filter(discount__in=filtered_discounts),
|
all_category_discounts.filter(discount__in=filtered_discounts),
|
||||||
))
|
)
|
||||||
|
|
||||||
|
clause_sets = (
|
||||||
|
cls._annotate_with_past_uses(i, user) for i in clause_sets
|
||||||
|
)
|
||||||
|
|
||||||
|
# The set of all potential discount clauses
|
||||||
|
discount_clauses = set(itertools.chain(*clause_sets))
|
||||||
|
|
||||||
# Replace discounts with the filtered ones
|
# Replace discounts with the filtered ones
|
||||||
# These are the correct subclasses (saves query later on), and have
|
# These are the correct subclasses (saves query later on), and have
|
||||||
|
@ -164,15 +167,26 @@ class DiscountController(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _annotate_with_past_uses(cls, queryset, user):
|
def _annotate_with_past_uses(cls, queryset, user):
|
||||||
''' Annotates the queryset with a usage count for that discount by the
|
''' Annotates the queryset with a usage count for that discount claus
|
||||||
given user. '''
|
by the given user. '''
|
||||||
|
|
||||||
|
if queryset.model == conditions.DiscountForCategory:
|
||||||
|
matches = (
|
||||||
|
Q(category=F('discount__discountitem__product__category'))
|
||||||
|
)
|
||||||
|
elif queryset.model == conditions.DiscountForProduct:
|
||||||
|
matches = (
|
||||||
|
Q(product=F('discount__discountitem__product'))
|
||||||
|
)
|
||||||
|
|
||||||
|
in_carts = (
|
||||||
|
Q(discount__discountitem__cart__user=user) &
|
||||||
|
Q(discount__discountitem__cart__status=commerce.Cart.STATUS_PAID)
|
||||||
|
)
|
||||||
|
|
||||||
past_use_quantity = When(
|
past_use_quantity = When(
|
||||||
(
|
in_carts & matches,
|
||||||
Q(discountitem__cart__user=user) &
|
then="discount__discountitem__quantity",
|
||||||
Q(discountitem__cart__status=commerce.Cart.STATUS_PAID)
|
|
||||||
),
|
|
||||||
then="discountitem__quantity",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
past_use_quantity_or_zero = Case(
|
past_use_quantity_or_zero = Case(
|
||||||
|
|
|
@ -398,6 +398,29 @@ class DiscountTestCase(RegistrationCartTestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(2, len(discounts))
|
self.assertEqual(2, len(discounts))
|
||||||
|
|
||||||
|
def test_product_discount_applied_on_different_invoices(self):
|
||||||
|
# quantity=1 means "quantity per product"
|
||||||
|
self.add_discount_prod_1_includes_prod_3_and_prod_4(quantity=1)
|
||||||
|
cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
cart.add_to_cart(self.PROD_1, 1) # Enable the discount
|
||||||
|
discounts = DiscountController.available_discounts(
|
||||||
|
self.USER_1,
|
||||||
|
[],
|
||||||
|
[self.PROD_3, self.PROD_4],
|
||||||
|
)
|
||||||
|
self.assertEqual(2, len(discounts))
|
||||||
|
# adding one of PROD_3 should make it no longer an available discount.
|
||||||
|
cart.add_to_cart(self.PROD_3, 1)
|
||||||
|
cart.next_cart()
|
||||||
|
|
||||||
|
# should still have (and only have) the discount for prod_4
|
||||||
|
discounts = DiscountController.available_discounts(
|
||||||
|
self.USER_1,
|
||||||
|
[],
|
||||||
|
[self.PROD_3, self.PROD_4],
|
||||||
|
)
|
||||||
|
self.assertEqual(1, len(discounts))
|
||||||
|
|
||||||
def test_discounts_are_released_by_refunds(self):
|
def test_discounts_are_released_by_refunds(self):
|
||||||
self.add_discount_prod_1_includes_prod_2(quantity=2)
|
self.add_discount_prod_1_includes_prod_2(quantity=2)
|
||||||
cart = TestingCartController.for_user(self.USER_1)
|
cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
|
Loading…
Reference in a new issue