Реализация двусторонней синхронизации каталога товаров с МойСклад

Товары на сайте показывают неправильные остатки, менеджеры правят цены в **МойСклад**, а на сайте они не обновляются. Заказы приходится переносить вручную — это приводит к 20–30% ошибок отгрузки и потерям клиентов. **Двусторонняя синхронизация** решает проблему: товары, остатки, заказы и статусы обн

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Реализация двусторонней синхронизации каталога товаров с МойСклад
Сложный
~2-4 недели

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

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1410
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1281
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    979
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1237
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    977
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    993

Товары на сайте показывают неправильные остатки, менеджеры правят цены в МойСклад, а на сайте они не обновляются. Заказы приходится переносить вручную — это приводит к 20–30% ошибок отгрузки и потерям клиентов. Двусторонняя синхронизация решает проблему: товары, остатки, заказы и статусы обновляются автоматически. Результат — актуальные данные на сайте и в МойСклад без вашего участия. Экономия на ручном вводе данных достигает 200 000 руб. в год.

Какие проблемы решает двусторонняя синхронизация?

Расхождение остатков — самая частая боль. Клиент видит товар в наличии, а на складе его уже нет, или наоборот. Наша синхронизация обновляет остатки каждые 5 минут, снижая количество ошибок отгрузки на 80% по сравнению с ручным управлением. Вторая проблема — ручной перенос заказов. Сотрудники тратят до 2 часов в день на дублирование информации. Двусторонняя синхронизация автоматически создаёт заказ в МойСклад при оформлении на сайте и обновляет статус при отгрузке. Третья — конфликты данных. Если цену изменили одновременно в двух системах, мы используем чёткое правило источника истины: цены и остатки — из МойСклад, SEO и изображения — с сайта.

Как мы реализуем интеграцию

Архитектура и стек

Используем REST API МойСклад версии 1.2, PHP 8.3 с фреймворком Laravel 11 и очередями через Redis. Для аутентификации — Bearer-токен. Все запросы идут через HTTP-клиент с повторными попытками при ошибках. Webhook настроены на события изменения заказов, чтобы статусы обновлялись мгновенно. Архитектура учитывает N+1 запросы и использует чанкованную загрузку данных (пачки по 100 элементов).

Согласно официальной документации API МойСклад, это обеспечивает надёжную интеграцию.

Пример реализации клиента API
class MoiSkladClient { private string $baseUrl = 'https://api.moysklad.ru/api/remap/1.2'; private function headers(): array { return [ 'Authorization' => 'Bearer ' . config('services.moysklad.token'), 'Content-Type' => 'application/json;charset=utf-8', 'Accept-Encoding' => 'gzip', ]; } public function get(string $path, array $params = []): array { return Http::withHeaders($this->headers()) ->get("{$this->baseUrl}/{$path}", $params) ->throw() ->json(); } public function post(string $path, array $data): array { return Http::withHeaders($this->headers()) ->post("{$this->baseUrl}/{$path}", $data) ->throw() ->json(); } } 
Сущность Источник истины Примечание
Товары (название, описание, цена) МойСклад Менеджеры правят там
Остатки МойСклад Обновляются при поступлении/продаже
Изображения товаров Сайт Загружаются через CMS
SEO-поля (meta, slug) Сайт Не существуют в МС
Заказы Сайт → МС Создаются на сайте, уходят в МС
Статусы заказов МС → Сайт Менеджер меняет в МС

Получение товаров и остатков

class ProductSyncService { public function fetchProducts(int $offset = 0, int $limit = 100): array { return $this->ms->get('entity/product', [ 'offset' => $offset, 'limit' => $limit, 'expand' => 'productFolder,images', 'filter' => 'archived=false', ]); } public function fetchStocks(): array { return $this->ms->get('report/stock/all/current', [ 'stockType' => 'stock', 'includeRelated' => false, ]); } public function syncToSite(): void { $offset = 0; $limit = 100; do { $response = $this->fetchProducts($offset, $limit); $products = $response['rows']; foreach ($products as $msProduct) { $this->upsertProduct($msProduct); } $offset += $limit; } while ($offset < $response['meta']['size']); $stocks = $this->fetchStocks(); foreach ($stocks as $stock) { Product::whereExternalId($stock['assortmentId']) ->update(['stock' => max(0, (int)$stock['stock'])]); } } private function upsertProduct(array $msProduct): void { $msId = $msProduct['id']; $data = [ 'external_id' => $msId, 'name' => $msProduct['name'], 'article' => $msProduct['article'] ?? null, 'price' => $this->parsePrice($msProduct['salePrices'][0]['value'] ?? 0), 'description' => $msProduct['description'] ?? '', 'ms_updated_at'=> Carbon::parse($msProduct['updated']), ]; $product = Product::updateOrCreate(['external_id' => $msId], $data); } private function parsePrice(int $msPrice): float { return $msPrice / 100; } } 

Передача заказов и webhook

public function pushOrder(Order $order): string { $positions = []; foreach ($order->items as $item) { $positions[] = [ 'assortment' => [ 'meta' => [ 'href' => "{$this->baseUrl}/entity/product/{$item->product->external_id}", 'type' => 'product', ], ], 'quantity' => $item->quantity, 'price' => $item->price * 100, ]; } $msOrder = $this->ms->post('entity/customerorder', [ 'name' => "Заказ #{$order->id}", 'organization' => [ 'meta' => [ 'href' => "{$this->baseUrl}/entity/organization/" . config('services.moysklad.org_id'), 'type' => 'organization', ], ], 'agent' => $this->getOrCreateCounterparty($order->customer), 'positions' => $positions, 'description' => "Источник: сайт\nEmail: {$order->customer->email}", 'attributes' => [ [ 'meta' => ['href' => $this->orderIdAttributeHref()], 'value' => (string)$order->id, ], ], ]); $order->update(['ms_order_id' => $msOrder['id']]); return $msOrder['id']; } 

При изменении заказа в МойСклад webhook уведомляет наш сервер, и статус автоматически обновляется на сайте:

public function handleMsWebhook(Request $request): Response { $events = $request->json('events', []); foreach ($events as $event) { if ($event['meta']['type'] === 'customerorder') { $msOrderId = basename($event['meta']['href']); SyncOrderStatusJob::dispatch($msOrderId); } } return response()->noContent(); } 
class SyncOrderStatusJob implements ShouldQueue { public function handle(MoiSkladClient $ms): void { $msOrder = $ms->get("entity/customerorder/{$this->msOrderId}"); // маппинг статусов и обновление заказа } } 

Как часто обновляются данные и как разрешаются конфликты?

Остатки синхронизируются каждые 5 минут, товары и заказы — каждые 10 минут. Это обеспечивает актуальность без излишней нагрузки на API. При необходимости частоту можно настроить индивидуально.

При двусторонней синхронизации конфликты неизбежны. Наш подход: цена, остатки, артикул — приоритет МойСклад; SEO-поля и изображения — сайт; название — по дате последнего изменения. Мы гарантируем, что вы не потеряете важные изменения. Ошибки отгрузки обходятся в среднем в 50 000 руб. в месяц — синхронизация сводит их к минимуму.

Сравнение подходов

Характеристика Односторонняя Двусторонняя
Товары, цены, остатки
Заказы (сайт→МС)
Статусы (МС→сайт)
Разрешение конфликтов не требуется
Ручной труд менеджеров ~1 час/день автоматически
Ошибки отгрузки 20-30% <5%

Двусторонняя синхронизация в 10 раз сокращает время на обработку заказов и практически исключает ошибки отгрузки.

Почему стоит выбрать двустороннюю синхронизацию?

Более 5 лет мы интегрируем интернет-магазины с МойСклад. За это время реализовали 30+ проектов, включая сложные с тысячью SKU. Наши инженеры имеют подтверждённую экспертизу в Laravel и REST API. Мы даём гарантию на работоспособность интеграции и оперативно исправляем возможные нестыковки. Экономия на ручном вводе данных достигает 200 000 руб. в год, а затраты на исправление ошибок отгрузки снижаются до 50 000 руб. в месяц.

Процесс и сроки работы

  1. Аналитика — изучаем структуру каталога, определяем сущности и правила маппинга.
  2. Проектирование — проектируем интеграцию: какие поля, как часто, обработка ошибок.
  3. Реализация — пишем код, настраиваем webhook, очереди.
  4. Тестирование — запускаем тестовую синхронизацию на копии данных, сверяем результаты.
  5. Деплой — разворачиваем на боевом сервере, проводим мониторинг.

Ориентировочные сроки: односторонняя синхронизация (МойСклад → сайт) — 2–3 рабочих дня; двусторонняя (с заказами, статусами, webhook) — 6–8 рабочих дней. Срок может увеличиться при сложной структуре каталога (вариации, комплекты). Проведём бесплатный аудит вашей текущей системы и предложим оптимальное решение. Свяжитесь с нами, чтобы навсегда забыть о ручном переносе данных. Получите консультацию уже сегодня.