There is a requirement in some stores that the price of products depend on certain conditions. Example conditions:
- User role.
- Country of visitor.
- Product availability.
- Product demand.
- User having a special code which may be stored as a user field.
- Customer's IP address.
Note that the requirement is to display the personalized price to the customer everywhere on the site e.g. on the product display page, on search results etc. Not to be confused with applying a discount during checkout or altering the price of the product in any way as part of an order.
This issue is created to gather interested parties and discuss the architecture of a solution, which would probably be provided as a separate contrib module.
Note: The commerce_pricelist module is related and there is an ongoing effort to make a D8 port. This approach is more generic though than price lists and it may be better moving it into a separate module.
Architecture
Price Calculation
The design follows the design of Promotions to large extend. A new entity will be introduced called PriceRule. It has conditions, using the commerce conditions system, which define whether the rule should be applied when calculating a product's price. Example conditions could be a user role, product variation type or product variation id, or any custom condition etc. It also defines a calculation, using a provided Calculation plugin, which is responsible for calculating the purchasable entity's price. Example calculations could be a price list, a percentage off, a fixed amount off or any custom calculation.
Multiple price rules can be defined and they can be ordered in terms of priority.
Price calculation in Commerce 2 is done via Price Resolvers. A custom price resolver will be introduced that will be added to the chain prior to the default price resolver. It calls all price rules available, and looks for the first price rule that applies to the product and returns a price. As soon as we get a price, the rest of of the rules are ignored. The rules are looked up in the priority order defined.
There are some considerations yet related to passing around context that the price rule may need for the conditions or for the calculation, especially in custom solutions, but this should be workable.
Performance/Caching
Most use cases would only have one (or very few) price rules enabled, so the performance hit might not be that great. We still want to make performance as best as possible, because the price rule may have to be executed multiple several times in listing pages, or to accommodate for cases of large stores that may require multiple price rules.
The key is to make use of the Cache API and cache, when possible, the results of the conditions evaluation and the calculation of the price. The calculated price of every product that is affected by the price rule, and possibly those that do not, can be cached. The price rule will be responsible for generating a unique cache key that should contain information about the context required to evaluate the conditions e.g. user id, store id, product id etc. If the key needs to be larger than the 255 characters foreseen by the database structure, we may consider using a low collision risk hashing algorithm to shorten the price rule-specific part of the key. The value of the cached data would be the calculated Price object if the the conditions for the context pass, or empty (or an invalidating value) if the conditions pass and the price rule should fall through.
Additionally, in simple cases of a single known price-rule, we could explore if calculation of the context can be done only once and stored in the user session, for example if the condition is that the user belongs to a specific country.
This system hasn't been tested yet, it's still theoretically explored.
Page caching
It is important to investigate the impact of dynamic product prices on the ability to cache pages, such as product pages or views pages with product listings etc. It may be that this falls outside of the responsibility of the module, but we could clearly inform users of the module about the consequences. There may also be opportunities to create cache contexts based on the price rules conditions per product that caching solutions can use, or there may be opportunity to integrate with modules such as authcache or bigpipe.
Search API integration
The requirement is to be able to display the correct personalised product prices throughout the website, and that should include search results. We should be able to accommodate filtering search results by price ranges, and that can be challenging. The solution still needs to be explored, but for search indexing purposes (assuming Search API), we apparently need to provide multiple indexed pseudo-fields depending on the conditions (per price rule/per product?) so that they can be indexed. Facets/Views should be then customising the query issued to the search engine to query the right pseudo-fields instead of the product price field.
Comments
Comment #2
krystalcode commentedComment #3
krystalcode commentedComment #4
krystalcode commentedComment #5
krystalcode commentedComment #6
krystalcode commentedComment #7
krystalcode commentedUpdated description to include proposed architecture.
Comment #8
agoradesign commentedWould be very interested in how you solve the Search API part :)
We'll probably start a project in late 2017 / Q1 2018 where we'll have different prices for normal customers and employees (user role), as well as a couple of date limited discounts (based on SKU, category,...). I already thought that the Search API integration with price range search could get difficult in that use case. Yours sounds even more challenging
Comment #9
krystalcode commented@agoradesign It would be challenging, that's for sure!
Update on the issue, there's a module implementing the Price Calculation section of the Architecture at https://www.drupal.org/project/commerce_price_rule.
There's also a discussion on which namespace the code should reside, please discuss at https://www.drupal.org/node/2368053#comment-12216847
Comment #10
jayarex07 commentedIs it possible to have a a custom condition that updates pricing after a product attribute value is selected?
I plan to create a Price Rule for each Attribute Value that increases the price of the Product.
ie. The Product is $10. The Price rule will add $2 using a custom condition that is limited by Attribute Value
Your help with this issue would seriously make my day.
Comment #11
krystalcode commentedHi @jayarex07,
Can you create an issue at the Commerce Price Rule module, and I'll help you there. I've recently had similar requirements for a project and there is a relatively easy solution for your requirements with Commerce Price Rule.
Comment #12
batkorsubscribe