Fashion- и beauty-сеть с сотнями магазинов теряет продажи в двух точках: товар «есть на сайте, но нет в магазине» и наоборот. Причина — раздельный учёт онлайн- и офлайн-остатков. Omnichannel-витрина решает это, когда онлайн-заказ работает с тем же физическим остатком, что и касса магазина, и резервирует конкретную единицу в ближайшей к покупателю точке. Ниже — разбор того, что для этой задачи даёт Saleor, headless commerce API с открытым исходным кодом (BSD-лицензия), на основе его документации. Это не кейс KT.Team, а обзор возможностей инструмента.
Что получает сеть: бизнес-результат
- Click-and-collect из любого магазина. Каждый магазин регистрируется как склад и становится точкой выдачи. Покупатель оформляет заказ онлайн, забирает в удобной точке — без отдельной интеграции для «самовывоза».
- Единые остатки вместо двух учётов. Один API отдаёт доступность товара по всем складам сразу. Размер «38, чёрный» виден там, где он физически лежит, — это снижает долю заказов, отменённых из-за отсутствия товара.
- Резерв на время оформления. Пока покупатель проходит чекаут, единица товара блокируется. Для дефицитных размеров и лимитированных beauty-линеек это устраняет двойную продажу последней штуки.
Мультискладская модель остатков
В Saleor остаток (`Stock`) — это связка «вариант товара ↔ склад ↔ количество». Каждый вариант (для fashion это SKU размер×цвет, для beauty — оттенок×объём) хранит остаток отдельно по каждому складу (Stock Overview). Сеть из сотен магазинов моделируется как сотни складов; касса каждого магазина и онлайн-витрина работают с одной и той же записью остатка.
Каждая запись остатка хранит три величины: `quantity` (всего), `quantityAllocated` (зарезервировано под подтверждённые заказы) и `quantityReserved` (заблокировано на время чекаута). Это разделение и есть основа честной доступности: витрина показывает не «сколько лежит на полке», а «сколько реально можно продать прямо сейчас».
Склад привязывается к каналу продаж (`channel`); один склад может обслуживать несколько каналов. Это позволяет вести разные канала (например, RU-розница и опт) на одном пуле остатков без дублирования данных.
Резервирование на время чекаута
Saleor поддерживает опциональное резервирование остатка под корзину на настраиваемое время (Stock Reservation). Когда покупатель кладёт товар в корзину, единицы вычитаются из доступного количества, которое видят другие пользователи. Сценарии:
1. Чекаут завершён в отведённое время — временный резерв превращается в постоянную аллокацию под заказ.
2. Время истекло — резерв снимается, товар снова доступен другим.
3. Поле `stockReservationExpires` в GraphQL-типе `Checkout` отдаёт время истечения резерва в ISO-8601 — фронтенд может показать таймер «товар закреплён за вами ещё N минут».
Резерв настраивается отдельно для авторизованных и анонимных покупателей в Site settings → Checkout Configuration. Для fashion с её распродажами размеров и для лимитированных beauty-дропов это снимает классический конфликт «двое купили последнюю штуку».
Click-and-collect: магазин как точка выдачи
Режим click-and-collect задаётся на уровне склада через `clickAndCollectOption` с тремя значениями (Stock Overview):
- Local stock — выдача только из собственного остатка этого магазина. Покупатель видит «доступно к самовывозу здесь сегодня».
- All warehouses — заказ на самовывоз можно собрать из остатков всех складов канала (товар довозится в точку выдачи).
- Disabled — точка не работает на самовывоз.
При выборе click-and-collect Saleor использует адрес склада для расчёта налогов — это корректно для сети с точками в разных налоговых юрисдикциях. Для покупателя это означает реальный ответ на вопрос «в каком магазине рядом есть мой размер прямо сейчас».
Маршрутизация заказа и приоритет складов
Saleor умеет фулфилить один заказ из нескольких складов и создавать несколько отгрузок по одному заказу. Ключевое для управления запасами — «control stock allocation priority among warehouses»: порядок списания между складами настраивается. Сеть задаёт логику «сначала ближайший к покупателю магазин, затем региональный хаб, затем центральный склад», и заказ автоматически резервирует товар в нужной точке.
События об остатках публикуются через вебхуки: `PRODUCT_VARIANT_OUT_OF_STOCK`, `PRODUCT_VARIANT_BACK_IN_STOCK`, `PRODUCT_VARIANT_STOCK_UPDATED`. Они срабатывают при изменении доступности по складам канала, что позволяет синхронизировать остатки с внешними системами (ERP, кассовое ПО) без поллинга.
Архитектурная рамка
Saleor — headless API: бизнес-логика маршрутизации, нотификаций и интеграций с кассами выносится в отдельные сервисы рядом с ядром через вебхуки и GraphQL, без форка самого Saleor. Это отвечает принципу минимальной модификации ядра и сохраняет отчуждаемость: команда или подрядчик меняется без переписывания платформы. Учёт остатков остаётся в одном месте, а каналы, кассы и витрина — это потребители одного API.
Вывод по бизнес-процессу
Процесс «единый остаток + резерв + выдача» на Saleor выглядит так: онлайн-заказ обращается к мультискладской модели → по приоритету складов выбирается ближайшая точка с наличием → единица переходит из `quantity` в `quantityReserved` на время чекаута → после оплаты резерв становится аллокацией под заказ → магазин-склад в режиме click-and-collect выдаёт товар. Результат для сети: меньше отмен из-за «нет в наличии», нет двойных продаж дефицитных размеров и оттенков, а каждый из сотен магазинов работает как точка онлайн-выдачи на общем пуле остатков.