Shipping can be very complicated. Depending on the domain, a wide variety of shipping scenarios are found in the wild. For instance, calculation of shipping costs can depend on:
- Shipping method (e.g., standard, courier)
- Shipping address
- Time of day of order (e.g., if requesting next-day delivery)
- Weight of items in basket
- Customer type (e.g., business accounts get discounted shipping rates)
- Offers and vouchers that give free or discounted shipping
Further complications can arise such as:
- Only making certain shipping methods available to certain customers
- Tax is only applicable in certain situations
Oscar can handle all of these shipping scenarios.
Shipping is handled using "method" objects which represent a means of shipping an order (e.g., "standard" or "next-day" delivery). Each method is essentially a named calculator that takes a basket and is able to calculate the shipping costs with and without tax.
For example, you may model "standard" delivery by having a calculator object that charges a fixed price for each item in the basket. The method object could be configured by passing the fixed price to be used for calculation.
Shipping is first encountered by customers within the checkout flow, on the "shipping method" view.
It is the responsibility of this class to either:
- Offer an a set of delivery methods for the customer to choose from, displaying the cost of each.
- If there is only one method available, to construct the appropriate shipping method and set it within the checkout session context.
The ShippingMethodView
class handles this behaviour. Its core implementation looks up a list of available shipping methods using the oscar.shipping.repository.Repository
class. If there is only one, then this is written out to the session and a redirect is issued to the next step of the checkout. If more than one, then each available method is displayed so the customer can choose.
Oscar ships with a simple model for calculating shipping based on a charge per order, and a charge per item. This is the OrderAndItemLevelChargeMethod
class and is configured by setting the two charges used for the calculation. You can use this model to provide multiple methods - each identified by a code.
The core Repository
class will load all defined OrderAndItemLevelChargeMethod
models and make them available to the customer. If none are set, then a FreeShipping method object will be returned.
Each method object must subclass ShippingMethod
from oscar.shipping.methods
which provides the required interface. Note that the interface does not depend on the many other factors that can affect shipping (e.g., shipping address). The way to handle this is within your "factory" method which returns available shipping methods.
Simple really - follow these steps:
- Subclass
oscar.shipping.methods.ShippingMethod
and implement the methodsbasket_charge_incl_tax
andbasket_charge_excl_tax
for calculating shipping costs. - Override the default
shipping.repository.Repository
class and implement your domain logic for determining which shipping methods are returned based on the user, basket and shipping address passed in.