Source code for oscar.apps.shipping.methods

from decimal import Decimal as D

from django.utils.translation import gettext_lazy as _

from oscar.core import prices


[docs]class Base(object): """ Shipping method interface class This is the superclass to the classes in this module. This allows using all shipping methods interchangeably (aka polymorphism). The interface is all properties. """ #: Used to store this method in the session. Each shipping method should #: have a unique code. code = '__default__' #: The name of the shipping method, shown to the customer during checkout name = 'Default shipping' #: A more detailed description of the shipping method shown to the customer #: during checkout. Can contain HTML. description = '' #: Whether the charge includes a discount is_discounted = False
[docs] def calculate(self, basket): """ Return the shipping charge for the given basket """ raise NotImplementedError
[docs] def discount(self, basket): """ Return the discount on the standard shipping charge """ # The regular shipping methods don't add a default discount. # For offers and vouchers, the discount will be provided # by a wrapper that Repository.apply_shipping_offer() adds. return D('0.00')
[docs]class Free(Base): """ This shipping method specifies that shipping is free. """ code = 'free-shipping' name = _('Free shipping')
[docs] def calculate(self, basket): # If the charge is free then tax must be free (musn't it?) and so we # immediately set the tax to zero return prices.Price( currency=basket.currency, excl_tax=D('0.00'), tax=D('0.00'))
[docs]class NoShippingRequired(Free): """ This is a special shipping method that indicates that no shipping is actually required (e.g. for digital goods). """ code = 'no-shipping-required' name = _('No shipping required')
[docs]class FixedPrice(Base): """ This shipping method indicates that shipping costs a fixed price and requires no special calculation. """ code = 'fixed-price-shipping' name = _('Fixed price shipping') # Charges can be either declared by subclassing and overriding the # class attributes or by passing them to the constructor charge_excl_tax = None charge_incl_tax = None def __init__(self, charge_excl_tax=None, charge_incl_tax=None): if charge_excl_tax is not None: self.charge_excl_tax = charge_excl_tax if charge_incl_tax is not None: self.charge_incl_tax = charge_incl_tax
[docs] def calculate(self, basket): return prices.Price( currency=basket.currency, excl_tax=self.charge_excl_tax, incl_tax=self.charge_incl_tax)
[docs]class OfferDiscount(Base): """ Wrapper class that applies a discount to an existing shipping method's charges. """ is_discounted = True def __init__(self, method, offer): self.method = method self.offer = offer # Forwarded properties @property def code(self): """ Returns the :py:attr:`code <oscar.apps.shipping.methods.Base.code>` of the wrapped shipping method. """ return self.method.code @property def name(self): """ Returns the :py:attr:`name <oscar.apps.shipping.methods.Base.name>` of the wrapped shipping method. """ return self.method.name @property def discount_name(self): """ Returns the :py:attr:`name <oscar.apps.offer.abstract_models.BaseOfferMixin.name>` of the applied Offer. """ return self.offer.name @property def description(self): """ Returns the :py:attr:`description <.Base.description>` of the wrapped shipping method. """ return self.method.description
[docs] def calculate_excl_discount(self, basket): """ Returns the shipping charge for the given basket without discount applied. """ return self.method.calculate(basket)
[docs]class TaxExclusiveOfferDiscount(OfferDiscount): """ Wrapper class which extends OfferDiscount to be exclusive of tax. """
[docs] def calculate(self, basket): base_charge = self.method.calculate(basket) discount = self.offer.shipping_discount(base_charge.excl_tax, base_charge.currency) excl_tax = base_charge.excl_tax - discount return prices.Price( currency=base_charge.currency, excl_tax=excl_tax)
[docs] def discount(self, basket): base_charge = self.method.calculate(basket) return self.offer.shipping_discount(base_charge.excl_tax, base_charge.currency)
[docs]class TaxInclusiveOfferDiscount(OfferDiscount): """ Wrapper class which extends OfferDiscount to be inclusive of tax. """
[docs] def calculate(self, basket): base_charge = self.method.calculate(basket) discount = self.offer.shipping_discount(base_charge.incl_tax, base_charge.currency) incl_tax = base_charge.incl_tax - discount excl_tax = self.calculate_excl_tax(base_charge, incl_tax) return prices.Price( currency=base_charge.currency, excl_tax=excl_tax, incl_tax=incl_tax)
[docs] def calculate_excl_tax(self, base_charge, incl_tax): """ Return the charge excluding tax (but including discount). """ if incl_tax == D('0.00'): return D('0.00') # We assume we can linearly scale down the excl tax price before # discount. excl_tax = base_charge.excl_tax * ( incl_tax / base_charge.incl_tax) return excl_tax.quantize(D('0.01'))
[docs] def discount(self, basket): base_charge = self.method.calculate(basket) return self.offer.shipping_discount(base_charge.incl_tax, base_charge.currency)