Налаштування часткового повернення замовлення 1С-Бітрікс

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

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

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

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

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • 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
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Налаштування часткового повернення замовлення 1С-Бітрікс

Часткове повернення — повернення однієї або кількох позицій із замовлення, а не всього замовлення цілком. Поширений сценарій: покупець замовив 5 товарів, один виявився з дефектом, хоче повернути лише його. Або частина позицій не підійшла. Модуль sale Бітрікс підтримує часткове повернення на рівні API, але інтерфейс в особистому кабінеті та логіка розрахунку суми до повернення потребують налаштування.

Розрахунок суми часткового повернення

Це найнетривіальніша частина. У замовленні може бути знижка на все замовлення, купони, умови доставки, різні ставки ПДВ. При частковому поверненні потрібно перерахувати суму з урахуванням усіх цих факторів.

Підходи:

Підхід 1: Пропорційний — повертаємо пропорцію від загальної суми. Простий, але може давати копійчані розбіжності через округлення.

Підхід 2: За фактичною вартістю позиції — беремо $basketItem->getFinalPrice() × кількість. Це фінальна ціна після всіх знижок на позицію. Рекомендується.

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

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

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, Тінькофф) підтримують partial refund через окремий метод API. Приклад для Ю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 у запиті повернення.

Склад робіт

  • Калькулятор суми повернення з урахуванням знижок, кількості, ПДВ
  • PartialReturnManager: створення повернення через Sale ORM
  • Інтерфейс вибору позицій в особистому кабінеті
  • Інтеграція з платіжними системами для partial refund
  • Формування коригувального чека для податкової
  • Логіка повернення суми доставки за певних умов

Терміни: базова механіка часткового повернення — 1–2 тижні. Повна версія з фіскальними чеками та інтеграцією кількох платіжних систем — 3–5 тижнів.