Движения остатков

Каждая операция ниже сгенерирована из OpenAPI v2 и содержит параметры, поля запроса, поля ответа, ошибки и примеры.

Движения остатков

GET /api/v2/stock-movements

Получить журнал движений остатков

operationIdlistStockMovements
РазделДвижения остатков
Безопасность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"
}