Разработка на 1С-Битрикс Михаил Базаров
Блог-note

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

Просмотров: 2333
Задача: В магазине реализован внутренний счет (бонусный счет) для покупателей. Необходимо, чтобы при оплате заказа бонусами учитывалась только
стоимость товаров, а стоимость доставки не входила в сумму оплаты с внутреннего счета.
Использование внутреннего счета в качестве бонусного описано в записях блога:
Пополнение внутреннего счета из 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С-Битрикс

Стоимость разработки сайта зависит от объёма и сложности проекта. Ниже приведены ориентировочные цены, как правило не выходят за обозначенные рамки. Срок разработки зависит от сложности проекта: как правило называю сроки с запасом.

Финальная стоимость и сроки разработки обговариваются на этапе обсуждения. Скачайте опросник на разработку, заполните как можно подробнее и вышлите удобным способом. После ознакомления смогу задать уточняющие вопросы и оценить проект.

Включено в стоимость разработки

В цену уже заложено всё, что нужно для легального и быстрого запуска. Доплачивать за базовые вещи не придётся.

  • Лицензия и модули

    Лицензия на «1С-Битрикс» нужной редакции, дополнительные модули и видео-инструкции по работе с готовым проектом.

  • Скорость и SEO

    Оптимизация кода и сервера под быструю загрузку, базовая SEO-оптимизация и добавление сайта в поисковые системы.

Блог-note

Заметки по 1С-Битрикс