Задача: после того как заказ, в интернет-магазине, перешел в статус "Выполнен" начислить пользователю 5% от стоимости заказа, на накопительный счет - с которого можно оплатить будущие заказы.
Для этого нам нужно событие смены статуса заказа OnSaleStatusOrder или (вариант в новом ядре) OnSaleStatusOrderChange и метод CSaleUserAccount::UpdateAccount изменяющий сумму на счете пользователя
Добавим следующий код в init.php, пояснения ниже
use Bitrix\Main\Loader;
AddEventHandler('sale', 'OnSaleStatusOrder', 'OrderComplete');
function OrderComplete($orderID, &$arFields)
{
if ($arFields == 'F') {
Loader::includeModule('sale');
$order = \Bitrix\Sale\Order::load($orderID);
$orderUser = $order->getUserId();
$orderSumm = $order->getPrice();
$bonusPercent = orderSumm * 5 / 100;
CSaleUserAccount::UpdateAccount(
$orderUser,
$bonusPercent,
'RUB',
'За оформленный заказ',
$orderID,
false
);
}
}
- Подключили модуль sale Loader::includeModule('sale');;
- Проверили, что статус заказ перешел в выполнен if ($arFields == 'F') ;
- Зная id заказа, из переменной $orderID, получили ID пользователя $orderUser = $order->getUserId(); и сумму заказа $orderSumm = $order->getPrice();;
- Высчитали 5% из стоимости заказа $bonusPercent;
- Методом CSaleUserAccount::UpdateAccount начислил пользователю сумму бонуса, основанием сделали ID заказа;
В личном кабинете можно вывести историю начислений и списаний, например так:
Начислить процент без учета стоимости доставки и без учета оплаты с накопительного счета
Расширяем задачу: нужно начислять 10% но при этом учитывать только стоимость товаров, без учета стоимости доставки. Дополнительно, не учитываем полную или частичную оплату с внутреннего счета. То есть: начисляем бонус, только на реально потраченные деньги.
AddEventHandler('sale', 'OnSaleStatusOrder', 'OrderComplete');
function OrderComplete($orderID, &$arFields)
{
if ($arFields == 'F') {
Loader::includeModule('sale');
$order = \Bitrix\Sale\Order::load($orderID);
$orderUser = $order->getUserId();
$orderSumm = $order->getPrice();
$orderDeliveryPrice = $order->getDeliveryPrice();
$orderPayments = \Bitrix\Sale\PaymentCollection::getList([
'select' => ['SUM'],
'filter' => [
'=ORDER_ID' => $orderID,
'=PAY_SYSTEM_ID' => 1,
'=PAID' => 'Y'
]
]);
while ($orderPayment = $orderPayments->fetch()) {
$orderPayid = $orderPayment['SUM'];
}
// Общая сумма минус доставка и минус накопительный счет
if (empty($orderPayid)) {
$realOrderPrice = $orderSumm - $orderDeliveryPrice;
} else {
$realOrderPrice = $orderSumm - $orderDeliveryPrice - $orderPayId;
}
$bonusPercent = $realOrderPrice * 10 / 100;
CSaleUserAccount::UpdateAccount(
$orderUser,
$bonusPercent,
'RUB',
'За оформленный заказ',
$orderID,
'За заказ №' . $orderID,
);
}
}
Дополнительно, к коду из первого варианта добавили:
- Получили стоимость доставки $orderDeliveryPrice = $order->getDeliveryPrice();;
- Получили коллекцию оплат для заказа $orderPayments = \Bitrix\Sale\PaymentCollection::getList, отфильтровав только оплату с внутреннего счета с 'select' => ['SUM'], - так как нам нужн только сумма оплаты;
- В переменной $realOrderPrice высчитали сумму заказа за минусом стоимости доставки и оплаты с накопительного счета;
Пример реализации данного функционала, в одной из серий видеокурса по разработке проекта на 1С-Битрикс Перейти в серию
Усложнение. Процент для начисления в зависимости от суммы заказа
Расширяем задачу, дальше: начислять разный процент, на внутренний счет, в зависимости от стоимости заказа.
Создаем и заполняем HL инфоблок с пользовательскими полями:
- 'UF_BONUS_FROM' - Стоимость заказа от
- UF_BONUS_TO' - Стоимость заказа до
- UF_BONUS_PRICE' - Величина процента начисления
После наполнения:
И расширяем код из примеров выше:
use Bitrix\Main\Loader;
use Bitrix\Highloadblock as HL;
use Bitrix\Main\Entity;
AddEventHandler('sale', 'OnSaleStatusOrder', 'OrderComplete');
function OrderComplete($orderID, &$arFields)
{
if ($arFields == 'F') {
Loader::includeModule('sale');
Loader::includeModule('highloadblock');
$order = \Bitrix\Sale\Order::load($orderID); // 123 - ID заказа
$orderUser = $order->getUserId();
$orderSumm = $order->getPrice();
$orderDeliveryPrice = $order->getDeliveryPrice();
$orderPayments = \Bitrix\Sale\PaymentCollection::getList([
'select' => ['SUM'],
'filter' => [
'=ORDER_ID' => $orderID,
'=PAY_SYSTEM_ID' => 13,
'=PAID' => 'Y'
]
]);
while ($orderPayment = $orderPayments->fetch()) {
$orderPayid = $orderPayment['SUM'];
}
if (empty($orderPayid)) {
$orderPriceBonus = $orderSumm - $orderDeliveryPrice;
} else {
$orderPriceBonus = $orderSumm - $orderDeliveryPrice - $orderPayid;
}
$hlblockDatas = HL\HighloadBlockTable::getById(5)->fetch();
$entityHlBonus = HL\HighloadBlockTable::compileEntity($hlblockDatas);
$entityDataClassBonus = $entityHlBonus->getDataClass();
$bonusData = $entityDataClassBonus::getList(array(
'select' => array('UF_BONUS_FROM', 'UF_BONUS_TO', 'UF_BONUS_PRICE'),
));
while ($arBonusData = $bonusData->Fetch()) {
$priceFrom = $arBonusData['UF_BONUS_FROM'];
$priceTo = $arBonusData['UF_BONUS_TO'];
if (($orderPriceBonus > $priceFrom) && ($orderPriceBonus < $priceTo)) {
$priceBonus = $arBonusData['UF_BONUS_PRICE'];
$summToAddBonus = $orderPriceBonus * $priceBonus / 100;
CSaleUserAccount::UpdateAccount(
$orderUser,
$summToAddBonus,
'RUB',
'За заказ №' . $orderID,
$orderID,
false
);
}
}
}
}
- Подключили модуль Hl инфоблоков Loader::includeModule('highloadblock');
- Минимальное и максимальное значения, из HLблока загоняем в переменные $priceFrom и $priceTo
- Получили значения пользовательских полей инфоблока и отсекли диапазаон подпадающий под условия начисления
if (($orderPriceBonus > $priceFrom) && ($orderPriceBonus < $priceTo))
Дополнено: использование нового подхода с отключенными устаревшими событиями
Для переписывания кода на использование новых событий Битрикс, нам потребуется использовать событие OnSaleOrderSaved:
use Bitrix\Main\Loader;
use Bitrix\Main\EventManager;
use Bitrix\Main\EventResult;
EventManager::getInstance()->addEventHandler(
'sale',
'OnSaleOrderSaved',
'OrderComplete'
);
function OrderComplete(\Bitrix\Main\Event $event)
{
$order = $event->getParameter('ENTITY');
if ($order instanceof \Bitrix\Sale\Order) {
$arFields = $order->getFieldValues();
$orderID = $arFields['ID'];
// Теперь можно продолжить выполнение кода, как и ранее
Loader::includeModule('sale');
Loader::includeModule('highloadblock');
if ($arFields['STATUS_ID'] == 'F') {
// Код из примеров выше
}
}
return new EventResult(EventResult::SUCCESS);
}
В этой версии кода я заменил AddEventHandler на использование EventManager, и заменил событие на OnSaleOrderSaved, так как оно позволяет нам получить объект заказа без необходимости его загрузки.
Обратите внимание, что в этой версии кода параметры события передаются через $event->getParameter('ENTITY'), и мы можем проверить, является ли объект $order экземпляром Bitrix\Sale\Order.