Стрічка змін
Усе, що змінюється у WMS — відредаговано товар, прийнято товар, відвантажено замовлення, — записується до стрічки змін. Ваша система регулярно читає стрічку, застосовує зміни й підтверджує, докуди дочитала. WMS сама нічого не надсилає: ваша система запитує.
Читання стрічки потребує можливості Читання change feed. Які типи даних з'являються в стрічці, перелічено в Типах даних інтеграції. Стрічка несе лише зміни, зроблені у WMS: те, що імпортувала ваша власна система, до неї не повертається.
Цикл читання
-
Отримайте свою контрольну точку — останню підтверджену подію:
GET /api/v1/integrations/checkpoints/ERP_MAIN{"id": 1, "externalSystemCode": "ERP_MAIN", "lastAcknowledgedEventId": 4211}Під час найпершого запуску
lastAcknowledgedEventIdнемає: читайте з початку або почніть з ресинхронізації. -
Прочитайте сторінку подій після цієї події:
GET /api/v1/integrations/change-feed?externalSystemCode=ERP_MAIN&limit=100&fromEventId=4211limit— обов'язковий, від 1 до 500.fromEventId— повертаються події після цієї; не передавайте його, щоб почати з початку.entityTypes— необов'язковий; повторіть його, щоб читати лише деякі типи даних:&entityTypes=RECEIPT&entityTypes=OUTBOUND_ORDER. Не підтверджуйте події, прочитані так, — див. правила нижче.
{"externalSystemCode": "ERP_MAIN","events": [{"event": {"eventId": 4212,"entityType": "RECEIPT","internalEntityId": "01a17a6f-60a6-77be-b765-b7e9341d7efe","scopeType": "WAREHOUSE","warehouseId": "01a16373-d6be-7746-8eab-fc8e447bfea8","entityVersion": 3,"occurredAt": "2026-10-27T10:02:11.418Z"},"payload": {"id": {"int": "01a17a6f-60a6-77be-b765-b7e9341d7efe", "ext": "rcpt-2026-0105"},"status": "RECEIVED"}}]}Події йдуть за зростанням
eventId.event.internalEntityId— WMS-ідентифікатор запису, те саме значення, що йpayload.id.int.payload— це поточний стан запису в момент читання, з ідентифікаторами у вигляді пар{int, ext}(див. Формування payload). Якщо запис змінювався кілька разів, кожна подія несе його останній стан. -
Застосуйте події у своїй системі.
-
Підтвердіть останню подію сторінки:
POST /api/v1/integrations/checkpoints/acknowledge{"checkpointId": 1, "eventId": 4212} -
Повторюйте з кроку 2 з
fromEventId, що дорівнює цій події, доки сторінка не повернеться порожньою. Тоді зачекайте — зазвичай хвилину — і почніть знову.
На що можна покладатися
- Подія може прийти двічі. Якщо ваша система застосувала сторінку й зупинилася до підтвердження, сторінка прийде знову під час наступного запуску. Зробіть застосування події безпечним для повторення: шукайте запис за ідентифікатором і перезаписуйте його, а не додавайте наосліп.
- Контрольна точка одна для всіх типів даних. Читайте стрічку без
entityTypes, а непотрібні типи пропускайте у власному коді. Якщо підтвердити останню подію сторінки, відфільтрованої за типом, події інших типів перед нею буде пропущено назавжди. - Контрольна точка рухається лише вперед. Повторне підтвердження вже підтвердженої події приймається й нічого не змінює; підтвердження старішої події відхиляється з 400.
- Не пропускайте мовчки подію, що не застосувалася. Або зупиніться й повторіть сторінку пізніше, або покладіть подію до власної черги повторів перед підтвердженням сторінки.
- Якщо WMS вимагає повної ресинхронізації вашої системи, читання стрічки повертає 400, доки ви не завершите ресинхронізацію.
Код
Перша функція виконує цикл вище, доки стрічка не спорожніє. Друга переносить контрольну точку на подію, яку повернула ресинхронізація, щоб читання продовжилося одразу після знімка. Обидві використовують клієнт з Основ API.
- JavaScript
- Python
- 1C
// Reads the change feed from the last confirmed event to the end, hands every
// event to `handleEvent` and confirms each page. Run it on a schedule, e.g. every minute.
// The same event can arrive more than once, so `handleEvent` must be safe to repeat.
// Your system has one checkpoint for all data types: read every type here and
// skip the ones you do not need inside `handleEvent`.
async function pollChangeFeed(handleEvent, {limit = 100} = {}) {
const code = config.externalSystem;
const {body: checkpoint} = await wmsRequest('GET', `/api/v1/integrations/checkpoints/${code}`);
let lastEventId = checkpoint.lastAcknowledgedEventId ?? null;
let handled = 0;
for (;;) {
const query = {externalSystemCode: code, limit};
if (lastEventId !== null) query.fromEventId = lastEventId; // events after this one
const {body} = await wmsRequest('GET', '/api/v1/integrations/change-feed', {query});
if (body.events.length === 0) return handled;
for (const {event, payload} of body.events) {
await handleEvent(event, payload); // throw to stop: the page stays unconfirmed
handled += 1;
}
lastEventId = body.events.at(-1).event.eventId;
await wmsRequest('POST', '/api/v1/integrations/checkpoints/acknowledge', {
body: {checkpointId: checkpoint.id, eventId: lastEventId},
});
}
}
// After a resync: moves the checkpoint to the snapshot's event, so the next
// pollChangeFeed starts right after the snapshot. Never moves it back.
async function startChangeFeedAt(eventId) {
const code = config.externalSystem;
const {body: checkpoint} = await wmsRequest('GET', `/api/v1/integrations/checkpoints/${code}`);
const current = checkpoint.lastAcknowledgedEventId ?? null;
if (current !== null && Number(current) >= Number(eventId)) return;
await wmsRequest('POST', '/api/v1/integrations/checkpoints/acknowledge', {
body: {checkpointId: checkpoint.id, eventId},
});
}
def poll_change_feed(handle_event, *, limit=100):
"""Reads the change feed from the last confirmed event to the end, hands every
event to `handle_event(event, payload)` and confirms each page. Run it on a
schedule, e.g. every minute. The same event can arrive more than once, so
`handle_event` must be safe to repeat. Your system has one checkpoint for all
data types: read every type here and skip the ones you do not need inside
`handle_event`."""
code = CONFIG["external_system"]
checkpoint = wms_request("GET", f"/api/v1/integrations/checkpoints/{code}").json()
last_event_id = checkpoint.get("lastAcknowledgedEventId")
handled = 0
while True:
query = {"externalSystemCode": code, "limit": limit}
if last_event_id is not None:
query["fromEventId"] = last_event_id # events after this one
events = wms_request("GET", "/api/v1/integrations/change-feed", query=query).json()["events"]
if not events:
return handled
for item in events:
handle_event(item["event"], item["payload"]) # raise to stop: the page stays unconfirmed
handled += 1
last_event_id = events[-1]["event"]["eventId"]
wms_request("POST", "/api/v1/integrations/checkpoints/acknowledge",
body={"checkpointId": checkpoint["id"], "eventId": last_event_id})
def start_change_feed_at(event_id):
"""After a resync: moves the checkpoint to the snapshot's event, so the next
poll_change_feed starts right after the snapshot. Never moves it back."""
code = CONFIG["external_system"]
checkpoint = wms_request("GET", f"/api/v1/integrations/checkpoints/{code}").json()
current = checkpoint.get("lastAcknowledgedEventId")
if current is not None and int(current) >= int(event_id):
return
wms_request("POST", "/api/v1/integrations/checkpoints/acknowledge",
body={"checkpointId": checkpoint["id"], "eventId": event_id})
// Reads the change feed from the last confirmed event to the end, passes every
// event to ОбработатьСобытие and confirms each page. Run it from a scheduled job.
// The same event can arrive more than once, so ОбработатьСобытие must be safe to repeat.
// Your system has one checkpoint for all data types: read every type here and
// skip the ones you do not need inside ОбработатьСобытие.
Функция ОбработатьЛентуИзменений(Подключение, Лимит = 100) Экспорт
Код = Подключение.КодВнешнейСистемы;
КонтрольнаяТочка = ВыполнитьЗапросWMS(Подключение, "GET", "/api/v1/integrations/checkpoints/" + Код).Тело;
ПоследнееСобытие = КонтрольнаяТочка["lastAcknowledgedEventId"]; // Неопределено on the first run
Обработано = 0;
Пока Истина Цикл
Параметры = Новый Соответствие;
Параметры.Вставить("externalSystemCode", Код);
Параметры.Вставить("limit", Лимит);
Если ПоследнееСобытие <> Неопределено Тогда
Параметры.Вставить("fromEventId", ПоследнееСобытие); // events after this one
КонецЕсли;
События = ВыполнитьЗапросWMS(Подключение, "GET", "/api/v1/integrations/change-feed", Параметры).Тело["events"];
Если События.Количество() = 0 Тогда
Возврат Обработано;
КонецЕсли;
Для Каждого Элемент Из События Цикл
// An exception stops the run: the page stays unconfirmed and is read again next time.
ОбработатьСобытие(Элемент["event"], Элемент["payload"]);
Обработано = Обработано + 1;
КонецЦикла;
ПоследнееСобытие = События[События.ВГраница()]["event"]["eventId"];
Подтверждение = Новый Структура("checkpointId, eventId", КонтрольнаяТочка["id"], ПоследнееСобытие);
ВыполнитьЗапросWMS(Подключение, "POST", "/api/v1/integrations/checkpoints/acknowledge", , Подтверждение);
КонецЦикла;
КонецФункции
// After a resync: moves the checkpoint to the snapshot's event, so the next
// ОбработатьЛентуИзменений starts right after the snapshot. Never moves it back.
Процедура НачатьЛентуИзмененийС(Подключение, ИдСобытия) Экспорт
Код = Подключение.КодВнешнейСистемы;
КонтрольнаяТочка = ВыполнитьЗапросWMS(Подключение, "GET", "/api/v1/integrations/checkpoints/" + Код).Тело;
Текущее = КонтрольнаяТочка["lastAcknowledgedEventId"];
Если Текущее <> Неопределено И Текущее >= ИдСобытия Тогда
Возврат;
КонецЕсли;
Подтверждение = Новый Структура("checkpointId, eventId", КонтрольнаяТочка["id"], ИдСобытия);
ВыполнитьЗапросWMS(Подключение, "POST", "/api/v1/integrations/checkpoints/acknowledge", , Подтверждение);
КонецПроцедуры
Процедура ОбработатьСобытие(Событие, Данные)
// Your code: Событие["entityType"] says what changed, Данные is its current state.
// Skip the types you do not need; find your record by Данные["id"]["ext"] (your identifier).
КонецПроцедуры