Procházet zdrojové kódy

Fix non-exclusive offers with count condition

Issue: https://github.com/django-oscar/django-oscar/issues/3162

* Checking of `offer.benefit.max_affected_items` removed from the
`available` method of `LineOfferConsumer` class - available items should
not be limited by related benefit (it should be considered when applying
the offer).
* Improved `apply` method of `PercentageDiscountBenefit` - affected
items must include "quantities with discount applied".
* `is_satisfied` method of `CountCondition` updated to pass `offer`
instead of `None` to `line.quantity_without_offer_discount` - this
change is required to fix the initial issue.
* Updated `test_available_with_offer` test - initially this test was
added to check the lines that we removed from the `available` method of
`LineOfferConsumer` class (See fda27a183d/).
* Added tests to confirm that the initial issue is fixed.
master
Basil Dubyk před 5 roky
rodič
revize
5e1986388f

+ 1
- 5
src/oscar/apps/basket/utils.py Zobrazit soubor

@@ -143,7 +143,7 @@ class LineOfferConsumer(object):
143 143
     def consumers(self):
144 144
         return [x for x in self._offers.values() if self.consumed(x)]
145 145
 
146
-    def available(self, offer=None) -> int: # noqa (too complex (11))
146
+    def available(self, offer=None) -> int:
147 147
         """
148 148
         check how many items are available for offer
149 149
 
@@ -186,8 +186,4 @@ class LineOfferConsumer(object):
186 186
                 if check and offer not in x.combined_offers:
187 187
                     return 0
188 188
 
189
-            # respect max_affected_items
190
-            if offer.benefit.max_affected_items:
191
-                max_affected_items = min(offer.benefit.max_affected_items, max_affected_items)
192
-
193 189
         return max_affected_items - self.consumed(offer)

+ 1
- 0
src/oscar/apps/offer/benefits.py Zobrazit soubor

@@ -66,6 +66,7 @@ class PercentageDiscountBenefit(Benefit):
66 66
         affected_items = 0
67 67
         max_affected_items = self._effective_max_affected_items()
68 68
         for price, line in line_tuples:
69
+            affected_items += line.quantity_with_offer_discount(offer)
69 70
             if affected_items >= max_affected_items:
70 71
                 break
71 72
             if discount_amount_available == 0:

+ 1
- 1
src/oscar/apps/offer/conditions.py Zobrazit soubor

@@ -47,7 +47,7 @@ class CountCondition(Condition):
47 47
         num_matches = 0
48 48
         for line in basket.all_lines():
49 49
             if self.can_apply_condition(line):
50
-                num_matches += line.quantity_without_offer_discount(None)
50
+                num_matches += line.quantity_without_offer_discount(offer)
51 51
             if num_matches >= self.value:
52 52
                 return True
53 53
         return False

+ 1
- 1
tests/integration/basket/test_utils.py Zobrazit soubor

@@ -76,7 +76,7 @@ class TestLineOfferConsumer:
76 76
         offer1 = ConditionalOfferFactory(name='offer1', benefit=benefit)
77 77
         lines = basket.all_lines()
78 78
         assert lines[0].consumer.available(offer1) == 1
79
-        assert lines[1].consumer.available(offer1) == 5
79
+        assert lines[1].consumer.available(offer1) == 10
80 80
 
81 81
     def test_consumed_with_offer(self, filled_basket):
82 82
         offer1 = ConditionalOfferFactory(name='offer1')

+ 49
- 1
tests/integration/offer/test_condition.py Zobrazit soubor

@@ -3,13 +3,17 @@ from unittest import mock
3 3
 
4 4
 import pytest
5 5
 from django.test import TestCase
6
+from django.utils.timezone import now
6 7
 
7 8
 from oscar.apps.basket.models import Basket
8
-from oscar.apps.offer import custom, models
9
+from oscar.apps.offer import applicator, custom, models
10
+from oscar.core.loading import get_class
9 11
 from oscar.test import factories
10 12
 from oscar.test.basket import add_product
11 13
 from tests._site.model_tests_app.models import BasketOwnerCalledBarry
12 14
 
15
+Selector = get_class('partner.strategy', 'Selector')
16
+
13 17
 
14 18
 @pytest.fixture
15 19
 def products_some():
@@ -329,3 +333,47 @@ class TestCustomCondition(TestCase):
329 333
     def test_is_satisfied_by_match(self):
330 334
         self.basket.owner = factories.UserFactory(first_name="Barry")
331 335
         assert self.offer.is_condition_satisfied(self.basket)
336
+
337
+
338
+class TestOffersWithCountCondition(TestCase):
339
+
340
+    def setUp(self):
341
+        super().setUp()
342
+
343
+        self.basket = factories.create_basket(empty=True)
344
+
345
+        # Create range and add one product to it.
346
+        rng = factories.RangeFactory(name='All products', includes_all_products=True)
347
+        self.product = factories.ProductFactory()
348
+        rng.add_product(self.product)
349
+
350
+        # Create a non-exclusive offer #1.
351
+        condition1 = factories.ConditionFactory(range=rng, value=D('1'))
352
+        benefit1 = factories.BenefitFactory(range=rng, value=D('10'))
353
+        self.offer1 = factories.ConditionalOfferFactory(
354
+            condition=condition1, benefit=benefit1, start_datetime=now(),
355
+            name='Test offer #1', exclusive=False,
356
+        )
357
+
358
+        # Create a non-exclusive offer #2.
359
+        condition2 = factories.ConditionFactory(range=rng, value=D('1'))
360
+        benefit2 = factories.BenefitFactory(range=rng, value=D('5'))
361
+        self.offer2 = factories.ConditionalOfferFactory(
362
+            condition=condition2, benefit=benefit2, start_datetime=now(),
363
+            name='Test offer #2', exclusive=False,
364
+        )
365
+
366
+    def add_product(self):
367
+        self.basket.add_product(self.product)
368
+        self.basket.strategy = Selector().strategy()
369
+        applicator.Applicator().apply(self.basket)
370
+
371
+    def assertOffersApplied(self, offers):
372
+        applied_offers = self.basket.applied_offers()
373
+        self.assertEqual(len(offers), len(applied_offers))
374
+        for offer in offers:
375
+            self.assertIn(offer.id, applied_offers, msg=offer)
376
+
377
+    def test_both_non_exclusive_offers_are_applied(self):
378
+        self.add_product()
379
+        self.assertOffersApplied([self.offer1, self.offer2])

Načítá se…
Zrušit
Uložit