Модуль МойСклад: устройство
Как этим пользоваться — в разделе для склада. Здесь — что внутри.
Состав
apps/backend/src/modules/moysklad-integration:
| Файл | Ответственность |
|---|---|
client.ts | HTTP-клиент JSON API МойСклад |
credentials.ts | шифрование реквизитов (AES, ключ MOYSKLAD_ENCRYPTION_KEY) |
service.ts | модульный сервис Medusa |
retry-schedule.ts | расписание повторов |
stock-sync-report.ts | отчёт о цикле синхронизации остатков |
models/moysklad-connection-config.ts | подключение: реквизиты, флаг активности |
models/moysklad-order-sync.ts | состояние отправки конкретного заказа |
Реквизиты — токен либо логин/пароль — хранятся в базе в зашифрованном виде, не в переменных окружения. В окружении лежит только ключ шифрования; его смена делает сохранённые реквизиты нечитаемыми.
Ключ сопоставления
Товары сопоставляются по коду номенклатуры: SKU варианта Medusa ищется как
filter=code=<sku> в /entity/product. Не по названию и не по артикулу. Вариант без SKU в
синхронизации не участвует.
Остатки: jobs/moysklad-stock-sync.ts
Каждые 15 минут. Постранично (200 записей) собираются варианты и их уровни запаса, тянутся остатки из МойСклад и записываются в стандартный inventory Medusa.
Дополнительно вычитаются количества из заказов сайта за последние 48 часов
(STOCK_RESERVATION_WINDOW_MS): МойСклад ещё не знает об этих заказах, пока сотрудник их не
провёл, и без вычитания остаток был бы завышен.
:::note Статус наличия нигде не хранится Он всегда вычисляется из стандартного inventory: остаток > 0 → «в наличии»; остаток 0 при разрешённом backorder → «под заказ»; иначе «нет в наличии». Отдельного поля со статусом не существует, и заводить его не нужно. :::
Заказы: подписчики + повторы
subscribers/moysklad-order-placed.ts отправляет заказ сразу при оформлении;
subscribers/moysklad-order-canceled.ts помечает отмену. Задача
jobs/moysklad-order-sync-retry.ts раз в минуту добирает то, что не прошло.
Расписание повторов фиксированное — 1 мин, 5 мин, 30 мин, 2 ч, 12 ч, то есть 6 попыток
всего вместе с первой, синхронной. Дальше заказ остаётся в статусе error и ждёт ручного
повтора из админки.
Запись syncing, брошенная упавшим процессом, считается заброшенной через 10 минут
(STALE_SYNCING_MINUTES) и снова становится доступной для повтора — иначе она зависла бы навсегда.
:::warning Отмена отслеживается отдельно от создания
У moysklad_order_sync два независимых набора полей: status/attempts для создания и
cancellation_status/cancellation_attempts/cancellation_updated_at для отмены. Так повтор
отмены не сбрасывает status уже отправленного заказа и не выглядит как «заказ не
синхронизирован». Своя временная метка у отмены нужна, чтобы сверка на стороне создания не
сбрасывала проверку срока повтора.
:::
Что не синхронизируется
Цены, названия, изображения и категории живут в Medusa и в МойСклад не ездят. Из МойСклад берутся только остатки; в МойСклад уходят только заказы.
Грабли реального API
Реальный API строже моков — по заголовкам, ключу сопоставления и лимитам. Подробности
и другие особенности интеграций — docs/known-quirks.md.