В общем, если кому понадобится, написал такие обработчики. Пришлось разбираться с D7, опять лезть в исходники, потому, что классические обработчики уже не работают как надо. Обрабатываю события сохранения корзины и сохранения заказа (скидки за купоны в объект корзины записываются только при сохранении заказа).
Функция recalcBasketKitSets считает сумму комплектующих и сравнивает с ценой комплекта, и исправляет то или иное в пользу покупателя. Если на комплект назначена скидка, она распределяется на комплектующие.
Приходится менять BASE_PRICE комплекта, поскольку в корзине (стандартном компоненте sale.basket.basket) выводится именно она, плюс похоже корзина на каждом хите норовит обновить цены товаров в соответствии с каталогом. Возможно нужно как-то пользоваться провайдерами, но в мануале про это нет, а я не ОО-архитектор.
Код |
---|
use Bitrix\Main;
Main\EventManager::getInstance()->addEventHandler('sale', 'OnSaleBasketBeforeSaved', Array(CKitSetPriceHandlers, basketBeforeSavedHandler));
Main\EventManager::getInstance()->addEventHandler('sale', 'OnSaleOrderBeforeSaved', Array(CKitSetPriceHandlers, orderBeforeSavedHandler));
class CKitSetPriceHandlers
{
private function recalcBasketKitSets(\Bitrix\Sale\Basket $basket, $changeSetPrice = false)
{
$arBasketItems = $basket->getBasketItems();
foreach ($arBasketItems as $basketItem)
if ($basketItem->isBundleParent() && $basketItem->getFields()->isChanged('PRICE'))
{
$kitSetPrice = $basketItem->getField('PRICE');
$kitSetBasePrice = $basketItem->getField('BASE_PRICE');
$kitSetCurrency = $basketItem->getField('CURRENCY');
$kitSetQuantity = $basketItem->getField('QUANTITY');
$kitSetDiscount = $kitSetBasePrice > 0 ? $kitSetPrice / $kitSetBasePrice : 1;
$setCompTotalBasePrice = 0;
$setCompTotalPrice = 0;
$bMatchCurrency = true;
if (!$kitSetQuantity) continue;
$arBundleItems = $basketItem->getBundleCollection()->getBasketItems();
foreach ($arBundleItems as $bundleItem)
{
if ($bundleItem->getField('CURRENCY') <> $kitSetCurrency)
$bMatchCurrency = false;
$setCompQuantity = $bundleItem->getField('QUANTITY');
$setCompTotalPrice += $bundleItem->getField('PRICE') * $setCompQuantity / $kitSetQuantity;
$setCompTotalBasePrice += $bundleItem->getField('BASE_PRICE') * $setCompQuantity / $kitSetQuantity;
}
if ($setCompTotalPrice == $kitSetPrice || !$bMatchCurrency || !$setCompTotalBasePrice) // Цены комплекта и комплектующих уже выравнены, или что-то пошло не так
continue;
elseif (!$changeSetPrice || $setCompTotalPrice > $kitSetPrice) // Пересчитать цены компонентов (округление выполняется классами модуля)
foreach ($arBundleItems as $bundleItem)
{
$bundleItemNewPrice = $kitSetPrice * $bundleItem->getField('BASE_PRICE') / $setCompTotalBasePrice;
$bundleItem->setFieldNoDemand('PRICE', $bundleItemNewPrice);
}
else // Переназначить цену комплекта в меньшую сторону с учетом изначальной скидки (округление выполняется классами модуля)
{
foreach ($arBundleItems as $bundleItem) {
$bundleItemNewPrice = $bundleItem->getField('PRICE') * $kitSetDiscount;
$bundleItem->setFieldNoDemand('PRICE', $bundleItemNewPrice);
}
$kitSetNewPrice = $setCompTotalPrice * $kitSetDiscount;
$kitSetNewFields = array(
'PRICE' => $kitSetNewPrice,
'BASE_PRICE' => $setCompTotalPrice,
'DISCOUNT_PRICE' => $setCompTotalPrice - $kitSetNewPrice
);
$basketItem->setFieldsNoDemand($kitSetNewFields);
}
}
}
function basketBeforeSavedHandler(Main\Event $event)
{
/** @var Basket $basket */
$basket = $event->getParameter("ENTITY");
self::recalcBasketKitSets($basket, true);
return new Main\EventResult(Main\EventResult::SUCCESS);
}
function orderBeforeSavedHandler(Main\Event $event)
{
$order = $event->getParameter("ENTITY");
$basket = $order->getBasket();
self::recalcBasketKitSets($basket);
return new Main\EventResult(Main\EventResult::SUCCESS);
}
}
|