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 |







