We recently launched a new website for Weavers Wines which had been rebuilt in order to migrate them from Magento 1 to Magento 2. There were several bespoke features in the build. One such feature is their deal banners.
The client required to be able to automatically display deal banners for certain products on category, search result and product pages.
These deals would be along the lines of: buy X bottles for £Y.
The following information needed to be displayed:
- Single bottle price excluding tax
- Single bottle price including tax
- Quantity (number of bottles required to trigger the deal)
- Deal price excluding tax
- Deal price including tax
With regards to setting up the discount rules, Magento 2 already has the functionality to cater for this in the form of Cart Price Rules.
What are Cart Price Rules?
From the Magento 2 admin you can setup Cart Price Rules that process actions on the cart if certain conditions are met.
These actions might apply a fixed price or percentage discount to certain items in the cart. There are other actions that can be performed, many more with the aid of 3rd party modules. However, for our purposes we were only interested in the fixed price and percentage discount actions.
The conditions can be whether or not a product has a certain SKU, whether a product is in a category and/or has a specific attribute value. The different combinations of conditions can get quite complex. For this project we only required the condition where a product has a specific SKU as the deals were specific to each product.
Visit the magento docs for more information on Cart price rules.
While setting these rules up is extremely simple, the problem arises as to how to get them to display outside of the cart page.
By default this is the only area where Cart Price Rules become visible. This appears logical since the system doesn’t really know which rules are going to be active until you’ve added X amount of bottles to your cart, i.e. you have triggered the rule conditions.
With this being quite specific functionality we decided on writing a bespoke module which implemented the following steps.
Custom price model
First we created a custom product price using our module’s di.xml file.
This will hold the calculated price from the cart price rule that will be applied to the product when the customer visits the cart page.
Using a custom price model will help with the calculation of tax on the prices that we’ll be displaying on the deal banners.
Fetching a Cart Price Rule
When we render a product in a list or on the product page itself we need to know which Cart Price Rule to use for the Exclusive Deal banner. Which raises the question. Which rules can theoretically be applied to this product?
To begin with, we fetch a collection of all cart price rules. This collection should have inactive rules filtered out since we only want to display active rules.
We also need to sort the collection based on the Priority value that can be set on the rule in the admin. That way when we find the first rule that can be applied to our product, it will be the one with the highest priority.
We then loop over the rule collection and test if the current rule could be applied to the current product.
This involves checking the conditions that have been set on the rule. In our case we added a check to validate if the current product’s SKU matches the SKU that is defined in the condition.
This could be expanded to trigger on other conditions. For example if the product was in a certain category and/or had a certain attribute value. It could get quite extensive with the different combinations of conditions. If you don’t have the budget then you’ll want to narrow down this functionality to only what your project requires.
If the rule’s conditions match then we stop looping through the collection and use this rule to calculate the data we need for the deal banner.
There were two possible action types for our module. Either there was a fixed price for the items in the cart (EG: buy 12 for £120) or there was a percentage discount (EG: buy 12 get 10% off). Both these options had to be accounted for with separate logic that calculated the following:
- Quantity (how many bottles must be bought to trigger the deal)
- Deal price (Price of the quantity bottles that must be bought to trigger the deal)
We can then set the deal price on the product using the custom price model that was setup earlier. Keeping in mind that in this project, prices on products and price rules are all entered excluding tax.
For example the deal might be something like buy 12 bottles for £120. The cart_rule_price that we set would be £100. With the price stored in a price model we can now call this in whatever view template we are in and retrieve both the excluding and including tax prices.
Finally to display the single bottle prices we simply divide the deal prices by the rule’s quantity.
Notes on rounding and performance
There is the possibility of rounding discrepancies when calculating the cart rule prices of a product if the rule is a percentage discount. The method above of setting the price of the entire rule’s quantity helps to minimise this possibility.
If you were to set the cart rule price as the price of the single product, you would then need to multiply that price by the rule’s quantity - then you would run a higher risk of displaying rounding errors.
These discrepancies would only be 1-2p and when the customer arrives at the cart the correct price would be displayed by Magento, however in most circumstances this wouldn’t be an acceptable approach so more work would be required.
We should also consider performance - there is some impact in calculating the possibly applicable rules for each product in order to determine the information to display.
Since Magento 2 works with Varnish, the page will still be performant once cached. However, the underlying page also needs to be as quick as possible, so bear in mind a large set of price rules could create problems or necessitate a more complex solution such as an additional price index.