Движения остатков
Каждая операция ниже сгенерирована из OpenAPI v2 и содержит параметры, поля запроса, поля ответа, ошибки и примеры.
Движения остатков
Получить журнал движений остатков
| operationId | listStockMovements |
|---|---|
| Раздел | Движения остатков |
| Безопасность | bearerAuth |
Возвращает движения остатков, видимые аутентифицированному аккаунту. Используйте описанные query parameters для фильтрации, сортировки и пагинации.
For incremental export, request sort=id&after_id=<last_seen_id>. The after_id boundary is exclusive. Use the greatest returned id as the next high-water mark. The endpoint does not provide snapshot isolation; rows inserted concurrently with greater IDs may appear in later calls.
Both sides of one relocation use movement_type=stock_transfer and share the same non-null movement_group_id; use that opaque value to correlate the negative and positive rows.
Since the picking-lot update, actual picking appends two atomic order rows: operation=reservation_release has positive quantity_delta for the former reservation, and operation=physical_issue has negative quantity_delta with the actual batch, production_date and expiration_date. Each pair shares movement_group_id and sums to zero, preserving cumulative availability. Do not sum only negative order rows as consumption: that would count both the reservation and the physical issue. Use source=order_picking.issue to select new physical issues. Earlier rows are not rewritten; historical current picked lots can be read from orders.
transfer_kind distinguishes within_warehouse (document types 3/4) and between_warehouses (6/7); movement_group_id is opaque. stock_after remains null. The journal does not cover every manual loss/correction path.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
Since the picking-lot update, actual picking appends two atomic order rows: operation=reservation_release has positive quantity_delta for the former reservation, and operation=physical_issue has negative quantity_delta with the actual batch, production_date and expiration_date. Each pair shares movement_group_id and sums to zero, preserving cumulative availability. Do not sum only negative order rows as consumption: that would count both the reservation and the physical issue. Use source=order_picking.issue to select new physical issues. Earlier rows are not rewritten; historical current picked lots can be read from orders.
transfer_kind distinguishes within_warehouse (document types 3/4) and between_warehouses (6/7); movement_group_id is opaque. stock_after remains null. The journal does not cover every manual loss/correction path.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
Since the picking-lot update, actual picking appends two atomic order rows: operation=reservation_release has positive quantity_delta for the former reservation, and operation=physical_issue has negative quantity_delta with the actual batch, production_date and expiration_date. Each pair shares movement_group_id and sums to zero, preserving cumulative availability. Do not sum only negative order rows as consumption: that would count both the reservation and the physical issue. Use source=order_picking.issue to select new physical issues. Earlier rows are not rewritten; historical current picked lots can be read from orders.
transfer_kind distinguishes within_warehouse (document types 3/4) and between_warehouses (6/7); movement_group_id is opaque. stock_after remains null. The journal does not cover every manual loss/correction path.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
Since the picking-lot update, actual picking appends two atomic order rows: operation=reservation_release has positive quantity_delta for the former reservation, and operation=physical_issue has negative quantity_delta with the actual batch, production_date and expiration_date. Each pair shares movement_group_id and sums to zero, preserving cumulative availability. Do not sum only negative order rows as consumption: that would count both the reservation and the physical issue. Use source=order_picking.issue to select new physical issues. Earlier rows are not rewritten; historical current picked lots can be read from orders.
transfer_kind distinguishes within_warehouse (document types 3/4) and between_warehouses (6/7); movement_group_id is opaque. stock_after remains null. The journal does not cover every manual loss/correction path.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
Since the picking-lot update, actual picking appends two atomic order rows: operation=reservation_release has positive quantity_delta for the former reservation, and operation=physical_issue has negative quantity_delta with the actual batch, production_date and expiration_date. Each pair shares movement_group_id and sums to zero, preserving cumulative availability. Do not sum only negative order rows as consumption: that would count both the reservation and the physical issue. Use source=order_picking.issue to select new physical issues. Earlier rows are not rewritten; historical current picked lots can be read from orders.
transfer_kind distinguishes within_warehouse (document types 3/4) and between_warehouses (6/7); movement_group_id is opaque. stock_after remains null. The journal does not cover every manual loss/correction path.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
Since the picking-lot update, actual picking appends two atomic order rows: operation=reservation_release has positive quantity_delta for the former reservation, and operation=physical_issue has negative quantity_delta with the actual batch, production_date and expiration_date. Each pair shares movement_group_id and sums to zero, preserving cumulative availability. Do not sum only negative order rows as consumption: that would count both the reservation and the physical issue. Use source=order_picking.issue to select new physical issues. Earlier rows are not rewritten; historical current picked lots can be read from orders.
transfer_kind distinguishes within_warehouse (document types 3/4) and between_warehouses (6/7); movement_group_id is opaque. stock_after remains null. The journal does not cover every manual loss/correction path.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
Пример HTTP-запроса (cURL)
Подставьте свой сервер и токен. ID из примера замените реальными доступными ID. Для тела запроса сохраните подходящий JSON из раздела ниже в request.json; перед отправкой замените демонстрационные значения. Заголовок Idempotency-Key использует ключ одной логической операции.
curl --globoff --request GET "${NWMS_BASE_URL}/api/v2/stock-movements" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${NWMS_TOKEN}"Параметры
| Имя | Где | Обязательное | Тип | Style | Explode | Ограничения | Описание | Пример |
|---|---|---|---|---|---|---|---|---|
page |
query | нет | integer:int64 |
form | true | min: 1 | Номер страницы. Этот фильтр также можно передать как filter[page], если это поддерживает Laravel query adapter. |
1 |
per_page |
query | нет | integer:int64 |
form | true | min: 1 max: 500 |
Количество записей на странице. По умолчанию 100; максимум 500. Этот фильтр также можно передать как filter[per_page], если это поддерживает Laravel query adapter. |
100 |
sort |
query | нет | string |
form | false | pattern: ^-?[A-Za-z0-9_]+(,-?[A-Za-z0-9_]+)*$ | Поля сортировки через запятую. Префикс - означает сортировку по убыванию. Допустимые поля: id, occurred_at, created_at. По умолчанию: id. Этот фильтр также можно передать как filter[sort], если это поддерживает Laravel query adapter. |
"id" |
after_id |
query | нет | integer:int64 |
form | true | min: 0 | Для инкрементальной выгрузки используйте sort=id и after_id. Граница after_id исключающая (id > after_id). Этот фильтр также можно передать как filter[after_id], если это поддерживает Laravel query adapter. |
1000 |
id |
query | нет | integer:int64 |
form | true | min: 1 | Фильтр по id с точным совпадением. Этот фильтр также можно передать как filter[id], если это поддерживает Laravel query adapter. |
1001 |
warehouse_id |
query | нет | integer:int64 |
form | true | min: 1 | Фильтр по warehouse_id с точным совпадением. Этот фильтр также можно передать как filter[warehouse_id], если это поддерживает Laravel query adapter. |
42 |
product_id |
query | нет | integer:int64 |
form | true | min: 1 | Фильтр по product_id с точным совпадением. Этот фильтр также можно передать как filter[product_id], если это поддерживает Laravel query adapter. |
1201 |
base_product_id |
query | нет | integer:int64 |
form | true | min: 1 | Фильтр по base_product_id с точным совпадением. Этот фильтр также можно передать как filter[base_product_id], если это поддерживает Laravel query adapter. |
1200 |
document_type_id |
query | нет | integer:int64 |
form | true | min: 1 | Фильтр по document_type_id с точным совпадением. Этот фильтр также можно передать как filter[document_type_id], если это поддерживает Laravel query adapter. |
1 |
document_id |
query | нет | integer:int64 |
form | true | min: 1 | Фильтр по document_id с точным совпадением. Этот фильтр также можно передать как filter[document_id], если это поддерживает Laravel query adapter. |
5101 |
movement_type |
query | нет | string |
form | true | Фильтр по movement_type с точным совпадением. Этот фильтр также можно передать как filter[movement_type], если это поддерживает Laravel query adapter. |
"stock_transfer" |
|
movement_group_id |
query | нет | string |
form | true | Фильтр по movement_group_id с точным совпадением. Этот фильтр также можно передать как filter[movement_group_id], если это поддерживает Laravel query adapter. |
"relocation:1722099600000000" |
|
source |
query | нет | string |
form | true | Фильтр по source с точным совпадением. Этот фильтр также можно передать как filter[source], если это поддерживает Laravel query adapter. |
"api" |
|
barcode |
query | нет | string |
form | true | Фильтр по barcode с частичным текстовым совпадением. Этот фильтр также можно передать как filter[barcode], если это поддерживает Laravel query adapter. |
"4601234567890" |
|
batch |
query | нет | string |
form | true | Фильтр по batch с частичным текстовым совпадением. Этот фильтр также можно передать как filter[batch], если это поддерживает Laravel query adapter. |
"LOT-2026" |
|
occurred_at_from |
query | нет | string:date |
form | true | Нижняя включительная граница даты для occurred_at. Этот фильтр также можно передать как filter[occurred_at_from], если это поддерживает Laravel query adapter. |
"2026-07-14" |
|
occurred_at_to |
query | нет | string:date |
form | true | Верхняя включительная граница даты для occurred_at. Этот фильтр также можно передать как filter[occurred_at_to], если это поддерживает Laravel query adapter. |
"2026-07-14" |
|
created_at_from |
query | нет | string:date |
form | true | Нижняя включительная граница даты для created_at. Этот фильтр также можно передать как filter[created_at_from], если это поддерживает Laravel query adapter. |
"2026-07-14" |
|
created_at_to |
query | нет | string:date |
form | true | Верхняя включительная граница даты для created_at. Этот фильтр также можно передать как filter[created_at_to], если это поддерживает Laravel query adapter. |
"2026-07-14" |
|
production_date_from |
query | нет | string:date |
form | true | Нижняя включительная граница даты для production_date. Этот фильтр также можно передать как filter[production_date_from], если это поддерживает Laravel query adapter. |
"2026-07-14" |
|
production_date_to |
query | нет | string:date |
form | true | Верхняя включительная граница даты для production_date. Этот фильтр также можно передать как filter[production_date_to], если это поддерживает Laravel query adapter. |
"2026-07-14" |
|
expiration_date_from |
query | нет | string:date |
form | true | Нижняя включительная граница даты для expiration_date. Этот фильтр также можно передать как filter[expiration_date_from], если это поддерживает Laravel query adapter. |
"2026-07-14" |
|
expiration_date_to |
query | нет | string:date |
form | true | Верхняя включительная граница даты для expiration_date. Этот фильтр также можно передать как filter[expiration_date_to], если это поддерживает Laravel query adapter. |
"2026-07-14" |
Тело запроса
Тело запроса отсутствует.
Ответы
200 Successful response.
Successful response.
| Поле | Обязательное | Тип | Флаги | Ограничения | Описание | Пример |
|---|---|---|---|---|---|---|
data |
да | array<StockMovement> |
|
|||
data[] |
нет | StockMovement |
Строка append-only журнала движений остатков. | |
||
data[].id |
да | integer:int64 |
readOnly | min: 1 | 1001 |
|
data[].created_at |
нет | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].updated_at |
нет | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].movement_type |
да | StockMovementType |
Stock movement type emitted by the backend. Current values are acceptance, order, stock_transfer, inventory, quant_unpacking, and movement. The set may grow when new document sources are added. |
"stock_transfer" |
||
data[].movement_group_id |
да | string | null |
Opaque correlation ID shared by both sides of one relocation or stock-transfer line. | "relocation:1722099600000000" |
||
data[].source |
нет | string | null |
"api" |
|||
data[].domain_id |
нет | integer:int64 | null |
min: 1 | 3 |
||
data[].warehouse_id |
нет | integer:int64 | null |
min: 1 | 42 |
||
data[].product_id |
нет | integer:int64 | null |
min: 1 | 1201 |
||
data[].base_product_id |
нет | integer:int64 | null |
min: 1 | 1200 |
||
data[].quantity_delta |
нет | number:float | null |
-2 |
|||
data[].base_quantity_delta |
нет | number:float | null |
-12 |
|||
data[].stock_after |
нет | number:float | null |
18 |
|||
data[].document |
нет | StockMovementDocument |
|
|||
data[].document.type_id |
нет | integer:int64 | null |
3 |
|||
data[].document.id |
нет | integer:int64 | null |
min: 1 | 8101 |
||
data[].document.line_id |
нет | integer:int64 | null |
min: 1 | 7001 |
||
data[].place_id |
нет | integer:int64 | null |
min: 1 | 501 |
||
data[].barcode |
нет | string | null |
"4601234567890" |
|||
data[].batch |
нет | string | null |
"LOT-2026" |
|||
data[].production_date |
нет | string:date | null |
"2026-01-15" |
|||
data[].expiration_date |
нет | string:date | null |
"2027-01-15" |
|||
data[].occurred_at |
нет | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].transfer_kind |
нет | enum | null |
enum: within_warehouse, between_warehouses | "within_warehouse" |
||
data[].operation |
нет | enum | null |
enum: reservation_release, physical_issue | Set for new picking audit pairs; null for older and other operations. | "physical_issue" |
|
links |
да | PaginationLinks |
|
|||
links.first |
да | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=1" |
|||
links.last |
да | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=2" |
|||
links.prev |
да | string:uri | null |
|
|||
links.next |
да | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=2" |
|||
meta |
да | PaginationMeta |
|
|||
meta.api_version |
да | enum |
enum: v2 | "v2" |
||
meta.current_page |
да | integer:int32 |
min: 1 | 1 |
||
meta.from |
да | integer:int32 | null |
min: 1 | 1 |
||
meta.last_page |
да | integer:int32 |
min: 1 | 2 |
||
meta.path |
да | string:uri |
"https://betawms.ffcdek.ru/api/v2/orders" |
|||
meta.per_page |
да | integer:int32 |
min: 1 | 50 |
||
meta.to |
да | integer:int32 | null |
min: 1 | 50 |
||
meta.total |
да | integer:int32 |
min: 0 | 73 |
Примеры ответа
success
{
"data": [
{
"id": 120001,
"movement_type": "stock_transfer",
"movement_group_id": "relocation:1722099600000000",
"source": "api",
"domain_id": 3,
"warehouse_id": 42,
"product_id": 1201,
"base_product_id": 1200,
"quantity_delta": -2,
"base_quantity_delta": -12,
"stock_after": null,
"document": {
"type_id": 3
},
"place_id": 501,
"barcode": "4601234567890",
"batch": "LOT-2026",
"production_date": "2026-01-15",
"expiration_date": "2027-01-15",
"occurred_at": "2026-07-14T10:30:00Z",
"created_at": "2026-07-14T10:30:00Z",
"updated_at": "2026-07-14T10:30:00Z"
}
],
"links": {
"first": "https://betawms.ffcdek.ru/api/v2/stock-movements?page=1",
"last": "https://betawms.ffcdek.ru/api/v2/stock-movements?page=2",
"prev": null,
"next": "https://betawms.ffcdek.ru/api/v2/stock-movements?page=2"
},
"meta": {
"api_version": "v2",
"current_page": 1,
"from": 1,
"last_page": 2,
"path": "https://betawms.ffcdek.ru/api/v2/stock-movements",
"per_page": 100,
"to": 100,
"total": 123
}
}
401 Требуется аутентификация
Unauthorized
Требуется аутентификация.
| Поле | Обязательное | Тип | Флаги | Ограничения | Описание | Пример |
|---|---|---|---|---|---|---|
message |
да | string |
"The request could not be processed." |
|||
error |
да | string |
"BAD_REQUEST" |
Примеры ответа
AUTH_REQUIRED
{
"message": "Authentication is required.",
"error": "AUTH_REQUIRED"
}
403 Доступ запрещен
Forbidden
Аутентифицированному аккаунту запрещено выполнять это действие.
| Поле | Обязательное | Тип | Флаги | Ограничения | Описание | Пример |
|---|---|---|---|---|---|---|
message |
да | string |
"The request could not be processed." |
|||
error |
да | string |
"BAD_REQUEST" |
Примеры ответа
FORBIDDEN
{
"message": "The authenticated account is not allowed to perform this action.",
"error": "FORBIDDEN"
}
429 Превышен лимит запросов
TooManyRequests
Превышен лимит запросов.
| Поле | Обязательное | Тип | Флаги | Ограничения | Описание | Пример |
|---|---|---|---|---|---|---|
message |
да | string |
"Too Many Attempts." |
|||
error |
да | string |
"RATE_LIMITED" |
500 Внутренняя ошибка сервера
InternalServerError
Произошла непредвиденная ошибка сервера.
| Поле | Обязательное | Тип | Флаги | Ограничения | Описание | Пример |
|---|---|---|---|---|---|---|
message |
да | string |
"The request could not be processed." |
|||
error |
да | string |
"BAD_REQUEST" |
Примеры ответа
INTERNAL_SERVER_ERROR
{
"message": "An unexpected server error occurred.",
"error": "INTERNAL_SERVER_ERROR"
}