Преглед изворни кода

Set maximum line quantity on basket page.

master
Alexander Gaevsky пре 8 година
родитељ
комит
e60bb4e99b

+ 4
- 0
docs/source/releases/v1.6.rst Прегледај датотеку

@@ -94,6 +94,10 @@ Minor changes
94 94
  - For variant products we now display their own images on product detail page
95 95
    and fallback to parent product images if they don't have any.
96 96
 
97
+ - Basket line form quantity field now has "max" attribute with the maximum
98
+   allowed quantity in it based on product availability and basket threshold
99
+   (see :issue:`1412`).
100
+
97 101
 .. _incompatible_in_1.6:
98 102
 
99 103
 Backwards incompatible changes in Oscar 1.6

+ 17
- 9
src/oscar/apps/basket/abstract_models.py Прегледај датотеку

@@ -135,21 +135,29 @@ class AbstractBasket(models.Model):
135 135
                 .order_by(self._meta.pk.name))
136 136
         return self._lines
137 137
 
138
-    def is_quantity_allowed(self, qty):
138
+    def max_allowed_quantity(self):
139 139
         """
140
-        Test whether the passed quantity of items can be added to the basket
140
+        Returns maximum product quantity, that can be added to the basket
141
+        with the respect to basket quantity threshold.
141 142
         """
142
-        # We enforce a max threshold to prevent a DOS attack via the offers
143
-        # system.
144 143
         basket_threshold = settings.OSCAR_MAX_BASKET_QUANTITY_THRESHOLD
145 144
         if basket_threshold:
146 145
             total_basket_quantity = self.num_items
147 146
             max_allowed = basket_threshold - total_basket_quantity
148
-            if qty > max_allowed:
149
-                return False, _(
150
-                    "Due to technical limitations we are not able "
151
-                    "to ship more than %(threshold)d items in one order.") \
152
-                    % {'threshold': basket_threshold}
147
+            return max_allowed, basket_threshold
148
+
149
+    def is_quantity_allowed(self, qty):
150
+        """
151
+        Test whether the passed quantity of items can be added to the basket
152
+        """
153
+        # We enforce a max threshold to prevent a DOS attack via the offers
154
+        # system.
155
+        max_allowed, basket_threshold = self.max_allowed_quantity()
156
+        if max_allowed is not None and qty > max_allowed:
157
+            return False, _(
158
+                "Due to technical limitations we are not able "
159
+                "to ship more than %(threshold)d items in one order.") \
160
+                % {'threshold': basket_threshold}
153 161
         return True, None
154 162
 
155 163
     # ============

+ 10
- 0
src/oscar/apps/basket/forms.py Прегледај датотеку

@@ -19,6 +19,16 @@ class BasketLineForm(forms.ModelForm):
19 19
         super(BasketLineForm, self).__init__(*args, **kwargs)
20 20
         self.instance.strategy = strategy
21 21
 
22
+        max_allowed_quantity = None
23
+        num_available = getattr(self.instance.purchase_info.availability, 'num_available', None)
24
+        basket_max_allowed_quantity = self.instance.basket.max_allowed_quantity()[0]
25
+        if all([num_available, basket_max_allowed_quantity]):
26
+            max_allowed_quantity = min(num_available, basket_max_allowed_quantity)
27
+        else:
28
+            max_allowed_quantity = num_available or basket_max_allowed_quantity
29
+        if max_allowed_quantity:
30
+            self.fields['quantity'].widget.attrs['max'] = max_allowed_quantity
31
+
22 32
     def clean_quantity(self):
23 33
         qty = self.cleaned_data['quantity']
24 34
         if qty > 0:

+ 19
- 2
tests/integration/basket/test_forms.py Прегледај датотеку

@@ -1,6 +1,6 @@
1 1
 from decimal import Decimal as D
2 2
 
3
-from django.test import TestCase
3
+from django.test import TestCase, override_settings
4 4
 from django.conf import settings
5 5
 import mock
6 6
 
@@ -65,8 +65,8 @@ class TestBasketLineForm(TestCase):
65 65
         form = self.build_form(quantity=invalid_qty)
66 66
         self.assertFalse(form.is_valid())
67 67
 
68
+    @override_settings(OSCAR_MAX_BASKET_QUANTITY_THRESHOLD=10)
68 69
     def test_enforce_max_line_quantity_for_existing_product(self):
69
-        settings.OSCAR_MAX_BASKET_QUANTITY_THRESHOLD = 10
70 70
         self.basket.flush()
71 71
         product = factories.create_product(num_in_stock=20)
72 72
         add_product(self.basket, D('100'), 4, product)
@@ -77,6 +77,23 @@ class TestBasketLineForm(TestCase):
77 77
         form = self.build_form(quantity=11)
78 78
         self.assertFalse(form.is_valid())
79 79
 
80
+    def test_line_quantity_max_attribute_per_num_available(self):
81
+        self.basket.flush()
82
+        product = factories.create_product(num_in_stock=20)
83
+        add_product(self.basket, D('100'), 4, product)
84
+        self.line = self.basket.all_lines()[0]
85
+        form = self.build_form()
86
+        self.assertIn('max="20"', str(form['quantity']))
87
+
88
+    @override_settings(OSCAR_MAX_BASKET_QUANTITY_THRESHOLD=10)
89
+    def test_line_quantity_max_attribute_per_basket_threshold(self):
90
+        self.basket.flush()
91
+        product = factories.create_product(num_in_stock=20)
92
+        add_product(self.basket, D('100'), 4, product)
93
+        self.line = self.basket.all_lines()[0]
94
+        form = self.build_form()
95
+        self.assertIn('max="6"', str(form['quantity']))
96
+
80 97
     def test_basketline_formset_ordering(self):
81 98
         # when we use a unordered queryset in the Basketlineformset, the
82 99
         # discounts will be lost because django will query the database

+ 11
- 1
tests/integration/basket/test_models.py Прегледај датотеку

@@ -1,7 +1,7 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 from decimal import Decimal as D
3 3
 
4
-from django.test import TestCase
4
+from django.test import TestCase, override_settings
5 5
 
6 6
 from oscar.apps.basket.models import Basket
7 7
 from oscar.apps.catalogue.models import Option
@@ -242,6 +242,16 @@ class TestANonEmptyBasket(TestCase):
242 242
         self.assertEqual(self.basket.total_excl_tax_excl_discounts, 100)
243 243
         self.assertEqual(self.basket.total_incl_tax_excl_discounts, 100)
244 244
 
245
+    @override_settings(OSCAR_MAX_BASKET_QUANTITY_THRESHOLD=20)
246
+    def test_max_allowed_quantity(self):
247
+        self.basket.add_product(self.product, quantity=3)
248
+        self.assertEquals(self.basket.max_allowed_quantity()[0], 7)
249
+
250
+    @override_settings(OSCAR_MAX_BASKET_QUANTITY_THRESHOLD=20)
251
+    def test_is_quantity_allowed(self):
252
+        self.assertTrue(self.basket.is_quantity_allowed(qty=3)[0])
253
+        self.assertFalse(self.basket.is_quantity_allowed(qty=11)[0])
254
+
245 255
 
246 256
 class TestMergingTwoBaskets(TestCase):
247 257
 

Loading…
Откажи
Сачувај