Setting up automatic order transfer to the supplier when dropshipping 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages

Configuring Automatic Order Transmission to the Supplier in 1C-Bitrix Dropshipping

A manager should not manually forward an email to the supplier for every order. This slows down processing, introduces errors, and does not scale. Automatic transmission is an order-creation event handler that routes line items to suppliers without human involvement.

Event and Trigger Point

Bitrix fires the OnSaleOrderSaved event every time an order is saved. We are only interested in the moment a new order is created:

// /local/php_interface/init.php
AddEventHandler(
    'sale',
    'OnSaleOrderSaved',
    function(\Bitrix\Sale\Order $order) {
        // New orders only, not updates
        if ($order->isNew()) {
            \Local\Dropshipping\OrderDispatcher::dispatch($order);
        }
    }
);

Order Dispatcher

namespace Local\Dropshipping;

class OrderDispatcher
{
    public static function dispatch(\Bitrix\Sale\Order $order): void
    {
        $grouped = self::groupBasketBySupplier($order->getBasket());

        foreach ($grouped as $supplierId => $lines) {
            $supplier = SupplierRepository::findById($supplierId);
            if (!$supplier) continue;

            $payload = self::buildPayload($order, $supplier, $lines);

            $sent = match ($supplier['UF_CHANNEL']) {
                'webhook' => WebhookSender::send($supplier, $payload),
                'email'   => EmailSender::send($supplier, $order, $lines),
                'ftp'     => FtpSender::send($supplier, $payload),
                default   => false,
            };

            SupplierOrderLog::create([
                'order_id'    => $order->getId(),
                'supplier_id' => $supplierId,
                'status'      => $sent ? 'sent' : 'failed',
                'payload'     => json_encode($payload),
            ]);
        }
    }

    private static function groupBasketBySupplier(
        \Bitrix\Sale\Basket $basket
    ): array {
        $result = [];
        foreach ($basket as $item) {
            $supplierId = SupplierRepository::getByProduct((int)$item->getProductId());
            if ($supplierId) {
                $result[$supplierId][] = $item;
            }
        }
        return $result;
    }
}

Payload for the Supplier

The data structure transmitted to the supplier must unambiguously identify the order and contain everything needed for picking and shipping:

private static function buildPayload(
    \Bitrix\Sale\Order $order,
    array $supplier,
    array $items
): array {
    $props = $order->getPropertyCollection();

    return [
        'order_id'        => $order->getId(),
        'order_date'      => $order->getDateInsert()->format('Y-m-d H:i:s'),
        'delivery_address' => [
            'city'    => $props->getItemByOrderPropertyCode('CITY')?->getValue(),
            'address' => $props->getItemByOrderPropertyCode('ADDRESS')?->getValue(),
            'zip'     => $props->getItemByOrderPropertyCode('ZIP')?->getValue(),
        ],
        'recipient' => [
            'name'  => $props->getItemByOrderPropertyCode('NAME')?->getValue(),
            'phone' => $props->getItemByOrderPropertyCode('PHONE')?->getValue(),
            'email' => $props->getItemByOrderPropertyCode('EMAIL')?->getValue(),
        ],
        'items' => array_map(fn($item) => [
            'sku'      => SupplierRepository::getSupplierSku($item->getProductId(), $supplier['ID']),
            'name'     => $item->getField('NAME'),
            'quantity' => (int)$item->getQuantity(),
            'price'    => (float)$item->getPrice(),
        ], $items),
        'comment'  => $order->getField('USER_DESCRIPTION'),
        'store_id' => $supplier['UF_STORE_ID'],
    ];
}

Retry Logic on Failure

Network errors happen. When transmission fails, the order enters a retry queue:

// /local/agents/retry_failed_dispatches.php
// Agent runs every 15 minutes

$failed = SupplierOrderLog::findFailed(maxAttempts: 3, olderThan: 15);

foreach ($failed as $log) {
    $order    = \Bitrix\Sale\Order::load($log['order_id']);
    $supplier = SupplierRepository::findById($log['supplier_id']);

    $sent = WebhookSender::send($supplier, json_decode($log['payload'], true));
    SupplierOrderLog::incrementAttempts($log['id'], $sent ? 'sent' : 'failed');
}

After three failed attempts, the manager is notified with the error details.

Implementation Timeline

Scope Timeline
Single supplier, email 2–3 days
Multi-supplier, webhook + email + queue 1–1.5 weeks
With logging, retries, and manager notifications 2 weeks