Перейти до основного вмісту

Стрічка змін

Усе, що змінюється у WMS — відредаговано товар, прийнято товар, відвантажено замовлення, — записується до стрічки змін. Ваша система регулярно читає стрічку, застосовує зміни й підтверджує, докуди дочитала. WMS сама нічого не надсилає: ваша система запитує.

Читання стрічки потребує можливості Читання change feed. Які типи даних з'являються в стрічці, перелічено в Типах даних інтеграції. Стрічка несе лише зміни, зроблені у WMS: те, що імпортувала ваша власна система, до неї не повертається.

Цикл читання​

  1. Отримайте свою контрольну точку — останню підтверджену подію:

    GET /api/v1/integrations/checkpoints/ERP_MAIN
    {"id": 1, "externalSystemCode": "ERP_MAIN", "lastAcknowledgedEventId": 4211}

    Під час найпершого запуску lastAcknowledgedEventId немає: читайте з початку або почніть з ресинхронізації.

  2. Прочитайте сторінку подій після цієї події:

    GET /api/v1/integrations/change-feed?externalSystemCode=ERP_MAIN&limit=100&fromEventId=4211
    • limit — обов'язковий, від 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). Якщо запис змінювався кілька разів, кожна подія несе його останній стан.

  3. Застосуйте події у своїй системі.

  4. Підтвердіть останню подію сторінки:

    POST /api/v1/integrations/checkpoints/acknowledge
    {"checkpointId": 1, "eventId": 4212}
  5. Повторюйте з кроку 2 з fromEventId, що дорівнює цій події, доки сторінка не повернеться порожньою. Тоді зачекайте — зазвичай хвилину — і почніть знову.

На що можна покладатися​

  • Подія може прийти двічі. Якщо ваша система застосувала сторінку й зупинилася до підтвердження, сторінка прийде знову під час наступного запуску. Зробіть застосування події безпечним для повторення: шукайте запис за ідентифікатором і перезаписуйте його, а не додавайте наосліп.
  • Контрольна точка одна для всіх типів даних. Читайте стрічку без entityTypes, а непотрібні типи пропускайте у власному коді. Якщо підтвердити останню подію сторінки, відфільтрованої за типом, події інших типів перед нею буде пропущено назавжди.
  • Контрольна точка рухається лише вперед. Повторне підтвердження вже підтвердженої події приймається й нічого не змінює; підтвердження старішої події відхиляється з 400.
  • Не пропускайте мовчки подію, що не застосувалася. Або зупиніться й повторіть сторінку пізніше, або покладіть подію до власної черги повторів перед підтвердженням сторінки.
  • Якщо WMS вимагає повної ресинхронізації вашої системи, читання стрічки повертає 400, доки ви не завершите ресинхронізацію.

Код​

Перша функція виконує цикл вище, доки стрічка не спорожніє. Друга переносить контрольну точку на подію, яку повернула ресинхронізація, щоб читання продовжилося одразу після знімка. Обидві використовують клієнт з Основ API.

// 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},
});
}