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.

enter image description here

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.

enter image description here

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.

Était-ce utile?

La 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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à magento.stackexchange
scroll top