Главная

7 мин

Минимальный контракт API для продаж и склада: сущности и события

Минимальный контракт API для продаж и склада: набор объектов и событий, который ускоряет подключение новых сервисов без переделок и потери данных.

Минимальный контракт API для продаж и склада: сущности и события

Зачем нужен минимальный контракт и что он решает

Интеграции между продажами и складом часто тормозят не из-за API как такового, а из-за разнобоя в смыслах. В одной системе это «клиент», в другой - «контрагент». Где-то статус «Отгружено» означает «выехало со склада», а где-то - «передано перевозчику». Добавьте разные идентификаторы: один сервис хранит свой номер заказа, другой генерирует новый, и через пару недель уже непонятно, что с чем связано.

«Быстрые» интеграции без общего контракта выглядят дешевле, но позже становятся источником потерь: дубли, неверные остатки, повторные списания, ручные сверки и срочные правки при каждом новом сервисе. Особенно больно, когда подключается еще один канал продаж или новая WMS: старые точечные договоренности начинают конфликтовать.

Минимальный контракт - это общий набор объектов и событий, который нужен всем системам, чтобы одинаково понимать одну и ту же операцию. Он не пытается описать все нюансы ERP, CRM и склада. Он фиксирует базовую общую часть: единые идентификаторы, ключевые статусы и обязательные поля.

Обычно такой контракт покрывает простую цепочку: продажа (создание заказа и изменения), резерв (что зарезервировано и почему), отгрузка (что физически ушло со склада), приемка (что пришло обратно или поступило на склад), корректировки (инвентаризация, списания, пересортица).

Простой пример: новый сервис доставки получает событие «Отгрузка создана» и не гадает, что значит статус, где брать адрес и как сопоставить позиции. Он читает один формат - с теми же ID и правилами, что и остальные участники интеграции.

Принципы: границы, источники данных и идентификаторы

Чтобы контракт для продаж и склада реально ускорял интеграции, важно договориться не о списке полей, а о правилах. Они отвечают на четыре вопроса: где проходит граница домена, кто владелец данных, как выглядят идентификаторы, и что считать справочником, а что транзакцией.

Границы доменов: не смешивать ответственность

Продажи, склад, финансы и доставка часто используют одни и те же слова («статус», «отгрузка», «оплата»), но вкладывают разные смыслы. Если смешать ответственность, любая правка превращается в цепную реакцию.

Ориентир простой: каждый домен хранит и меняет только то, чем управляет. Продажи управляют заказом и его жизненным циклом, склад - движениями и резервом, финансы - платежами и сверкой, доставка - маршрутом и трекингом.

Один источник правды для каждого объекта

Для каждой сущности выбирается один мастер: система, которая создает и изменяет объект. Остальные получают копию и могут добавлять свои атрибуты, но не переписывают чужую часть.

Например, карточка товара (наименование, штрихкод, единица измерения) принадлежит PIM или ERP. Склад не должен «исправлять» название товара, а продажи - менять базовую упаковку, даже если так удобнее в интерфейсе.

Идентификаторы: неизменяемость и сопоставление

Сразу заложите два типа ID: внутренний (в системе-владельце) и внешний (для интеграции). Внешний ID должен быть стабильным и не меняться при переносах, слияниях и миграциях.

Практичное правило: любой сервис принимает запросы с внешним ID и возвращает оба. Так проще строить соответствия и чинить расхождения без ручных таблиц.

Справочники и транзакции: разные модели жизни

Справочники описывают «что это» (товар, склад, контрагент, валюта) и меняются редко. Транзакции описывают «что произошло» (заказ, отгрузка, возврат, приход, перемещение) и фиксируются как факты.

Если сомневаетесь, задайте вопрос: объект можно переименовать без последствий или любое изменение должно быть видно как событие? Во втором случае это транзакция, и ее лучше хранить как отдельную сущность с датой, автором и причиной.

Базовые справочные сущности: что должно быть всегда

Если вы хотите, чтобы новый сервис подключался без долгих согласований, начните с общего словаря. Контракт для продаж и склада чаще ломается не на заказах и остатках, а на том, что разные системы по-разному понимают, кто продает, где лежит товар и что именно считается одной позицией.

Хорошая база справочников - это не «полные мастер-данные», а короткий набор сущностей, которые дают контекст любой операции и позволяют сопоставлять документы между системами.

Минимальный набор, который почти всегда нужен:

  • Организация и подразделение: кто владелец операции (юрлицо) и где она произошла (филиал/точка). Часто хватает company_id, branch_id, названия и статуса активности.
  • Склад и место хранения: склад нужен почти всем, а зона или ячейка - когда важна точность (например, адресное хранение). Даже если ячеек пока нет, полезно зарезервировать поле location_id.
  • Товар и SKU (вариант): товар - «что продаем» (модель), SKU - «в каком виде» (цвет, комплектация, серийность). Добавьте единицу измерения и коэффициенты (штука, короб, кг), чтобы не спорить о пересчетах.
  • Контрагент: минимальная карточка покупателя или поставщика для документов. Не тяните сразу все реквизиты, но храните стабильный counterparty_id, тип (customer/supplier), имя и, при необходимости, налоговый идентификатор.
  • Actor (пользователь или сервис): кто создал, изменил, подтвердил. Это помогает в аудите и разборе инцидентов.

Пример из практики: заказ оформлен в интернет-магазине на филиал А, отгрузка делается со склада B, а сборка идет из конкретной зоны. Если в справочниках нет единых branch, warehouse и location, то два документа уже не получится надежно склеить, даже при одинаковых номерах.

Держите эти сущности небольшими, с понятными идентификаторами и признаком архивности. Остальное можно наращивать позже, не меняя основу.

Складские сущности: остатки, резервы и движения

Склад ломает интеграции не столько сложной логикой, сколько разными трактовками простых слов: «остаток», «резерв», «движение». В контракт важно заложить эти сущности так, чтобы любой новый сервис понимал: сколько товара доступно, что уже обещано клиентам, и почему цифры изменились.

Остаток (stock balance) - снимок «сколько есть сейчас» в конкретном месте. Его нужно раскладывать как минимум по складу (warehouse), месту (location) и SKU. Важно фиксировать статус количества: например, «доступно», «в карантине/брак», «в пути», чтобы сервисы не списывали то, чем нельзя торговать.

Резерв (reservation) отвечает на вопрос «что уже занято». Резерв всегда ссылается на причину (обычно заказ или строку заказа), имеет автора (система/пользователь), срок действия и явную отмену. Так вы избегаете вечных резервов, когда заказ уже отменен, а склад так и не освободился.

Движение (stock movement) - источник правды о том, почему поменялся остаток: приход, расход или перемещение. Когда движения есть, спор «товар пропал» быстро превращается в «вот операция, вот причина».

Минимальные поля, которые стоит закрепить

Лучше договориться о небольшом наборе, который одинаков во всех сервисах:

  • warehouse_id, location_id, sku_id (где и что)
  • quantity и единица измерения (сколько)
  • stock_status (доступный, брак, в пути)
  • reservation_id или reference (на что зарезервировано/списано)
  • movement_type и reason_code (что произошло и почему)

Инвентаризация (inventory count) добавляет «факт»: сколько реально нашли, какая разница с учетом и кто подтвердил результат.

Партии/серии/срок годности подключайте только если это влияет на продажи или списания (например, лекарства, питание, гарантийная техника). Тогда эти атрибуты должны появляться одинаково в остатках, резервах и движениях. Если они живут только в одном месте, учет быстро начнет расходиться.

Продажи: заказ, отгрузка, возврат и платежи

Чтобы новый сервис быстро подключался к продажам, ему обычно достаточно четырех сущностей: заказ, отгрузка, возврат и платеж.

Заказ: что покупатель попросил

Заказ фиксирует намерение, а не факт отгрузки. Его должно быть можно прочитать без походов в другие сервисы: посчитать сумму, понять состав и сроки.

Минимальный набор, который чаще всего спасает от переделок:

  • Идентификаторы: order_id, номер заказа, дата/время создания, клиент (customer_id)
  • Строки: item_id/sku, количество, цена, скидка, ставка/сумма налога, итог по строке
  • Деньги: валюта, суммы до/после налогов, округления
  • Адреса и контакт: доставка и биллинг (как минимум страна/город/строка адреса)
  • Сроки: обещанная дата отгрузки/доставки, комментарий по условиям

Статусы заказа должны быть простыми и однозначными. Рабочий минимум: draft (черновик), confirmed (подтвержден), canceled (отменен), closed (закрыт). Важнее не названия, а правило: статус меняется только по понятному событию (подтвердили, отменили, полностью отгрузили и закрыли).

Отгрузка и возврат: что реально произошло

Отгрузка (shipment/fulfillment) описывает факт: что именно уехало, когда и откуда. Важно хранить связь с заказом и состав отгрузки по строкам, потому что частичные отгрузки - норма.

Возврат (return) должен ссылаться на конкретную отгрузку или ее строки, чтобы не возникало споров, что именно вернули. Добавьте причину возврата и состояние товара (например, новый/вскрыт/поврежден).

Платеж можно держать минимальным: payment_id, order_id, сумма, валюта, способ, статус (создан, авторизован, оплачен, отменен, возврат), дата операции. Если заказ подтвержден, но платеж только авторизован, сервисы доставки и поддержки не должны считать его «оплаченным» по умолчанию.

События интеграции: какие и зачем

Интеграционные события нужны, чтобы сервисы продаж и склада узнавали о фактах, которые уже произошли, и реагировали без прямых вызовов друг друга. Один сервис публикует факт, остальные подписываются и действуют.

Важно различать событие и команду. Команда - это запрос «сделай, пожалуйста» (например, зарезервируй товар). Событие - уведомление «это уже случилось» (например, резерв создан). Команды обычно требуют ответ и могут быть отклонены. События не спрашивают разрешения и должны быть правдой.

По продажам минимальный набор событий часто сводится к OrderCreated, OrderConfirmed, OrderCanceled. Этого хватает, чтобы подключать антифрод, бонусы, уведомления, печать документов и аналитику.

Для склада обычно нужны факты о резерве и изменении остатков: StockReserved, StockReleased, StockMoved, StockAdjusted (корректировка по инвентаризации/браку). Они помогают опираться на историю, а не угадывать состояние.

Логистика добавляет жизненный цикл отгрузки и возврата: ShipmentCreated, ShipmentShipped, ShipmentDelivered, ReturnReceived. Тогда доставка, CRM и поддержка видят статус одинаково, без ручных сверок.

Чтобы события были полезны и безопасны, в каждом сообщении держите обязательный минимум:

  • eventId
  • type
  • occurredAt
  • source
  • version

Если есть цепочка действий (например, заказ создал резерв, затем создалась отгрузка), добавляйте correlationId. Он связывает события в одну историю и сильно упрощает поиск проблем в интеграции.

Правила контракта: статусы, версии, идемпотентность

Чтобы контракт не развалился через месяц, нужны правила вокруг сущностей и событий. Они делают интеграции предсказуемыми, даже когда сервисы развиваются независимо.

У каждого объекта должны быть общие базовые поля, чтобы его можно было хранить, обновлять и сверять между системами:

  • id (стабильный идентификатор объекта)
  • createdAt и updatedAt (когда появился и когда менялся)
  • status (состояние объекта сейчас)
  • source или originSystem (кто создал запись, если это важно для расследований)

Со статусами лучше быть скучными: короткий список и четкие правила переходов. Главное - запрет на неожиданные прыжки. Если заказ уже shipped, он не должен внезапно стать draft. Когда нужен откат, используйте отдельное событие (например, возврат), а не переписывание истории.

Идемпотентность нужна в двух местах: в запросах и в событиях.

  • Для команд (создать заказ, подтвердить резерв) используйте idempotencyKey: при повторе сервер обязан вернуть тот же результат, а не создавать дубликат.
  • Для событий закладывайте повторную доставку как норму: потребитель должен уметь обработать одно и то же событие два раза без побочных эффектов, опираясь на eventId и хранение факта обработки.

Версионирование схем делайте простым: совместимость назад обязательна. Новые поля можно добавлять, старые нельзя переименовывать и менять смысл. Если поле устарело, помечайте его как deprecated и задавайте срок вывода.

Ошибки должны быть удобны для поддержки: код, понятное сообщение и детали по полям. Если не прошла валидация, возвращайте общий код (например, VALIDATION_ERROR) и список проблемных полей с причинами - так интегратор исправит запрос без переписки и догадок.

Как собрать контракт за 1-2 недели: пошаговый план

Чтобы уложиться в 1-2 недели, не пытайтесь описать весь бизнес сразу. Начните с минимума, который дает ценность: продать, отгрузить, принять поставку, пересчитать склад. Эти сценарии быстро покажут, каких объектов и событий не хватает.

План работ (без лишней бюрократии)

Соберите 3-4 минимальных сценария и запишите их как короткие истории: кто что делает и какой результат должен увидеть (например, заказ создан, товар зарезервирован, отгрузка подтверждена).

Дальше нарисуйте на одном листе сущности и связи: товар, склад, контрагент, заказ, отгрузка, приход, движение, остаток, резерв. На этом шаге важно договориться об идентификаторах: где мастер, где внешний ключ, что считается «одним и тем же объектом».

Затем зафиксируйте статусы и переходы для заказа и отгрузки. Не нужно много: 4-6 статусов, но с понятными правилами, что можно менять и что является финальным.

После этого определите события и их payload. Для каждого события ответьте на три вопроса: что случилось, какие ключи нужны получателю, как понять, что событие уже обработано (через eventId, версию и правила дедупликации).

И отдельно согласуйте «мелочи», которые ломают интеграции: время и часовой пояс, формат дат, округления, валюта, единицы измерения, правила пересчета (штуки, коробки, кг).

После согласования сделайте тестовые данные и эталонные примеры сообщений. Это экономит дни обсуждений: все смотрят на один и тот же образец.

Что должно быть готово на выходе

  • Одна схема сущностей и связей (читаемая без пояснений).
  • Таблица статусов и переходов для заказа и отгрузки.
  • Перечень событий с кратким описанием и примером payload.
  • Набор тестовых объектов и 5-10 эталонных сообщений для проверок в разных системах.

Частые ошибки и ловушки при проектировании

Частая проблема - когда справочник и транзакция живут в одном объекте. Например, в «Товаре» начинают хранить последнюю цену продажи, остаток и дату последней отгрузки. В итоге справочник постоянно меняется, кеши ломаются, а новые сервисы не понимают, что считать источником правды.

Вторая ловушка - изменяемые идентификаторы и слабые связи между документами. Если номер заказа можно «переименовать», а отгрузка не хранит ссылку на заказ, то через месяц никто не сможет уверенно ответить, что именно было отгружено и за что вернули деньги.

Еще больнее, когда резервы отсутствуют или «прячутся» в остатке. Тогда продажи видят «остаток 10» и продают, а склад уже отложил 8 под другой заказ. Начинаются споры и ручные сверки.

События интеграции часто делают без версии и без correlationId. Тогда поиск причины расхождения превращается в гадание: какое событие было первым, где потерялось подтверждение, к какому заказу относится движение.

Чтобы контракт помогал, а не пугал, не перегружайте модель с первого дня. Обычно достаточно закрыть базовые сценарии и оставить место для расширения.

Чаще всего забывают:

  • частичные отгрузки (один заказ уехал в 2-3 машины)
  • частичные возвраты (вернули 1 из 5 позиций)
  • раздельные статусы «создано», «подтверждено», «отменено» для каждого документа
  • идемпотентность на событиях и командах

Пример: сервис доставки прислал событие «Отгрузка создана», а потом «Отгрузка обновлена» без версии. Склад пересчитал движение дважды, и остатки «уплыли». Версия события и correlationId быстро показали бы, где произошло повторение.

Быстрая проверка: чеклист минимального контракта

Перед тем как отдавать контракт новым командам и внешним сервисам, полезно сделать короткую проверку. Цель простая: интеграция не должна держаться на догадках и не должна ломаться при повторной доставке событий.

Пробегитесь по пунктам и честно ответьте «да/нет»:

  • Единые идентификаторы: для order, shipment, sku, warehouse, location есть стабильные ID, которые не меняются со временем и не зависят от конкретной системы-источника.
  • Статусы и переходы: для заказа и отгрузки определены статусы и разрешенные переходы. Понятно, что делать при отмене, частичной отгрузке и возврате.
  • Склад как отдельная модель: есть сущности для резерва (reservation) и движения (stock movement), а не только «остаток числом». Можно объяснить, откуда взялся остаток и почему он изменился.
  • События не пустые: каждое событие несет eventId, время, source, version и correlationId, чтобы связать цепочку «заказ -> отгрузка -> платеж» и разбирать инциденты.
  • Повторы и точность: описаны правила идемпотентности и повторной доставки (что считается дублем, по чему дедупликация). Согласованы единицы измерения, округления и часовой пояс.

Если по двум и более пунктам ответ «нет», сюрпризы почти гарантированы. Типичный случай: сервис доставки присылает повтор ShipmentCreated, и без идемпотентности вы получаете двойное списание со склада или второй раз отправленный чек. Такая проверка занимает 10 минут, но экономит дни на разборе ошибок в продакшене.

Пример сценария: подключаем новый сервис без переделки ядра

Интернет-магазин растет и решает сразу две задачи: подключить новый сервис доставки (для расчета сроков и трекинга) и внедрить новую WMS на складе. Раньше многое держалось на ручных сверках: менеджер сравнивал «что в заказе», «что собрано» и «что уехало», а остатки часто не совпадали.

Минимальный контракт снимает главную боль: все системы говорят одними и теми же объектами и событиями. Ядро продаж не знает детали WMS и доставки, оно публикует и принимает стандартные факты: заказ подтвержден, резерв создан, отгрузка отправлена.

Типовой поток выглядит так: OrderConfirmed -> StockReserved -> ShipmentShipped -> StockMoved. Благодаря этому новую WMS можно подключить как исполнителя складских событий, а сервис доставки - как подписчика на события по отгрузке, не ломая существующие процессы.

Исключения тоже становятся понятными, потому что у них есть место в контракте:

  • отмена заказа: OrderCanceled снимает резервы и прекращает сборку
  • недостача: StockReservationFailed или StockAdjusted фиксирует факт и причину
  • частичная отгрузка: ShipmentPartiallyShipped и корректное уменьшение резерва
  • возврат: ReturnRegistered -> StockMoved (в отдельную зону или на основной склад)
  • ошибка повтора: идемпотентность не дает создать дубль резерва или движения

Чтобы оценить результат, заранее договоритесь о метриках: сколько дней заняла интеграция, сколько инцидентов было в первые 2 недели, и как часто возникают расхождения по остаткам между продажами и складом. Если контракт сделан нормально, эти цифры падают заметно и без героических разборов.

Следующие шаги: как внедрять и где пригодится помощь

Начните с инвентаризации. Соберите список объектов и событий, которые уже есть в ваших системах (ERP, WMS, касса, маркетплейсы, CRM), и сопоставьте их с тем, что вы хотите закрепить как минимум. Быстро выяснится, где у вас разные названия для одного и того же, где не хватает идентификаторов, а где статусы существуют только «в голове» у команды.

Дальше договоритесь о владельцах данных. Без этого интеграции превращаются в споры «чья правда». Зафиксируйте ответственность по ключевым зонам: кто ведет товары и справочники, какой источник считается главным по остаткам и резервам, где рождается заказ и кто подтверждает отгрузку и возврат, какой сервис сообщает об оплате и по каким правилам.

Пилот планируйте так, чтобы он дал эффект, но не утонул в деталях. Хороший вариант - один склад и один канал продаж (например, интернет-магазин), плюс четкий набор сценариев: заказ, резерв, отгрузка, возврат. После пилота масштабируйте на остальные склады и каналы, не меняя контракт, а добавляя новые источники.

Инфраструктуру тоже стоит продумать заранее: очередь или шина событий, единые журналы, алерты и метрики доставки, повторные попытки и дедупликация. Простое правило: если событие нельзя найти и объяснить через неделю, оно не помогает бизнесу.

Если нужна помощь на стороне интеграции и инфраструктуры, GSE.kz может выступить системным интегратором: помочь собрать архитектуру, настроить мониторинг и отказоустойчивость, а также подобрать и поставить серверную платформу под шину и сервисы (например, на базе серверов S200) с поддержкой 24/7."}

Частые вопросы

Что такое минимальный контракт API для продаж и склада?

Минимальный контракт API фиксирует общие объекты, статусы, идентификаторы и события для продаж и склада. Он помогает системам одинаково понимать заказ, резерв, отгрузку и изменение остатка.

Зачем нужен общий контракт между продажами и складом?

Он снижает число точечных доработок при подключении WMS, доставки, интернет-магазина или нового канала продаж. Команды используют одни и те же ID и правила вместо ручного сопоставления документов.

Какие справочники нужны в первую очередь?

Обычно хватает товара и SKU, контрагента, организации, филиала, склада и места хранения. Эти данные дают контекст заказам, резервам, отгрузкам и движениям товара.

Как правильно использовать идентификаторы?

Для каждого объекта задайте внутренний ID системы-владельца и стабильный внешний ID для обмена. Внешний ID не должен меняться при миграции, переименовании или переносе данных.

Чем отличаются остаток, резерв и движение товара?

Остаток показывает количество в конкретном складе или ячейке на данный момент. Резерв показывает, какая часть уже закреплена за заказом или другой причиной, а движение объясняет, почему количество изменилось.

Чем заказ отличается от отгрузки?

Заказ фиксирует намерение покупателя купить товар. Отгрузка фиксирует физический факт: какие позиции, в каком количестве, когда и откуда отправили. Один заказ может иметь несколько частичных отгрузок.

В чем разница между командой и событием?

Команда просит систему выполнить действие, например создать резерв, и может получить отказ. Событие сообщает о свершившемся факте, например о созданном резерве, чтобы другие сервисы обновили свои данные.

Что должно быть в интеграционном событии?

Минимум включает `eventId`, тип события, время, источник и версию. Для связанных действий добавьте `correlationId`, чтобы проследить цепочку от заказа до отгрузки или возврата.

Как защититься от дублей в интеграции?

Идемпотентность защищает от повторов. При повторной команде сервер возвращает прежний результат, а получатель события распознает его по `eventId` и не создает второй резерв, списание или чек.

С чего начать внедрение минимального контракта?

Начните с одного склада, одного канала продаж и четырех сценариев: заказ, резерв, отгрузка и возврат. Согласуйте владельцев данных, статусы, форматы времени, валюты и единицы измерения, затем проверьте обмен на тестовых сообщениях.

Запрос

Пришлите спецификацию. Остальное сделаем мы

Одна позиция или объект под ключ, любое направление. Вашу поставку от первого звонка до акта ввода ведёт один менеджер.

Расчёт бесплатноДокументы для госзакупок и тендеровОдин договор и одна гарантия