Bläddra i källkod

Consolidate shipping docs

The howto recipe on shipping has been rewritten to describe the new
changes to shipping functionality. The old shipping app reference has
been merged in too to avoid duplication.
master
David Winterbottom 11 år sedan
förälder
incheckning
c58b181885

+ 115
- 105
docs/source/howto/how_to_configure_shipping.rst Visa fil

@@ -2,152 +2,162 @@
2 2
 How to configure shipping
3 3
 =========================
4 4
 
5
-Configuring shipping is not trivial.  It generally requires creating a
6
-'shipping' app within your project where you can define your own shipping
7
-methods as well as a 'repository' class which determines when methods are
8
-available.
5
+Shipping can be very complicated.  Depending on the domain, a wide variety of
6
+shipping scenarios are found in the wild.  For instance, calculation of
7
+shipping costs can depend on:
9 8
 
10
-This recipe explains in more detail how Oscar models shipping as well as the
11
-steps involved in configuring shipping for your project.
9
+* Shipping method (e.g., standard, courier)
10
+* Shipping address
11
+* Time of day of order (e.g., if requesting next-day delivery)
12
+* Weight of items in basket
13
+* Customer type (e.g., business accounts get discounted shipping rates)
14
+* Offers and vouchers that give free or discounted shipping
12 15
 
13
-How Oscar handles shipping charges
14
-----------------------------------
16
+Further complications can arise such as:
15 17
 
16
-Oscar uses a "repository" class to manage shipping charges.  The class is used
17
-in two ways:
18
+* Only making certain shipping methods available to certain customers
19
+* Tax is only applicable in certain situations
20
+  
21
+Oscar can handle all of these shipping scenarios. 
18 22
 
19
-* **It provides a list of shipping methods available to the user.**  This is used to
20
-  generate the content for the shipping methods page of checkout, where the user
21
-  can choose a shipping method.  The methods available generally depend on the
22
-  user, the basket and the shipping address.
23
+Shipping in Oscar
24
+~~~~~~~~~~~~~~~~~
23 25
 
24
-* **It allows a shipping method to be retrieved based on a identifying code.**  When
25
-  a user selects a shipping method during checkout, it is persisted in the
26
-  session using a code.  This code is used to retrieve the chosen shipping
27
-  method when it is required.
26
+Configuring shipping charges requires overriding Oscar's core 'shipping' app
27
+and providing your own ``Repository`` class (see :doc:`/topics/customisation`) that
28
+returns your chosen shipping method instances.
28 29
 
29
-The default shipping repository `can be seen here`_.  It defaults to only
30
-providing one shipping method, which has no charge.
30
+The primary responsibility of the
31
+``Repository`` class is to provide the available shipping methods for a
32
+particular scenario. This is done via the 
33
+:func:`~oscar.apps.shipping.repository.Repository.get_shipping_methods` method,
34
+which returns the shipping methods available to the customer.
31 35
 
32
-.. note::
36
+This method is called in several places:
33 37
 
34
-    Oscar's checkout process includes a page for choosing your shipping method.
35
-    If there is only one method available for your basket then it will be chosen
36
-    automatically and the user immediately redirected to the next step.
38
+* To look up a "default" shipping method so that sample shipping charges can be
39
+  shown on the basket detail page.
37 40
 
38
-Custom shipping charges
39
------------------------
41
+* To list the available shipping methods on the checkout shipping method page. 
40 42
 
41
-In order to control shipping logic for your project, you need to define your own
42
-repository class (see :doc:`/topics/customisation`).  It normally makes
43
-sense to subclass the core ``Repository`` class and override the
44
-``get_shipping_methods`` and ``find_by_code`` methods.
43
+* To check the selected shipping method is still available when an order is
44
+  submitted.
45 45
 
46
-Here's a very simple example where all shipping costs are a fixed price,
47
-irrespective of basket and shipping address::
46
+The ``get_shipping_methods`` method takes the basket, user, shipping address
47
+and request as parameters. These can be used to provide different sets of
48
+shipping methods depending on the circumstances. For instance, you could use
49
+the shipping address to provide international shipping rates if the address is
50
+overseas.
48 51
 
49
-    # myproject/shipping/repository.py
52
+The default behaviour is to return a single free shipping method.
50 53
 
51
-    from decimal import Decimal as D
52
-    from oscar.apps.shipping import repository, methods as core_methods
54
+.. note::
53 55
 
54
-    class Repository(repository.Repository):
55
-        methods = [core_methods.FixedPrice(D('9.99'))]
56
+    Oscar's checkout process includes a page for choosing your shipping method.
57
+    If there is only one method available for your basket (as is the default)
58
+    then it will be chosen automatically and the user immediately redirected to
59
+    the next step.
56 60
 
57
-        def get_shipping_methods(self, user, basket, shipping_addr=None, **kwargs):
58
-            return self.prime_methods(basket, self.methods)
61
+Custom repositories
62
+-------------------
59 63
 
60
-        def find_by_code(self, code, basket):
61
-            for method in self.methods:
62
-                if code == method.code:
63
-                    return self.prime_method(basket, method)
64
+If the available shipping methods are the same for all customers and shipping
65
+addresses, then override the ``methods`` property of the repository:
64 66
 
65
-Note that both these methods must return 'primed' method instances, which means
66
-the basket instance has been injected into the method.  This allows the method
67
-instance to return the shipping charge directly without requiring the basket to
68
-be passed again (which is useful in templates).
67
+.. code-block:: python
69 68
 
70
-As you can see the ``get_shipping_methods`` can depend on several things:
69
+   from oscar.apps.shipping import repository
70
+   from . import methods
71 71
 
72
-* the user in question (e.g., staff get cheaper shipping rates)
73
-* the basket (e.g., shipping is charged based on the weight of the basket)
74
-* the shipping address (e.g., overseas shipping is more expensive)
72
+   class Repository(repository.Repository):
73
+       methods = (methods.Standard(), methods.Express())
75 74
 
76
-Here's a more involved example repository that has two fixed price charges::
75
+For more complex logic, override the ``get_available_shipping_methods`` method:
77 76
 
78
-    # myproject/shipping/repository.py
77
+.. code-block:: python
79 78
 
80
-    from decimal import Decimal as D
81
-    from oscar.apps.shipping import repository, methods as core_methods
79
+   from oscar.apps.shipping import repository
80
+   from . import methods
82 81
 
83
-    # We create subclasses so we can give them different codes and names
84
-    class Standard(core_methods.FixedPrice):
85
-        code = 'standard'
86
-        name = _("Standard shipping")
82
+   class Repository(repository.Repository):
87 83
 
88
-    class Express(core_methods.FixedPrice):
89
-        code = 'express'
90
-        name = _("Express shipping")
84
+       def get_available_shipping_methods(
85
+               self, basket, user=None, shipping_addr=None, 
86
+               request=None, **kwargs):
87
+           methods = (methods.Standard())
88
+           if shipping_addr and shipping.addr.country.code == 'GB':
89
+               # Express is only available in the UK
90
+               methods = (methods.Standard(), methods.Express())
91
+           return methods
91 92
 
92
-    class Repository(repository.Repository):
93
-        methods = [Standard(D('10.00')), Express(D('20.00'))]
93
+Note that the ``get_shipping_methods`` method wraps
94
+``get_available_shipping_methods`` in order to handle baskets that don't
95
+require shipping and to apply shipping discounts.
94 96
 
95
-        def get_shipping_methods(self, user, basket, shipping_addr=None, **kwargs):
96
-            return self.prime_methods(basket, self.methods)
97
+Shipping methods
98
+----------------
97 99
 
98
-        def find_by_code(self, code, basket):
99
-            for method in self.methods:
100
-                if code == method.code:
101
-                    return self.prime_method(basket, method)
100
+Shipping methods need to implement a certain API. They need to have the
101
+following properties which define the metadata about the shipping method:
102 102
 
103
-.. _`can be seen here`: https://github.com/tangentlabs/django-oscar/blob/master/oscar/apps/shipping/repository.py
103
+* ``code`` - This is used as an identifier for the shipping method and so should
104
+  be unique amongst the shipping methods available in your shop.
104 105
 
105
-Shipping methods
106
-----------------
106
+* ``name`` - The name of the shipping method. This will be visible to the
107
+  customer during checkout.
107 108
 
108
-The repository class is responsible for return shipping method instances.  Oscar
109
-defines several of these but it is easy to write your own, their interface is
110
-simple.
109
+* ``description`` - An optional description of the shipping method. This can
110
+  contain HTML.
111 111
 
112
-The base shipping method class ``oscar.apps.shipping.methods.Base`` (that
113
-all shipping methods should subclass has API:
112
+Further, each method must implement a ``calculate`` method which accepts the
113
+basket instance as a parameter and returns a ``Price`` instance.  Most shipping
114
+methods subclass
115
+:class:`~oscar.apps.shipping.methods.Base`, which stubs this API.
114 116
 
115
-.. autoclass:: oscar.apps.shipping.methods.Base
116
-    :members:
117
-    :noindex:
117
+Here's an example:
118 118
 
119
-Core shipping methods
120
-~~~~~~~~~~~~~~~~~~~~~
119
+.. code-block:: python
121 120
 
122
-The shipping methods that ship with Oscar are:
121
+   from oscar.apps.shipping import methods
122
+   from oscar.core import prices
123 123
 
124
-* ``oscar.apps.shipping.methods.Free``.  No shipping charges.
124
+   class Standard(methods.Base):
125
+       code = 'standard'
126
+       name = 'Standard shipping (free)'
125 127
 
126
-* ``oscar.apps.shipping.methods.FixedPrice``.  This simply charges a fixed price for 
127
-  shipping, irrespective of the basket contents.
128
+       def calculate(self, basket):
129
+           return prices.Price(
130
+               currency=basket.currency, 
131
+               excl_tax=D('0.00'), incl_tax=D('0.00'))
132
+
133
+Core shipping methods
134
+~~~~~~~~~~~~~~~~~~~~~
128 135
 
129
-* ``oscar.apps.shipping.methods.OfferDiscount``. This applies a discount
130
-  to an existing shipping method's charges.
136
+Oscar ships with several re-usable shipping methods which can be used as-is, or
137
+subclassed and customised:
131 138
 
132
-* ``oscar.apps.shipping.methods.TaxExclusiveOfferDiscount``. Children of ``OfferDiscount``
139
+* :class:`~oscar.apps.shipping.methods.Free` - no shipping charges
133 140
 
134
-* ``oscar.apps.shipping.methods.TaxInclusiveOfferDiscount``.  Children of ``OfferDiscount``
141
+* :class:`~oscar.apps.shipping.methods.FixedPrice` - fixed-price shipping charges.  
142
+  Example usage:
135 143
 
136
-To apply your domain logic for shipping, you will need to override
137
-the default repository class (see :doc:`/topics/customisation`) and alter
138
-the implementation of the ``get_shipping_methods`` method.  This method
139
-should return a list of "shipping method" classes already instantiated
140
-and holding a reference to the basket instance.
144
+.. code-block:: python
141 145
 
142
-Building a custom shipping method
143
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
146
+   from oscar.apps.shipping import methods
147
+   from oscar.core import prices
144 148
 
145
-At a minimum, a custom shipping method class should define a ``code`` and
146
-``name`` attribute to distinguish it from other methods.  It is also normal to
147
-override the ``basket_charge_incl_tax`` and ``basket_charge_excl_tax`` methods
148
-to implement your custom shipping charge logic.
149
+   class Standard(methods.Base):
150
+       code = 'standard'
151
+       name = 'Standard shipping'
152
+       charge_excl_tax = D('5.00')
149 153
 
150
-.. tip::
154
+   class Express(methods.Base):
155
+       code = 'express'
156
+       name = 'Express shipping'
157
+       charge_excl_tax = D('10.00')
151 158
 
152
-    Most of the shipping logic should live in the repository class, the method
153
-    instance is only responsible for returning the charge for a given basket.
159
+There is also a weight-based shipping method, 
160
+:class:`~oscar.apps.shipping.abstract_models.AbstractWeightBased`
161
+which determines a shipping charge by calculating the weight of a basket's
162
+contents and looking this up in a model-based set of weight bands.
163
+           

+ 2
- 79
docs/source/ref/apps/shipping.rst Visa fil

@@ -2,85 +2,8 @@
2 2
 Shipping
3 3
 ========
4 4
 
5
-Shipping can be very complicated.  Depending on the domain, a wide variety of shipping
6
-scenarios are found in the wild.  For instance, calculation of shipping costs can depend on:
7
-
8
-* Shipping method (e.g., standard, courier)
9
-* Shipping address
10
-* Time of day of order (e.g., if requesting next-day delivery)
11
-* Weight of items in basket
12
-* Customer type (e.g., business accounts get discounted shipping rates)
13
-* Offers and vouchers that give free or discounted shipping
14
-
15
-Further complications can arise such as:
16
-
17
-* Only making certain shipping methods available to certain customers
18
-* Tax is only applicable in certain situations
19
-  
20
-Oscar can handle all of these shipping scenarios. 
21
-
22
-Shipping in Oscar
23
------------------
24
-
25
-Shipping is handled using "method" objects which represent a means of shipping
26
-an order (e.g., "standard" or "next-day" delivery).  Each method is essentially a
27
-named calculator that takes a basket and is able to calculate the shipping
28
-costs with and without tax.  
29
-
30
-For example, you may model "standard" delivery by having a calculator object
31
-that charges a fixed price for each item in the basket.  The method object
32
-could be configured by passing the fixed price to be used for calculation.
33
-
34
-Shipping within checkout
35
-------------------------
36
-
37
-Shipping is first encountered by customers within the checkout flow, on the "shipping
38
-method" view.  
39
-
40
-It is the responsibility of this class to either:
41
-
42
-1. Offer an a set of delivery methods for the customer to choose from, displaying
43
-   the cost of each.
44
-2. If there is only one method available, to construct the appropriate shipping method
45
-   and set it within the checkout session context.
46
-
47
-The ``ShippingMethodView`` class handles this behaviour.  Its core
48
-implementation looks up a list of available shipping methods using the
49
-``oscar.shipping.repository.Repository`` class.  If there is only one, then
50
-this is written out to the session and a redirect is issued to the next step of
51
-the checkout.  If more than one, then each available method is displayed so the
52
-customer can choose.
53
-
54
-Default behaviour 
55
------------------
56
-Oscar ships with a simple model for calculating shipping based on a charge per
57
-order, and a charge per item.  This is the ``OrderAndItemLevelChargeMethod``
58
-class and is configured by setting the two charges used for the calculation.
59
-You can use this model to provide multiple methods - each identified by a code.
60
-
61
-The core ``Repository`` class will load all defined
62
-``OrderAndItemLevelChargeMethod`` models and make them available to the
63
-customer.  If none are set, then a `FreeShipping` method object will be
64
-returned.  
65
-
66
-Shipping method classes
67
------------------------
68
-
69
-Each method object must subclass ``ShippingMethod`` from
70
-``oscar.shipping.methods`` which provides the required interface. Note that the interface
71
-does not depend on the many other factors that can affect shipping (e.g., shipping address).  The
72
-way to handle this is within your "factory" method which returns available shipping methods. 
73
-
74
-Writing your own shipping method
75
---------------------------------
76
-
77
-Simple really - follow these steps:  
78
-
79
-1. Subclass ``oscar.shipping.methods.ShippingMethod`` and implement
80
-   the methods ``basket_charge_incl_tax`` and ``basket_charge_excl_tax`` for calculating shipping costs.
81
-2. Override the default ``shipping.repository.Repository`` class and implement your domain logic
82
-   for determining which shipping methods are returned based on the user, basket and shipping address
83
-   passed in.
5
+See :doc:`/howto/how_to_configure_shipping` for details on how shipping works
6
+in Oscar.
84 7
 
85 8
 Methods
86 9
 -------

+ 25
- 24
docs/source/releases/v0.8.rst Visa fil

@@ -57,18 +57,19 @@ Reworked shipping app
57 57
 
58 58
 Several parts of the shipping app have been changed. The most important is a
59 59
 change to the API of shipping methods to avoid a potential thread safety issue.
60
+Any existing Oscar sites with custom shipping methods will need to adjust them
61
+to confirm to the new API.
60 62
 
61 63
 Other changes to the shipping app include:
62 64
 
63 65
 * All shipping models now have abstract base classes, similar to
64
-  the rest of Oscar.
66
+  the rest of Oscar, allowing them to be customised in the standard way.
65 67
 
66
-* ``WeightBand.upper_limit`` is now a ``DecimalField``, just like the other
68
+* The ``WeightBand.upper_limit`` model field is now a ``DecimalField``, just like the other
67 69
   weight-related fields.
68 70
 
69 71
 * The Django admin interface for the ``WeightBased`` shipping method has been
70
-  made slightly more useful. Contributions for a dedicated dashboard app are
71
-  most welcome!
72
+  made slightly more useful. 
72 73
 
73 74
 See the 
74 75
 :ref:`backwards incompatible changes <incompatible_shipping_changes_in_0.8>` 
@@ -124,16 +125,16 @@ Backwards incompatible changes in 0.8
124 125
 
125 126
 .. _incompatible_shipping_changes_in_0.8:
126 127
 
127
-Shipping app
128
-------------
128
+Shipping
129
+--------
129 130
 
130 131
 The shipping method API has been altered to avoid potential thread-safety
131 132
 issues. Prior to v0.8, shipping methods had a ``set_basket`` method which
132
-allowed a basket instance to be assigned to the method. This was really a
133
-crutch to allow templates to have easy access to shipping charges. However, it
134
-was also a design problem as shipping methods could be instantiated at
135
-compile-time leading to a thread safety issue where multiple threads could
136
-assign a basket to the same shipping method instance.
133
+allowed a basket instance to be assigned. This was really a crutch to allow
134
+templates to have easy access to shipping charges. However, it was also a
135
+design problem as shipping methods could be instantiated at compile-time
136
+leading to a thread safety issue where multiple threads could assign a basket
137
+to the same shipping method instance.
137 138
 
138 139
 In Oscar 0.8, shipping methods are stateless services that have a method
139 140
 :func:`~oscar.apps.shipping.methods.Base.calculate` that takes a basket and
@@ -143,28 +144,27 @@ shipping charges to be accessed from templates.
143 144
 This API change does require quite a few changes as both the shipping method
144 145
 and shipping charge now need to be passed around separately:
145 146
 
147
+* Shipping methods no longer have ``charge_excl_tax``,
148
+  ``charge_incl_tax`` and ``is_tax_known`` properties.
149
+
146 150
 * The :class:`~oscar.apps.order.utils.OrderCreator` class now requires the
147 151
   ``shipping_charge`` to be passed to ``place_order``.
148 152
 
149
-* The :class:`~oscar.apps.order.utils.OrderCreator` class no longer defaults to
150
-  free shipping: a shipping method and charge has to be explicitly passed in.
151
-
152 153
 * The signature of the :class:`~oscar.apps.checkout.calculators.OrderTotalCalculator` 
153
-  class has changed to accept the shipping charge rather than a shipping
154
-  method instance.
154
+  class has changed to accept ``shipping_charge`` rather than a
155
+  ``shipping_method`` instance.
155 156
 
156 157
 * The signature of the
157 158
   :func:`~oscar.apps.checkout.session.CheckoutSessionMixin.get_order_totals` 
158
-  method has changed to accept the shipping charge rather than a shipping
159
-  method instance.
159
+  method has changed to accept the ``shipping_charge`` rather than a
160
+  ``shipping_method`` instance.
160 161
 
161
-Other potentially breaking changes to the shipping app:
162
+Other potentially breaking changes related to shipping include:
162 163
 
163
-* Shipping methods no longer have ``charge_excl_tax``,
164
-  ``charge_incl_tax`` and ``is_tax_known`` properties.
164
+* The :class:`~oscar.apps.order.utils.OrderCreator` class no longer defaults to
165
+  free shipping: a shipping method and charge have to be explicitly passed in.
165 166
 
166
-* The ``Base`` shipping method class now lives in
167
-  ``oscar.apps.shipping.methods``.
167
+* The ``Base`` shipping method class now lives in ``oscar.apps.shipping.methods``.
168 168
 
169 169
 * The ``find_by_code`` method of the shipping ``Repository`` class has been
170 170
   removed as it is no longer used. 
@@ -184,14 +184,15 @@ Other potentially breaking changes to the shipping app:
184 184
 Misc
185 185
 ----
186 186
 
187
-* The ``shipping`` app saw a few renames; please see the section above.
188 187
 * The ``oscar_calculate_scores`` command has been `rewritten`_ to use the ORM
189 188
   instead of raw SQL. That exposed a bug in the previous calculations,
190 189
   where purchases got weighed less than any other event. When you upgrade,
191 190
   your total scores will be change. If you rely on the old behaviour,
192 191
   just extend the ``Calculator`` class and adjust the weights.
192
+
193 193
 * ``Product.score`` was just duplicating ``ProductRecord.score`` and has been
194 194
   removed. Use ``Product.stats.score`` instead.
195
+
195 196
 * Oscar has child products to model tightly coupled products, and
196 197
   ``Product.recommended_products`` to model products that are loosely related
197 198
   (e.g. used for upselling). ``Product.related_products`` was a

Laddar…
Avbryt
Spara