Buys X get cheapest as Y from same category magento 2
-
24-02-2021 - |
Question
As per the answer from here I have created an event for salesrule_validator_process and the code is below.
class CustomCartPriceRules implements ObserverInterface {
public function __construct() {
}
/**
*
* @param \Magento\Framework\Event\Observer $observer
* @return void
*/
public function execute(\Magento\Framework\Event\Observer $observer) {
$result = $observer->getEvent()->getResult();
$item = $observer->getEvent()->getItem();
$rule = $observer->getEvent()->getRule();
$address = $observer->getEvent()->getAddress();
$qty = $item->getQty();
if ($rule->getData('name') == 'Buy 2 get 1 free'):
// overriding magento rules here and updating Almusbah Offer
$result->setAmount(0)
->setBaseAmount(0)
->setOriginalAmount(0)
->setBaseOriginalAmount(0);
$item->setDiscountPercent(0);
$products = [];
$z = 0;
$totalQty = 0;
$dA = 0;
foreach ($address->getAllItems() as $q):
$validate = $rule->getActions()->validate($q);
if ($validate):
$products[$z]['sku'] = $q->getData('sku');
$products[$z]['qty'] = $q->getData('qty');
$totalQty += $q->getData('qty');
$products[$z]['totalQty'] = $totalQty;
$freeQty = (int) ($totalQty / 3);
$products[$z]['freeQty'] = $freeQty;
if ($products[$z]['freeQty'] > 0):
$products[$z]['applyDiscount'] = 1;
$dicountPercent = 100;
$dA = ($products[$z]['freeQty'] * $q->getData('calculation_price'));
$products[$z]['discountAmount'] = $dA;
$totalQty -= 3*$products[$z]['freeQty']; // appying on each 3 items
endif;
$z++;
endif;
endforeach;
//echo '<pre>';print_r($products);exit;
$chkCurrentSku = $item->getSku();
foreach ($products as $p):
if(isset($p['applyDiscount']) && !empty($p['applyDiscount']) && $chkCurrentSku == $p['sku']):
$discountAmount = $p['discountAmount'];
$fullDiscount = 100;
$item->setDiscountPercent($fullDiscount);
$this->almusbahBuy2Get1Offer($discountAmount,$result);
endif;
endforeach;
endif;
}
public function almusbahBuy2Get1Offer($discountAmount, $result) {
$result->setAmount($discountAmount)
->setBaseAmount($discountAmount)
->setOriginalAmount($discountAmount)
->setBaseOriginalAmount($discountAmount);
}
}
My admin configuration is as follows.
When I add 2 items to productwith product A 4 qty and B with 2 qty.Where B is cheaper than A.So B should be free for 4 products of A.Instead one product from A and one from B are added into discount.
See screenshot below.
In this case product with price 14 and qty 2 should be added as discounted instead of 1 from 14 and another of 145.
Can someone please help how i can add the cheaper product as discounted in the above code.
Solution
Your calculation discounts always the third item without regarding the price. In order to dicount the cheapest items you need to sort the item collection by the calculation_price
, calculate the total items to be discounted and then discount the items beginning with the cheapest order item.
A code like the following should work. But please keep in mind, I haven't tested it!
//first the total free quantity needs to be calculated based on all items in the quote
$quote = $observer->getEvent()->getQuote();
$totalFreeQty = (int) ($quote->getItemsSummaryQty() / 3);
//second you need all quote items sorted by calculation_price ascending;
//that won't work with collection sorting, therefore you need to use the items array
//to make sorting easy we use the price as key
$allItems = $quote->getAllVisibleItems();
$sortedItems = [];
foreach ($allItems as $quoteItem) {
//convert the float price into integer preserving all decimal positions
$priceKey = (int) ($quoteItem->getData('calculation_price') * 1000);
//if item qith the same price already exists, we increment until we find a free key
//in this way we make sure that no key is overwritten and we keep the price sort order
//in case that two items have the same price the first item of the collection will be sorted first
while (isset($sortedItems[$priceKey])){
$priceKey++;
}
$sortedItems[$priceKey] = $quoteItem;
}
ksort($sortedItems,SORT_NUMERIC);
//now you can use your foreach loop as follows (I have changed only the freeQty calculation)
foreach ($sortedItems as $q):
$validate = $rule->getActions()->validate($q);
if ($validate):
$products[$z]['sku'] = $q->getData('sku');
$products[$z]['qty'] = $q->getData('qty');
$products[$z]['freeQty'] = 0;
if ($totalFreeQty > 0):
if ($totalFreeQty > $products[$z]['qty']){
$products[$z]['freeQty'] = $products[$z]['qty'];
$totalFreeQty = $totalFreeQty - $products[$z]['qty'];
} else {
$products[$z]['freeQty'] = $totalFreeQty;
$totalFreeQty = 0
}
$products[$z]['applyDiscount'] = 1;
$dicountPercent = 100;
$dA = ($products[$z]['freeQty'] * $q->getData('calculation_price'));
$products[$z]['discountAmount'] = $dA;
endif;
$z++;
endif;
endforeach;