Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 7 commits
  • 7 files changed
  • 0 comments
  • 3 contributors
2  AUTHORS.rst
Source Rendered
@@ -9,6 +9,7 @@ CONTRIBUTORS
9 9
10 10 * Adrien Lemaire
11 11 * airtonix
  12 +* Aleš Kocjančič
12 13 * Audrey Roy
13 14 * Ben Lopatin
14 15 * Benjamin Wohlwend
@@ -26,6 +27,7 @@ CONTRIBUTORS
26 27 * Per Rosengren
27 28 * Raúl Cumplido
28 29 * Rolo Mawlabaux
  30 +* Stephen Muss
29 31 * Thomas Woolford
30 32
31 33 TRANSLATORS
4 CHANGELOG.rst
Source Rendered
... ... @@ -1,6 +1,10 @@
1 1 Version NEXT
2 2 ==============
3 3
  4 +* Made Backends internationalizable, as well as the BillingShippingForm
  5 + thanks to the introduciton of a new optional backend_verbose_name attribute
  6 + to backends.
  7 +* Added a order_required decorator to fix bug #84
4 8 * Added get_product_reference method to Product (for extensibility)
5 9 * Cart object is not saved to database if it is empty (#147)
6 10 * Changed spelling mistakes in methods from `payed` to `paid` on the Order
11 shop/forms.py
@@ -2,6 +2,7 @@
2 2 """Forms for the django-shop app."""
3 3 from django import forms
4 4 from django.forms.models import modelformset_factory
  5 +from django.utils.translation import ugettext_lazy as _
5 6
6 7 from shop.backends_pool import backends_pool
7 8 from shop.models.cartmodel import CartItem
@@ -9,13 +10,12 @@
9 10
10 11 def get_shipping_backends_choices():
11 12 shipping_backends = backends_pool.get_shipping_backends_list()
12   - return tuple(
13   - [(x.url_namespace, x.backend_name) for x in shipping_backends])
  13 + return tuple([(x.url_namespace, getattr(x, 'backend_verbose_name', x.backend_name)) for x in shipping_backends])
14 14
15 15
16 16 def get_billing_backends_choices():
17 17 billing_backends = backends_pool.get_payment_backends_list()
18   - return tuple([(x.url_namespace, x.backend_name) for x in billing_backends])
  18 + return tuple([(x.url_namespace, getattr(x, 'backend_verbose_name', x.backend_name)) for x in billing_backends])
19 19
20 20
21 21 class BillingShippingForm(forms.Form):
@@ -24,9 +24,8 @@ class BillingShippingForm(forms.Form):
24 24 defined in settings.SHOP_SHIPPING_BACKENDS and
25 25 settings.SHOP_PAYMENT_BACKENDS)
26 26 """
27   - shipping_method = forms.ChoiceField(
28   - choices=get_shipping_backends_choices())
29   - payment_method = forms.ChoiceField(choices=get_billing_backends_choices())
  27 + shipping_method = forms.ChoiceField(choices=get_shipping_backends_choices(), label=_('Shipping method'))
  28 + payment_method = forms.ChoiceField(choices=get_billing_backends_choices(), label=_('Payment method'))
30 29
31 30
32 31 class CartItemModelForm(forms.ModelForm):
2  shop/payment/backends/pay_on_delivery.py
... ... @@ -1,12 +1,14 @@
1 1 # -*- coding: utf-8 -*-
2 2 from django.conf.urls.defaults import patterns, url
3 3 from django.http import HttpResponseRedirect
  4 +from django.utils.translation import ugettext_lazy as _
4 5 from shop.util.decorators import on_method, shop_login_required
5 6
6 7
7 8 class PayOnDeliveryBackend(object):
8 9
9 10 backend_name = "Pay On Delivery"
  11 + verbose_backend_name = _("Pay On Delivery")
10 12 url_namespace = "pay-on-delivery"
11 13
12 14 def __init__(self, shop):
5 shop/shipping/backends/flat_rate.py
@@ -5,8 +5,9 @@
5 5 from django.conf.urls.defaults import patterns, url
6 6 from django.shortcuts import render_to_response
7 7 from django.template import RequestContext
  8 +from django.utils.translation import ugettext_lazy as _
8 9
9   -from shop.util.decorators import on_method, shop_login_required
  10 +from shop.util.decorators import on_method, shop_login_required, order_required
10 11
11 12
12 13 class FlatRateShipping(object):
@@ -16,6 +17,7 @@ class FlatRateShipping(object):
16 17 """
17 18 url_namespace = 'flat'
18 19 backend_name = 'Flat rate'
  20 + verbose_backend_name = _('Flat rate')
19 21
20 22 def __init__(self, shop):
21 23 self.shop = shop # This is the shop reference, it allows this backend
@@ -23,6 +25,7 @@ def __init__(self, shop):
23 25 self.rate = getattr(settings, 'SHOP_SHIPPING_FLAT_RATE', '10')
24 26
25 27 @on_method(shop_login_required)
  28 + @on_method(order_required)
26 29 def view_process_order(self, request):
27 30 """
28 31 A simple (not class-based) view to process an order.
23 shop/tests/shipping.py
@@ -9,6 +9,7 @@
9 9
10 10 from shop.backends_pool import backends_pool
11 11 from shop.models.ordermodel import Order
  12 +from shop.shipping.backends.flat_rate import FlatRateShipping
12 13 from shop.shipping.api import ShippingAPI
13 14 from shop.tests.util import Mock
14 15 from shop.tests.utils.context_managers import SettingsOverride
@@ -156,6 +157,11 @@ def test_adding_shipping_costs_twice_works(self):
156 157
157 158 class FlatRateShippingTestCase(TestCase):
158 159 """Tests for ``shop.shipping.backends.flat_rate.FlatRateShipping``."""
  160 + def setUp(self):
  161 + self.backend = FlatRateShipping(shop=ShippingAPI())
  162 + self.user = User.objects.create(username="test", email="test@example.com")
  163 + self.request = Mock()
  164 + setattr(self.request, 'user', self.user)
159 165
160 166 def test_must_be_logged_in_if_setting_is_true(self):
161 167 with SettingsOverride(SHOP_FORCE_LOGIN=True):
@@ -165,3 +171,20 @@ def test_must_be_logged_in_if_setting_is_true(self):
165 171 resp = self.client.get(reverse('flat_process'))
166 172 self.assertEqual(resp.status_code, 302)
167 173 self.assertTrue('accounts/login/' in resp._headers['location'][1])
  174 +
  175 + def test_order_required_before_shipping_processed(self):
  176 + """ See issue #84 """
  177 + # Session only (no order)
  178 + response = self.client.get(reverse('flat_process'))
  179 + self.assertEqual(response.status_code, 302)
  180 +
  181 + # User logged in (no order)
  182 + view = self.backend.view_process_order(self.request)
  183 + self.assertEqual(view.get('location'), '/')
  184 +
  185 + # User logged in with order
  186 + order = Order()
  187 + setattr(order, 'user', self.user)
  188 + order.save()
  189 + view = self.backend.view_process_order(self.request)
  190 + self.assertEqual(view.get('location'), reverse('checkout_payment'))
34 shop/util/decorators.py
... ... @@ -1,8 +1,12 @@
1 1 """Decorators for the django-shop application."""
  2 +from functools import wraps
  3 +
2 4 from django.contrib.auth import REDIRECT_FIELD_NAME
3 5 from django.contrib.auth.decorators import user_passes_test
  6 +from django.http import HttpResponseRedirect
4 7
5 8 from shop.util.login_mixin import get_test_func
  9 +from shop.util.order import get_order_from_request
6 10
7 11
8 12 def on_method(function_decorator):
@@ -37,3 +41,33 @@ def shop_login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME,
37 41 if function:
38 42 return actual_decorator(function)
39 43 return actual_decorator
  44 +
  45 +
  46 +def order_required(redirect_url='/'):
  47 + """
  48 + Ensures that an order exists before carrying out any additional functions
  49 + that rely on one.
  50 +
  51 + If an order does not exist the browser will be redirected to another page
  52 + supplied in the optional keyword argument `redirect_url`.
  53 +
  54 + Usage:
  55 + @order_required
  56 + def some_view(...
  57 +
  58 + OR:
  59 + @order_required(redirect_url='/some/path/')
  60 + def some_view(...
  61 + """
  62 + if callable(redirect_url):
  63 + func = redirect_url
  64 + decorator = order_required()
  65 + return decorator(func)
  66 +
  67 + def decorator(func):
  68 + def inner(request, *args, **kwargs):
  69 + if get_order_from_request(request) is None:
  70 + return HttpResponseRedirect(redirect_url)
  71 + return func(request, *args, **kwargs)
  72 + return wraps(func)(inner)
  73 + return decorator

No commit comments for this range

Something went wrong with that request. Please try again.