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 in Oscar¶
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 within checkout¶
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.
Shipping method classes¶
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.
Writing your own shipping method¶
Simple really - follow these steps:
- Subclass oscar.shipping.methods.ShippingMethod and implement the methods basket_charge_incl_tax and basket_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.
- class oscar.apps.shipping.base.Base¶
Shipping method interface class
This is the superclass to the classes in methods.py, and a de-facto superclass to the classes in models.py. This allows using all shipping methods interchangeably (aka polymorphism).
The interface is all properties.
- charge_excl_tax = Decimal('0.00')¶
Shipping charge including taxes
- charge_incl_tax = None¶
Shipping charge excluding taxes
- code = '__default__'¶
Used to store this method in the session. Each shipping method should
- description = ''¶
A more detailed description of the shipping method shown to the customer
- is_tax_known = False¶
Whether we now the shipping tax applicable (and hence whether
- name = 'Default shipping'¶
The name of the shipping method, shown to the customer during checkout
- class oscar.apps.shipping.methods.NoShippingRequired¶
This is a special shipping method that indicates that no shipping is actually required (eg for digital goods).
- class oscar.apps.shipping.models.OrderAndItemCharges(*args, **kwargs)¶
Standard shipping method
This method has two components: * a charge per order * a charge per item
Many sites use shipping logic which fits into this system. However, for more complex shipping logic, a custom shipping method object will need to be provided that subclasses ShippingMethod.
Return basket total excluding tax.
Default implementation assumes shipping is tax free.
Return basket total including tax
- class oscar.apps.shipping.models.WeightBand(*args, **kwargs)¶
Represents a weight band which are used by the WeightBasedShipping method.
- class oscar.apps.shipping.repository.Repository¶
Repository class responsible for returning ShippingMethod objects for a given user, basket etc
- find_by_code(code, basket)¶
Return the appropriate Method object for the given code
- get_default_shipping_method(user, basket, shipping_addr=None, request=None, **kwargs)¶
Return a ‘default’ shipping method to show on the basket page to give the customer an indication of what their order will cost.
- get_shipping_methods(user, basket, shipping_addr=None, request=None, **kwargs)¶
Return a list of all applicable shipping method objects for a given basket, address etc.
We default to returning the Method models that have been defined but this behaviour can easily be overridden by subclassing this class and overriding this method.
- prime_method(basket, method)¶
Prime an individual method instance
- prime_methods(basket, methods)¶
Prime a list of shipping method instances
This involves injecting the basket instance into each and adding any discount wrappers.