Надходження

Надходження — це приймання: оператор ТСД зіставляє привезений постачальником товар із накладною. Повний цикл на одній сторінці: ви надсилаєте завдання одним запитом, стежите за перебігом приймання і забираєте результат назад.

Завдання — документ надходження з вашої облікової системи: список товарів із кількостями за накладною та іменем постачальника. Для інвентаризацій є окремий вид документа — Перерахунок, влаштований точно так само.

Приклад. Прийшла машина від «ТОВ Постачальник». Облікова система надсилає на ТСД завдання за накладною на 45 позицій. Комірник сканує коробки на рампі — до кінця розвантаження система бачить: 43 позиції прийнято повністю, по двох недовіз.

Основи взаємодії

Базова адреса API: https://combopocket.online/v2/exchange.

Усі операції виконуються методом POST з JSON-тілом.

Кожен запит несе заголовок X-API-Token — входу в систему й сесій немає, система перевіряє дані за токеном.

Відповідь завжди несе поле ok у тілі: true — успіх, false — відмова з причиною в error.

HTTP-код — рівень транспорту: 200 — запит дійшов і оброблений (результат — в ok), 4xx — запит до обробки не дійшов, 5xx — збій на нашому боці.

Докладно конверт відповіді, таблиця кодів і авторизація розібрані в розділі «З чого почати» — тут вони працюють точно так само.

Ключові поняття

Зовнішній ідентифікатор (external_id) — ідентифікатор документа у вашій обліковій системі. Це головний ключ усього обміну: за ним ви оновлюєте завдання, опитуєте статус, забираєте результат і видаляєте документ.

Ідентифікатор унікальний у межах вашої бази — другого документа з таким самим external_id не буде.

Виконавець (assignee_token) — токен оператора, якому призначається приймання. Кожен оператор ТСД має власний токен, і завдання з'явиться на терміналі саме того оператора, чий токен ви вказали.

Токен виконавця потрібен лише під час надсилання — решта операцій працює за external_id.

Версія документа (docversion) — ваша мітка версії, будь-який рядок. Вона захищає від зайвої роботи: якщо ви повторно надішлете документ із тією самою версією, сервер відповість unchanged і нічого не перезапише.

Змінюєте вміст (наприклад, постачальник надіслав виправлену накладну) — змінюйте версію, і сервер замінить завдання цілком.

Життєвий цикл

Ви
upload
передали накладну → статус NotStarted
Оператор
сканує
перше сканування → InProgress
Оператор
завершує
приймання виконано → Done
Ви
download
забрали результат
статуси змінює тільки робота оператора — API їх не торкається Done — фінальний стан: виконане приймання не відновлюється

Поки завдання у статусі NotStarted, його можна вільно перезаписувати повторними upload — сервер замінить вміст цілком.

Щойно оператор почав приймання (InProgress) або завершив його (Done), перезапис заборонено: сервер відповість відмовою conflict, щоб робота оператора не була втрачена.

Надсилання завдання

POST /receipts/upload передати завдання на приймання

Створює нове завдання або цілком замінює наявне (поки воно NotStarted).

Операція виконується атомарно: або завдання записано повністю, або не записано взагалі — часткових станів не буває.

ПолеТипПризначення
external_idstring ≤100обов. Ідентифікатор документа у вашій системі — ключ усіх подальших операцій.
assignee_tokenstring ≤36обов. Токен оператора-виконавця. Завдання з'явиться на його терміналі.
namestring ≤200обов. Назва завдання — її бачить оператор у списку на ТСД.
datedatetimeобов. Дата документа у форматі ISO 8601 зі зсувом часового поясу: 2026-07-10T12:00:00+03:00. Форма без зсуву теж приймається й означає київський час.
vendorstring ≤200 Постачальник. Оператор бачить, чиє постачання приймає; поле повернеться й під час вивантаження результату.
docversionstring ≤100 Ваша мітка версії. Збіглася зі збереженою — повтор ігнорується (unchanged).
commentstring ≤500 Коментар-інструкція оператору.
store_namestring ≤200 Назва магазину. Допомагає оператору відрізняти завдання, коли на ТСД вивантажені документи кількох магазинів.
settingsobject Налаштування поведінки завдання на терміналі — див. нижче. Не є обов'язковими цілком.
rowsarrayобов. Рядки накладної. Може бути порожнім [] — завдання «прийми все, що привезли»: оператор сканує вільно, результат повернеться в extra_rows.

Налаштування поведінки на терміналі settings

Необов'язковий об'єкт — задає, що оператор бачить на екрані завдання і що йому дозволено.

Передавати settings цілком не обов'язково; будь-яке відсутнє поле (як і весь об'єкт) трактується як true.

Налаштування зберігаються в документі й завжди повертаються назад у status і download — навіть якщо ви їх не надсилали.

ПолеТипПризначення (типово — увімкнено)
show_fact_qtybool Показувати оператору фактичну кількість за позицією.
show_progressbool Показувати прогрес-бари перебігу приймання.
allow_add_unlistedbool Дозволяти додавати товари поза завданням — знайдені в базі штрих-кодів, але відсутні в рядках документа. За false оператор працює строго за накладною.
show_book_qtybool Показувати облікову кількість (заявлену за накладною) за позицією.

Кожен рядок rows[] — одна позиція накладної:

ПолеТипПризначення
item_idstring ≤100обов. Ідентифікатор товару у вашій системі. Унікальний у межах документа. Якщо товар обліковується з характеристиками (колір, розмір), включіть характеристику в ідентифікатор — наприклад, склейте коди номенклатури й характеристики: кожна характеристика стає окремою позицією зі своїм item_id і своїми штрих-кодами.
namestring ≤150 Назва товару — її бачить оператор.
charstring ≤150 Характеристика (колір, розмір, об'єм).
qty_plannumber ≥ 0обов. Кількість за накладною: скільки заявлено постачальником.
qty_factnumber Стартовий факт. Зазвичай не передається (0) — його наповнює оператор; потрібен для переносу частково виконаних приймань.
line_numint Номер рядка в накладній — у цьому порядку рядки повернуться під час вивантаження результату.
qty_int_onlybool Товар вважається лише цілими числами (штучний).
bc_arrayarray Штрих-коди позиції: {"code": "…", "is_sku": false}. Формат коду єдиний у всьому API: is_sku: true — ваговий код (СКЮ). Позиція без штрих-кодів припустима.
ЗапитPOST /v2/exchange/receipts/upload
{
  "external_id": "TTN-2026-0778",
  "assignee_token": "22222222-3333-4444-5555-666666666666",
  "name": "Постачання за накладною №778",
  "date": "2026-07-10T08:30:00+03:00",
  "vendor": "ТОВ Постачальник",
  "docversion": "v1",
  "store_name": "Магазин на Хрещатику",
  "settings": { "show_fact_qty": true, "show_progress": true,
                "allow_add_unlisted": false, "show_book_qty": true },
  "rows": [
    { "item_id": "MLK-001", "name": "Молоко 2,5%", "char": "0,9 л",
      "qty_plan": 120, "line_num": 1, "qty_int_only": true,
      "bc_array": [ { "code": "4820000001234", "is_sku": false } ] },
    { "item_id": "SYR-40", "name": "Сир Гауда",
      "qty_plan": 12.6, "line_num": 2,
      "bc_array": [ { "code": "210", "is_sku": true } ] }
  ]
}
Відповідь — завдання створено
{ "ok": true, "doc_status": "created" }

Поле doc_status каже, що саме сталося: created — завдання створено, updated — наявне замінено цілком, unchanged — версія збіглася, нічого не змінювалося.

Відмова — оператор уже почав приймання
{ "ok": false, "error": "conflict", "status": "InProgress" }
Відмова — помилка в даних
{
  "ok": false,
  "error": "validation_failed",
  "recommendation": "повтор item_id у документі: MLK-001"
}

Перевірка даних

Сервер перевіряє завдання до запису — або приймається все, або ніщо:

ПравилоНавіщо
item_id унікальний у документі Один рядок — одна позиція; повтор — майже завжди помилка вивантаження.
Один штрих-код — один товар Код, прив'язаний до двох різних item_id, зробив би сканування неоднозначним.
item_id ≤ 100, code ≤ 50 символів Ключові поля не обрізаються мовчки — перевищення це відмова.
name, char ≤ 150 Представницькі поля м'яко усікаються — відмова не потрібна.
Кількість рядків ≤ ліміту бази Захист від аномально великих завдань; ліміт налаштовується для вашої бази (типово 1000 рядків).
date у форматі ISO 8601 Рекомендовано зі зсувом поясу (+03:00); приймається також Z і форма без зсуву (= київський час).

Отримання результату

Далі працюють три читальні операції та видалення. Токен виконавця їм не потрібен — тільки external_id.

POST /receipts/list усі надходження бази одним списком

Повертає всі надходження вашої бази — тіло запиту не потрібне (порожній {}). Зручний як огляд: що вивантажено, що в роботі, що готове до забирання.

ВідповідьPOST /v2/exchange/receipts/list
{
  "ok": true,
  "truncated": false,
  "docs": [
    { "name": "Постачання за накладною №778",
      "external_id": "TTN-2026-0778",
      "status": "Done",
      "docversion": "v1",
      "assignee_token": "22222222-3333-4444-5555-666666666666",
      "created_on_tsd": false,
      "started_at": "2026-07-10T09:05:00+03:00",
      "completed_at": "2026-07-10T10:20:00+03:00" }
  ]
}

Зверніть увагу на два поля.

created_on_tsd: true позначає надходження, які оператор створив просто на терміналі — наприклад, прийшло незаплановане постачання, і його почали приймати до появи накладної в системі. Вашій системі такі документи поки невідомі, і ви самі вирішуєте, чи забирати їх.

truncated: true означає, що список уперся в запобіжний ліміт (налаштовується для бази, типово 1000) і показано лише його частину — ознака того, що виконані документи пора видаляти.

POST /receipts/status статус і прогрес, не вивантажуючи рядків

Легка операція для опитування перебігу приймання: замість вивантаження всіх рядків сервер віддає готові лічильники. Саме її варто викликати за розкладом.

Запит
{ "external_id": "TTN-2026-0778" }
Відповідь
{
  "ok": true,
  "status": "InProgress",
  "started_at": "2026-07-10T09:05:00+03:00",
  "completed_at": null,
  "items_total": 45,
  "items_counted": 30,
  "items_extra": 1,
  "progress_percent": 66,
  "discrepancy_count": 2,
  "settings": { "show_fact_qty": true, "show_progress": true,
                "allow_add_unlisted": false, "show_book_qty": true }
}
ПолеЩо означає
status NotStarted — оператор ще не почав, InProgress — приймає, Done — приймання завершено.
items_totalУсього позицій у накладній.
items_counted Позиції, які оператор уже обробив. Враховується й «порахований нуль» — коли оператор підтвердив, що позицію не привезли взагалі.
items_extraПозиції, додані оператором понад накладну.
progress_percentВідсоток виконання: counted / total.
discrepancy_count Позиції, де прийняте не збіглося із заявленим, — заздалегідь видно обсяг недовозу й пересорту.
settings Налаштування поведінки на терміналі — рівно ті, що ви надіслали під час надсилання, або типові значення. Склад полів — у розділі «Надсилання завдання».
POST /receipts/download вивантажити результат по рядках

Повний результат приймання. Рядки повертаються в порядку line_num — тому самому, у якому ви їх надсилали, тому зіставлення з накладною тривіальне.

Поля рядків симетричні надсиланню: що ви надіслали в upload, під тими самими іменами повернеться тут.

У шапці відповіді є й vendor — постачальник із завдання, — і налаштування документа (settings).

Запит
{ "external_id": "TTN-2026-0778" }
Відповідь
{
  "ok": true,
  "external_id": "TTN-2026-0778",
  "status": "Done",
  "docversion": "v1",
  "vendor": "ТОВ Постачальник",
  "created_at": "2026-07-10T08:30:00+03:00",
  "started_at": "2026-07-10T09:05:00+03:00",
  "completed_at": "2026-07-10T10:20:00+03:00",
  "comment_initial": "Прийняти до 11:00, машина під розвантаженням",
  "comment_user": "Дві коробки молока пошкоджені, не прийняв",
  "settings": { "show_fact_qty": true, "show_progress": true,
                "allow_add_unlisted": false, "show_book_qty": true },
  "rows": [
    { "item_id": "MLK-001", "name": "Молоко 2,5%", "char": "0,9 л",
      "qty_plan": 120, "qty_fact": 118, "line_num": 1, "is_counted": true,
      "bc_array": [ { "code": "4820000001234", "is_sku": false } ] }
  ],
  "extra_rows": [
    { "item_id": "manual-3f2a…", "name": "Кефір 1%",
      "qty_plan": 0, "qty_fact": 6, "line_num": -1, "is_counted": true,
      "bc_array": [ { "code": "4820000007777", "is_sku": false } ] }
  ],
  "photos": [
    { "name": "da75cf11-a672-4965-a545-3556ba4f1c4e",
      "comment": "Пошкоджена коробка, вид збоку" }
  ]
}

Поділ rows / extra_rows відповідає на головне питання приймання.

rows — доля позицій накладної: за різницею qty_plan і qty_fact видно недовіз.

extra_rows — товар, який привезли, хоча в накладній його не було: пересорт або незаявлена позиція.

Прапорець is_counted вирішує класичну неоднозначність нуля: qty_fact: 0 з is_counted: true означає «оператор перевірив — позицію не привезли», а з is_counted: false — «до позиції не дійшли».

Без цього прапорця недовіз неможливо відрізнити від неперевіреного рядка.

Масив photos — метадані фотографій, знятих оператором: name (ідентифікатор знімка) і comment (підпис). Самі зображення в документ не вкладаються — download залишається легким.

Показати знімки можна, взагалі не вивантажуючи документ (photos_thumbnails), а отримати оригінал — за іменем (download_photo).

Обидві операції нижче; усе про фото зібрано й на окремій сторінці «Фотографії».

POST /receipts/photos_thumbnails мініатюри всіх фото документа

Повертає за external_id масив мініатюр — для прев'ю без вивантаження документа.

У кожного знімка: ім'я оригіналу, ім'я мініатюри, підпис і сама мініатюра в base64.

Запит
{ "external_id": "TTN-2026-0778" }
Відповідь
{
  "ok": true,
  "photos": [
    { "name": "da75cf11-a672-4965-a545-3556ba4f1c4e",
      "thumb_name": "da75cf11-a672-4965-a545-3556ba4f1c4e.thumb",
      "comment": "Пошкоджена коробка, вид збоку",
      "thumb_b64": "/9j/4AAQSkZJRgABAQ…" }
  ]
}
POST /receipts/download_photo отримати одне фото документа

Повертає оригінал однієї фотографії в base64. Ім'я беріть із відповіді photos_thumbnails або з масиву photos в download.

Фото віддається по одному — так відповідь не розростається, і ви тягнете тільки те, що справді потрібно.

Запит
{ "name": "da75cf11-a672-4965-a545-3556ba4f1c4e" }
Відповідь
{
  "ok": true,
  "name": "da75cf11-a672-4965-a545-3556ba4f1c4e",
  "comment": "Пошкоджена коробка, вид збоку",
  "size": 148213,
  "data_b64": "/9j/4AAQSkZJRgABAQ…"
}
Відмова — фото не знайдено
{ "ok": false, "error": "not_found" }
Ендпойнт прив'язаний до виду документа так само, як вивантаження: фото надходження доступне лише через /receipts/download_photo. Те саме ім'я, подане в /recounts/download_photo, поверне not_found — як і документ чужого виду. Той самий not_found приходить, якщо знімок уже видалено з сервера.
POST /receipts/delete видалити надходження з термінала

Видаляє документ і всі його дані. Викликайте після того, як результат успішно забрано й проведено у вашій системі, — так список на терміналі не заростає виконаними прийманнями.

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

Відповідьі для наявного, і для вже видаленого
{ "ok": true, "doc_status": "deleted" }
Видалення діє й на документ, з яким оператор працює просто зараз, — сервер цьому не перешкоджає. Правило просте: видаляйте те, що забрали, і не видаляйте те, що ще в роботі.

Час у відповідях

Усі дати й час (created_at, started_at, completed_at) сервер віддає зі зсувом часового поясу: 2026-07-10T08:30:00+03:00. Незаповнений час — null.

Надсилаючи date, вказуйте власний зсув — момент буде зрозуміло однозначно, навіть якщо ваша система працює в іншому поясі. Форма без зсуву теж приймається й означає київський час.

Подробиці контракту часу — на сторінці «З чого почати».

Довідник відмов

ПомилкаКоли виникаєЩо робити
conflict upload: оператор уже приймає постачання (у відповіді status: InProgress або Done) Дочекайтеся завершення й заберіть результат; змінити документ у роботі не можна.
validation_failed Помилка в даних запиту Виправте за текстом recommendation і повторіть.
not_found download/status: документа з таким external_id немає Перевірте ідентифікатор; можливо, документ уже видалено.
unauthorized Токен бази не передано або він невідомий Перевірте заголовок X-API-Token.

Рекомендований алгоритм

  1. Отримали накладну — сформуйте завдання й надішліть його upload-ом із новим docversion. Повтор запиту безпечний: завдяки версії сервер не зробить подвійної роботи.
  2. Отримали created або updated — завдання на терміналі оператора. Отримали validation_failed — виправте дані за текстом recommendation і повторіть.
  3. За розкладом опитуйте status (або оглядово — list).
  4. Статус став Done — викличте download і проведіть результат: rows — по рядках накладної (розбіжності — в акт), extra_rows — як незаявлений товар.
  5. Результат проведено — викличте delete, щоб убрати виконане приймання з термінала.
  6. Періодично проглядайте list на предмет created_on_tsd: true — приймань, створених оператором вручну.