Налаштування обміну товару на 1С-Бітрікс

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

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

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

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

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

Налаштування обміну товару на 1С-Бітрікс

Обмін товару — сценарій, що відрізняється від повернення: покупець не хоче грошей, він хоче інший товар (інший розмір, колір, модель). У 1С-Бітрікс немає окремого модуля «Обмін» — це комбінація повернення вихідного товару і створення нового замовлення, зазвичай із заліком вартості. Налаштування потребує кастомної логіки поверх модуля sale.

Бізнес-логіка обміну

Дві схеми обміну, які потрібно підтримувати:

Схема 1: Обмін 1-до-1 — товар однакової вартості. Створюється повернення на вихідний товар, створюється нове замовлення на заміну. Доплата/повернення різниці = 0.

Схема 2: Обмін з доплатою/поверненням різниці — товар іншої вартості. Якщо новий дорожчий — покупець доплачує. Якщо дешевший — повертаємо різницю.

Структура даних обміну

Для зберігання зв'язку «вихідне повернення → нове замовлення» створюємо Highload-блок або таблицю:

class ExchangeTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName(): string { return 'local_sale_exchange'; }

    public static function getMap(): array
    {
        return [
            new \Bitrix\Main\ORM\Fields\IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
            new \Bitrix\Main\ORM\Fields\IntegerField('ORIGINAL_ORDER_ID'),
            new \Bitrix\Main\ORM\Fields\IntegerField('RETURN_ID'),          // ID заявки на повернення
            new \Bitrix\Main\ORM\Fields\IntegerField('NEW_ORDER_ID'),       // ID нового замовлення
            new \Bitrix\Main\ORM\Fields\IntegerField('ORIGINAL_BASKET_ID'), // позиція у вихідному замовленні
            new \Bitrix\Main\ORM\Fields\IntegerField('NEW_PRODUCT_ID'),     // новий товар
            new \Bitrix\Main\ORM\Fields\FloatField('ORIGINAL_PRICE'),
            new \Bitrix\Main\ORM\Fields\FloatField('NEW_PRICE'),
            new \Bitrix\Main\ORM\Fields\FloatField('DIFF_AMOUNT'),          // сума доплати (+) або повернення (-)
            new \Bitrix\Main\ORM\Fields\StringField('STATUS'),              // pending, paid, completed
            new \Bitrix\Main\ORM\Fields\DatetimeField('CREATED_AT'),
        ];
    }
}

Створення обміну

namespace Local\Returns;

class ExchangeManager
{
    public function initiateExchange(array $params): array
    {
        // $params:
        // - original_order_id: int
        // - original_basket_id: int (позиція, яку міняємо)
        // - new_product_id: int (на що міняємо)
        // - new_product_props: [] (розмір, колір тощо)

        \Bitrix\Main\Loader::includeModule('sale');
        \Bitrix\Main\Loader::includeModule('catalog');

        $order = \Bitrix\Sale\Order::load($params['original_order_id']);
        if (!$order || $order->getUserId() !== $this->currentUserId) {
            throw new \RuntimeException('Order not found');
        }

        // Отримуємо вихідну позицію
        $originalItem = null;
        foreach ($order->getBasket() as $item) {
            if ($item->getId() === (int)$params['original_basket_id']) {
                $originalItem = $item;
                break;
            }
        }

        if (!$originalItem) {
            throw new \RuntimeException('Basket item not found');
        }

        $originalPrice = $originalItem->getFinalPrice();

        // Ціна нового товару
        $newPrice = $this->getProductPrice($params['new_product_id']);
        $diffAmount = $newPrice - $originalPrice;

        // Створюємо заявку на повернення вихідного товару
        $returnManager = new ReturnManager();
        $returnId = $returnManager->createReturn(
            $params['original_order_id'],
            [['basket_id' => $params['original_basket_id'], 'quantity' => 1, 'reason' => 'exchange']],
            'EXCHANGE'
        );

        // Створюємо нове замовлення на заміну
        $newOrderId = $this->createExchangeOrder(
            $order->getUserId(),
            $params['new_product_id'],
            $params['new_product_props'] ?? [],
            $diffAmount,
            $order
        );

        // Зберігаємо зв'язок
        $exchangeId = ExchangeTable::add([
            'ORIGINAL_ORDER_ID' => $params['original_order_id'],
            'RETURN_ID'         => $returnId,
            'NEW_ORDER_ID'      => $newOrderId,
            'ORIGINAL_BASKET_ID'=> $params['original_basket_id'],
            'NEW_PRODUCT_ID'    => $params['new_product_id'],
            'ORIGINAL_PRICE'    => $originalPrice,
            'NEW_PRICE'         => $newPrice,
            'DIFF_AMOUNT'       => $diffAmount,
            'STATUS'            => $diffAmount > 0 ? 'pending_payment' : 'pending_ship',
            'CREATED_AT'        => new \Bitrix\Main\Type\DateTime(),
        ])->getId();

        return [
            'exchange_id'  => $exchangeId,
            'return_id'    => $returnId,
            'new_order_id' => $newOrderId,
            'diff_amount'  => $diffAmount,
            'needs_payment'=> $diffAmount > 0,
        ];
    }

    private function createExchangeOrder(
        int $userId,
        int $productId,
        array $props,
        float $diffAmount,
        \Bitrix\Sale\Order $originalOrder
    ): int {
        $order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
        $order->setPersonTypeId($originalOrder->getPersonTypeId());

        // Копіюємо адресу доставки з вихідного замовлення
        $propertyCollection = $order->getPropertyCollection();
        foreach ($originalOrder->getPropertyCollection() as $prop) {
            $newProp = $propertyCollection->getItemByOrderPropertyId($prop->getPropertyId());
            if ($newProp) {
                $newProp->setValue($prop->getValue());
            }
        }

        $basket = \Bitrix\Sale\Basket::create(SITE_ID);
        $item   = $basket->createItem('catalog', $productId);
        $item->setField('QUANTITY', 1);

        if ($props) {
            $item->setField('PROPS', $props);
        }

        $order->setBasket($basket);

        // Якщо повертаємо різницю — створюємо знижку на суму (originalPrice - newPrice)
        if ($diffAmount < 0) {
            $order->getDiscount()->setData([
                'COUPON_DISCOUNT' => abs($diffAmount),
            ]);
        }

        // Копіюємо доставку
        $shipmentCollection = $order->getShipmentCollection();
        $shipment = $shipmentCollection->createItem();
        $shipment->setField('DELIVERY_ID', $this->getDefaultDeliveryId());

        $result = $order->save();

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

        return $order->getId();
    }
}

Синхронізація статусів обміну

При завершенні повернення (отримано старий товар) і оплаті нового замовлення — позначаємо обмін завершеним:

\Bitrix\Main\EventManager::getInstance()->addEventHandler(
    'sale',
    'OnSaleOrderReturnStatusChange',
    function (\Bitrix\Main\Event $event) {
        if ($event->getParameter('NEW_STATUS_ID') !== 'RECEIVED') return;

        $returnId = $event->getParameter('RETURN_ID');

        $exchange = ExchangeTable::getList([
            'filter' => ['RETURN_ID' => $returnId],
            'limit'  => 1,
        ])->fetch();

        if (!$exchange) return;

        // Перевіряємо, чи оплачено нове замовлення
        $newOrder = \Bitrix\Sale\Order::load($exchange['NEW_ORDER_ID']);
        if ($newOrder && $newOrder->isPaid()) {
            ExchangeTable::update($exchange['ID'], ['STATUS' => 'completed']);
        } else {
            ExchangeTable::update($exchange['ID'], ['STATUS' => 'awaiting_payment']);
        }
    }
);

Повідомлення покупцю про обмін

class ExchangeNotifications
{
    public static function sendExchangeCreated(int $exchangeId): void
    {
        $exchange = ExchangeTable::getById($exchangeId)->fetch();

        $diffAmount = (float)$exchange['DIFF_AMOUNT'];

        if ($diffAmount > 0) {
            $message = "Обмін створено. Для завершення необхідно доплатити {$diffAmount}. "
                . "Посилання на оплату: https://example.com/order/{$exchange['NEW_ORDER_ID']}/pay/";
        } elseif ($diffAmount < 0) {
            $message = "Обмін схвалено. Після отримання товару повернемо "
                . abs($diffAmount) . " на вашу картку.";
        } else {
            $message = "Обмін схвалено. Нове замовлення #{$exchange['NEW_ORDER_ID']} буде відправлено "
                . "після отримання товару, що повертається.";
        }

        \CEvent::Send('EXCHANGE_CREATED', SITE_ID, [
            'ORDER_ID'    => $exchange['ORIGINAL_ORDER_ID'],
            'NEW_ORDER_ID'=> $exchange['NEW_ORDER_ID'],
            'MESSAGE'     => $message,
        ]);
    }
}

Склад робіт

  • Таблиця local_sale_exchange для зберігання зв'язку повернення ↔ нове замовлення
  • Клас ExchangeManager: створення повернення + нового замовлення в одній транзакції
  • Форма обміну в особистому кабінеті: вибір нового товару/розміру
  • Обробка доплати при обміні на дорожчий товар
  • Обробник подій: автоматичний статус обміну при отриманні товару
  • Email-повідомлення: створення обміну, отримання товару, завершення

Терміни: базова логіка обміну 1-до-1 — 2–3 тижні. Обмін із доплатою та повною автоматизацією — 4–6 тижнів.