Задача: В магазине реализован внутренний счет (бонусный счет) для покупателей. Необходимо, чтобы при оплате заказа бонусами учитывалась только
стоимость товаров, а стоимость доставки не входила в сумму оплаты с внутреннего счета.
Использование внутреннего счета в качестве бонусного описано в записях блога:
Пополнение внутреннего счета из 1С
Бонус за заказ, на внутренний счет
В настройках платежной системы "Внутренний счет" установлено ограничение (штатная возможность 1С-Битрикс), что с него можно оплатить только 30% стоимости заказа.
После того как пользователь оформляет заказ, происходит его частичная оплата и заказ переводится в статус "Частично оплачен".
Это происходит, только если у пользователя не нулевой баланс внутреннего счета (далее по тексту "бонусный счет" или "бонусы") и если пользователь выбрал оплату бонусами. Остальные заказы создаются со статусом "Новый ожидается оплата" и при оплате сразу переходят в статус "Полностью оплачен".
Событие OnSaleStatusOrder, меняем сумму оплаченную с внутреннего счета
На текущий момент, при выборе оплаты бонусами, автоматически списывается 30% от всей суммы заказа (товары + доставка), что не соответствует потребности.
Нужно перехватить событие изменения статуса заказа на "Частично оплачен", отменить предыдущую оплату бонусами (которая учитывает доставку), и повторно списать бонусы только за товары.
Добавляем обработчик события OnSaleStatusOrder (изменение статуса заказа) в init.php:
AddEventHandler('sale', 'OnSaleStatusOrder', 'UpdateOrderPayment');
function UpdateOrderPayment($orderID, &$arFields)
{
// Загружаем заказ по его ID
$order = Bitrix\Sale\Order::load($orderID);
// Проверяем, что статус заказа сменился на 'P' (частичная оплата)
// и что заказ еще полностью не оплачен
if (
($arFields == 'P') and
(!$order->isPaid())
) {
// Получаем коллекцию товаров в заказе (корзину)
$basket = $order->getBasket();
$itemsTotalPrice = 0;
// Подсчитываем общую стоимость товаров в корзине
foreach ($basket as $basketItem) {
$itemsTotalPrice += $basketItem->getPrice() * $basketItem->getQuantity();
}
// Получаем коллекцию оплат
$paymentCollection = $order->getPaymentCollection();
// Получение стоимости доставки
$deliveryPrice = $order->getDeliveryPrice();
$totalBonusPaid = 0;
$dbRes = \Bitrix\Sale\PaymentCollection::getList(
[
'select' => [
'ID',
'PAY_SYSTEM_ID'
],
'filter' => [
// Можно тут явно получить по ID => 1
'=ORDER_ID' => $orderID,
]
]
);
while ($item = $dbRes->fetch()) {
// Если явно получаем в фильтре по ID => 1 можно не городить лишний if, достаточно проверить на не пустоту результата
// Если среди оплат есть оплата бонусами
if ($item['PAY_SYSTEM_ID'] == 1) {
// Получаем именно эту оплату $item['ID'] - это ID оплаты а не платежной системы!!!
$payment = $paymentCollection->getItemById($item['ID']);
// Возращаем оплату
$r = $payment->setReturn('Y');
// Нужно сохранить иначе деньги не вернуться на бонусный счет
$order->save();
// Рассчитываем 30% от стоимости товаров для бонусного платежа
$bonusPart = $itemsTotalPrice * 0.3;
$totalBonusPaid = $bonusPart;
// Получаем текущий баланс бонусных средств пользователя
$userCurrentAccount = CSaleUserAccount::GetByUserID(
$order->getUserId(),
"RUB" // Валюта бонусных счетов
);
// Если бонусов меньше, чем требуется к списанию, списываем всё доступное
if ($totalBonusPaid > $userCurrentAccount['CURRENT_BUDGET']) {
$totalBonusPaid = $userCurrentAccount['CURRENT_BUDGET'];
}
// В стоимость оплаты внутреннего счета ставим суммы использованных бонусов
$result = $payment->setFields([
'SUM' => $totalBonusPaid,
'PAID' => 'Y', // Устанавливаем статус как оплаченный
]);
$paydFromBonus = true;
} else {
if ($paydFromBonus) {
$payment = $paymentCollection->getItemById($item['ID']);
// Остальные платежи - оставшееся значение
$remainingSum = $itemsTotalPrice - $totalBonusPaid + $deliveryPrice;
$payment->setFields([
'PAID' => 'N',
'SUM' => $remainingSum,
]);
}
}
}
// Сохраняем заказ
$order->save();
}
}
Получили стомость всех товаров в корзине заказа, высчитали из нее 30%, отменив предыдущую оплату бонусами (товары + доставка) изменили сумму оплаты только за товары и сделали эту оплату оплаченной.
Остальной платеж (платежную сиситему) пересчитали с учетом оплаченного бонусами.
В методе \Bitrix\Sale\PaymentCollection::getList мы находим бонусный счет по ID системы оплаты, он всегда первый, так как эта система создается при установке Битрикс.
Но, если по каким-то соображениям, вы создали свою систему оплаты с внутреннего счета, найти его ID можно так:
$innerPaysysId = \Bitrix\Sale\PaySystem\Manager::getInnerPaySystemId()
echo $innerPaysysId;
Перерасчет оплаты бонусами на этапе офомления заказа
Позже, добавлю в эту заметку вариант без использования init.php (в целом не любитель добавлять в него логику, если можно обойтись). Можно тоже самое сделать на уровне компонента/шаблона sale.order.ajax.
Коротко: суть в том что в файле confirm.php мы уже знаем ID оформленного заказа и можем получить факт использования бонусного (внутреннего) счета. По этому всю "работу" можем провернуть прямо здесь.
По большому счету, ровно теми же методами, что приведены выше. Просто перебрав платежные системы заказа:
// Узнаем была ли оплата с внутреннего смета?
// Это черновик для блога - позже сделаю нормально
$innerPaysysId = \Bitrix\Sale\PaySystem\Manager::getInnerPaySystemId()
$dbRes = \Bitrix\Sale\PaymentCollection::getList(
[
'select' => [
'ID',
'PAY_SYSTEM_ID'
],
'filter' => [
'=ORDER_ID' => $arResult['ORDER_ID'],
"PAY_SYSTEM_ID" => $innerPaysysId,
]
]
);
while ($item = $dbRes->fetch()) {
// Если внутренний счет
// Логика обработки и изменения платежки
}