Sari la conținut

Stocuri ​

Stocul per locație pentru fiecare produs, plus o vedere agregată per produs și operațiuni de ajustare a stocului.

Endpoint-uri ​

MetodăCaleDescriere
GET/api/v1/inventoryListă paginată a rândurilor de stoc (unul per produs+locație); filtre product_id, location_id, sku, low_stock.
GET/api/v1/inventory/summaryTotal agregat per produs, peste toate locațiile.
POST/api/v1/inventory/add-stockAdaugă stoc la un produs într-o locație.
POST/api/v1/inventory/remove-stockScoate stoc dintr-un produs la o locație.

Filtre la listare (GET /api/v1/inventory) ​

ParametruTipNote
product_idintegerFiltrează după produs (products.product_id).
location_idintegerFiltrează după locație (locations.id).
skustringFiltrează după SKU-ul produsului.
low_stockbooleantrue → doar produsele sub min_stock_level (când e configurat).
per_pageintegerImplicit 100.

Total agregat (GET /api/v1/inventory/summary) ​

Un rând per produs, însumat peste toate locațiile. Fiecare rând conține product_id, location_count și vocabularul unic de stoc:

CâmpSemnificație
on_handToate unitățile fizic prezente, în orice locație.
blockedDin on_hand, unitățile din locații de tip quarantine, qc_hold, pending_approval sau din locații care nu sunt active.
reservedreserved_quantity pe rânduri plus cantitatea sarcinilor de relocare deschise.
pickableUnitățile din locații pickable și active (fără cele blocate).
sellableUnitățile din locații sellable și active (fără cele blocate).
availablepickable minus rezervările din locațiile pickable, minim 0. Singurul număr de promis clienților.
in_transitUnitățile din relocări cu starea in_transit.

Câmpurile vechi rămân ca alias-uri: total_quantity = on_hand, total_reserved = reserved, available_quantity = available. Obiectul product inclus aduce și price și price_eur (decimal, ca string), deci prețul e disponibil fără un apel separat per rând.

Fiecare rând din GET /api/v1/inventory conține în plus state (available, reserved, non_pickable sau blocked:<motiv>, cu motivul quarantine, qc_hold, pending_approval ori location_inactive), iar location include type, is_pickable și is_sellable. Filtrul in_stock de pe /products înseamnă acum available > 0. Obiectul product inclus aduce și price și price_eur (decimal, ca string), deci prețul e disponibil fără un apel separat per rând.

ParametruTipNote
product_idintegerFiltrează după produs (products.product_id).
searchstringCaută în name, sku, barcode (aplicat înainte de agregare, deci total reflectă rezultatul).
in_stockboolean1 → doar produse cu total_quantity > 0; 0 → doar produse cu totalul pe zero. Filtrul e agregat (HAVING), iar total / last_page reflectă setul filtrat.
per_pageintegerImplicit 100.

in_stock=0 aici ≠ pe /products

summary se construiește grupând rândurile de inventar, deci un produs fără niciun rând de stoc nu apare deloc aici. in_stock=0 înseamnă „rândurile existente însumează zero", nu „produs niciodată stocat". Pentru a prinde și produsele fără niciun rând, folosește /products?in_stock=0.

Schema răspunsului pentru chei de client ​

O cheie legată de un client extern citește stocul propriu într-o schemă explicită (scrierea de stoc nu este disponibilă cheilor de client). Cheile tenantului și cheile house păstrează schema de integrare descrisă mai sus.

GET /api/v1/inventory, un rând per locație: product_id, sku, location_code, warehouse ({ "code": "…" } sau null), quantity, available_quantity, state, batch_number, lot_number, expiry_date.

GET /api/v1/inventory/summary, un rând per produs: product_id, sku, on_hand, available, reserved, committed, in_transit.

Exemple (verificate automat față de schemele înghețate):

json
{
  "data": [
    {
      "product_id": 812,
      "sku": "BRK-PAD-001",
      "location_code": "CULOAR1-RAFT-3",
      "warehouse": { "code": "MAIN" },
      "quantity": 20,
      "available_quantity": 18,
      "state": "available",
      "batch_number": "B-2026-06",
      "lot_number": null,
      "expiry_date": null
    }
  ]
}
json
{
  "data": [
    {
      "product_id": 812,
      "sku": "BRK-PAD-001",
      "on_hand": 20,
      "available": 18,
      "reserved": 2,
      "committed": 2,
      "in_transit": 0
    }
  ]
}

Avize de expediție (ASN) pentru chei de client ​

Pagina Avize de expediție descrie cererile, răspunsurile și anularea.

GET /api/v1/asns, GET /api/v1/asns/{id}, POST /api/v1/asns și POST /api/v1/asns/{id}/cancel răspund, pentru o cheie de client, cu: id, reference, status, expected_on, expected_at, arrived_at, carrier, tracking_number, container_number, seal_number, source, client_confirmed_at, closed_short_at, warehouse ({ "code": "…" } sau null) și lines[] cu id, product_id, sku, expected_quantity, received_quantity, accepted_quantity, damaged_quantity, rejected_quantity, batch_number, expiry_date, origin. Nota de sosire a depozitului, sesiunea de recepție și câmpul notes nu fac parte din răspuns. Cheile tenantului păstrează schema de integrare.

Adăugare stoc ​

Câmpuri pentru POST /api/v1/inventory/add-stock:

CâmpTipObligatoriuNote
product_idintegerdaTrebuie să existe (products.product_id).
location_idintegerdaTrebuie să existe (locations.id).
quantityinteger ≥ 1daCantitatea de adăugat.
batch_numberstring ≤ 50nuLot/șarjă.
lot_numberstring ≤ 50nu
expiry_dateYYYY-MM-DDnuData de expirare.
notestextnuNotă liberă, salvată în mișcarea de stoc.
bash
curl -X POST https://tenant.notsowms.ro/api/v1/inventory/add-stock \
  -H "Authorization: Bearer tenant.7c4a8d09ca3762af61e59520943dc26494f8941b" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 812,
    "location_id": 14,
    "quantity": 20,
    "batch_number": "B-2026-06",
    "notes": "Recepție furnizor"
  }'

Răspuns 201 — rândul de stoc actualizat (cu produsul și locația):

json
{
  "data": {
    "id": 3391,
    "product_id": 812,
    "location_id": 14,
    "quantity": 20,
    "reserved_quantity": 0,
    "batch_number": "B-2026-06",
    "product": { "product_id": 812, "sku": "BRK-PAD-001", "name": "Plăcuțe frână față" },
    "location": { "id": 14, "location_code": "CULOAR1-RAFT-3" }
  }
}

Scoatere stoc ​

Câmpuri pentru POST /api/v1/inventory/remove-stock:

CâmpTipObligatoriuNote
product_idintegerdaTrebuie să existe (products.product_id).
location_idintegerdaTrebuie să existe (locations.id).
quantityinteger ≥ 1daCantitatea de scos.
notestextnuNotă liberă, salvată în mișcarea de stoc.

Dacă produsul nu are stoc la acea locație sau cantitatea cerută depășește stocul disponibil, primești 422:

json
{ "error": "Insufficient stock." }

Fără cod de motiv

Operațiunile de stoc nu primesc un cod de motiv de la apelant. Fiecare adăugare/scoatere se înregistrează automat în inventory_transactions (vizibil în pagina Mișcări stocuri) cu un motiv setat de server (Adăugare stoc via API / Eliminare stoc via API). Singurul text liber pe care îl poți atașa este notes.

Documentație API notso WMS