Скидка на доставку в зависимости от суммы заказа Bitrix

Скидка на доставку
в зависимости 
от суммы заказа. Bitrix

Йо-йо! Сейчас я занимаюсь созданием одного очень интересного для меня сайта. Интересен он тем, что в нём есть довольно обширная система лояльности клиентов. Там есть и скидки по дисконтным картам, которые хранятся в 1С, и несколько групп служб доставок, на каждую из которых есть своя скидка. А ещё есть особая группа служб доставок, на которые нужно дать особую скидку.

Задача

Есть несколько служб доставок, которые ограничены регионами. В том случае, если сумма товаров с учётом скидок более 20.000 рублей и если цена на доставку меньше или равно 10% от стоимости товаров, то дать скидку на доставку 50%, а если больше, то дать скидку на доставку равную 10% от стоимости товаров.

Давайте запишем в виде формулы

$basketPrice = цена товаров
$deliveryPrice = цена на доставку
$maxDeliveryPrice = $basketPrice * (10 / 100); максимально возможная скидка на доставку

$newValue = новое значение цены на доставку - то, что мы в итоге считаем


if($basketPrice > 20000){
	
	$prepareValue = $deliveryPrice / 2; - 50% от стоимости доставки
	
	if($prepareValue >= $maxDeliveryPrice){
		 $newValue = $deliveryPrice - $maxDeliveryPrice;
	} else {
		$newValue = $prepareValue;
	}
}

Думаю сейчас вам стало понятно, что конкретно нужно вычислить.

Проблемы с которыми я столкнулся

Мы же говорим о битриксе, да… Так вот, для того чтобы применить новую цену на доставку, нужно использовать событие «onSaleDeliveryServiceCalculate«. Но фишка в том, что это событие не содержит объект корзины или заказа, чтобы из него получить сумму товаров. Я попробовал использовать метод для получения корзины:

$basket = Sale\Basket::loadItemsForFUser(Sale\Fuser::getId(), Bitrix\Main\Context::getCurrent()->getSite());

Из статьи «Работа с корзиной в битрикс d7» я увидел, что сильно замедлился расчёт стоимости доставки. Поэтому решил делать иначе.

Решение

Мне понадобиться обработать 2 события: «OnSaleComponentOrderCreated» и «onSaleDeliveryServiceCalculate». На OnSaleComponentOrderCreated я получу цену на товары, а на onSaleDeliveryServiceCalculate я получу цену на доставку, посчитаю скидку и применю новую цену на доставку.

Получение цены товаров

// Получить цену товаров при создании корзины
AddEventHandler("sale", "OnSaleComponentOrderCreated", "xakpl_OnSaleComponentOrderCreated");
function xakpl_OnSaleComponentOrderCreated($order, &$arUserResult, $request, &$arParams, &$arResult, &$arDeliveryServiceAll, &$arPaySystemServiceAll){
    $_SESSION['XAKPL_ORDER_BASKET_PRICE'] = $order->getBasket()->getPrice();
}

Я получил из события объект заказа, из него объект корзины и цену на товары со скидками и наценками. Если вам будет нужен результат без учёта скидок и наценок, то взамен getPrice() используйте getBasePrice().

Расчёт цена на доставку

// Изменить цену доставки после получения расчёта стоимости
AddEventHandler("sale", "onSaleDeliveryServiceCalculate", "xakpl_onSaleDeliveryServiceCalculate");
function xakpl_onSaleDeliveryServiceCalculate($result, $shipment, $deliveryID){
        if(isset($_SESSION['XAKPL_ORDER_BASKET_PRICE']) )
        {
            $basketPrice = $_SESSION['XAKPL_ORDER_BASKET_PRICE'];
			// Получаем цену доставки
            $deliveryPrice = $result->getDeliveryPrice();
			// Считаем максимально возможную цену на доставку
            $maxDeliveryPrice = intval($basketPrice) * (10 / 100); // 10%
            $newValue = $deliveryPrice;
            if($basketPrice > 20000){
				// Считаем цену на доставку со скидкой 50%
                $prepareValue = $deliveryPrice / 2;
                if($prepareValue >= $maxDeliveryPrice){
                     $newValue = $deliveryPrice - $maxDeliveryPrice;
                } else {
                    $newValue = $prepareValue; 
                }
				// Записываем новое значение цены на доставку
                $shipment->setBasePriceDelivery($newValue, true);
            }
        }
}

Тут пояснение излишне, комментарии говорят сами за себя.

Применяем к списку служб доставки

На этом можно было бы остановиться, но нужно ещё учесть, что надо применять скидку только к определённым службам доставки.

Для идентификации службы доставки у нас в событии есть переменная $deliveryID. Сам идентификатор нужной службы доставки мы можем найти в админке, в списке служб доставки. Ну и я не стал заморачиваться и получилось в итоге вот что:

// Изменить цену доставки после получения расчёта стоимости
AddEventHandler("sale", "onSaleDeliveryServiceCalculate", "xakpl_onSaleDeliveryServiceCalculate");
function xakpl_onSaleDeliveryServiceCalculate($result, $shipment, $deliveryID){
	// Проверка id службы доставки
	if($deliveryID == 61 || $deliveryID == 75 || $deliveryID == 67 || $deliveryID == 94 || $deliveryID == 76  ){
        if(isset($_SESSION['XAKPL_ORDER_BASKET_PRICE']) )
        {
            $basketPrice = $_SESSION['XAKPL_ORDER_BASKET_PRICE'];
			// Получаем цену доставки
            $deliveryPrice = $result->getDeliveryPrice();
			// Считаем максимально возможную цену на доставку
            $maxDeliveryPrice = intval($basketPrice) * (10 / 100); // 10%
            $newValue = $deliveryPrice;
            if($basketPrice > 20000){
				// Считаем цену на доставку со скидкой 50%
                $prepareValue = $deliveryPrice / 2;
                if($prepareValue >= $maxDeliveryPrice){
                     $newValue = $deliveryPrice - $maxDeliveryPrice;
                } else {
                    $newValue = $prepareValue; 
                }
				// Записываем новое значение цены на доставку
                $shipment->setBasePriceDelivery($newValue, true);
            }
        }
	}
}