Bläddra i källkod

Merge branch 'releases/0.6'

Conflicts:
	docs/source/releases/index.rst
	oscar/__init__.py
	oscar/apps/customer/wishlists/views.py
	oscar/apps/dashboard/catalogue/views.py
	oscar/apps/offer/receivers.py
	oscar/apps/order/abstract_models.py
	oscar/templates/oscar/basket/partials/basket_totals.html
	oscar/templates/oscar/catalogue/detail.html
	oscar/templates/oscar/customer/order/order_detail.html
	oscar/templates/oscar/customer/wishlists/wishlists_delete_product.html
master
David Winterbottom 12 år sedan
förälder
incheckning
4aa44c2dfd
92 ändrade filer med 33626 tillägg och 16097 borttagningar
  1. 2
    1
      .mailmap
  2. 4
    4
      docs/source/howto/how_to_integrate_payment.rst
  3. 1
    0
      docs/source/releases/index.rst
  4. 88
    0
      docs/source/releases/v0.6.1.rst
  5. 1
    2
      docs/source/releases/v0.6.rst
  6. 1
    0
      oscar/apps/analytics/__init__.py
  7. 0
    4
      oscar/apps/analytics/models.py
  8. 1
    0
      oscar/apps/catalogue/__init__.py
  9. 41
    4
      oscar/apps/catalogue/abstract_models.py
  10. 0
    2
      oscar/apps/catalogue/models.py
  11. 1
    0
      oscar/apps/catalogue/receivers.py
  12. 2
    0
      oscar/apps/customer/__init__.py
  13. 20
    0
      oscar/apps/customer/abstract_models.py
  14. 0
    28
      oscar/apps/customer/alerts/receivers.py
  15. 0
    4
      oscar/apps/customer/models.py
  16. 3
    3
      oscar/apps/customer/wishlists/views.py
  17. 1
    0
      oscar/apps/partner/__init__.py
  18. 0
    3
      oscar/apps/partner/models.py
  19. 1
    1
      oscar/apps/payment/abstract_models.py
  20. 5
    1
      oscar/apps/shipping/models.py
  21. 6
    9
      oscar/defaults.py
  22. Binär
      oscar/locale/ar/LC_MESSAGES/django.mo
  23. 516
    482
      oscar/locale/ar/LC_MESSAGES/django.po
  24. Binär
      oscar/locale/bg_BG/LC_MESSAGES/django.mo
  25. 462
    429
      oscar/locale/bg_BG/LC_MESSAGES/django.po
  26. Binär
      oscar/locale/ca/LC_MESSAGES/django.mo
  27. 486
    477
      oscar/locale/ca/LC_MESSAGES/django.po
  28. Binär
      oscar/locale/da/LC_MESSAGES/django.mo
  29. 492
    492
      oscar/locale/da/LC_MESSAGES/django.po
  30. Binär
      oscar/locale/de/LC_MESSAGES/django.mo
  31. 1152
    1010
      oscar/locale/de/LC_MESSAGES/django.po
  32. Binär
      oscar/locale/el_GR/LC_MESSAGES/django.mo
  33. 1302
    1223
      oscar/locale/el_GR/LC_MESSAGES/django.po
  34. Binär
      oscar/locale/en/LC_MESSAGES/django.mo
  35. 461
    428
      oscar/locale/en/LC_MESSAGES/django.po
  36. Binär
      oscar/locale/es/LC_MESSAGES/django.mo
  37. 496
    516
      oscar/locale/es/LC_MESSAGES/django.po
  38. Binär
      oscar/locale/eu/LC_MESSAGES/django.mo
  39. 595
    570
      oscar/locale/eu/LC_MESSAGES/django.po
  40. Binär
      oscar/locale/fa/LC_MESSAGES/django.mo
  41. 651
    627
      oscar/locale/fa/LC_MESSAGES/django.po
  42. Binär
      oscar/locale/fr/LC_MESSAGES/django.mo
  43. 546
    522
      oscar/locale/fr/LC_MESSAGES/django.po
  44. Binär
      oscar/locale/it/LC_MESSAGES/django.mo
  45. 753
    735
      oscar/locale/it/LC_MESSAGES/django.po
  46. Binär
      oscar/locale/it_IT/LC_MESSAGES/django.mo
  47. 468
    436
      oscar/locale/it_IT/LC_MESSAGES/django.po
  48. Binär
      oscar/locale/ja/LC_MESSAGES/django.mo
  49. 468
    435
      oscar/locale/ja/LC_MESSAGES/django.po
  50. Binär
      oscar/locale/ko/LC_MESSAGES/django.mo
  51. 8165
    0
      oscar/locale/ko/LC_MESSAGES/django.po
  52. Binär
      oscar/locale/nb_NO/LC_MESSAGES/django.mo
  53. 464
    433
      oscar/locale/nb_NO/LC_MESSAGES/django.po
  54. Binär
      oscar/locale/nl/LC_MESSAGES/django.mo
  55. 1150
    1089
      oscar/locale/nl/LC_MESSAGES/django.po
  56. Binär
      oscar/locale/pl/LC_MESSAGES/django.mo
  57. 576
    514
      oscar/locale/pl/LC_MESSAGES/django.po
  58. Binär
      oscar/locale/pt_BR/LC_MESSAGES/django.mo
  59. 580
    530
      oscar/locale/pt_BR/LC_MESSAGES/django.po
  60. Binär
      oscar/locale/pt_PT/LC_MESSAGES/django.mo
  61. 516
    484
      oscar/locale/pt_PT/LC_MESSAGES/django.po
  62. Binär
      oscar/locale/ro_RO/LC_MESSAGES/django.mo
  63. 462
    429
      oscar/locale/ro_RO/LC_MESSAGES/django.po
  64. Binär
      oscar/locale/ru_RU/LC_MESSAGES/django.mo
  65. 587
    524
      oscar/locale/ru_RU/LC_MESSAGES/django.po
  66. Binär
      oscar/locale/sk/LC_MESSAGES/django.mo
  67. 787
    712
      oscar/locale/sk/LC_MESSAGES/django.po
  68. Binär
      oscar/locale/sv/LC_MESSAGES/django.mo
  69. 462
    429
      oscar/locale/sv/LC_MESSAGES/django.po
  70. Binär
      oscar/locale/th_TH/LC_MESSAGES/django.mo
  71. 8166
    0
      oscar/locale/th_TH/LC_MESSAGES/django.po
  72. Binär
      oscar/locale/tr/LC_MESSAGES/django.mo
  73. 482
    458
      oscar/locale/tr/LC_MESSAGES/django.po
  74. Binär
      oscar/locale/uk/LC_MESSAGES/django.mo
  75. 571
    543
      oscar/locale/uk/LC_MESSAGES/django.po
  76. Binär
      oscar/locale/vi/LC_MESSAGES/django.mo
  77. 466
    435
      oscar/locale/vi/LC_MESSAGES/django.po
  78. Binär
      oscar/locale/zh_CN/LC_MESSAGES/django.mo
  79. 564
    544
      oscar/locale/zh_CN/LC_MESSAGES/django.po
  80. Binär
      oscar/locale/zh_HK/LC_MESSAGES/django.mo
  81. 470
    446
      oscar/locale/zh_HK/LC_MESSAGES/django.po
  82. 18
    16
      oscar/templates/oscar/basket/partials/basket_content.html
  83. 4
    10
      oscar/templates/oscar/catalogue/detail.html
  84. 39
    29
      oscar/templates/oscar/customer/order/order_detail.html
  85. 1
    2
      oscar/templates/oscar/customer/wishlists/wishlists_delete_product.html
  86. 15
    12
      oscar/templates/oscar/customer/wishlists/wishlists_detail.html
  87. 23
    7
      oscar/templates/oscar/dashboard/catalogue/product_update.html
  88. 1
    1
      oscar/templates/oscar/dashboard/reviews/review_list.html
  89. 1
    1
      oscar/templatetags/reviews_tags.py
  90. 15
    1
      tests/integration/catalogue/product_tests.py
  91. 5
    0
      tests/integration/payment/model_tests.py
  92. 10
    0
      tests/integration/shipping/model_method_tests.py

+ 2
- 1
.mailmap Visa fil

@@ -8,4 +8,5 @@ Kura <kura@deviling.net> Kura <kura@tangentlabs.co.uk>
8 8
 Mohammad Abbas <mohammad.abbas86@gmail.com> Mohammad Abbas <mohammad@mohammad-abbas.(none)> Mohammad Abbas <mohammad@mohammad-laptop.(none)>
9 9
 Oliver Randell <oliverrandell@gmail.com> OliverRandell <oliverrandell@gmail.com>
10 10
 Paweł Kowalski <bitrut@gmail.com> Paweł Kowalski <contact@pawelkowalski.info>
11
-Łukasz Lechowicz <llechowicz@gmail.com> Łukasz Lechowicz <owad@owad.(none)>
11
+Ashia Zawaduk <ashia@liftinteractive.com> Ashia <ashia@liftinteractive.com>
12
+Tuna Vargi <tuna40@gmail.com> tuna vargi <tuna40@gmail.com>

+ 4
- 4
docs/source/howto/how_to_integrate_payment.rst Visa fil

@@ -84,20 +84,20 @@ For example::
84 84
     # Subclass the core Oscar view so we can customise
85 85
     class PaymentDetailsView(views.PaymentDetailsView):
86 86
 
87
-        def handle_payment(self, order_number, total_incl_tax, **kwargs):
87
+        def handle_payment(self, order_number, total, **kwargs):
88 88
             # Talk to payment gateway.  If unsuccessful/error, raise a
89 89
             # PaymentError exception which we allow to percolate up to be caught
90 90
             # and handled by the core PaymentDetailsView.
91
-            reference = gateway.pre_auth(order_number, total_incl_tax, kwargs['bankcard'])
91
+            reference = gateway.pre_auth(order_number, total.incl_tax, kwargs['bankcard'])
92 92
 
93 93
             # Payment successful! Record payment source
94 94
             source_type, __ = models.SourceType.objects.get_or_create(
95 95
                 name="SomeGateway")
96 96
             source = models.Source(
97 97
                 source_type=source_type,
98
-                amount_allocated=total_incl_tax,
98
+                amount_allocated=total.incl_tax,
99 99
                 reference=reference)
100 100
             self.add_payment_source(source)
101 101
 
102 102
             # Record payment event
103
-            self.add_payment_event('pre-auth', total_incl_tax)
103
+            self.add_payment_event('pre-auth', total.incl_tax)

+ 1
- 0
docs/source/releases/index.rst Visa fil

@@ -12,4 +12,5 @@ Release notes for each version of Oscar published to PyPI.
12 12
     v0.5.2
13 13
     v0.5.3
14 14
     v0.6
15
+    v0.6.1
15 16
     v0.7

+ 88
- 0
docs/source/releases/v0.6.1.rst Visa fil

@@ -0,0 +1,88 @@
1
+=========================
2
+Oscar 0.6.1 release notes
3
+=========================
4
+
5
+This is Oscar 0.6.1.  It fixes one potentially serious data loss issue and a
6
+few minor bugs.
7
+
8
+Possible data loss from deleted users
9
+-------------------------------------
10
+
11
+Before this release, the foreign key from the 
12
+:class:`~oscar.apps.order.abstract_models.Order` model to the ``User`` model
13
+did not specify an ``on_delete`` behaviour.  The default is for deletes to
14
+cascade to related objects, even if the field is nullable.  Hence, deleting a
15
+user would also delete any orders they had placed.
16
+
17
+As of 0.6.1, the foreign keys to user, shipping address and billing address on
18
+the ``Order`` model specify ``on_delete=SET_NULL`` to avoid orders being
19
+deleted accidentally.
20
+
21
+See `Django's docs`_ for more info on ``on_delete`` options.
22
+
23
+Missing translations
24
+~~~~~~~~~~~~~~~~~~~~
25
+
26
+The 0.6 release failed to include several translations from Transifex due to a
27
+problem in the way we updated translation files before release.  This
28
+release rectifies that and includes the latest translation files.
29
+
30
+Known issues
31
+============
32
+
33
+* Django 1.4 only: The changes in `#1127`_ mean you explicitly need to register
34
+  a call to ``migrate_alerts_to_users`` when the ``post_save`` signal is
35
+  emitted for a ``User`` model.
36
+
37
+Bug fixes
38
+=========
39
+
40
+The following bugs were fixed:
41
+
42
+* `#1109`_ - Workaround for a bug in Bootstrap regarding the collapsing of the
43
+  navigation bar.
44
+
45
+* `#1121`_ - Added a confirmation view to removing products from wish lists
46
+  because one can't POST to it in all cases.
47
+
48
+* `#1127`_ required that the ``migrate_alerts_to_user`` function is now
49
+  explicitly called in Oscar's base User class. It previously was wired up as
50
+  a ``post_save`` signal receiver on the User model, which does not work in
51
+  Django 1.5+.
52
+
53
+* `#1128`_ - Calls to ``Source.debit`` without an ``amount`` argument were
54
+  failing as ``balance`` was being called as a method instead of a property.
55
+
56
+* `#1130`_ - Variant products were not fetching the product class instance
57
+  correctly within ``is_shipping_required``.
58
+
59
+* `#1132`_ and `#1149`_ - Rich text attributes were not supported. Should be
60
+  displayed correctly now. Also introduced hooks for adding support for e.g.
61
+  ``file`` and ``image`` types.
62
+
63
+* `#1133`_ - The order detail page for anonymous checkouts failed to render if
64
+  reviews were disabled.
65
+
66
+* `#1134`_ - Fixed a bug caused where unicode characters in child products'
67
+  titles were incorrectly handled.
68
+
69
+* `#1138`_ - Adjust the 
70
+  :class:`~oscar.apps.shipping.models.OrderAndItemCharges` shipping method to
71
+  not count lines that don't require shipping.
72
+
73
+* `#1146`_ - Various templates were adjusted to gracefully handle deleted
74
+  products.
75
+
76
+.. _`#1109`: https://github.com/tangentlabs/django-oscar/issues/1109
77
+.. _`#1121`: https://github.com/tangentlabs/django-oscar/issues/1121
78
+.. _`#1127`: https://github.com/tangentlabs/django-oscar/issues/1127
79
+.. _`#1128`: https://github.com/tangentlabs/django-oscar/issues/1128
80
+.. _`#1130`: https://github.com/tangentlabs/django-oscar/issues/1130
81
+.. _`#1132`: https://github.com/tangentlabs/django-oscar/issues/1132
82
+.. _`#1133`: https://github.com/tangentlabs/django-oscar/issues/1133
83
+.. _`#1134`: https://github.com/tangentlabs/django-oscar/issues/1134
84
+.. _`#1138`: https://github.com/tangentlabs/django-oscar/issues/1138
85
+.. _`#1146`: https://github.com/tangentlabs/django-oscar/issues/1146
86
+.. _`#1149`: https://github.com/tangentlabs/django-oscar/issues/1149
87
+.. _`Django's docs`: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.on_delete
88
+

+ 1
- 2
docs/source/releases/v0.6.rst Visa fil

@@ -94,7 +94,7 @@ fulfillment partners.
94 94
 It also allows better support for international sites as stockrecords can be
95 95
 created for partners in different countries, who sell in different currencies.
96 96
 
97
-See the `documentation on pricing and availability`_ for more details.
97
+See the :doc:`documentation on pricing and availability </topics/prices_and_availability>` for more details.
98 98
 
99 99
 .. warning::
100 100
 
@@ -102,7 +102,6 @@ See the `documentation on pricing and availability`_ for more details.
102 102
     one stockrecord per product.
103 103
 
104 104
 .. _`deprecated`: `Features deprecated in 0.6`_
105
-.. _`documentation on pricing and availability`: topics/prices_and_availability
106 105
 
107 106
 Pricing and availability
108 107
 ~~~~~~~~~~~~~~~~~~~~~~~~

+ 1
- 0
oscar/apps/analytics/__init__.py Visa fil

@@ -0,0 +1 @@
1
+from . import receivers  # noqa

+ 0
- 4
oscar/apps/analytics/models.py Visa fil

@@ -17,7 +17,3 @@ class UserProductView(AbstractUserProductView):
17 17
 
18 18
 class UserSearch(AbstractUserSearch):
19 19
     pass
20
-
21
-
22
-# Import receiver functions
23
-from oscar.apps.analytics.receivers import *  # noqa

+ 1
- 0
oscar/apps/catalogue/__init__.py Visa fil

@@ -0,0 +1 @@
1
+from . import receivers  # noqa

+ 41
- 4
oscar/apps/catalogue/abstract_models.py Visa fil

@@ -1,6 +1,8 @@
1 1
 from itertools import chain
2 2
 from datetime import datetime, date
3 3
 import logging
4
+from django.utils.html import strip_tags
5
+from django.utils.safestring import mark_safe
4 6
 import os
5 7
 import warnings
6 8
 
@@ -413,7 +415,7 @@ class AbstractProduct(models.Model):
413 415
 
414 416
     @property
415 417
     def is_shipping_required(self):
416
-        return self.product_class.requires_shipping
418
+        return self.get_product_class().requires_shipping
417 419
 
418 420
     @property
419 421
     def has_stockrecords(self):
@@ -433,8 +435,7 @@ class AbstractProduct(models.Model):
433 435
         """
434 436
         pairs = []
435 437
         for value in self.attribute_values.select_related().all():
436
-            pairs.append("%s: %s" % (value.attribute.name,
437
-                                     value.value))
438
+            pairs.append(value.summary())
438 439
         return ", ".join(pairs)
439 440
 
440 441
     # Deprecated stockrecord methods
@@ -921,7 +922,43 @@ class AbstractProductAttributeValue(models.Model):
921 922
         verbose_name_plural = _('Product Attribute Values')
922 923
 
923 924
     def __unicode__(self):
924
-        return u"%s: %s" % (self.attribute.name, self.value)
925
+        return self.summary()
926
+
927
+    def summary(self):
928
+        """
929
+        Gets a string representation of both the attribute and it's value,
930
+        used e.g in product summaries.
931
+        """
932
+        return u"%s: %s" % (self.attribute.name, self.value_as_text)
933
+
934
+    @property
935
+    def value_as_text(self):
936
+        """
937
+        Returns a string representation of the attribute's value. To customise
938
+        e.g. image attribute values, declare a _image_as_text property and
939
+        return something appropriate.
940
+        """
941
+        property_name = '_%s_as_text' % self.attribute.type
942
+        return getattr(self, property_name, self.value)
943
+
944
+    @property
945
+    def _richtext_as_text(self):
946
+        return strip_tags(self.value)
947
+
948
+    @property
949
+    def value_as_html(self):
950
+        """
951
+        Returns a HTML representation of the attribute's value. To customise
952
+        e.g. image attribute values, declare a _image_as_html property and
953
+        return e.g. an <img> tag.
954
+        Defaults to the _as_text representation.
955
+        """
956
+        property_name = '_%s_as_html' % self.attribute.type
957
+        return getattr(self, property_name, self.value_as_text)
958
+
959
+    @property
960
+    def _richtext_as_html(self):
961
+        return mark_safe(self.value)
925 962
 
926 963
 
927 964
 class AbstractAttributeOptionGroup(models.Model):

+ 0
- 2
oscar/apps/catalogue/models.py Visa fil

@@ -62,5 +62,3 @@ class Option(AbstractOption):
62 62
 
63 63
 class ProductImage(AbstractProductImage):
64 64
     pass
65
-
66
-from .receivers import *  # noqa

+ 1
- 0
oscar/apps/catalogue/receivers.py Visa fil

@@ -26,6 +26,7 @@ def delete_image_files(sender, instance, **kwargs):
26 26
             except ThumbnailError:
27 27
                 pass
28 28
 
29
+
29 30
 # connect for all models with ImageFields - add as needed
30 31
 models_with_images = [ProductImage, Category]
31 32
 for sender in models_with_images:

+ 2
- 0
oscar/apps/customer/__init__.py Visa fil

@@ -0,0 +1,2 @@
1
+from oscar.apps.customer.history import *  # noqa
2
+from oscar.apps.customer.alerts.receivers import *  # noqa

+ 20
- 0
oscar/apps/customer/abstract_models.py Visa fil

@@ -13,7 +13,10 @@ from django.utils.translation import ugettext_lazy as _
13 13
 from oscar.apps.customer.managers import CommunicationTypeManager
14 14
 from oscar.core.compat import AUTH_USER_MODEL
15 15
 
16
+
16 17
 if hasattr(auth_models, 'BaseUserManager'):
18
+    ProductAlert = models.get_model('customer', 'ProductAlert')
19
+
17 20
     # Only define custom UserModel when Django >= 1.5
18 21
     class UserManager(auth_models.BaseUserManager):
19 22
 
@@ -83,6 +86,23 @@ if hasattr(auth_models, 'BaseUserManager'):
83 86
         def get_short_name(self):
84 87
             return self.first_name
85 88
 
89
+        def _migrate_alerts_to_user(self):
90
+            """
91
+            Transfer any active alerts linked to a user's email address to the
92
+            newly registered user.
93
+            """
94
+            alerts = ProductAlert.objects.filter(
95
+                email=self.email, status=ProductAlert.ACTIVE)
96
+            alerts.update(user=self, key=None, email=None)
97
+
98
+        def save(self, *args, **kwargs):
99
+            super(AbstractUser, self).save(*args, **kwargs)
100
+            # Migrate any "anonymous" product alerts to the registered user
101
+            # Ideally, this would be done via a post-save signal. But we can't
102
+            # use get_user_model to wire up signals to custom user models
103
+            # see Oscar ticket #1127, Django ticket #19218
104
+            self._migrate_alerts_to_user()
105
+
86 106
 
87 107
 class AbstractEmail(models.Model):
88 108
     """

+ 0
- 28
oscar/apps/customer/alerts/receivers.py Visa fil

@@ -1,12 +1,6 @@
1 1
 from django.conf import settings
2 2
 from django.db.models import get_model
3 3
 from django.db.models.signals import post_save
4
-from django.db import connection
5
-
6
-from oscar.core.compat import get_user_model
7
-
8
-
9
-User = get_user_model()
10 4
 
11 5
 
12 6
 def send_product_alerts(sender, instance, created, **kwargs):
@@ -16,28 +10,6 @@ def send_product_alerts(sender, instance, created, **kwargs):
16 10
     utils.send_product_alerts(instance.product)
17 11
 
18 12
 
19
-def migrate_alerts_to_user(sender, instance, created, **kwargs):
20
-    """
21
-    Transfer any active alerts linked to a user's email address to the newly
22
-    registered user.
23
-    """
24
-    if not created:
25
-        return
26
-    ProductAlert = get_model('customer', 'ProductAlert')
27
-
28
-    # This signal will be raised when creating a superuser as part of syncdb,
29
-    # at which point only a subset of tables will be created.  Thus, we test if
30
-    # the alert table exists before trying to exercise the ORM.
31
-    table = ProductAlert._meta.db_table
32
-    if table in connection.introspection.table_names():
33
-        alerts = ProductAlert.objects.filter(
34
-            email=instance.email, status=ProductAlert.ACTIVE)
35
-        alerts.update(user=instance, key=None, email=None)
36
-
37
-
38
-post_save.connect(migrate_alerts_to_user, sender=User)
39
-
40
-
41 13
 if settings.OSCAR_EAGER_ALERTS:
42 14
     StockRecord = get_model('partner', 'StockRecord')
43 15
     post_save.connect(send_product_alerts, sender=StockRecord)

+ 0
- 4
oscar/apps/customer/models.py Visa fil

@@ -15,7 +15,3 @@ class Notification(abstract_models.AbstractNotification):
15 15
 
16 16
 class ProductAlert(abstract_models.AbstractProductAlert):
17 17
     pass
18
-
19
-
20
-from oscar.apps.customer.history import *  # noqa
21
-from oscar.apps.customer.alerts.receivers import *  # noqa

+ 3
- 3
oscar/apps/customer/wishlists/views.py Visa fil

@@ -265,7 +265,7 @@ class WishListRemoveProduct(LineMixin, PageTitleMixin, DeleteView):
265 265
     active_tab = "wishlists"
266 266
 
267 267
     def get_page_title(self):
268
-        return _(u'Remove %s') % self.object.product.get_title()
268
+        return _(u'Remove %s') % self.object.get_title()
269 269
 
270 270
     def get_object(self, queryset=None):
271 271
         self.fetch_line(
@@ -281,14 +281,14 @@ class WishListRemoveProduct(LineMixin, PageTitleMixin, DeleteView):
281 281
 
282 282
     def get_success_url(self):
283 283
         msg = _("'%(title)s' was removed from your '%(name)s' wish list") % {
284
-            'title': self.product.get_title(),
284
+            'title': self.line.get_title(),
285 285
             'name': self.wishlist.name}
286 286
         messages.success(self.request, msg)
287 287
 
288 288
         # We post directly to this view on product pages; and should send the
289 289
         # user back there if that was the case
290 290
         referrer = self.request.META.get('HTTP_REFERER', '')
291
-        if self.product.get_absolute_url() in referrer:
291
+        if self.product and self.product.get_absolute_url() in referrer:
292 292
             return referrer
293 293
         else:
294 294
             return reverse(

+ 1
- 0
oscar/apps/partner/__init__.py Visa fil

@@ -0,0 +1 @@
1
+from oscar.apps.partner.receivers import *  # noqa

+ 0
- 3
oscar/apps/partner/models.py Visa fil

@@ -17,6 +17,3 @@ class StockRecord(AbstractStockRecord):
17 17
 
18 18
 class StockAlert(AbstractStockAlert):
19 19
     pass
20
-
21
-
22
-from oscar.apps.partner.receivers import *  # noqa

+ 1
- 1
oscar/apps/payment/abstract_models.py Visa fil

@@ -152,7 +152,7 @@ class AbstractSource(models.Model):
152 152
         Convenience method for recording debits against this source
153 153
         """
154 154
         if amount is None:
155
-            amount = self.balance()
155
+            amount = self.balance
156 156
         self.amount_debited += amount
157 157
         self.save()
158 158
         self._create_transaction(

+ 5
- 1
oscar/apps/shipping/models.py Visa fil

@@ -78,7 +78,8 @@ class OrderAndItemCharges(ShippingMethod):
78 78
 
79 79
         charge = self.price_per_order
80 80
         for line in self._basket.lines.all():
81
-            charge += line.quantity * self.price_per_item
81
+            if line.product.is_shipping_required:
82
+                charge += line.quantity * self.price_per_item
82 83
         return charge
83 84
 
84 85
     @property
@@ -115,6 +116,9 @@ class WeightBased(ShippingMethod):
115 116
 
116 117
     @property
117 118
     def charge_incl_tax(self):
119
+        # Note, when weighing the basket, we don't check whether the item
120
+        # requires shipping or not.  It is assumed that if something has a
121
+        # weight, then it requires shipping.
118 122
         scales = Scales(attribute_code=self.weight_attribute,
119 123
                         default_weight=self.default_weight)
120 124
         weight = scales.weigh_basket(self._basket)

+ 6
- 9
oscar/defaults.py Visa fil

@@ -61,15 +61,12 @@ OSCAR_MODERATE_REVIEWS = False
61 61
 # Accounts
62 62
 OSCAR_ACCOUNTS_REDIRECT_URL = 'customer:profile-view'
63 63
 
64
-# This enables sending alert notifications/emails
65
-# instantly when products get back in stock
66
-# by listening to stock record update signals
67
-# this might impact performace for large numbers
68
-# stock record updates.
69
-# Alternatively, the management command
70
-# ``oscar_send_alerts`` can be used to
71
-# run periodically, e.g. as a cronjob. In this case
72
-# instant alerts should be disabled.
64
+# This enables sending alert notifications/emails instantly when products get
65
+# back in stock by listening to stock record update signals.
66
+# This might impact performance for large numbers of stock record updates.
67
+# Alternatively, the management command ``oscar_send_alerts`` can be used to
68
+# run periodically, e.g. as a cron job. In this case eager alerts should be
69
+# disabled.
73 70
 OSCAR_EAGER_ALERTS = True
74 71
 
75 72
 # Registration

Binär
oscar/locale/ar/LC_MESSAGES/django.mo Visa fil


+ 516
- 482
oscar/locale/ar/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/bg_BG/LC_MESSAGES/django.mo Visa fil


+ 462
- 429
oscar/locale/bg_BG/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/ca/LC_MESSAGES/django.mo Visa fil


+ 486
- 477
oscar/locale/ca/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/da/LC_MESSAGES/django.mo Visa fil


+ 492
- 492
oscar/locale/da/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/de/LC_MESSAGES/django.mo Visa fil


+ 1152
- 1010
oscar/locale/de/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/el_GR/LC_MESSAGES/django.mo Visa fil


+ 1302
- 1223
oscar/locale/el_GR/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/en/LC_MESSAGES/django.mo Visa fil


+ 461
- 428
oscar/locale/en/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/es/LC_MESSAGES/django.mo Visa fil


+ 496
- 516
oscar/locale/es/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/eu/LC_MESSAGES/django.mo Visa fil


+ 595
- 570
oscar/locale/eu/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/fa/LC_MESSAGES/django.mo Visa fil


+ 651
- 627
oscar/locale/fa/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/fr/LC_MESSAGES/django.mo Visa fil


+ 546
- 522
oscar/locale/fr/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/it/LC_MESSAGES/django.mo Visa fil


+ 753
- 735
oscar/locale/it/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/it_IT/LC_MESSAGES/django.mo Visa fil


+ 468
- 436
oscar/locale/it_IT/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/ja/LC_MESSAGES/django.mo Visa fil


+ 468
- 435
oscar/locale/ja/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/ko/LC_MESSAGES/django.mo Visa fil


+ 8165
- 0
oscar/locale/ko/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/nb_NO/LC_MESSAGES/django.mo Visa fil


+ 464
- 433
oscar/locale/nb_NO/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/nl/LC_MESSAGES/django.mo Visa fil


+ 1150
- 1089
oscar/locale/nl/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/pl/LC_MESSAGES/django.mo Visa fil


+ 576
- 514
oscar/locale/pl/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/pt_BR/LC_MESSAGES/django.mo Visa fil


+ 580
- 530
oscar/locale/pt_BR/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/pt_PT/LC_MESSAGES/django.mo Visa fil


+ 516
- 484
oscar/locale/pt_PT/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/ro_RO/LC_MESSAGES/django.mo Visa fil


+ 462
- 429
oscar/locale/ro_RO/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/ru_RU/LC_MESSAGES/django.mo Visa fil


+ 587
- 524
oscar/locale/ru_RU/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/sk/LC_MESSAGES/django.mo Visa fil


+ 787
- 712
oscar/locale/sk/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/sv/LC_MESSAGES/django.mo Visa fil


+ 462
- 429
oscar/locale/sv/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/th_TH/LC_MESSAGES/django.mo Visa fil


+ 8166
- 0
oscar/locale/th_TH/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/tr/LC_MESSAGES/django.mo Visa fil


+ 482
- 458
oscar/locale/tr/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/uk/LC_MESSAGES/django.mo Visa fil


+ 571
- 543
oscar/locale/uk/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/vi/LC_MESSAGES/django.mo Visa fil


+ 466
- 435
oscar/locale/vi/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/zh_CN/LC_MESSAGES/django.mo Visa fil


+ 564
- 544
oscar/locale/zh_CN/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Binär
oscar/locale/zh_HK/LC_MESSAGES/django.mo Visa fil


+ 470
- 446
oscar/locale/zh_HK/LC_MESSAGES/django.po
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 18
- 16
oscar/templates/oscar/basket/partials/basket_content.html Visa fil

@@ -27,8 +27,8 @@
27 27
         <div class="basket-title hidden-phone">
28 28
             <div class="row-fluid">
29 29
                 <h4 class="span6">{% trans "Items to buy now" %}</h4>
30
-                <h4 class="span2">{% trans "Quantity" %}</h4>
31
-                <h4 class="span2 align-right">{% trans "Price" %}</h4>
30
+                <h4 class="span3">{% trans "Quantity" %}</h4>
31
+                <h4 class="span1 align-right">{% trans "Price" %}</h4>
32 32
                 <h4 class="span2 align-right">{% trans "Total" %}</h4>
33 33
             </div>
34 34
         </div>
@@ -58,26 +58,28 @@
58 58
                                 <h4><a href="{{ product.get_absolute_url }}">{{ line.description }}</a></h4>
59 59
                                 <p class="availability {{ session.availability.code }}">{{ session.availability.message }}</p>
60 60
                             </div>
61
-                            <div class="span2">
61
+                            <div class="span3">
62 62
                                 <div class="checkout-quantity control-group {% if form.errors %}error{% endif %}">
63 63
                                     {{ form.quantity }}
64 64
                                     <button class="btn" type="submit">{% trans "Update" %}</button>
65
-                                    <a href="#" data-id="{{ forloop.counter0 }}" data-behaviours="remove" class="inline">{% trans "Remove" %}</a>
66
-                                    {% if request.user.is_authenticated %}
67
-                                        | <a href="#" data-id="{{ forloop.counter0 }}" data-behaviours="save" class="inline">{% trans "Save for later" %}</a>
68
-                                    {% endif %}
69
-                                    <div style="display:none">
70
-                                        {{ form.save_for_later }}
71
-                                        {{ form.DELETE }}
72
-                                    </div>
73
-                                    {% for field_errors in form.errors.values %}
74
-                                        {% for error in field_errors %}
75
-                                            <span class="help-block"><i class="icon-exclamation-sign"></i> {{ error }}</span>
65
+                                    <p>
66
+                                        <a href="#" data-id="{{ forloop.counter0 }}" data-behaviours="remove" class="inline">{% trans "Remove" %}</a>
67
+                                        {% if request.user.is_authenticated %}
68
+                                            | <a href="#" data-id="{{ forloop.counter0 }}" data-behaviours="save" class="inline">{% trans "Save for later" %}</a>
69
+                                        {% endif %}
70
+                                        <div style="display:none">
71
+                                            {{ form.save_for_later }}
72
+                                            {{ form.DELETE }}
73
+                                        </div>
74
+                                        {% for field_errors in form.errors.values %}
75
+                                            {% for error in field_errors %}
76
+                                                <span class="help-block"><i class="icon-exclamation-sign"></i> {{ error }}</span>
77
+                                            {% endfor %}
76 78
                                         {% endfor %}
77
-                                    {% endfor %}
79
+                                    </p>
78 80
                                 </div>
79 81
                             </div>
80
-                            <div class="span2">
82
+                            <div class="span1">
81 83
                                 <p class="price_color align-right">
82 84
                                     {% if line.is_tax_known %}
83 85
                                         {{ line.unit_price_incl_tax|currency:line.price_currency }}

+ 4
- 10
oscar/templates/oscar/catalogue/detail.html Visa fil

@@ -155,16 +155,10 @@
155 155
             </tr>
156 156
         {% endif %}
157 157
         {% for av in product.attribute_values.all %}
158
-        <tr>
159
-            <th>{{ av.attribute.name }}</th>
160
-            <td>
161
-                {% if av.attribute.type == "richtext" %}
162
-                    {{ av.value|safe }}
163
-                {% else %}
164
-                    {{ av.value }}
165
-                {% endif %}
166
-            </td>
167
-        </tr>
158
+            <tr>
159
+                <th>{{ av.attribute.name }}</th>
160
+                <td>{{ av.value_as_html }}</td>
161
+            </tr>
168 162
         {% endfor %}
169 163
         {% iffeature "reviews" %}
170 164
             <tr>

+ 39
- 29
oscar/templates/oscar/customer/order/order_detail.html Visa fil

@@ -26,31 +26,41 @@
26 26
         </thead>
27 27
         <tbody>
28 28
             {% for line in order.lines.all %}
29
-            <tr>
30
-                <td>
31
-                    <p><a href="{{ line.product.get_absolute_url }}">{{ line.description }}</a></p>
32
-                    {% iffeature "reviews" %}
33
-                        {% if line.product|is_review_permitted:user %}
34
-                            <a class="btn" href="{% url 'catalogue:reviews-add' product_slug=line.product.slug product_pk=line.product.id %}">{% trans 'Write a review' %}</a>
35
-                        {% endif %}
36
-                    {% endiffeature %}
37
-                </td>
38
-                <td>{{ line.est_dispatch_date|default:"-" }}</td>
39
-                <td>{{ line.quantity }}</td>
40
-                <td>{{ line.line_price_before_discounts_excl_tax|currency:order.currency }}</td>
41
-                <td>{{ line.line_price_before_discounts_incl_tax|currency:order.currency }}</td>
42
-                <td width="90">
43
-                    {% if line.product %}
44
-                    <form id="line_form_{{ line.id }}" action="{% url 'customer:order-line' order_number=order.number line_id=line.id %}" method="POST">
45
-                        {% csrf_token %}
46
-                        <input type="hidden" name="action" value="reorder" />
47
-                        <button class="btn btn-success" type="submit">{% trans 'Re-order' %}</button>
48
-                    </form>
49
-                    {% else %}
50
-                        {% trans 'Not available anymore' %}
51
-                    {% endif %}
52
-                </td>
53
-            </tr>
29
+                {% with product=line.product %}
30
+                    <tr>
31
+                        <td>
32
+                            {% if product %}
33
+                                <p>
34
+                                    <a href="{{ product.get_absolute_url }}">{{ line.description }}</a>
35
+                                </p>
36
+                                {% iffeature "reviews" %}
37
+                                    {% if product|is_review_permitted:user %}
38
+                                        <a class="btn" href="{% url 'catalogue:reviews-add' product_slug=product.slug product_pk=product.id %}">{% trans 'Write a review' %}</a>
39
+                                    {% endif %}
40
+                                {% endiffeature %}
41
+                            {% else %}
42
+                                <p>
43
+                                    {{ line.description }}
44
+                                </p>
45
+                            {% endif %}
46
+                        </td>
47
+                        <td>{{ line.est_dispatch_date|default:"-" }}</td>
48
+                        <td>{{ line.quantity }}</td>
49
+                        <td>{{ line.line_price_before_discounts_excl_tax|currency:order.currency }}</td>
50
+                        <td>{{ line.line_price_before_discounts_incl_tax|currency:order.currency }}</td>
51
+                        <td width="90">
52
+                            {% if product %}
53
+                                <form id="line_form_{{ line.id }}" action="{% url 'customer:order-line' order_number=order.number line_id=line.id %}" method="POST">
54
+                                    {% csrf_token %}
55
+                                    <input type="hidden" name="action" value="reorder" />
56
+                                    <button class="btn btn-success" type="submit">{% trans 'Re-order' %}</button>
57
+                                </form>
58
+                            {% else %}
59
+                                {% trans 'Not available anymore' %}
60
+                            {% endif %}
61
+                        </td>
62
+                    </tr>
63
+                {% endwith %}
54 64
             {% endfor %}
55 65
 
56 66
             {% with discounts=order.basket_discounts %}
@@ -136,11 +146,11 @@
136 146
 
137 147
     <h3>{% trans 'Shipping Method' %}</h3>
138 148
     <p>{{ order.shipping_method }}</p>
139
-    
149
+
140 150
     <hr>
141 151
 
142 152
     <h3>{% trans 'Shipping Address' %}</h3>
143
-    
153
+
144 154
     <table class="table table-striped table-bordered">
145 155
         <tr>
146 156
             <th>{% trans 'Address:' %}</th>
@@ -150,8 +160,8 @@
150 160
         <tr>
151 161
             <td>
152 162
                 <p>{% for field in order.shipping_address.active_address_fields %}
153
-                {{ field }}<br/>
154
-                {% endfor %}</p>
163
+                        {{ field }}<br/>
164
+                    {% endfor %}</p>
155 165
             </td>
156 166
             <td><p>{{ order.shipping_address.phone_number }}</p></td>
157 167
             <td><p>{{ order.shipping_address.notes }}</p></td>

+ 1
- 2
oscar/templates/oscar/customer/wishlists/wishlists_delete_product.html Visa fil

@@ -34,8 +34,7 @@
34 34
         </p>
35 35
 
36 36
         <div class="form-actions">
37
-            <button type="submit" class="btn btn-large">{% trans 'Remove' %}</button> {% trans 'or' %} <a href="{{ wishlist.get_absolute_url }}">{% trans 'cancel' %}</a>
37
+            <button type="submit" class="btn btn-large btn-danger">{% trans 'Remove' %}</button> {% trans 'or' %} <a href="{{ wishlist.get_absolute_url }}">{% trans 'cancel' %}</a>
38 38
         </div>
39 39
     </form>
40 40
 {% endblock tabcontent %}
41
-

+ 15
- 12
oscar/templates/oscar/customer/wishlists/wishlists_detail.html Visa fil

@@ -37,21 +37,24 @@
37 37
                     </tr>
38 38
 
39 39
                     {% for form in form %}
40
-                        {% with line=form.instance %}
40
+                        {% with line=form.instance product=line.instance.product %}
41 41
                             <tr>
42 42
                                 <td>
43
-                                    {% with image=form.instance.product.primary_image %}
44
-                                        {% thumbnail image.original "100x100" upscale=False as thumb %}
45
-                                            <a href="{{ form.instance.product.get_absolute_url }}">
46
-                                                <img class="thumbnail" src="{{ thumb.url }}" alt="{{ form.instance.product.get_title }}" />
47
-                                            </a>
48
-                                        {% endthumbnail %}
49
-                                    {% endwith %}
43
+                                    {% if product %}
44
+                                        {% with image=product.primary_image %}
45
+                                            {% thumbnail image.original "100x100" upscale=False as thumb %}
46
+                                                <a href="{{ product.get_absolute_url }}">
47
+                                                    <img class="thumbnail" src="{{ thumb.url }}" alt="{{ product.get_title }}" />
48
+                                                </a>
49
+                                            {% endthumbnail %}
50
+                                        {% endwith %}
51
+                                    {% endif %}
50 52
                                 </td>
51 53
                                 <td>
52
-                                    <a href="{{ line.product.get_absolute_url }}">{{ line.get_title }}</a>
53
-                                    {% if not line.product %}
54
-                                        <em>({% trans 'Not available anymore' %})</em>
54
+                                    {% if product %}
55
+                                        <a href="{{ line.product.get_absolute_url }}">{{ line.get_title }}</a>
56
+                                    {% else %}
57
+                                        <em>{{ line.get_title }} ({% trans 'Not available anymore' %})</em>
55 58
                                     {% endif %}
56 59
                                 </td>
57 60
                                 <td>
@@ -66,7 +69,7 @@
66 69
                                 </td>
67 70
                                 <td>
68 71
                                     <div class="btn-group">
69
-                                        {% if line.product %}
72
+                                        {% if product %}
70 73
                                             <a class="btn" href="{{ line.product.get_absolute_url }}">{% trans 'View product' %}</a>
71 74
                                             <button class="btn dropdown-toggle" data-toggle="dropdown">
72 75
                                                 <span class="caret"></span>

+ 23
- 7
oscar/templates/oscar/dashboard/catalogue/product_update.html Visa fil

@@ -3,6 +3,7 @@
3 3
 {% load i18n %}
4 4
 {% load thumbnail %}
5 5
 {% load staticfiles %}
6
+{% load form_tags %}
6 7
 
7 8
 
8 9
 {% block body_class %}{{ block.super }} create-page catalogue{% endblock %}
@@ -146,14 +147,29 @@
146 147
                             <div class="well product-details">
147 148
                                 {% block product_details_content %}
148 149
                                     <span class="help-block">{{ form.non_field_errors }}</span>
149
-                                    {% for field in form %}
150
-                                        {% if field.is_hidden %}
150
+                                        {% for field in form.hidden_fields %}
151 151
                                             {{ field }}
152
-                                        {% endif %}
153
-                                        {% if 'attr' not in field.id_for_label %}
154
-                                            {% include "partials/form_field.html" with field=field %}
155
-                                        {% endif %}
156
-                                    {% endfor %}
152
+                                        {% endfor %}
153
+
154
+                                        {% for field in form.visible_fields %}
155
+                                            {% if 'attr' not in field.id_for_label %}
156
+                                                {% comment %}
157
+                                                    Make the field widget type available to templates so we can mark-up
158
+                                                    checkboxes differently to other widgets.
159
+                                                {% endcomment %}
160
+                                                {% annotate_form_field field %}
161
+
162
+                                                {% comment %}
163
+                                                    We use a separate template for each field so that forms can be rendered
164
+                                                    field-by-field easily #}
165
+                                                {% endcomment %}
166
+                                                {% if field.widget_type == 'CheckboxInput' %}
167
+                                                    {% include 'partials/form_field_checkbox.html' with field=field %}
168
+                                                {% else %}
169
+                                                    {% include 'partials/form_field.html' with field=field %}
170
+                                                {% endif %}
171
+                                            {% endif %}
172
+                                        {% endfor %}
157 173
 
158 174
                                     {% with parent=product.parent %}
159 175
                                         {% if parent %}

+ 1
- 1
oscar/templates/oscar/dashboard/reviews/review_list.html Visa fil

@@ -107,7 +107,7 @@
107 107
                                                 <span class="caret"></span>
108 108
                                             </a>
109 109
                                             <ul class="dropdown-menu pull-right">
110
-                                                {% if review.title %}
110
+                                                {% if review.title and review.product %}
111 111
                                                     <li><a href="{% url 'catalogue:reviews-detail' product_slug=review.product.slug product_pk=review.product.id pk=review.id %}">{% trans "View on site" %}</a></li>
112 112
                                                 {% endif %}
113 113
                                                 <li><a href="{% url 'dashboard:reviews-update' pk=review.id %}">{% trans "Edit" %}</a></li>

+ 1
- 1
oscar/templatetags/reviews_tags.py Visa fil

@@ -35,4 +35,4 @@ def may_vote(review, user):
35 35
 
36 36
 @register.filter
37 37
 def is_review_permitted(product, user):
38
-    return product.is_review_permitted(user)
38
+    return product and product.is_review_permitted(user)

+ 15
- 1
tests/integration/catalogue/product_tests.py Visa fil

@@ -10,7 +10,8 @@ from oscar.apps.catalogue.models import (Product, ProductClass,
10 10
 class ProductTests(TestCase):
11 11
 
12 12
     def setUp(self):
13
-        self.product_class,_ = ProductClass.objects.get_or_create(name='Clothing')
13
+        self.product_class, _ = ProductClass.objects.get_or_create(
14
+            name='Clothing')
14 15
 
15 16
 
16 17
 class ProductCreationTests(ProductTests):
@@ -61,6 +62,19 @@ class VariantProductTests(ProductTests):
61 62
         self.assertEquals("Clothing", p.get_product_class().name)
62 63
 
63 64
 
65
+class TestAVariant(TestCase):
66
+
67
+    def setUp(self):
68
+        clothing = ProductClass.objects.create(
69
+            name='Clothing', requires_shipping=True)
70
+        self.parent = clothing.products.create(
71
+            title="Parent")
72
+        self.variant = self.parent.variants.create()
73
+
74
+    def test_delegates_requires_shipping_logic(self):
75
+        self.assertTrue(self.variant.is_shipping_required)
76
+
77
+
64 78
 class ProductAttributeCreationTests(TestCase):
65 79
 
66 80
     def setUp(self):

+ 5
- 0
tests/integration/payment/model_tests.py Visa fil

@@ -22,6 +22,11 @@ class TestAPaymentSource(TestCase):
22 22
         self.source.allocate(D('100.00'))
23 23
         self.source.debit(D('80.00'))
24 24
 
25
+    def test_full_debit_doesnt_error(self):
26
+        self.source.allocate(D('100.00'))
27
+        self.source.debit()
28
+        self.assertEquals(D('0.00'), self.source.balance)
29
+
25 30
     def test_refund_doesnt_error(self):
26 31
         self.source.allocate(D('100.00'))
27 32
         self.source.debit(D('80.00'))

+ 10
- 0
tests/integration/shipping/model_method_tests.py Visa fil

@@ -32,6 +32,16 @@ class OrderAndItemChargesTests(TestCase):
32 32
         self.assertEquals(D('5.00') + D('1.00'),
33 33
                           self.method.charge_incl_tax)
34 34
 
35
+    def test_single_item_basket_that_doesnt_require_shipping(self):
36
+        # Create a product that doesn't require shipping
37
+        record = factories.create_stockrecord()
38
+        product = record.product
39
+        product.product_class.requires_shipping = False
40
+        product.product_class.save()
41
+
42
+        self.basket.add_product(record.product)
43
+        self.assertEquals(D('5.00'), self.method.charge_incl_tax)
44
+
35 45
     def test_multi_item_basket(self):
36 46
         record = factories.create_stockrecord()
37 47
         self.basket.add_product(record.product, 7)

Laddar…
Avbryt
Spara