We need to support multiple stockrecords per product.
Example stories:
- Customers in the UK use a stockrecord from a UK partner, customers from the US use a stockrecord from a US partner etc. We use IP lookup to determine which stockrecord is appropriate for a customer (this is the Meridian problem).
- A product is available in several second-hand versions, each with a different price and description (like Alibris from Borders UK if anyone remembers that). The customer can choose which stockrecord/partner to use when adding to basket.
- A product has several possible partners. We fulfil from one partner until it's stocklevel is zero then switch to the next one dynamically.
Fundamentally, the price and availability of a product now depend on the request, session and user. The current API for looking up prices can't handle this as prices and availability are looked up as simple attributes:
{{ product.stockrecord.price_incl_tax }} {{ product.is_available_to_buy }}
These properties won't have access to the request.
Sometimes the appropriate stockrecord for a product will be looked up dynamically (eg Meridian) but sometimes it will be set in advance and will need to be stored within the basket line.
I think we're going to need to add a new field to the basket line to track which stockrecord will be used. It will need to be NULLable for backwards compatibility. Then the basket will be able to determine its line prices without needing the request instance to look things up dynamically.
Handling a NULL value for the stockrecord is tricky. We could assume that this indicates that there is a one-to-one relationship between products and stockrecord - and possibly grab that stockrecord and save it. If the stockrecord field is NULL and there are multiple stockrecords per products, we would have to raise an exception as we would have no way of knowing which was the right stock record.
Everything around the order should work ok as it stores its prices internally.
Products are the trickiest problem as this is such a big API change. It mainly affects templates though - there aren't many places in python land where a product price is accessed directly.
To get prices in templates we could provide a set of template filters:
{% load stockrecord_filters %} {{ product|price_incl_tax }} {{ product|availability_code }} {% if product|is_available_to_buy %} ... {% endif %}
Seems a bit clunky.
Use a templatetag to load the appropriate stockrecord into the template context:
{% load stockrecord_tags %} {% get_stockrecord product as record %} {{ record.price_incl_tax }} {{ record.is_available_to_buy }}
This is probably better although some properties (eg is_available_to_buy
)
don't really belong on the stockrecord. We could return a different object from
a StockRecord instance though.
We could attach properties onto the product instance directly:
{% load stockrecord_tags %} {% update_product product %} {{ product.price_incl_tax }} {{ product.is_available_to_buy }}
We could even do this on batch:
{% load stockrecord_tags %} {% update_products products %} {% for product in products %} {{ product.price_incl_tax }} {{ product.is_available_to_buy }} {% endfor %}
which might be faster.
All the above options would require a lot of template changes. To mitigate
this, we could keep a stockrecord
property on the product model that returns
the first stockrecord available. This would avoid Oscar projects which have a
one-to-one relationship from having to make lots of template changes.
But it might make things confusing going forward.