前言,最近黑五活动来临,运营决定搞个买二送一,买四送二,送的商品以购物车最便宜的来。
遇到的问题
在Magento2中,虽然有买X送Y的购物车优惠规则,但是,仅仅是针对单个SKU来设置规则的,而我们是针对购物车所有产品来计算规则的。
然后也有考虑通过插件来处理,但是市面上的插件都是买一送一,送最便宜这种的,就是买X送Y,X和Y都是要选择固定的商品。所以,最终只能考虑代码处理。
开始后台设置规则
1.通过谷歌搜索到的方法设置。先在后台设置一个购物车优惠规则,命名为”Buy 2 get 1 free“
2.设置优惠规则条件,如果是针对所有产品,则为空
3.设置优惠规则,Percent of product price discount,优惠比率为100%。如图所示
开始编写代码
1.在/vendor/magento/module-sales-rule/etc/events.xml,中添加事件。如果有重写此模块,则在重写模块的events.xml中增加标签,没有events.xml则新增此文件后再增加
<event name="salesrule_validator_process">
<observer name="custom_cart_rules" instance="\Magento\SalesRule\Observer\CustomCartPriceRules" />
</event>
2.创建绑定事件的文件,在vendor/magento/module-sales-rule/Observer/CustomCartPriceRules.php
<?php
namespace Magento\SalesRule\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\App\ObjectManager;
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;
//first the total free quantity needs to be calculated based on all items in the quote
$quote = $observer->getEvent()->getQuote();
$totalFreeQty = (int) ($quote->getItemsSummaryQty() / 2);
//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;
$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);
}
}
查看效果