-
Server API Документация
- Аутентификация
- Базовый URL
- Общие принципы
- 1. Загрузка партнеров (Partners)
- 2. Загрузка единиц измерения (Unit of Measures)
- 3. Загрузка категорий номенклатуры (Categories)
- 4. Загрузка товаров (Products)
- 5. Загрузка прайс-листов (Price Lists)
- 6. Загрузка складов (Warehouses)
- 7. Загрузка остатков (Remainders)
- 8. Загрузка магазинов (Distributor Shops)
- 9. Загрузка накладных (Invoices)
- 10. Загрузка доставок (Deliveries)
- 11. Загрузка фотографий товаров (Product Photos)
- 12. Получение заказов (Orders)
- Порядок загрузки данных
- Обработка ошибок
- Асинхронная обработка
- Примеры использования
Server API Документация
API для загрузки данных от внешних систем дистрибьюторов в систему Kibet.
Аутентификация
Все запросы к Server API требуют аутентификации через Bearer token:
Authorization: Bearer YOUR_DISTRIBUTOR_SERVER_TOKEN
Server token привязан к конкретному дистрибьютору и хранится в поле server_token модели Distributor.
Базовый URL
/api/server/v1/{endpoint}
Общие принципы
Для endpoints загрузки данных (POST)
- Запросы отправляются методом
POST - Content-Type:
application/json - Все данные обрабатываются асинхронно через фоновые задачи
- При успешном принятии запроса возвращается статус 200 и сообщение о принятии данных
- При наличии существующих записей (по
external_key) данные обновляются, иначе создаются новые - Все даты передаются в формате ISO 8601:
YYYY-MM-DD
Для endpoints работы с заказами (GET, PUT)
- Используются RESTful методы:
GETдля получения данных,PUTдля обновления - Content-Type:
application/json - Данные обрабатываются синхронно, результат возвращается немедленно
- Заказы идентифицируются по ID базы данных (не по external_key)
1. Загрузка партнеров (Partners)
Партнеры - это юридические лица, которые являются владельцами магазинов.
Endpoint
POST /api/server/v1/partners
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
partners |
array | Да | Массив объектов партнеров |
partners[].external_key |
string | Да | Уникальный идентификатор партнера во внешней системе |
partners[].name |
string | Да | Название партнера |
partners[].price_list |
string/null | Нет | external_key прайс-листа, привязываемого к партнеру |
Пример запроса
{
"partners": [
{
"external_key": "partner_001",
"name": "ООО Торговый Дом",
"price_list": "retail_2024"
},
{
"external_key": "partner_002",
"name": "ИП Иванов",
"price_list": "wholesale_2024"
}
]
}
Пример ответа
{
"message": "Partners is loaded"
}
Особенности
- Уникальность проверяется по паре
[distributor_id, external_key] - При совпадении обновляются поля
nameиprice_list_id - Поле
price_list— опциональное. Если переданexternal_keyсуществующего прайс-листа, партнер привязывается к нему. Если не передан или прайс-лист не найден, полеprice_list_idустанавливается вnull - Прайс-лист должен быть предварительно загружен через endpoint
/api/server/v1/price_lists - Привязка прайс-листа к партнеру определяет, какие цены видит магазин этого партнера в мобильном приложении при просмотре каталога
2. Загрузка единиц измерения (Unit of Measures)
Единицы измерения используются для определения в каких единицах измеряются товары (кг, шт, л и т.д.).
Endpoint
POST /api/server/v1/unit_of_measures
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
unit_of_measures |
array | Да | Массив объектов единиц измерения |
unit_of_measures[].external_key |
string | Да | Уникальный идентификатор единицы измерения во внешней системе |
unit_of_measures[].name |
string | Да | Название единицы измерения |
Пример запроса
{
"unit_of_measures": [
{
"external_key": "kg",
"name": "Килограмм"
},
{
"external_key": "pcs",
"name": "Штука"
},
{
"external_key": "liter",
"name": "Литр"
}
]
}
Пример ответа
{
"message": "Unit of measures is loaded"
}
Особенности
- Уникальность проверяется по паре
[distributor_id, external_key] - При совпадении обновляется только поле
name
3. Загрузка категорий номенклатуры (Categories)
Категории используются для классификации товаров. Поддерживается иерархическая структура категорий (родитель-потомок).
Endpoint
POST /api/server/v1/categories
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
categories |
array | Да | Массив объектов категорий |
categories[].external_key |
string | Да | Уникальный идентификатор категории во внешней системе |
categories[].name |
string | Да | Название категории |
categories[].info |
string/null | Нет | Дополнительная информация о категории |
categories[].parent |
string/null | Нет | external_key родительской категории (для иерархической структуры) |
Пример запроса
{
"categories": [
{
"external_key": "food",
"name": "Продукты питания",
"info": "Все виды продуктов питания",
"parent": null
},
{
"external_key": "dairy",
"name": "Молочные продукты",
"info": "Молоко, сыр, йогурты",
"parent": "food"
},
{
"external_key": "milk",
"name": "Молоко",
"parent": "dairy"
},
{
"external_key": "beverages",
"name": "Напитки",
"parent": null
}
]
}
Пример ответа
{
"message": "Categories is loaded"
}
Особенности
- Уникальность проверяется по паре
[distributor_id, external_key] - При совпадении обновляются поля
nameиinfo - Поддерживается иерархическая структура: категория может иметь родительскую категорию
- Для корневых категорий поле
parentдолжно бытьnullили отсутствовать - Родительская категория указывается через её
external_key - Если родительская категория не найдена, в логах появится предупреждение, но загрузка не прервется
- При повторной загрузке категории без поля
parent, её связь с родителем будет удалена - Загрузка происходит в три этапа:
- Создание/обновление всех категорий
- Установка связей parent-child для категорий с указанным
parent - Удаление связей для категорий, где
parentбольше не указан
Пример иерархии
Продукты питания (food)
├── Молочные продукты (dairy)
│ ├── Молоко (milk)
│ └── Сыр (cheese)
└── Мясо (meat)
Напитки (beverages)
├── Газированные (soda)
└── Соки (juice)
4. Загрузка товаров (Products)
Товары - это номенклатура дистрибьютора с привязкой к категориям и единицам измерения.
Endpoint
POST /api/server/v1/products
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
products |
array | Да | Массив объектов товаров |
products[].external_key |
string | Да | Уникальный идентификатор товара во внешней системе |
products[].name |
string | Да | Название товара |
products[].category |
string | Да | external_key категории товара |
products[].article |
string/null | Нет | Артикул товара |
products[].ean |
string/null | Нет | Штрих-код (EAN) товара |
products[].info |
string/null | Нет | Дополнительная информация о товаре |
products[].product_unit_of_measures |
array | Да | Массив единиц измерения для товара |
products[].product_unit_of_measures[].unit_of_measure |
string | Да | external_key единицы измерения |
products[].product_unit_of_measures[].quantum |
integer | Да | Квант (минимальное количество для заказа) |
products[].product_unit_of_measures[].count |
integer | Да | Количество базовых единиц в данной единице измерения |
Пример запроса
{
"products": [
{
"external_key": "milk_32",
"name": "Молоко 3.2%",
"category": "dairy",
"article": "MLK-001",
"ean": "4607012345678",
"info": "Пастеризованное молоко",
"product_unit_of_measures": [
{
"unit_of_measure": "pcs",
"quantum": 1,
"count": 1
},
{
"unit_of_measure": "box",
"quantum": 12,
"count": 12
}
]
},
{
"external_key": "bread_white",
"name": "Хлеб белый",
"category": "bakery",
"article": "BRD-001",
"product_unit_of_measures": [
{
"unit_of_measure": "pcs",
"quantum": 1,
"count": 1
}
]
}
]
}
Пример ответа
{
"message": "Products is loaded"
}
Особенности
- Уникальность проверяется по паре
[distributor_id, external_key] - При совпадении обновляются все поля товара
- Категория должна быть предварительно загружена через endpoint
/api/server/v1/categories - Единицы измерения должны быть предварительно загружены через endpoint
/api/server/v1/unit_of_measures - При обновлении товара все старые единицы измерения удаляются и создаются новые
- Каждая единица измерения может использоваться для товара только один раз
quantum- минимальное количество для заказа в данной единице измерения (целое положительное число)count- сколько базовых единиц содержится в данной единице измерения (целое положительное число)- Если категория или единица измерения с указанным
external_keyне найдены, будет ошибка валидации
Примеры использования единиц измерения
Пример 1: Молоко в бутылках и ящиках - Штука (pcs): quantum=1, count=1 - продается поштучно - Ящик (box): quantum=12, count=12 - в ящике 12 бутылок, минимум 12 штук
Пример 2: Сахар в килограммах и мешках - Килограмм (kg): quantum=1, count=1 - продается от 1 кг - Мешок (bag): quantum=1, count=50 - мешок содержит 50 кг
5. Загрузка прайс-листов (Price Lists)
Прайс-листы содержат цены на товары дистрибьютора. Каждый прайс-лист включает набор товаров с их ценами.
Endpoint
POST /api/server/v1/price_lists
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
price_lists |
array | Да | Массив объектов прайс-листов |
price_lists[].external_key |
string | Да | Уникальный идентификатор прайс-листа во внешней системе |
price_lists[].name |
string | Да | Название прайс-листа |
price_lists[].product_prices |
array | Да | Массив цен на товары |
price_lists[].product_prices[].product |
string | Да | external_key товара |
price_lists[].product_prices[].price |
number | Да | Цена товара |
Пример запроса
{
"price_lists": [
{
"external_key": "retail_2024",
"name": "Розничные цены 2024",
"product_prices": [
{
"product": "milk_32",
"price": 85.50
},
{
"product": "bread_white",
"price": 50.00
},
{
"product": "cheese_hard",
"price": 650.00
}
]
},
{
"external_key": "wholesale_2024",
"name": "Оптовые цены 2024",
"product_prices": [
{
"product": "milk_32",
"price": 75.00
},
{
"product": "bread_white",
"price": 42.00
}
]
}
]
}
Пример ответа
{
"message": "Price lists is loaded"
}
Особенности
- Уникальность проверяется по паре
[distributor_id, external_key] - При совпадении обновляется название прайс-листа
- Все товары должны быть предварительно загружены через endpoint
/api/server/v1/products - При обновлении прайс-листа все старые цены удаляются и создаются новые
- Если товар с указанным
external_keyне найден, будет ошибка валидации - Цена должна быть неотрицательным числом
- Один товар может присутствовать в прайс-листе только один раз
6. Загрузка складов (Warehouses)
Склады - это места хранения товаров дистрибьютора. На складах учитываются остатки товаров.
Endpoint
POST /api/server/v1/warehouses
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
warehouses |
array | Да | Массив объектов складов |
warehouses[].external_key |
string | Да | Уникальный идентификатор склада во внешней системе |
warehouses[].name |
string | Да | Название склада |
warehouses[].address |
string/null | Нет | Адрес склада |
Пример запроса
{
"warehouses": [
{
"external_key": "warehouse_main",
"name": "Главный склад",
"address": "г. Москва, ул. Складская, д. 15"
},
{
"external_key": "warehouse_secondary",
"name": "Второй склад",
"address": "г. Москва, ул. Промышленная, д. 8"
},
{
"external_key": "warehouse_mobile",
"name": "Мобильный склад"
}
]
}
Пример ответа
{
"message": "Warehouses is loaded"
}
Особенности
- Уникальность проверяется по паре
[distributor_id, external_key] - При совпадении обновляются поля
nameиaddress - Поле
addressявляется опциональным - Склады используются для учета остатков товаров
7. Загрузка остатков (Remainders)
Остатки - это текущее количество товаров на складах в разных единицах измерения.
Endpoint
POST /api/server/v1/remainders
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
remainders |
array | Да | Массив объектов остатков |
remainders[].warehouse |
string | Да | external_key склада |
remainders[].product |
string | Да | external_key товара |
remainders[].unit_of_measure |
string | Да | external_key единицы измерения |
remainders[].count |
integer | Да | Количество товара на складе |
Пример запроса
{
"remainders": [
{
"warehouse": "warehouse_main",
"product": "milk_32",
"unit_of_measure": "pcs",
"count": 150
},
{
"warehouse": "warehouse_main",
"product": "milk_32",
"unit_of_measure": "box",
"count": 12
},
{
"warehouse": "warehouse_secondary",
"product": "bread_white",
"unit_of_measure": "pcs",
"count": 200
},
{
"warehouse": "warehouse_main",
"product": "cheese_hard",
"unit_of_measure": "kg",
"count": 45
}
]
}
Пример ответа
{
"message": "Remainders is loaded"
}
Особенности
- Уникальность проверяется по триплету
[warehouse_id, product_id, unit_of_measure_id] - При совпадении обновляется только поле
count - Склад, товар и единица измерения должны быть предварительно загружены
countдолжен быть неотрицательным целым числом- Можно загружать остатки одного товара на разных складах
- Можно загружать остатки одного товара в разных единицах измерения на одном складе
- Если склад, товар или единица измерения с указанным
external_keyне найдены, будет ошибка валидации
Примеры использования
Пример 1: Разные склады
json
{
"warehouse": "warehouse_main",
"product": "milk_32",
"unit_of_measure": "pcs",
"count": 150
}
и
json
{
"warehouse": "warehouse_secondary",
"product": "milk_32",
"unit_of_measure": "pcs",
"count": 80
}
Один и тот же товар может иметь разные остатки на разных складах.
Пример 2: Разные единицы измерения
json
{
"warehouse": "warehouse_main",
"product": "milk_32",
"unit_of_measure": "pcs",
"count": 150
}
и
json
{
"warehouse": "warehouse_main",
"product": "milk_32",
"unit_of_measure": "box",
"count": 12
}
На одном складе можно учитывать остатки товара в разных единицах измерения (например, 150 штук или 12 ящиков).
8. Загрузка магазинов (Distributor Shops)
Магазины - это торговые точки, которые принадлежат партнерам и получают товары от дистрибьютора.
Endpoint
POST /api/server/v1/distributor_shops
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
shops |
array | Да | Массив объектов магазинов |
shops[].external_key |
string | Да | Уникальный идентификатор магазина во внешней системе |
shops[].name |
string | Да | Название магазина |
shops[].address |
string | Да | Адрес магазина |
shops[].partner |
string | Да | external_key партнера-владельца магазина |
shops[].inn |
string | Нет | ИНН магазина |
shops[].kpp |
string | Нет | КПП магазина |
shops[].warehouse |
string | Нет | external_key склада, привязанного к магазину |
shops[].stop |
boolean | Нет | Стоп-отгрузка (по умолчанию false) |
shops[].credit_limit |
decimal | Нет | Кредитный лимит |
shops[].debt |
decimal | Нет | Задолженность |
Пример запроса
{
"shops": [
{
"external_key": "shop_001",
"name": "Продукты у дома",
"address": "г. Москва, ул. Ленина, д. 10",
"partner": "partner_001",
"inn": "7707123456",
"kpp": "770701001",
"warehouse": "warehouse_main",
"stop": false,
"credit_limit": 50000.00,
"debt": 12500.50
},
{
"external_key": "shop_002",
"name": "Магазин на углу",
"address": "г. Москва, ул. Пушкина, д. 5",
"partner": "partner_002",
"inn": "7708654321"
}
]
}
Пример ответа
{
"message": "Shops is loaded"
}
Особенности
- Уникальность проверяется по паре
[distributor_id, external_key] - Партнер должен быть предварительно загружен через endpoint
/api/server/v1/partners - При указании
external_keyпартнера система автоматически находит соответствующего партнера - Если указан
warehouse, склад должен быть предварительно загружен через endpoint/api/server/v1/warehouses - Если
warehouseуказан, но склад не найден — возвращается ошибкаNot found warehouses with external_key: ...
9. Загрузка накладных (Invoices)
Накладные содержат информацию о товарах, которые должны быть доставлены в магазины.
Endpoint
POST /api/server/v1/invoices
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
invoices |
array | Да | Массив объектов накладных |
invoices[].external_key |
string | Да | Уникальный идентификатор накладной во внешней системе |
invoices[].shop |
string | Да | external_key магазина-получателя |
invoices[].number |
string | Да | Номер накладной |
invoices[].date |
string | Да | Дата накладной (формат: YYYY-MM-DD) |
invoices[].shipping_date |
string/null | Нет | Дата отгрузки (формат: YYYY-MM-DD) |
invoices[].invoice_items |
array | Да | Массив товарных позиций накладной |
invoices[].invoice_items[].number |
number | Да | Номер позиции в накладной |
invoices[].invoice_items[].product |
string | Да | external_key товара |
invoices[].invoice_items[].unit_of_measure |
string | Да | external_key единицы измерения |
invoices[].invoice_items[].count |
number | Да | Количество товара |
invoices[].invoice_items[].price |
number | Да | Цена за единицу |
invoices[].invoice_items[].amount |
number | Да | Общая сумма позиции |
Пример запроса
{
"invoices": [
{
"external_key": "invoice_001",
"shop": "shop_001",
"number": "НАК-2024-001",
"date": "2024-12-05",
"shipping_date": "2024-12-06",
"invoice_items": [
{
"number": 1,
"product": "milk_32",
"unit_of_measure": "pcs",
"count": 50,
"price": 75.50,
"amount": 3775.00
},
{
"number": 2,
"product": "bread_white",
"unit_of_measure": "pcs",
"count": 100,
"price": 45.00,
"amount": 4500.00
}
]
}
]
}
Пример ответа
{
"message": "Invoices is loaded"
}
Особенности
- Уникальность накладной проверяется по паре
[shop_id, external_key] - Магазин должен быть предварительно загружен через endpoint
/api/server/v1/distributor_shops - Товар должен быть предварительно загружен через endpoint
/api/server/v1/products - Единица измерения должна быть предварительно загружена через endpoint
/api/server/v1/unit_of_measures - Если магазин, товар или единица измерения с указанным
external_keyне найдены, будет ошибка валидации и уведомление на email дистрибьютора - Товарные позиции (
invoice_items) создаются/обновляются вместе с накладной - При загрузке накладной система автоматически устанавливает FK-связь товарных позиций с записями в справочниках товаров и единиц измерения
10. Загрузка доставок (Deliveries)
Доставки связывают накладные с конкретными водителями и магазинами для организации логистики.
Endpoint
POST /api/server/v1/deliveries
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
deliveries |
array | Да | Массив объектов доставок |
deliveries[].external_key |
string | Да | Уникальный идентификатор доставки во внешней системе |
deliveries[].shop |
string | Да | external_key магазина-получателя |
deliveries[].number |
string | Да | Номер доставки |
deliveries[].date |
string | Да | Дата доставки (формат: YYYY-MM-DD) |
deliveries[].driver |
string | Да | Имя водителя |
deliveries[].invoices |
array | Да | Массив external_key накладных для доставки |
Пример запроса
{
"deliveries": [
{
"external_key": "delivery_001",
"shop": "shop_001",
"number": "ДОС-2024-001",
"date": "2024-12-06",
"driver": "Иванов Иван Иванович",
"invoices": [
"invoice_001",
"invoice_002"
]
},
{
"external_key": "delivery_002",
"shop": "shop_002",
"number": "ДОС-2024-002",
"date": "2024-12-06",
"driver": "Петров Петр Петрович",
"invoices": [
"invoice_003"
]
}
]
}
Пример ответа
{
"message": "Deliveries is loaded"
}
Особенности
- Уникальность проверяется по паре
[distributor_id, external_key] - Магазин должен быть предварительно загружен через endpoint
/api/server/v1/distributor_shops - Все накладные в массиве
invoicesдолжны быть предварительно загружены через endpoint/api/server/v1/invoices - Если магазин или накладные с указанными
external_keyне найдены, будет ошибка валидации - Водитель создается автоматически, если не существует
11. Загрузка фотографий товаров (Product Photos)
Фотографии товаров позволяют загружать изображения для номенклатуры дистрибьютора. Фотографии загружаются по URL и сохраняются в системе через Active Storage на S3.
Endpoint
POST /api/server/v1/product_photos
Параметры запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
product_photos |
array | Да | Массив объектов фотографий товаров |
product_photos[].product_external_key |
string | Да | external_key товара, к которому привязывается фотография |
product_photos[].external_key |
string | Да | Уникальный идентификатор фотографии во внешней системе |
product_photos[].photo_url |
string | Да | URL для загрузки фотографии |
Пример запроса
{
"product_photos": [
{
"product_external_key": "milk_32",
"external_key": "photo_milk_32_1",
"photo_url": "https://example.com/images/milk_front.jpg"
},
{
"product_external_key": "milk_32",
"external_key": "photo_milk_32_2",
"photo_url": "https://example.com/images/milk_back.jpg"
},
{
"product_external_key": "bread_white",
"external_key": "photo_bread_1",
"photo_url": "https://example.com/images/bread.jpg"
}
]
}
Пример ответа
{
"message": "Product photos is loaded"
}
Особенности
- Уникальность проверяется по паре
[product_id, external_key] - Товар должен быть предварительно загружен через endpoint
/api/server/v1/products - Если товар с указанным
external_keyне найден, будет ошибка валидации - При совпадении
external_keyдля одного товара фотография обновляется - Фотографии загружаются асинхронно через фоновое задание
- Система скачивает изображение по указанному URL и сохраняет его в S3 (Yandex Cloud)
- Поддерживаются стандартные форматы изображений (JPEG, PNG, GIF и др.)
- Один товар может иметь несколько фотографий с разными
external_key - Если загрузка фотографии по URL не удалась, ошибка логируется, но обработка продолжается
12. Получение заказов (Orders)
Endpoints для работы с заказами, созданными покупателями через мобильное приложение. В отличие от других Server API endpoints, которые используют POST для загрузки данных, endpoints для заказов используют RESTful подход для чтения и обновления.
12.1. Получение списка незабранных заказов
Возвращает список заказов, которые ещё не были экспортированы во внешнюю систему (где exported_at IS NULL).
Endpoint
GET /api/server/v1/orders
Параметры запроса
Без параметров. Возвращает все незабранные заказы текущего дистрибьютора.
Пример запроса
curl -X GET http://localhost:3000/api/server/v1/orders \
-H "Authorization: Bearer YOUR_SERVER_TOKEN"
Пример ответа (200 OK)
{
"orders": [
{
"id": 1,
"distributor_shop": "shop_001",
"comment": "Срочная доставка",
"shipping_date": "2026-01-20T10:00:00.000Z",
"status": "new",
"created_at": "2026-01-14T12:00:00.000Z",
"updated_at": "2026-01-14T12:00:00.000Z",
"order_items": [
{
"product": "PROD-123",
"unit_of_measure": "шт",
"count": 10,
"price": "150.50"
},
{
"product": "PROD-456",
"unit_of_measure": "кг",
"count": 5,
"price": "200.00"
}
]
},
{
"id": 2,
"distributor_shop": "shop_002",
"comment": null,
"shipping_date": "2026-01-21T14:00:00.000Z",
"status": "new",
"created_at": "2026-01-14T15:30:00.000Z",
"updated_at": "2026-01-14T15:30:00.000Z",
"order_items": [
{
"product": "PROD-789",
"unit_of_measure": "л",
"count": 20,
"price": "85.00"
}
]
}
]
}
Особенности
- Возвращаются только заказы, где
exported_at IS NULL(ещё не экспортированные) - Заказы отсортированы по дате создания (от старых к новым)
- Для каждого заказа включается полная информация о товарных позициях
- В товарных позициях возвращается
product- external_key товара (для связи с внешней системой) - Единица измерения возвращается как
unit_of_measure(название, например "шт", "кг") - Поле
distributor_shopсодержит external_key магазина дистрибьютора - Информация о магазине покупателя не возвращается (внутренняя информация системы)
- После экспорта заказа он больше не будет появляться в этом списке
- Используется eager loading для предотвращения N+1 запросов
Поля заказа
| Поле | Тип | Описание |
|---|---|---|
id |
integer | ID заказа в системе |
distributor_shop |
string | External key магазина дистрибьютора |
comment |
string/null | Комментарий к заказу |
shipping_date |
datetime/null | Желаемая дата доставки |
status |
string | Статус заказа: "new", "in_progress", "shipped" |
created_at |
datetime | Дата создания заказа |
updated_at |
datetime | Дата последнего обновления |
order_items |
array | Массив товарных позиций |
Поля товарной позиции
| Поле | Тип | Описание |
|---|---|---|
product |
string | External key товара для связи с вашей системой |
unit_of_measure |
string | Название единицы измерения (шт, кг, л и т.д.) |
count |
integer | Количество товара |
price |
decimal | Цена за единицу товара |
12.2. Обновление заказа
Обновляет заказ: при первом вызове помечает его как экспортированный (устанавливает exported_at), а также позволяет обновить статус заказа в любой момент. При смене статуса сотрудникам магазина автоматически отправляется уведомление.
Endpoint
PUT /api/server/v1/orders/:id
Параметры запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
id |
integer | Да | ID заказа в URL (path parameter) |
status |
string | Нет | Новый статус заказа: "new", "in_progress", "shipped" |
Тело запроса в формате JSON (опционально):
{
"status": "in_progress"
}
Пример запроса — пометить как экспортированный
curl -X PUT http://localhost:3000/api/server/v1/orders/1 \
-H "Authorization: Bearer YOUR_SERVER_TOKEN"
Пример запроса — обновить статус
curl -X PUT http://localhost:3000/api/server/v1/orders/1 \
-H "Authorization: Bearer YOUR_SERVER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "shipped"}'
Пример ответа (200 OK)
{
"message": "Order marked as exported",
"order": {
"id": 1,
"status": "shipped",
"exported_at": "2026-01-14T15:30:45.123Z"
}
}
Возможные ошибки
404 Not Found - заказ не найден или принадлежит другому дистрибьютору:
{
"error": "Order not found"
}
422 Unprocessable Entity - передан неизвестный статус:
{
"error": "Invalid status: unknown_status"
}
401 Unauthorized - неверный токен аутентификации:
{
"errors": "Unauthorized"
}
Особенности
exported_atустанавливается только при первом вызове; повторные вызовы не перезаписывают это поле- Статус можно обновлять многократно, в том числе после того, как заказ уже помечен как экспортированный
- При изменении статуса сотрудники магазина получают push-уведомление
- Можно обновлять только заказы своего дистрибьютора
- Заказ идентифицируется по ID базы данных (не по external_key)
Рекомендуемый workflow для внешней системы
- Периодический опрос: Раз в N минут/часов вызывать
GET /api/server/v1/ordersдля получения новых заказов - Обработка заказов: Импортировать полученные заказы в свою систему
- Подтверждение экспорта: Для каждого успешно импортированного заказа вызвать
PUT /api/server/v1/orders/:id - Обновление статуса: При изменении статуса в своей системе вызывать
PUT /api/server/v1/orders/:idс параметромstatus - Обработка ошибок: При ошибке импорта не вызывать PUT, заказ останется в списке для повторной попытки
Порядок загрузки данных
Для корректной работы системы рекомендуется загружать данные в следующем порядке:
- Unit of Measures - единицы измерения (независимы)
- Categories - категории номенклатуры (независимы, поддерживают иерархию)
- Warehouses - склады (независимы)
- Products - товары (требуют существующих категорий и единиц измерения)
- Product Photos - фотографии товаров (требуют существующих товаров)
- Price Lists - прайс-листы (требуют существующих товаров)
- Partners - партнеры (могут загружаться без
price_list; для привязки прайс-листа требуют существующих прайс-листов) - Remainders - остатки товаров на складах (требуют существующих складов, товаров и единиц измерения)
- Distributor Shops - магазины (требуют существующих партнеров)
- Invoices - накладные (требуют существующих магазинов, товаров и единиц измерения)
- Deliveries - доставки (требуют существующих магазинов и накладных)
Примечание: Partners можно загрузить без
price_listна первом этапе (до загрузки Price Lists), а затем повторно загрузить сprice_listпосле того, как прайс-листы будут готовы — запись обновится.
Обработка ошибок
Ошибка аутентификации
{
"errors": "Unauthorized"
}
Статус: 401
Ошибка валидации JSON
При невалидной структуре данных будет возвращена ошибка с описанием проблемы:
{
"error": "JSON validation failed: The property '#/shops/0' did not contain a required property of 'name'"
}
Ошибка отсутствия связанных данных
Если указаны external_key несуществующих сущностей:
{
"error": "Not found shops with external_key: shop_999, shop_888"
}
При загрузке накладных возможны комбинированные ошибки (несколько типов объединяются через ;):
{
"error": "Not found products with external_key: milk_32, bread_white; Not found unit_of_measures with external_key: box"
}
Асинхронная обработка
Endpoints для загрузки данных (POST) обрабатываются асинхронно через фоновые задачи:
Exchange::PartnerExchangeJob- обработка партнеровExchange::UnitOfMeasureExchangeJob- обработка единиц измеренияExchange::CategoryExchangeJob- обработка категорий номенклатурыExchange::ProductExchangeJob- обработка товаровExchange::PriceListExchangeJob- обработка прайс-листовExchange::WarehouseExchangeJob- обработка складовExchange::DistributorShopExchangeJob- обработка магазиновExchange::InvoiceExchangeJob- обработка накладныхExchange::DeliveryExchangeJob- обработка доставок
Это означает, что: - Успешный ответ от API означает только принятие данных в очередь - Фактическая обработка происходит в фоне через систему фоновых задач - Ошибки обработки логируются в систему
Важно: Endpoints для работы с заказами (GET /api/server/v1/orders и PUT /api/server/v1/orders/:id) работают синхронно и возвращают результат немедленно, без использования фоновых задач.
Примеры использования
cURL
# Загрузка партнеров с привязкой прайс-листа
curl -X POST http://localhost:3000/api/server/v1/partners \
-H "Authorization: Bearer YOUR_SERVER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"partners": [
{
"external_key": "partner_001",
"name": "ООО Торговый Дом",
"price_list": "retail_2024"
}
]
}'
# Загрузка магазинов
curl -X POST http://localhost:3000/api/server/v1/distributor_shops \
-H "Authorization: Bearer YOUR_SERVER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"shops": [
{
"external_key": "shop_001",
"name": "Продукты у дома",
"address": "г. Москва, ул. Ленина, д. 10",
"partner": "partner_001"
}
]
}'
Ruby
require 'net/http'
require 'json'
uri = URI('http://localhost:3000/api/server/v1/partners')
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.path, {
'Authorization' => 'Bearer YOUR_SERVER_TOKEN',
'Content-Type' => 'application/json'
})
request.body = {
partners: [
{ external_key: 'partner_001', name: 'ООО Торговый Дом' }
]
}.to_json
response = http.request(request)
puts response.body
Python
import requests
import json
url = 'http://localhost:3000/api/server/v1/partners'
headers = {
'Authorization': 'Bearer YOUR_SERVER_TOKEN',
'Content-Type': 'application/json'
}
data = {
'partners': [
{
'external_key': 'partner_001',
'name': 'ООО Торговый Дом'
}
]
}
response = requests.post(url, headers=headers, data=json.dumps(data))
print(response.json())