Настройка синхронизации остатков поставщиков при дропшиппинге 1С-Битрикс

Наша компания занимается разработкой, поддержкой и обслуживанием решений на Битрикс и Битрикс24 любой сложности. От простых одностраничных сайтов до сложных интернет магазинов, CRM систем с интеграцией 1С и телефонии. Опыт разработчиков подтвержден сертификатами от вендора.
Предлагаемые услуги
Показано 1 из 1 услугВсе 1626 услуг
Настройка синхронизации остатков поставщиков при дропшиппинге 1С-Битрикс
Простая
~1 рабочий день
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    811
  • 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 Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    745
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    976

Настройка синхронизации остатков поставщиков при дропшиппинге 1С-Битрикс

Покупатель заказывает товар, который давно распродан у поставщика — классическая проблема дропшиппинга. Чтобы этого не было, остатки на сайте должны отражать реальное наличие у поставщика. Битрикс хранит остатки в b_catalog_store_product, задача — держать эту таблицу в актуальном состоянии.

Архитектура синхронизации

Три варианта получения данных от поставщика:

Push-вебхук от поставщика — поставщик сам уведомляет при изменении остатка. Самый быстрый, требует от поставщика технической возможности отправлять POST-запросы.

Pull-фид по расписанию — агент Битрикс скачивает файл (XML/CSV/JSON) с FTP или HTTP поставщика каждые N минут. Самый распространённый вариант.

Прямой доступ к API поставщика — запрашиваем остаток в момент просмотра товара или добавления в корзину. Нагружает API поставщика, но даёт точные данные.

Pull-синхронизация через фид

Агент Битрикс запускается по расписанию и обновляет остатки:

// /local/lib/Dropshipping/StockSync/FeedProcessor.php
namespace Local\Dropshipping\StockSync;

class FeedProcessor
{
    public function syncSupplier(int $supplierId): SyncResult
    {
        $supplier = SupplierRepository::findById($supplierId);
        $raw      = $this->downloadFeed($supplier['UF_FEED_URL'], $supplier['UF_API_KEY']);
        $items    = $this->parseFeed($raw, $supplier['UF_FEED_FORMAT']);

        $updated = 0;
        $skipped = 0;

        foreach ($items as $item) {
            $productId = SupplierProductMap::findProductId(
                $supplierId,
                $item['sku']
            );

            if (!$productId) {
                $skipped++;
                continue;
            }

            $this->updateStock($productId, $supplier['UF_STORE_ID'], (int)$item['quantity']);
            $updated++;
        }

        return new SyncResult($supplierId, $updated, $skipped);
    }

    private function updateStock(int $productId, int $storeId, int $quantity): void
    {
        $existing = \CCatalogStoreProduct::GetList(
            [],
            ['PRODUCT_ID' => $productId, 'STORE_ID' => $storeId]
        )->Fetch();

        if ($existing) {
            \CCatalogStoreProduct::Update($existing['ID'], ['AMOUNT' => $quantity]);
        } else {
            \CCatalogStoreProduct::Add([
                'PRODUCT_ID' => $productId,
                'STORE_ID'   => $storeId,
                'AMOUNT'     => $quantity,
            ]);
        }

        // Обновляем общий остаток в b_catalog_product
        $total = $this->getTotalStock($productId);
        \CCatalogProduct::Update($productId, ['QUANTITY' => $total]);
    }
}

Парсинг форматов фидов

Поставщики используют разные форматы. Реализуем интерфейс и конкретные парсеры:

interface FeedParserInterface
{
    public function parse(string $raw): array; // возвращает [{sku, quantity}, ...]
}

class CsvFeedParser implements FeedParserInterface
{
    public function parse(string $raw): array
    {
        $lines  = str_getcsv($raw, "\n");
        $result = [];

        // Определяем разделитель: ; или ,
        $delimiter = str_contains($lines[0], ';') ? ';' : ',';

        foreach (array_slice($lines, 1) as $line) { // пропускаем заголовок
            [$sku, $qty] = str_getcsv($line, $delimiter);
            if ($sku && is_numeric($qty)) {
                $result[] = ['sku' => trim($sku), 'quantity' => (int)$qty];
            }
        }

        return $result;
    }
}

class XmlFeedParser implements FeedParserInterface
{
    public function parse(string $raw): array
    {
        $xml    = simplexml_load_string($raw, 'SimpleXMLElement', LIBXML_NOCDATA);
        $result = [];

        foreach ($xml->offer as $offer) {
            $result[] = [
                'sku'      => (string)$offer->vendorCode,
                'quantity' => (int)$offer->stock,
            ];
        }

        return $result;
    }
}

Push-синхронизация (вебхук от поставщика)

// /local/ajax/supplier/stock-webhook.php
\Bitrix\Main\Application::getInstance()->initializeExtended();

$key        = $_SERVER['HTTP_X_SUPPLIER_KEY'] ?? '';
$supplierId = \Local\Dropshipping\SupplierAuth::validate($key);

if (!$supplierId) {
    http_response_code(403);
    die('{"error":"Unauthorized"}');
}

$body  = json_decode(file_get_contents('php://input'), true);
$items = $body['items'] ?? [];

$processor = new \Local\Dropshipping\StockSync\FeedProcessor();
$result    = $processor->syncItems($supplierId, $items);

echo json_encode(['updated' => $result->updated, 'skipped' => $result->skipped]);

Регистрация агента синхронизации

// Агент запускается каждые 30 минут для каждого поставщика
\CAgent::Add([
    'NAME'       => '\Local\Dropshipping\StockSync\SyncAgent::run();',
    'MODULE_ID'  => 'local',
    'PERIOD'     => 1800, // 30 минут
    'NEXT_EXEC'  => date('d.m.Y H:i:s', time() + 1800),
    'ACTIVE'     => 'Y',
]);

Обработка товаров с нулевым остатком

При обнулении остатка важно не просто установить QUANTITY = 0, но и снять товар с продажи или показать «Нет в наличии»:

private function handleZeroStock(int $productId): void
{
    // Устанавливаем статус «Нет в наличии» через b_catalog_product
    \CCatalogProduct::Update($productId, [
        'QUANTITY'             => 0,
        'QUANTITY_TRACE'       => 'Y',
        'CAN_BUY_ZERO'        => 'N',
        'NEGATIVE_AMOUNT_TRACE' => 'N',
    ]);

    // Сбрасываем кеш карточки товара
    \Bitrix\Main\Data\TaggedCache::clearByTag('iblock_id_' . CATALOG_IBLOCK_ID);
}

Логирование и мониторинг

Каждая синхронизация логируется в таблицу HL-блока StockSyncLog:

Поле Значение
UF_SUPPLIER_ID ID поставщика
UF_DATE Дата синхронизации
UF_UPDATED Количество обновлённых позиций
UF_SKIPPED Не сопоставленные артикулы
UF_DURATION Время выполнения, мс
UF_ERROR Текст ошибки (если есть)

Если за последние 2 часа нет успешной синхронизации для активного поставщика — агент-монитор отправляет алерт на почту технического администратора.