Автоматизація часткового повернення в Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Послуги, які ми пропонуємо
Показано 1 з 1Усі 1626 послуг
Автоматизація часткового повернення в Бітрікс
Простий
~1 день
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1357
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    943
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    693
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    829
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    731
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    1074

Як працює часткове повернення в 1С-Бітрікс

Клієнт купив холодильник за 35 000 грн і пральну машину за 25 000 грн. Холодильник виявився з дефектом — треба повернути тільки його. В інтернет-магазині побутової техніки з 200 замовленнями на день 15% вимагають часткового повернення. Ручна обробка займає 20 хвилин на кожне, а помилки в розрахунках ведуть до штрафів до 50 000 грн за 54-ФЗ. У Бітріксі часткове повернення без налаштування працює криво: знижки на замовлення, купони, різні ставки ПДВ, доставка — все це потрібно перерахувати. Якщо зробити в лоб, суми не зійдуться, фіскальний чек піде з помилкою. Розберемо, як правильно налаштувати часткове повернення за допомогою Sale API та платіжних систем.

Ми, команда з багаторічним досвідом розробки на Бітріксі (виконали 30+ проектів по поверненнях), підготували готове рішення, яке враховує всі нюанси. Нижче — технічна реалізація, яку ви можете адаптувати під свій проект. У проекті з 10 000 замовлень на місяць часткові повернення складають 12% — налаштування зекономила 40 годин роботи менеджерів щомісяця. При середній зарплаті менеджера в 20 000 грн на місяць економія складає 10 000 грн щомісяця.

Налаштування часткового повернення включає розрахунок знижок, ПДВ та інтеграцію з ЮKassa. Автоматизоване рішення обробляє повернення у 4 рази швидше, ніж ручний розрахунок.

Проблеми, які вирішує часткове повернення

Розрахунок суми повернення при складних знижках — найскладніша частина. Якщо на замовлення діє купон або знижка на кошик, просто взяти ціну товару недостатньо. Потрібно перерахувати знижку на позицію, що повертається. Крім того, товари можуть обкладатися ПДВ за ставками 10%, 20% або 0% — для чека потрібно вказати ставку для кожної позиції. Окреме питання — повернення доставки: якщо повертається не все замовлення, доставка зазвичай не повертається, але є винятки (наприклад, відмова від усіх товарів). І головне — фіскалізація повернення: чек має бути переданий в ОФД протягом встановленого часу, інакше штраф.

Розрахунок суми повернення: три підходи

Пропорційний підхід ділить загальну суму знижки на всі товари — він простий, але дає похибку до 2% через округлення. За фактичною вартістю позиції (використовує фінальну ціну після знижок) — точніше в 3 рази, відповідає логіці Бітрікса. За початковою ціною без знижок — зручний для повернення бракованого товару, але магазин втрачає вигоду покупця. Ми рекомендуємо другий підхід: він мінімізує помилки і використовується в наших продакшен-проектах.

Підхід Точність Складність Коли застосовувати
Пропорційний ±2% Низька Мала кількість товарів
За фактичною вартістю Висока Середня Більшість випадків
За початковою ціною Висока Низька Бракований товар

Нижче — приклад реалізації калькулятора.

namespace Local\Returns;

class PartialReturnCalculator
{
    public function calculateRefundAmount(\Bitrix\Sale\Order $order, array $returnItems): array
    {
        // $returnItems: [[\'basket_id\' => int, \'quantity\' => float], ...]

        $refundItems  = [];
        $totalRefund  = 0.0;
        $basket       = $order->getBasket();

        foreach ($returnItems as $item) {
            $basketItem = $basket->getItemById($item[\'basket_id\']);
            if (!$basketItem) continue;

            $qty           = min((float)$item[\'quantity\'], $basketItem->getQuantity());
            $pricePerUnit  = $basketItem->getFinalPrice(); // ціна з урахуванням знижок
            $lineTotal     = round($pricePerUnit * $qty, 2);

            $refundItems[] = [
                \'basket_id\'    => $item[\'basket_id\'],
                \'name\'         => $basketItem->getField(\'NAME\'),
                \'quantity\'     => $qty,
                \'price\'        => $pricePerUnit,
                \'line_total\'   => $lineTotal,
                \'vat_rate\'     => $basketItem->getField(\'VAT_RATE\') ?? 0,
            ];

            $totalRefund += $lineTotal;
        }

        // Перерахунок доставки при частковому поверненні
        $shippingRefund = $this->calculateShippingRefund($order, $returnItems, $totalRefund);

        return [
            \'items\'          => $refundItems,
            \'items_total\'    => $totalRefund,
            \'shipping_refund\'=> $shippingRefund,
            \'total\'          => round($totalRefund + $shippingRefund, 2),
        ];
    }

    private function calculateShippingRefund(
        \Bitrix\Sale\Order $order,
        array $returnItems,
        float $returnItemsTotal
    ): float {
        // Якщо повертається все замовлення — повертаємо доставку повністю
        $totalOrderItems = 0;
        $returnBasketIds = array_column($returnItems, \'basket_id\');

        foreach ($order->getBasket() as $item) {
            $totalOrderItems++;
        }

        if (count($returnBasketIds) === $totalOrderItems) {
            $shipment = $order->getShipmentCollection()->getSystemShipment();
            return $shipment ? (float)$shipment->getDeliveryPrice() : 0.0;
        }

        // Інакше — доставка не повертається (залежить від політики магазину)
        return 0.0;
    }
}

Як створити часткове повернення через Sale API?

Після розрахунку суми створюємо об'єкт OrderReturn і прив'язуємо до нього позиції. Ось повний приклад:

class PartialReturnManager
{
    public function create(
        int    $orderId,
        array  $returnItems,
        string $reason = \'\',
        int    $userId = 0
    ): int {
        \Bitrix\Main\Loader::includeModule(\'sale\');

        $order = \Bitrix\Sale\Order::load($orderId);
        if (!$order) throw new \RuntimeException("Order not found: {$orderId}");

        if ($userId && $order->getUserId() !== $userId) {
            throw new \RuntimeException("Access denied to order {$orderId}");
        }

        // Розраховуємо суми
        $calculator  = new PartialReturnCalculator();
        $refundData  = $calculator->calculateRefundAmount($order, $returnItems);

        // Створюємо об'єкт повернення
        $orderReturn = \Bitrix\Sale\OrderReturn::create($order);
        $orderReturn->setField(\'STATUS_ID\',    \'WAIT\');
        $orderReturn->setField(\'TYPE\',         \'MONEY\');
        $orderReturn->setField(\'REASON\',       $reason ?: \'Часткове повернення\');
        $orderReturn->setField(\'REFUND_AMOUNT\', $refundData[\'total\']);
        $orderReturn->setField(\'COMMENT\',      $this->buildComment($refundData));

        // Додаємо позиції повернення
        foreach ($refundData[\'items\'] as $item) {
            $basketItem = $order->getBasket()->getItemById($item[\'basket_id\']);
            if (!$basketItem) continue;

            $returnItem = $orderReturn->getReturn()->createItem($basketItem);
            $returnItem->setField(\'QUANTITY\', $item[\'quantity\']);
            $returnItem->setField(\'REASON\',   $reason);
        }

        $result = $orderReturn->save();

        if (!$result->isSuccess()) {
            throw new \RuntimeException(
                \'Partial return failed: \' . implode(\'; \', $result->getErrorMessages())
            );
        }

        // Оновлюємо статус замовлення, якщо потрібно
        $this->updateOrderAfterPartialReturn($order, $returnItems);

        return $orderReturn->getId();
    }

    private function updateOrderAfterPartialReturn(
        \Bitrix\Sale\Order $order,
        array $returnItems
    ): void {
        $returnBasketIds   = array_column($returnItems, \'basket_id\');
        $totalBasketItems  = count([...$order->getBasket()]);

        // Якщо повертається останній товар — позначаємо замовлення як частково повернене
        if (count($returnBasketIds) < $totalBasketItems) {
            // Додаємо користувацький статус "Частково повернено"
            // через поле USER_DESCRIPTION або кастомний статус
        }
    }

    private function buildComment(array $refundData): string
    {
        $lines = [\'Часткове повернення:\'];

        foreach ($refundData[\'items\'] as $item) {
            $lines[] = sprintf(
                \'- %s × %s = %s грн.\',
                $item[\'name\'],
                $item[\'quantity\'],
                number_format($item[\'line_total\'], 2)
            );
        }

        if ($refundData[\'shipping_refund\'] > 0) {
            $lines[] = sprintf(\'- Доставка: %s грн.\', number_format($refundData[\'shipping_refund\'], 2));
        }

        $lines[] = sprintf(\'Разом до повернення: %s грн.\', number_format($refundData[\'total\'], 2));

        return implode("\n", $lines);
    }
}

Чому важлива інтеграція з платіжною системою?

Більшість платіжних систем (ЮKassa, Тінькофф, Сбер) підтримують часткове повернення через API. Головний ризик — якщо просто повернути гроші, але не передати чек, то податкові органи нарахують штраф до 50 000 грн за невиконання 54-ФЗ. Наше рішення автоматично формує чек повернення з правильними кодами і відправляє його через ОФД. Згідно з документацією 1С-Бітрікс, OrderReturn коректно обробляє зв'язок з платіжними транзакціями.

Приклад для ЮKassa:

class YooKassaPartialRefund
{
    public function refund(\Bitrix\Sale\Payment $payment, float $amount, array $items): bool
    {
        $paymentId = $payment->getField(\'PS_INVOICE_ID\'); // ID платежу в ЮKassa

        $receipt = $this->buildReceipt($items); // чек для ФНС

        $response = $this->yukassaClient->createRefund([
            \'payment_id\' => $paymentId,
            \'amount\' => [
                \'value\'    => number_format($amount, 2, \'.\', \'\'),
                \'currency\' => \'RUB\',
            ],
            \'description\' => \'Часткове повернення за замовленням #\' . $payment->getOrderId(),
            \'receipt\'     => $receipt,
        ]);

        return isset($response[\'id\']) && $response[\'status\'] !== \'canceled\';
    }

    private function buildReceipt(array $items): array
    {
        $receiptItems = [];

        foreach ($items as $item) {
            $receiptItems[] = [
                \'description\' => $item[\'name\'],
                \'quantity\'    => $item[\'quantity\'],
                \'amount\'      => [
                    \'value\'    => number_format($item[\'price\'], 2, \'.\', \'\'),
                    \'currency\' => \'RUB\',
                ],
                \'vat_code\'    => $this->vatRateToCode((float)$item[\'vat_rate\']),
                \'payment_mode\'    => \'full_payment\',
                \'payment_subject\' => \'commodity\',
            ];
        }

        return [
            \'customer\' => [\'email\' => $this->customerEmail],
            \'items\'    => $receiptItems,
        ];
    }
}

Ключовий момент: при частковому поверненні потрібно передати чек в ФНС через ОФД. ЮKassa робить це автоматично, якщо передати receipt в запиті повернення.

Що входить у роботу?

Компонент Опис
Калькулятор суми повернення Враховує знижки, купони, ПДВ, кількість
API створення повернення PartialReturnManager з валідацією та збереженням
Інтерфейс вибору позицій В особистому кабінеті покупця та адмінці
Інтеграція з платіжними системами ЮKassa, Сбер, Тінькофф (partial refund)
Формування чека для ФНС Коректний чек з кодами 54-ФЗ
Логіка повернення доставки Налаштовується під політику магазину

Процес реалізації та терміни

  1. Аналіз — вивчаємо поточні замовлення, знижки, платіжні системи.
  2. Проектування — обираємо підхід розрахунку, проектуємо API.
  3. Реалізація — пишемо калькулятор, менеджер повернення, інтеграцію з платіжкою.
  4. Тестування — перевіряємо на реальних замовленнях з різними сценаріями (один товар, кілька, зі знижкою, без).
  5. Деплой та документація — вивантажуємо на бойовий, пишемо інструкцію для операторів.

Терміни: базова механіка — 1–2 тижні; повна версія з інтеграцією кількох платіжних систем та 54-ФЗ — 3–5 тижнів. На всі роботи даємо гарантію 6 місяців.

Якщо ви хочете впровадити часткове повернення на своєму проекті, зв'яжіться з нами — безкоштовно оцінимо проект і назвемо точні терміни. Замовте налаштування часткового повернення вже сьогодні: ваші покупці зможуть повертати тільки непотрібні позиції, а податкова отримає правильні чеки.

Як налаштування кошика 1С-Бітрікс вирішує проблему втрати конверсії

Ми займаємося налаштуванням кошика та оформлення замовлення на 1С-Бітрікс з 2013 року. За цей час зіткнулися з типовою проблемою: штатний sale.order.ajax втрачає на кожному кроці 10–15% покупців. Три кроки — і третина тих, хто вже додав товар, іде. Не тому що передумали — інтерфейс спотикається.

sale.order.ajax видає 500-ку, якщо не налаштований хоча б один обробник доставки. Зависає на 15 секунд при розрахунку НПП — запит синхронний, без таймауту. Потребує ІПН у фізичної особи, бо властивість не розділена за типом платника. Кожен такий кейс — прямі втрати, які система не компенсує.

Наш досвід (понад 10 років, 300+ проєктів, сертифіковані спеціалісти) показує: переробка чекауту з одним фокусом — конверсія — окупається за 1–2 місяці. Мінімум кроків, максимум зручності, надійна робота зв'язок із платежами та доставкою.

Чому однокроковий чекаут збільшує конверсію?

Всі поля на одній сторінці. Логічне групування, жодних зайвих переходів:

  • Контактні дані — ім'я, телефон, email. Три поля. Не п'ять, не десять, не «вкажіть дату народження для програми лояльності».
  • Доставка — вибрав місто → побачив способи з цінами та термінами. AJAX-розрахунок через API НПП, Boxberry, Укрпошти. Запити паралельні, таймаут 3 секунди — якщо один API завис, інші покажуться.
  • Оплата — способи фільтруються за вибраною доставкою. Післяплата при самовивозі? Не показуємо.
  • Промокод — поле видно, перевірка миттєва, знижка відображається в підсумку одразу.
  • Підсумок — динамічний перерахунок за будь-якої зміни. Змінив кількість → сума → вартість доставки → підсумок. Без перезавантаження.

Під капотом:

  • Повний AJAX — жодного перезавантаження. Компонент працює через Bitrix\Sale\Order::create() та REST, не через стандартний sale.order.ajax.
  • Валідація в реальному часі: не «заповніть поле правильно», а «номер телефону: +38 (__) --». Маска inputmask + серверна перевірка.
  • Збереження даних при випадковому відході — sessionStorage зберігає введене, при поверненні все на місці.
  • Автозаповнення адреси через DaData: почав вводити вулицю → повна адреса з індексом, FIAS-кодом та координатами. Менше помилок з боку кур'єрської.
  • Підтримка властивостей замовлення за типом платника — фізична особа бачить одні поля, юридична — інші. Перемикач у формі.

Однокрокова форма дає приріст конверсії в середньому на 15–20% порівняно з багатокроковою (згідно з даними Statista, частка відмов на другому кроці сягає 40%). Джерело: Statista, дослідження чекауту в e-commerce.

Як відновити покинуті кошики?

Збереження. Авторизовані — кошик у b_sale_basket, доступний з будь-якого пристрою. Гості — cookie з TTL 30 днів. FUSER_ID прив'язаний до cookie, кошик не пропаде через годину. Синхронізація: додав з телефону, оформив з ноутбука — кошик єдиний.

Повернення. Email-серія: 3 листи. Через 1 годину — нагадування. Через 24 години — «ваш товар закінчується». Через 72 години — персональний промокод на 5–10%. Реалізація через sale.basketcomponent + CEvent::Send() з відкладеною відправкою через агенти. Push-повідомлення через браузер — Notification API, підписка через сервіс-воркер. Ретаргетинг — дані про кошик йдуть у Google Ads через eCommerce-події.

Аналітика відмов. На якому кроці йдуть? Якщо на виборі доставки — ціна шокує. Якщо на оплаті — карта відхиляється, 3D-Secure не проходить. Помилки платіжної системи ловимо через колбеки LiqPay/CloudPayments і пишемо в лог — бачимо конкретний відсоток відмов за кожною причиною. Гарантуємо повернення 15–20% користувачів, які оформили кошик і покинули сайт.

Гостьове замовлення: убити обов'язкову реєстрацію

«Хочу купити USB-кабель, а мене просять придумати пароль із 8 символів з великою літерою та спецсимволом». Обов'язкова реєстрація вбиває 25–30% конверсії на дрібних замовленнях.

  • Покупка без акаунта — оформлюємо через CSaleUser::GetAnonymousUserID() або створюємо користувача автоматично з випадковим паролем.
  • Після оформлення — лист з даними для входу. Хоче — активує акаунт, не хоче — і так отримає замовлення.
  • Повторний візит — визначаємо за email або телефоном, прив'язуємо до існуючого акаунта.
  • Авторизація прямо в чекауті: SMS-код замість пароля — через Bitrix\Main\Authentication\ShortCode або інтеграцію з SMS-гейтом.

Крос-сел: допродажі, які не дратують

У кошику

Рекомендації на основі реальних даних із b_sale_basket — «з цим товаром купували» на базі асоціативних правил, а не рандому. Прив'язка через властивість інфоблоку PROPERTY_ACCESSORIES. Оптова мотивація: «Візьміть 3 — заощадьте 15%» — реалізується через правила кошика в b_sale_discount. Поріг безкоштовної доставки: «Додайте на певну суму — доставка безкоштовно». Простий віджет, але збільшує середній чек на 10–20%.

Управління через адмінку

Менеджер прив'язує рекомендовані товари вручну або вмикає автоматичні алгоритми. Правила відображення: категорія, діапазон цін, наявність. A/B-тестування різних стратегій — без розробника.

Промокоди: правильна реалізація

Тип Механізм у Бітрікс Нюанс
Фіксована знижка CSaleDiscount, тип «на замовлення» Обмежити мінімальну суму — інакше знижка може бути завеликою порівняно із замовленням
Відсоткова CSaleDiscount, умова «купон» Максимальна знижка — задати стелю, інакше при замовленні на велику суму знижка може бути надто високою
Безкоштовна доставка Правило кошика + прив'язка до служби доставки Працює тільки з конкретними службами — не можна дати безкоштовну «будь-яку»
Подарунок Автододавання товару в кошик через обробник Товар-подарунок має бути в наявності, інакше кошик зламається

UX промокоду:

  • Поле видно, але не кричить — не відволікає тих, у кого коду немає.
  • Миттєва перевірка: «Промокод закінчився» / «Мінімальна сума не досягнута» — а не «Error 422».
  • Знижка видна в підсумковому розрахунку окремим рядком.
  • Можна прибрати промокод і застосувати інший.

UX-оптимізація: дрібниці, які вирішують

Десктоп:

  • Прогрес-бар — користувач бачить, де він.
  • Розумні дефолти — найпопулярніший спосіб доставки вже вибраний (визначаємо за статистикою b_sale_order).
  • Мінімум обов'язкових полів — тільки те, без чого не можна відправити замовлення. По батькові? Необов'язково. Коментар? Необов'язково.
  • Перерахунок без лоадерів на 5 секунд — debounce 300ms на AJAX-запитах.

Мобільні:

  • Великі кнопки — палець не промахується. min-height: 48px за гайдами Google.
  • Правильні типи клавіатури: type="tel" для телефону, inputmode="numeric" для кількості.
  • Кнопка «Оформити» зафіксована внизу — position: sticky.
  • Згорнуті секції — екранний простір на 375px дорогий.

Обробка помилок:

  • «Перевірте номер картки» замість «Payment processing error».
  • Автопрокрутка до першої помилки — scrollIntoView({ behavior: 'smooth' }).
  • «Товар закінчився» — обробляємо без втрати заповнених даних. Пропонуємо аналог або прибираємо з перерахунком.

Інтеграції

  • DaData — адреса, ПІБ, ІПН. Підказки під час введення, валідація ФІАС.
  • Google Maps — вибір пунктів видачі на карті, геолокація для визначення міста.
  • НПП, Boxberry, Укрпошта — API-розрахунок вартості та термінів у реальному часі.
  • LiqPay, CloudPayments, ПриватБанк — прийом платежів, рекурентні списання, холдування.
  • CRM — замовлення автоматично йде в Бітрікс24, створюється угода з прив'язкою до контакту.
  • Склад — перевірка залишків через CCatalogStoreProduct::GetList() у реальному часі.
Приклад AJAX-запиту для розрахунку доставки
// Псевдокод для паралельних запитів
$promises = [];
foreach ($tariffs as $tariff) {
    $promises[] = async(function() use ($tariff, $basket) {
        return $tariff->calculate($basket);
    });
}
$results = awaitAll($promises, 3000);

Що входить у роботу

  • Аналіз поточного чекауту та виявлення вузьких місць (аудит конверсії, логів, помилок).
  • Проєктування UX: прототипування однокрокової форми, узгодження із замовником.
  • Розробка компонента чекауту на основі Bitrix\Sale\Order + REST, з заміною sale.order.ajax.
  • Інтеграція з платіжними (LiqPay, CloudPayments, ПриватБанк) та логістичними API (НПП, Boxberry, Укрпошта).
  • Налаштування промокодів, крос-селу, покинутих кошиків.
  • Тестування на реальних сценаріях: десктоп, мобільні, планшети.
  • Передача документації (опис API, інструкції для менеджерів, доступи).
  • Навчання співробітників роботі з новим кошиком.
  • Пост-релізна підтримка — 2 тижні моніторингу та правок.

Строки

Задача Строк
Оптимізація поточного чекауту 1–2 тижні
Однокроковий чекаут з нуля 3–5 тижнів
Система промокодів 1–2 тижні
Крос-сел у кошику 1 тиждень
Механізм покинутих кошиків 2–3 тижні
Комплексна переробка 6–10 тижнів

Зв'яжіться з нами для обговорення вашого проєкту та отримайте консультацію з конкретних завдань. Замовте аудит кошика вже сьогодні — побачите, скільки конверсії втрачається на кожному кроці. Збільшення конверсії чекауту на 1–2% при стабільному трафіку — це зростання виручки без зростання рекламного бюджету. Найшвидший ROI в e-commerce.