Оплата заказа с внутреннего счета только за товары, без учета стоимости доставки

Просмотров: 1141

Задача: В магазине реализован внутренний счет (бонусный счет) для покупателей. Необходимо, чтобы при оплате заказа бонусами учитывалась только стоимость товаров, а стоимость доставки не входила в сумму оплаты с внутреннего счета.

Использование внутреннего счета в качестве бонусного описано в записях блога:
Пополнение внутреннего счета из 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()) {
    // Если внутренний счет
    // Логика обработки и изменения платежки
}

Стоимость разработки на 1С-Битрикс:

Индивидуальная разработка магазина

от 500 000 руб. от 5-ти недель

Разработка магазина на 1С-Битрикс с нуля. Дизайн, сборка и оптимизация производительности под конкретный проект и требования. Реализация любого функционала без ограничений готовых решений.

Запуск сайта на готовом решении

от 100 000 руб. от 7-ми дней

Вариант для тех, кто не хочет тратить много средств на индивидуальный проект, и не имеет серьезных требований к сайту. Магазин, быстро запускается на базе одного из 200-та готовых решений.

Мобильное приложение

от 500 000 руб. от 1-го месяца

Разработка кроссплатформенного мобильного приложения, которое не уступает нативным решениям как в производительности, так и пользовательском опыте. Публикуется в AppStore, GooglePlay и RuStore

Сайт компании

от 350 000 руб. от 1-го месяца

Корпоративный сайт с информационными разделами, каталогом товаров или услуг. Включает формы обратной связи карточек каталога, любое количество статичных и динамичных разделов.

Инфресурс

от 400 000 руб. от 5-ти недель

Информационный ресурс любой сложности. Сайт для СМИ, городской портал или многопользовательская доска объявлений. Внутренние форумы, блоги- по необходимости.

Лечение сайтов от вирусов

от 40 000 руб. от 2-х дней

Выполню полную проверку сайта и окружения. В случае обнаружения вирусов проведу полный комплекс лечения проекта и закрытия лазеек.