KT.Teamcopy as .md

Omnichannel-витрина на Saleor для fashion- и beauty-сетей: единые остатки и click-and-collect

Saleor — headless commerce API с открытым исходным кодом — поддерживает мультискладскую модель остатков, резервирование товара на время чекаута и режим click-

AIWebMobileData

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 выдаёт товар. Результат для сети: меньше отмен из-за «нет в наличии», нет двойных продаж дефицитных размеров и оттенков, а каждый из сотен магазинов работает как точка онлайн-выдачи на общем пуле остатков.

Горизонтальная схема потока omnichannel-заказа. Слева блок «Онлайн-витрина / мобильное приложение» с иконкой корзины. Стрелка вправо в центральный блок «Saleor API (GraphQL)», внутри которого подблок «Единый учёт остатков»: связка вариант товара (размер×цвет / оттенок×объём) ↔ склад ↔ три счётчика quantity / quantityReserved / quantityAllocated. От центрального блока вниз веер стрелок к ряду одинаковых иконок «Магазин-склад 1 … Магазин-склад N» (сотни точек, clickAndCollectOption = Local / All). Над веером подпись «Приоритет списания: ближайший магазин → региональный хаб → центральный склад». Справа замкнутый цикл из трёх состояний единицы товара: «В корзине → quantityReserved (таймер stockReservationExpires)» → «Оплачено → quantityAllocated» → «Выдача click-and-collect в магазине». Пунктирные стрелки от складов обратно к API подписаны вебхуками OUT_OF_STOCK / BACK_IN_STOCK / STOCK_UPDATED.

Какой бизнес-процесс улучшает

Процесс «единый остаток → резерв → выдача»: онлайн-заказ выбирает ближайший магазин-склад с наличием по настроенному приоритету, переводит единицу в quantityReserved на время чекаута, после оплаты — в quantityAllocated, и магазин в режиме click-and-collect выдаёт товар. Итог: меньше отмен «нет в наличии», нет двойных продаж дефицитных размеров и оттенков, каждый из сотен магазинов работает точкой онлайн-выдачи на общем пуле остатков.

Контакты

Обсудить сотрудничество

Оставьте актуальные контакты и опишите задачу. Мы вернемся с уточняющими вопросами и предложением по следующему шагу.