Просмотр исходного кода

Merge branch 'releases/0.3'

Conflicts:
	oscar/__init__.py
master
David Winterbottom 13 лет назад
Родитель
Сommit
c234388496

+ 13
- 2
CHANGELOG.rst Просмотреть файл

@@ -2,8 +2,19 @@
2 2
 Changelog
3 3
 =========
4 4
 
5
-0.3 
6
----
5
+0.3.2
6
+-----
7
+
8
+* Bug fix for basket calculations
9
+* Bug fix for absolute discount benefit calculations
10
+
11
+0.3.1 - 2012-08-08
12
+------------------
13
+
14
+* Now including the translation files.
15
+
16
+0.3 - 2012-08-08
17
+----------------
7 18
 
8 19
 * i18n support added - Oscar now ships with .po files for seven languages.
9 20
   Translation files are welcome.

+ 6
- 4
oscar/app.py Просмотреть файл

@@ -14,7 +14,7 @@ from oscar.apps.dashboard.app import application as dashboard_app
14 14
 
15 15
 class Shop(Application):
16 16
     name = None
17
-    
17
+
18 18
     catalogue_app = catalogue_app
19 19
     customer_app = customer_app
20 20
     basket_app = basket_app
@@ -23,7 +23,7 @@ class Shop(Application):
23 23
     search_app = search_app
24 24
     dashboard_app = dashboard_app
25 25
     offer_app = offer_app
26
-    
26
+
27 27
     def get_urls(self):
28 28
         urlpatterns = patterns('',
29 29
             (r'^products/', include(self.catalogue_app.urls)),
@@ -39,12 +39,14 @@ class Shop(Application):
39 39
             # from working.
40 40
             url(r'^password-reset/$', auth_views.password_reset, name='password-reset'),
41 41
             url(r'^password-reset/done/$', auth_views.password_reset_done, name='password-reset-done'),
42
-            url(r'^password-reset/confirm/$', auth_views.password_reset_confirm, name='password-reset-confirm'),
42
+            url(r'^password-reset/confirm/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
43
+                auth_views.password_reset_confirm, name='password-reset-confirm'),
43 44
             url(r'^password-reset/complete/$', auth_views.password_reset_complete, name='password-reset-complete'),
44 45
 
45 46
             (r'', include(self.promotions_app.urls)),
46 47
         )
47 48
         return urlpatterns
48
-    
49
+
50
+
49 51
 # 'shop' kept for legacy projects - 'application' is a better name
50 52
 shop = application = Shop()

+ 1
- 1
oscar/apps/basket/views.py Просмотреть файл

@@ -24,7 +24,7 @@ class BasketView(ModelFormSetView):
24 24
     form_class = BasketLineForm
25 25
     extra = 0
26 26
     can_delete = True
27
-    template_name='basket/basket.html'
27
+    template_name = 'basket/basket.html'
28 28
 
29 29
     def get_queryset(self):
30 30
         return self.request.basket.all_lines()

+ 11
- 2
oscar/apps/offer/models.py Просмотреть файл

@@ -187,7 +187,8 @@ class Condition(models.Model):
187 187
     description = __unicode__
188 188
 
189 189
     def consume_items(self, basket, lines=None):
190
-        return ()
190
+        raise NotImplementedError("This method should never be called - "
191
+                                  "ensure you are using the correct proxy model")
191 192
 
192 193
     def is_satisfied(self, basket):
193 194
         """
@@ -421,6 +422,7 @@ class CountCondition(Condition):
421 422
         consumed_products = []
422 423
         value = self.value if value is None else value
423 424
         for line in lines:
425
+
424 426
             if self.can_apply_condition(line.product):
425 427
                 quantity_to_consume = min(line.quantity_without_discount,
426 428
                                           value - len(consumed_products))
@@ -649,13 +651,20 @@ class AbsoluteDiscountBenefit(Benefit):
649 651
 
650 652
                 # Update line with discounts
651 653
                 line_discount = self.round(min(remaining_discount, quantity_affected * price))
652
-                line.discount(line_discount, quantity_affected)
654
+                if condition:
655
+                    # Pass zero as quantity to avoid double consumption
656
+                    line.discount(line_discount, 0)
657
+                else:
658
+                    line.discount(line_discount, quantity_affected)
653 659
 
654 660
                 # Update loop vars
655 661
                 affected_items += quantity_affected
656 662
                 remaining_discount -= line_discount
657 663
                 discount += line_discount
658 664
 
665
+        if discount > 0 and condition:
666
+            condition.consume_items(basket)
667
+
659 668
         return discount
660 669
 
661 670
 

+ 1
- 0
oscar/templates/oscar/customer/login_registration.html Просмотреть файл

@@ -24,6 +24,7 @@
24 24
             <h2>{% trans 'Log In' %}</h2>
25 25
             {% csrf_token %}
26 26
             {% include "partials/form_fields.html" with form=login_form %}
27
+			<p><a href="{% url password-reset %}">{% trans "I've forgotten my password" %}</a></p>
27 28
             <button name="login_submit" type="submit" value="Log In" class="btn btn-large btn-primary">{% trans 'Log In' %}</button>
28 29
         </form>
29 30
     </div>

+ 1
- 1
oscar/templates/oscar/dashboard/layout.html Просмотреть файл

@@ -8,7 +8,7 @@
8 8
 	<link rel="stylesheet" href="{{ STATIC_URL }}oscar/css/dashboard.css" />
9 9
 {% endblock %}
10 10
 
11
-{% block extrahead %} {%endblock extrahead %}
11
+{% block extrahead %} {% endblock extrahead %}
12 12
 
13 13
 {% block title %}
14 14
 {% trans "Dashboard" %} | {{ block.super }}

+ 1
- 1
oscar/templates/oscar/layout.html Просмотреть файл

@@ -18,7 +18,7 @@
18 18
             {% include "partials/nav_primary.html" %}
19 19
         {% endblock %}
20 20
     </header>
21
-    <div class="container-fluid page">
21
+	<div class="container-fluid page">
22 22
         {% block header %}{% endblock %}
23 23
         <div class="page_inner">
24 24
             {% block breadcrumbs %}{% endblock %}

+ 11
- 1
oscar/templates/oscar/registration/password_reset_complete.html Просмотреть файл

@@ -3,8 +3,18 @@
3 3
 
4 4
 {% block title %}{% trans 'Password reset complete' %} | {{ block.super }}{% endblock %}
5 5
 
6
+{% block breadcrumbs %}
7
+<ul class="breadcrumb">
8
+    <li>
9
+        <a href="{% url promotions:home %}">{% trans 'Home' %}</a>
10
+        <span class="divider">/</span>
11
+    </li>
12
+	<li class="active"><a href=".">{% trans 'Password reset complete' %}</a></li>
13
+</ul>
14
+{% endblock %}
15
+
6 16
 {% block content %}
7 17
 	<h1>{% trans 'Password reset complete' %}</h1>
8 18
 	<p>{% trans "Your password has been set.  You may go ahead and log in now." %}</p>
9
-	<p><a href="{{ login_url }}">{% trans 'Log in' %}</a></p>
19
+	<p><a href="{{ login_url }}" class="btn btn-large btn-primary">{% trans 'Log in' %}</a></p>
10 20
 {% endblock %}

+ 12
- 2
oscar/templates/oscar/registration/password_reset_confirm.html Просмотреть файл

@@ -3,6 +3,16 @@
3 3
 
4 4
 {% block title %}{% trans 'Password reset confirm' %} | {{ block.super }}{% endblock %}
5 5
 
6
+{% block breadcrumbs %}
7
+<ul class="breadcrumb">
8
+    <li>
9
+        <a href="{% url promotions:home %}">{% trans 'Home' %}</a>
10
+        <span class="divider">/</span>
11
+    </li>
12
+	<li class="active"><a href=".">{% trans 'Enter new password' %}</a></li>
13
+</ul>
14
+{% endblock %}
15
+
6 16
 {% block content %}
7 17
 
8 18
 {% if validlink %}
@@ -11,12 +21,12 @@
11 21
 
12 22
 <p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
13 23
 
14
-<form action="" method="post">{% csrf_token %}
24
+<form id="password_reset_form" action="" method="post">{% csrf_token %}
15 25
 {{ form.new_password1.errors }}
16 26
 <p class="aligned wide"><label for="id_new_password1">{% trans 'New password:' %}</label>{{ form.new_password1 }}</p>
17 27
 {{ form.new_password2.errors }}
18 28
 <p class="aligned wide"><label for="id_new_password2">{% trans 'Confirm password:' %}</label>{{ form.new_password2 }}</p>
19
-<p><input type="submit" value="{% trans 'Change my password' %}" /></p>
29
+<p><button class="btn btn-primary btn-large" type="submit">{% trans 'Change my password' %}</button></p>
20 30
 </form>
21 31
 
22 32
 {% else %}

+ 10
- 0
oscar/templates/oscar/registration/password_reset_done.html Просмотреть файл

@@ -3,6 +3,16 @@
3 3
 
4 4
 {% block title %}{% trans 'Password reset successful' %} | {{ block.super }}{% endblock %}
5 5
 
6
+{% block breadcrumbs %}
7
+<ul class="breadcrumb">
8
+    <li>
9
+        <a href="{% url promotions:home %}">{% trans 'Home' %}</a>
10
+        <span class="divider">/</span>
11
+    </li>
12
+	<li class="active"><a href=".">{% trans 'Password reset sent' %}</a></li>
13
+</ul>
14
+{% endblock %}
15
+
6 16
 {% block content %}
7 17
 
8 18
     <h1>{% trans 'Password reset successful' %}</h1>

+ 11
- 1
oscar/templates/oscar/registration/password_reset_form.html Просмотреть файл

@@ -3,13 +3,23 @@
3 3
 
4 4
 {% block title %}{% trans 'Password reset' %} | {{ block.super }}{% endblock %}
5 5
 
6
+{% block breadcrumbs %}
7
+<ul class="breadcrumb">
8
+    <li>
9
+        <a href="{% url promotions:home %}">{% trans 'Home' %}</a>
10
+        <span class="divider">/</span>
11
+    </li>
12
+	<li class="active"><a href=".">{% trans 'Password reset' %}</a></li>
13
+</ul>
14
+{% endblock %}
15
+
6 16
 {% block content %}
7 17
 
8 18
     <div class="sub-header">
9 19
         <h2>{% trans "Password reset" %}</h2>
10 20
     </div>
11 21
 
12
-    <form action="" method="post" class="form-stacked well">
22
+    <form id="password_reset_form" action="" method="post" class="form-stacked well">
13 23
     {% csrf_token %}
14 24
 
15 25
         <p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll e-mail instructions for setting a new one." %}</p>

+ 4
- 2
oscar/test/helpers.py Просмотреть файл

@@ -27,12 +27,14 @@ Partner, StockRecord = get_classes('partner.models', ('Partner',
27 27
 
28 28
 
29 29
 def create_product(price=None, title="Dummy title", product_class="Dummy item class",
30
-        partner="Dummy partner", partner_sku=None, upc=None, num_in_stock=10, attributes=None):
30
+        partner="Dummy partner", partner_sku=None, upc=None, num_in_stock=10,
31
+        attributes=None, **kwargs):
31 32
     """
32 33
     Helper method for creating products that are used in tests.
33 34
     """
34 35
     ic,_ = ProductClass._default_manager.get_or_create(name=product_class)
35
-    item = Product._default_manager.create(title=title, product_class=ic, upc=upc)
36
+    item = Product._default_manager.create(title=title, product_class=ic,
37
+                                           upc=upc, **kwargs)
36 38
     if price is not None or partner_sku or num_in_stock is not None:
37 39
         if not partner_sku:
38 40
             partner_sku = 'sku_%d_%d' % (item.id, random.randint(0, 10000))

+ 1
- 1
setup.py Просмотреть файл

@@ -25,7 +25,7 @@ setup(name='django-oscar',
25 25
       packages=find_packages(exclude=["sandbox*", "tests*"]),
26 26
       include_package_data=True,
27 27
       install_requires=[
28
-          'django==1.4',
28
+          'django>=1.4',
29 29
           'PIL==1.1.7',
30 30
           'South==0.7.3',
31 31
           'django-extra-views==0.2.0',

+ 0
- 0
tests/functional/customer/__init__.py Просмотреть файл


+ 49
- 0
tests/functional/customer/auth_tests.py Просмотреть файл

@@ -0,0 +1,49 @@
1
+import re
2
+
3
+from django.contrib.auth import models
4
+from django.core.urlresolvers import reverse
5
+from django.core import mail
6
+
7
+from django_webtest import WebTest
8
+
9
+
10
+class TestAUserWhoseForgottenHerPassword(WebTest):
11
+
12
+    def test_can_reset_her_password(self):
13
+        username, email, password = 'lucy', 'lucy@example.com', 'password'
14
+        models.User.objects.create_user(
15
+            username, email, password
16
+        )
17
+
18
+        # Fill in password reset form
19
+        page = self.app.get(reverse('password-reset'))
20
+        form = page.forms['password_reset_form']
21
+        form['email'] = email
22
+        response = form.submit()
23
+
24
+        # Response should be a redirect and an email should have been sent
25
+        self.assertEqual(302, response.status_code)
26
+        self.assertEqual(1, len(mail.outbox))
27
+
28
+        # Extract URL from email
29
+        email_body = mail.outbox[0].body
30
+        urlfinder = re.compile(r"http://example.com(?P<path>[-A-Za-z0-9\/\._]+)")
31
+        matches = urlfinder.search(email_body, re.MULTILINE)
32
+        self.assertTrue('path' in matches.groupdict())
33
+        path = matches.groupdict()['path']
34
+
35
+        # Reset password and check we get redirect
36
+        reset_page = self.app.get(path)
37
+        form = reset_page.forms['password_reset_form']
38
+        form['new_password1'] = 'monkey'
39
+        form['new_password2'] = 'monkey'
40
+        response = form.submit()
41
+        self.assertEqual(302, response.status_code)
42
+
43
+        # Now attempt to login with new password
44
+        url = reverse('customer:login')
45
+        form = self.app.get(url).forms['login_form']
46
+        form['login-username'] = email
47
+        form['login-password'] = 'monkey'
48
+        response = form.submit('login_submit')
49
+        self.assertEqual(302, response.status_code)

+ 2
- 1
tests/unit/offer/__init__.py Просмотреть файл

@@ -6,5 +6,6 @@ from oscar.apps.basket.models import Basket
6 6
 
7 7
 class OfferTest(TestCase):
8 8
     def setUp(self):
9
-        self.range = models.Range.objects.create(name="All products range", includes_all_products=True)
9
+        self.range = models.Range.objects.create(
10
+            name="All products range", includes_all_products=True)
10 11
         self.basket = Basket.objects.create()

+ 167
- 98
tests/unit/offer/benefit_tests.py Просмотреть файл

@@ -1,4 +1,4 @@
1
-from decimal import Decimal
1
+from decimal import Decimal as D
2 2
 
3 3
 from django.conf import settings
4 4
 
@@ -12,8 +12,8 @@ class PercentageDiscountBenefitTest(OfferTest):
12 12
 
13 13
     def setUp(self):
14 14
         super(PercentageDiscountBenefitTest, self).setUp()
15
-        self.benefit = models.PercentageDiscountBenefit(range=self.range, type="Percentage", value=Decimal('15.00'))
16
-        self.item = create_product(price=Decimal('5.00'))
15
+        self.benefit = models.PercentageDiscountBenefit(range=self.range, type="Percentage", value=D('15.00'))
16
+        self.item = create_product(price=D('5.00'))
17 17
         self.original_offer_rounding_function = getattr(settings, 'OSCAR_OFFER_ROUNDING_FUNCTION', None)
18 18
         if self.original_offer_rounding_function is not None:
19 19
             delattr(settings, 'OSCAR_OFFER_ROUNDING_FUNCTION')
@@ -24,32 +24,32 @@ class PercentageDiscountBenefitTest(OfferTest):
24 24
             settings.OSCAR_OFFER_ROUNDING_FUNCTION = self.original_offer_rounding_function
25 25
 
26 26
     def test_no_discount_for_empty_basket(self):
27
-        self.assertEquals(Decimal('0.00'), self.benefit.apply(self.basket))
27
+        self.assertEquals(D('0.00'), self.benefit.apply(self.basket))
28 28
 
29 29
     def test_no_discount_for_not_discountable_product(self):
30 30
         self.item.is_discountable = False
31 31
         self.item.save()
32 32
         self.basket.add_product(self.item, 1)
33
-        self.assertEquals(Decimal('0.00'), self.benefit.apply(self.basket))
33
+        self.assertEquals(D('0.00'), self.benefit.apply(self.basket))
34 34
 
35 35
     def test_discount_for_single_item_basket(self):
36 36
         self.basket.add_product(self.item, 1)
37
-        self.assertEquals(Decimal('0.15') * Decimal('5.00'), self.benefit.apply(self.basket))
37
+        self.assertEquals(D('0.15') * D('5.00'), self.benefit.apply(self.basket))
38 38
 
39 39
     def test_discount_for_multi_item_basket(self):
40 40
         self.basket.add_product(self.item, 3)
41
-        self.assertEquals(Decimal('3') * Decimal('0.15') * Decimal('5.00'), self.benefit.apply(self.basket))
41
+        self.assertEquals(D('3') * D('0.15') * D('5.00'), self.benefit.apply(self.basket))
42 42
 
43 43
     def test_discount_for_multi_item_basket_with_max_affected_items_set(self):
44 44
         self.basket.add_product(self.item, 3)
45 45
         self.benefit.max_affected_items = 1
46
-        self.assertEquals(Decimal('0.15') * Decimal('5.00'), self.benefit.apply(self.basket))
46
+        self.assertEquals(D('0.15') * D('5.00'), self.benefit.apply(self.basket))
47 47
 
48 48
     def test_discount_can_only_be_applied_once(self):
49 49
         self.basket.add_product(self.item, 3)
50 50
         self.benefit.apply(self.basket)
51 51
         second_discount = self.benefit.apply(self.basket)
52
-        self.assertEquals(Decimal('0.00'), second_discount)
52
+        self.assertEquals(D('0.00'), second_discount)
53 53
 
54 54
     def test_discount_can_be_applied_several_times_when_max_is_set(self):
55 55
         self.basket.add_product(self.item, 3)
@@ -58,75 +58,144 @@ class PercentageDiscountBenefitTest(OfferTest):
58 58
             self.assertTrue(self.benefit.apply(self.basket) > 0)
59 59
 
60 60
 
61
-class AbsoluteDiscountBenefitTest(OfferTest):
61
+class TestAbsoluteDiscount(OfferTest):
62 62
 
63 63
     def setUp(self):
64
-        super(AbsoluteDiscountBenefitTest, self).setUp()
64
+        super(TestAbsoluteDiscount, self).setUp()
65
+
65 66
         self.benefit = models.AbsoluteDiscountBenefit(
66
-            range=self.range, type="Absolute", value=Decimal('10.00'))
67
-        self.item = create_product(price=Decimal('5.00'))
68
-        self.original_offer_rounding_function = getattr(settings, 'OSCAR_OFFER_ROUNDING_FUNCTION', None)
67
+            range=self.range, type="Absolute", value=D('10.00'))
68
+        self.item = create_product(price=D('5.00'))
69
+
70
+        self.original_offer_rounding_function = getattr(
71
+            settings, 'OSCAR_OFFER_ROUNDING_FUNCTION', None)
69 72
         if self.original_offer_rounding_function is not None:
70 73
             delattr(settings, 'OSCAR_OFFER_ROUNDING_FUNCTION')
71 74
 
72 75
     def tearDown(self):
73
-        super(AbsoluteDiscountBenefitTest, self).tearDown()
74 76
         if self.original_offer_rounding_function is not None:
75 77
             settings.OSCAR_OFFER_ROUNDING_FUNCTION = self.original_offer_rounding_function
76 78
 
77
-    def test_no_discount_for_empty_basket(self):
78
-        self.assertEquals(Decimal('0.00'), self.benefit.apply(self.basket))
79
+    def test_gives_no_discount_for_an_empty_basket(self):
80
+        self.assertEquals(D('0.00'), self.benefit.apply(self.basket))
81
+
82
+    def test_gives_no_discount_for_a_non_discountable_product(self):
83
+        product = create_product(price=D('5.00'), is_discountable=False)
84
+        self.basket.add_product(product)
85
+        self.assertEquals(D('0.00'), self.benefit.apply(self.basket))
86
+
87
+    def test_gives_correct_discount_for_single_item_basket_cheaper_than_threshold(self):
88
+        product = create_product(price=D('5.00'))
89
+        self.basket.add_product(product)
90
+        self.assertEquals(D('5.00'), self.benefit.apply(self.basket))
91
+
92
+    def test_gives_correct_discount_for_single_item_basket_equal_to_threshold(self):
93
+        product = create_product(price=D('10.00'))
94
+        self.basket.add_product(product)
95
+        self.assertEquals(D('10.00'), self.benefit.apply(self.basket))
96
+
97
+    def test_gives_correct_discount_for_single_item_basket_more_expensive_than_threshold(self):
98
+        product = create_product(price=D('16.00'))
99
+        self.basket.add_product(product)
100
+        self.assertEquals(D('10.00'), self.benefit.apply(self.basket))
101
+
102
+    def test_gives_correct_discount_for_multi_item_basket_cheaper_than_threshold(self):
103
+        product = create_product(price=D('2.00'))
104
+        self.basket.add_product(product, 3)
105
+        self.assertEquals(D('6.00'), self.benefit.apply(self.basket))
106
+
107
+    def test_gives_correct_discount_for_multi_item_basket_more_expensive_than_threshold(self):
108
+        product = create_product(price=D('5.00'))
109
+        self.basket.add_product(product, 3)
110
+        self.assertEquals(D('10.00'), self.benefit.apply(self.basket))
111
+
112
+    def test_consumes_all_lines_for_multi_item_basket_cheaper_than_threshold(self):
113
+        product = create_product(price=D('2.00'))
114
+        self.basket.add_product(product, 3)
115
+        self.benefit.apply(self.basket)
116
+        for line in self.basket.all_lines():
117
+            self.assertTrue(line.has_discount)
118
+            self.assertEqual(0, line.quantity_without_discount)
79 119
 
80
-    def test_no_discount_for_not_discountable_product(self):
81
-        self.item.is_discountable = False
82
-        self.item.save()
83
-        self.basket.add_product(self.item, 1)
84
-        self.assertEquals(Decimal('0.00'), self.benefit.apply(self.basket))
120
+    def test_consumes_correct_quantity_for_multi_item_basket_more_expensive_than_threshold(self):
121
+        product = create_product(price=D('6.00'))
122
+        self.basket.add_product(product, 3)
123
+        self.benefit.apply(self.basket)
124
+        line = self.basket.all_lines()[0]
125
+        self.assertTrue(line.has_discount)
126
+        self.assertEqual(1, line.quantity_without_discount)
85 127
 
86
-    def test_discount_for_single_item_basket(self):
87
-        self.basket.add_product(self.item, 1)
88
-        self.assertEquals(Decimal('5.00'), self.benefit.apply(self.basket))
128
+    def test_gives_correct_discount_for_multi_item_basket_with_max_affected_items_set(self):
129
+        product = create_product(price=D('5.00'))
130
+        self.basket.add_product(product, 3)
131
+        self.benefit.max_affected_items = 1
132
+        self.assertEquals(D('5.00'), self.benefit.apply(self.basket))
89 133
 
90
-    def test_discount_for_multi_item_basket(self):
91
-        self.basket.add_product(self.item, 3)
92
-        self.assertEquals(Decimal('10.00'), self.benefit.apply(self.basket))
134
+    def test_gives_correct_discounts_when_applied_multiple_times(self):
135
+        product = create_product(price=D('5.00'))
136
+        self.basket.add_product(product, 3)
93 137
 
94
-    def test_discount_for_multi_item_basket_with_max_affected_items_set(self):
95
-        self.basket.add_product(self.item, 3)
96
-        self.benefit.max_affected_items = 1
97
-        self.assertEquals(Decimal('5.00'), self.benefit.apply(self.basket))
138
+        self.assertEquals(D('10.00'), self.benefit.apply(self.basket))
139
+        self.assertEquals(D('5.00'), self.benefit.apply(self.basket))
140
+        self.assertEquals(D('0.00'), self.benefit.apply(self.basket))
98 141
 
99
-    def test_discount_can_only_be_applied_once(self):
100
-        # Add 3 items to make total 15.00
101
-        self.basket.add_product(self.item, 3)
102
-        first_discount = self.benefit.apply(self.basket)
103
-        self.assertEquals(Decimal('10.00'), first_discount)
142
+    def test_gives_correct_discounts_when_applied_multiple_times_with_condition(self):
143
+        product = create_product(D('25000'))
144
+        rng = models.Range.objects.create(name='Dummy')
145
+        rng.included_products.add(product)
104 146
 
105
-        second_discount = self.benefit.apply(self.basket)
106
-        self.assertEquals(Decimal('5.00'), second_discount)
147
+        condition = models.ValueCondition(range=rng, type='Value', value=D('5000'))
107 148
 
108
-    def test_absolute_does_not_consume_twice(self):
109
-        product = create_product(Decimal('25000'))
149
+        self.basket.add_product(product, 5)
150
+        benefit = models.AbsoluteDiscountBenefit(range=rng, type='Absolute', value=D('100'))
151
+
152
+        for _ in range(5):
153
+            self.assertTrue(condition.is_satisfied(self.basket))
154
+            self.assertEquals(D('100'), benefit.apply(self.basket, condition))
155
+
156
+        self.assertFalse(condition.is_satisfied(self.basket))
157
+        self.assertEquals(D('0'), benefit.apply(self.basket, condition))
158
+
159
+    def test_consumes_all_products_for_heterogeneous_basket(self):
160
+        rng = models.Range.objects.create(name='Dummy')
161
+        products = [create_product(D('150')),
162
+                    create_product(D('300')),
163
+                    create_product(D('300'))]
164
+        for product in products:
165
+            rng.included_products.add(product)
166
+
167
+        condition = models.ValueCondition(range=rng, type='Value', value=D('500'))
168
+
169
+        basket = Basket.objects.create()
170
+        for product in products:
171
+            basket.add_product(product)
172
+
173
+        benefit = models.AbsoluteDiscountBenefit(range=rng, type='Absolute', value=D('100'))
174
+
175
+        self.assertTrue(condition.is_satisfied(basket))
176
+        self.assertEquals(D('100'), benefit.apply(basket, condition))
177
+        self.assertEquals(D('0'), benefit.apply(basket, condition))
178
+
179
+    def test_correctly_discounts_line(self):
180
+        product = create_product(D('500'))
110 181
         rng = models.Range.objects.create(name='Dummy')
111 182
         rng.included_products.add(product)
112
-        condition = models.ValueCondition(range=rng, type='Value', value=Decimal('5000'))
183
+
184
+        condition = models.ValueCondition(range=rng, type='Value', value=D('500'))
113 185
         basket = Basket.objects.create()
114
-        basket.add_product(product, 5)
115
-        benefit = models.AbsoluteDiscountBenefit(range=rng, type='Absolute', value=Decimal('100'))
186
+        basket.add_product(product, 1)
187
+
188
+        benefit = models.AbsoluteDiscountBenefit(range=rng, type='Absolute', value=D('100'))
189
+
116 190
         self.assertTrue(condition.is_satisfied(basket))
117
-        self.assertEquals(Decimal('100'), benefit.apply(basket, condition))
118
-        self.assertEquals(Decimal('100'), benefit.apply(basket, condition))
119
-        self.assertEquals(Decimal('100'), benefit.apply(basket, condition))
120
-        self.assertEquals(Decimal('100'), benefit.apply(basket, condition))
121
-        self.assertEquals(Decimal('100'), benefit.apply(basket, condition))
122
-        self.assertEquals(Decimal('0'), benefit.apply(basket, condition))
191
+        self.assertEquals(D('100'), benefit.apply(basket, condition))
192
+        self.assertEquals(D('100'), basket.all_lines()[0]._discount)
123 193
 
124 194
     def test_discount_is_applied_to_lines(self):
125
-        condition = models.Condition.objects.create(
195
+        condition = models.CountCondition.objects.create(
126 196
             range=self.range, type="Count", value=1)
127 197
         self.basket.add_product(self.item, 1)
128 198
         self.benefit.apply(self.basket, condition)
129
-
130 199
         self.assertTrue(self.basket.all_lines()[0].has_discount)
131 200
 
132 201
 
@@ -135,38 +204,38 @@ class MultibuyDiscountBenefitTest(OfferTest):
135 204
     def setUp(self):
136 205
         super(MultibuyDiscountBenefitTest, self).setUp()
137 206
         self.benefit = models.MultibuyDiscountBenefit(range=self.range, type="Multibuy", value=1)
138
-        self.item = create_product(price=Decimal('5.00'))
207
+        self.item = create_product(price=D('5.00'))
139 208
 
140 209
     def test_no_discount_for_empty_basket(self):
141
-        self.assertEquals(Decimal('0.00'), self.benefit.apply(self.basket))
210
+        self.assertEquals(D('0.00'), self.benefit.apply(self.basket))
142 211
 
143 212
     def test_discount_for_single_item_basket(self):
144 213
         self.basket.add_product(self.item, 1)
145
-        self.assertEquals(Decimal('5.00'), self.benefit.apply(self.basket))
214
+        self.assertEquals(D('5.00'), self.benefit.apply(self.basket))
146 215
 
147 216
     def test_discount_for_multi_item_basket(self):
148 217
         self.basket.add_product(self.item, 3)
149
-        self.assertEquals(Decimal('5.00'), self.benefit.apply(self.basket))
218
+        self.assertEquals(D('5.00'), self.benefit.apply(self.basket))
150 219
 
151 220
     def test_no_discount_for_not_discountable_product(self):
152 221
         self.item.is_discountable = False
153 222
         self.item.save()
154 223
         self.basket.add_product(self.item, 1)
155
-        self.assertEquals(Decimal('0.00'), self.benefit.apply(self.basket))
224
+        self.assertEquals(D('0.00'), self.benefit.apply(self.basket))
156 225
 
157 226
     def test_discount_does_not_consume_item_if_in_condition_range(self):
158 227
         self.basket.add_product(self.item, 1)
159 228
         first_discount = self.benefit.apply(self.basket)
160
-        self.assertEquals(Decimal('5.00'), first_discount)
229
+        self.assertEquals(D('5.00'), first_discount)
161 230
         second_discount = self.benefit.apply(self.basket)
162
-        self.assertEquals(Decimal('5.00'), second_discount)
231
+        self.assertEquals(D('5.00'), second_discount)
163 232
 
164 233
     def test_product_does_consume_item_if_not_in_condition_range(self):
165 234
         # Set up condition using a different range from benefit
166 235
         range = models.Range.objects.create(name="Small range")
167
-        other_product = create_product(price=Decimal('15.00'))
236
+        other_product = create_product(price=D('15.00'))
168 237
         range.included_products.add(other_product)
169
-        cond = models.ValueCondition(range=range, type="Value", value=Decimal('10.00'))
238
+        cond = models.ValueCondition(range=range, type="Value", value=D('10.00'))
170 239
 
171 240
         self.basket.add_product(self.item, 1)
172 241
         self.benefit.apply(self.basket, cond)
@@ -175,7 +244,7 @@ class MultibuyDiscountBenefitTest(OfferTest):
175 244
 
176 245
     def test_condition_consumes_most_expensive_lines_first(self):
177 246
         for i in range(10, 0, -1):
178
-            product = create_product(price=Decimal(i), title='%i'%i, upc='upc_%i' % i)
247
+            product = create_product(price=D(i), title='%i'%i, upc='upc_%i' % i)
179 248
             self.basket.add_product(product, 1)
180 249
 
181 250
         condition = models.CountCondition(range=self.range, type="Count", value=2)
@@ -183,34 +252,34 @@ class MultibuyDiscountBenefitTest(OfferTest):
183 252
         self.assertTrue(condition.is_satisfied(self.basket))
184 253
         # consume 1 and 10
185 254
         first_discount = self.benefit.apply(self.basket, condition=condition)
186
-        self.assertEquals(Decimal('1.00'), first_discount)
255
+        self.assertEquals(D('1.00'), first_discount)
187 256
 
188 257
         self.assertTrue(condition.is_satisfied(self.basket))
189 258
         # consume 2 and 9
190 259
         second_discount = self.benefit.apply(self.basket, condition=condition)
191
-        self.assertEquals(Decimal('2.00'), second_discount)
260
+        self.assertEquals(D('2.00'), second_discount)
192 261
 
193 262
         self.assertTrue(condition.is_satisfied(self.basket))
194 263
         # consume 3 and 8
195 264
         third_discount = self.benefit.apply(self.basket, condition=condition)
196
-        self.assertEquals(Decimal('3.00'), third_discount)
265
+        self.assertEquals(D('3.00'), third_discount)
197 266
 
198 267
         self.assertTrue(condition.is_satisfied(self.basket))
199 268
         # consume 4 and 7
200 269
         fourth_discount = self.benefit.apply(self.basket, condition=condition)
201
-        self.assertEquals(Decimal('4.00'), fourth_discount)
270
+        self.assertEquals(D('4.00'), fourth_discount)
202 271
 
203 272
         self.assertTrue(condition.is_satisfied(self.basket))
204 273
         # consume 5 and 6
205 274
         fifth_discount = self.benefit.apply(self.basket, condition=condition)
206
-        self.assertEquals(Decimal('5.00'), fifth_discount)
275
+        self.assertEquals(D('5.00'), fifth_discount)
207 276
 
208 277
         # end of items (one not discounted item in basket)
209 278
         self.assertFalse(condition.is_satisfied(self.basket))
210 279
 
211 280
     def test_condition_consumes_most_expensive_lines_first_when_products_are_repeated(self):
212 281
         for i in range(5, 0, -1):
213
-            product = create_product(price=Decimal(i), title='%i'%i, upc='upc_%i' % i)
282
+            product = create_product(price=D(i), title='%i'%i, upc='upc_%i' % i)
214 283
             self.basket.add_product(product, 2)
215 284
 
216 285
         condition = models.CountCondition(range=self.range, type="Count", value=2)
@@ -219,27 +288,27 @@ class MultibuyDiscountBenefitTest(OfferTest):
219 288
         self.assertTrue(condition.is_satisfied(self.basket))
220 289
         # consume 1 and 5
221 290
         first_discount = self.benefit.apply(self.basket, condition=condition)
222
-        self.assertEquals(Decimal('1.00'), first_discount)
291
+        self.assertEquals(D('1.00'), first_discount)
223 292
 
224 293
         self.assertTrue(condition.is_satisfied(self.basket))
225 294
         # consume 1 and 5
226 295
         second_discount = self.benefit.apply(self.basket, condition=condition)
227
-        self.assertEquals(Decimal('1.00'), second_discount)
296
+        self.assertEquals(D('1.00'), second_discount)
228 297
 
229 298
         self.assertTrue(condition.is_satisfied(self.basket))
230 299
         # consume 2 and 4
231 300
         third_discount = self.benefit.apply(self.basket, condition=condition)
232
-        self.assertEquals(Decimal('2.00'), third_discount)
301
+        self.assertEquals(D('2.00'), third_discount)
233 302
 
234 303
         self.assertTrue(condition.is_satisfied(self.basket))
235 304
         # consume 2 and 4
236 305
         third_discount = self.benefit.apply(self.basket, condition=condition)
237
-        self.assertEquals(Decimal('2.00'), third_discount)
306
+        self.assertEquals(D('2.00'), third_discount)
238 307
 
239 308
         self.assertTrue(condition.is_satisfied(self.basket))
240 309
         # consume 3 and 3
241 310
         third_discount = self.benefit.apply(self.basket, condition=condition)
242
-        self.assertEquals(Decimal('3.00'), third_discount)
311
+        self.assertEquals(D('3.00'), third_discount)
243 312
 
244 313
         # end of items (one not discounted item in basket)
245 314
         self.assertFalse(condition.is_satisfied(self.basket))
@@ -255,12 +324,12 @@ class FixedPriceBenefitTest(OfferTest):
255 324
 
256 325
     def setUp(self):
257 326
         super(FixedPriceBenefitTest, self).setUp()
258
-        self.benefit = models.FixedPriceBenefit(range=self.range, type="FixedPrice", value=Decimal('10.00'))
327
+        self.benefit = models.FixedPriceBenefit(range=self.range, type="FixedPrice", value=D('10.00'))
259 328
 
260 329
     def test_correct_discount_for_count_condition(self):
261
-        products = [create_product(Decimal('7.00')),
262
-                    create_product(Decimal('8.00')),
263
-                    create_product(Decimal('12.00'))]
330
+        products = [create_product(D('7.00')),
331
+                    create_product(D('8.00')),
332
+                    create_product(D('12.00'))]
264 333
 
265 334
         # Create range that includes the products
266 335
         range = models.Range.objects.create(name="Dummy range")
@@ -272,13 +341,13 @@ class FixedPriceBenefitTest(OfferTest):
272 341
         basket = Basket.objects.create()
273 342
         [basket.add_product(p, 2) for p in products]
274 343
 
275
-        benefit = models.FixedPriceBenefit(range=range, type="FixedPrice", value=Decimal('20.00'))
276
-        self.assertEquals(Decimal('2.00'), benefit.apply(basket, condition))
277
-        self.assertEquals(Decimal('12.00'), benefit.apply(basket, condition))
278
-        self.assertEquals(Decimal('0.00'), benefit.apply(basket, condition))
344
+        benefit = models.FixedPriceBenefit(range=range, type="FixedPrice", value=D('20.00'))
345
+        self.assertEquals(D('2.00'), benefit.apply(basket, condition))
346
+        self.assertEquals(D('12.00'), benefit.apply(basket, condition))
347
+        self.assertEquals(D('0.00'), benefit.apply(basket, condition))
279 348
 
280 349
     def test_correct_discount_is_returned(self):
281
-        products = [create_product(Decimal('8.00')), create_product(Decimal('4.00'))]
350
+        products = [create_product(D('8.00')), create_product(D('4.00'))]
282 351
         range = models.Range.objects.create(name="Dummy range")
283 352
         for product in products:
284 353
             range.included_products.add(product)
@@ -289,10 +358,10 @@ class FixedPriceBenefitTest(OfferTest):
289 358
 
290 359
         condition = models.CoverageCondition(range=range, type="Coverage", value=2)
291 360
         discount = self.benefit.apply(basket, condition)
292
-        self.assertEquals(Decimal('2.00'), discount)
361
+        self.assertEquals(D('2.00'), discount)
293 362
 
294 363
     def test_no_discount_when_product_not_discountable(self):
295
-        product = create_product(Decimal('18.00'))
364
+        product = create_product(D('18.00'))
296 365
         product.is_discountable = False
297 366
         product.save()
298 367
 
@@ -304,10 +373,10 @@ class FixedPriceBenefitTest(OfferTest):
304 373
 
305 374
         condition = models.CoverageCondition(range=product_range, type="Coverage", value=1)
306 375
         discount = self.benefit.apply(basket, condition)
307
-        self.assertEquals(Decimal('0.00'), discount)
376
+        self.assertEquals(D('0.00'), discount)
308 377
 
309 378
     def test_no_discount_is_returned_when_value_is_greater_than_product_total(self):
310
-        products = [create_product(Decimal('4.00')), create_product(Decimal('4.00'))]
379
+        products = [create_product(D('4.00')), create_product(D('4.00'))]
311 380
         range = models.Range.objects.create(name="Dummy range")
312 381
         for product in products:
313 382
             range.included_products.add(product)
@@ -318,12 +387,12 @@ class FixedPriceBenefitTest(OfferTest):
318 387
 
319 388
         condition = models.CoverageCondition(range=range, type="Coverage", value=2)
320 389
         discount = self.benefit.apply(basket, condition)
321
-        self.assertEquals(Decimal('0.00'), discount)
390
+        self.assertEquals(D('0.00'), discount)
322 391
 
323 392
     def test_discount_when_more_products_than_required(self):
324
-        products = [create_product(Decimal('4.00')),
325
-                    create_product(Decimal('8.00')),
326
-                    create_product(Decimal('12.00'))]
393
+        products = [create_product(D('4.00')),
394
+                    create_product(D('8.00')),
395
+                    create_product(D('12.00'))]
327 396
 
328 397
         # Create range that includes the products
329 398
         range = models.Range.objects.create(name="Dummy range")
@@ -336,14 +405,14 @@ class FixedPriceBenefitTest(OfferTest):
336 405
         [basket.add_product(p) for p in products]
337 406
         basket.add_product(products[0])
338 407
 
339
-        benefit = models.FixedPriceBenefit(range=range, type="FixedPrice", value=Decimal('20.00'))
408
+        benefit = models.FixedPriceBenefit(range=range, type="FixedPrice", value=D('20.00'))
340 409
         discount = benefit.apply(basket, condition)
341
-        self.assertEquals(Decimal('4.00'), discount)
410
+        self.assertEquals(D('4.00'), discount)
342 411
 
343 412
     def test_discount_when_applied_twice(self):
344
-        products = [create_product(Decimal('4.00')),
345
-                    create_product(Decimal('8.00')),
346
-                    create_product(Decimal('12.00'))]
413
+        products = [create_product(D('4.00')),
414
+                    create_product(D('8.00')),
415
+                    create_product(D('12.00'))]
347 416
 
348 417
         # Create range that includes the products
349 418
         range = models.Range.objects.create(name="Dummy range")
@@ -355,8 +424,8 @@ class FixedPriceBenefitTest(OfferTest):
355 424
         basket = Basket.objects.create()
356 425
         [basket.add_product(p, 2) for p in products]
357 426
 
358
-        benefit = models.FixedPriceBenefit(range=range, type="FixedPrice", value=Decimal('20.00'))
427
+        benefit = models.FixedPriceBenefit(range=range, type="FixedPrice", value=D('20.00'))
359 428
         first_discount = benefit.apply(basket, condition)
360
-        self.assertEquals(Decimal('4.00'), first_discount)
429
+        self.assertEquals(D('4.00'), first_discount)
361 430
         second_discount = benefit.apply(basket, condition)
362
-        self.assertEquals(Decimal('4.00'), second_discount)
431
+        self.assertEquals(D('4.00'), second_discount)

Загрузка…
Отмена
Сохранить