Fix errors with shipping discounts on free weightbased shipping.
The Repository.apply_shipping_offer() method returns the shipping method
object when there is no additional discount for an order. This however
broke with the WeightBased, because it's implementation does not work.
self.offer is never assigned, nor useful (a copy-paste error?).
Instead, it should work like the non-model `methods.Base` that also
states the method does not have a default shipping discount.
Weigh the basket using decimals, avoid float rounding issues
While the backend stores the information as floats, the calculations
don't have to do be affected by this. For example:
0.3 + 0.3 + 0.3 == 0.8999999999
Using decimals improves the calculation, and avoid falling into the
wrong weight band.
Allow zero weight baskets to still get a weight-based charge
There wasn't really a need to special-case zero weight baskets. This
does mean that stores that rely on this behaviour will need to create a
zero weight band.
You can't easily create multiple users with the UserFactory without
using a sequence. I also wasn't aware that one can use the nicer
"factories" import instead of "newfactories".
Note that we can't easily use a LazyAttribute for the email address, as
the username may contain spaces.
Multiply charges when exceeding top bands upper limit
This approach imitates sending multiple parcels for a defined cost, and
hopefully approximates reality much better.
Note we still make some assumptions about the cost structure to avoid
having to solve an NP hard problem.
This commit also removes the unused max_upper_limit property and
replaces it by a more useful top_band property.
Drop upper_charge logic for weight based shipping methods
Previously, weight based shipping methods accepted an upper charge
field. When no weight band matched the baskets weight, this was
returned. I am not aware of a single retailer who calculates shipping
charges like that. It's impossible to pick a sensible value for a
retailer, as there's no upper limit to the amount of items ordered, and
any shipping charges would have to cover costs for that.
Get weight-based shipping method to return charges again
Setting band.charge was accidentally killed in the refactor to the new
shipping method interface, so a correct charge was never returned. This
wasn't caught because there was no test for testing the actual shipping
charge calculation...
Both issues are remedied now. I'll have a more detailled look at the
shipping method tests when getting rid of upper_charge.
Rename and move Scales class, and make it overridable
Making it overridable allows altering the weighing method of the basket,
which I wanted to do in a project. As get_classes doesn't support
importing from top-level products, it had to be moved to a scales
module.
Other class names tend to use singular, so as the imports had to be
changed anyway, Scales was renamed to Scale.
We no longer need to pass the StockInfo instance as the basket has a
reference to the strategy class and can fetch it for itself. This means
we don't have to break backwards compatibility with Oscar < 0.6.