Skip to main content

Purchasing and inventory

The purchasing flow is:

PurchaseOrder → GRN (goods receipt) → purchase invoice → supplier payment

Inventory changes are emitted as stock movements. A purchase order by itself does not mean goods are on hand; receiving through a GRN is the inventory event.

Purchase orders​

Create a purchase order with the supplier, warehouse, currency, payment terms, and lines:

mutation CreatePurchaseOrder($input: CreatePurchaseOrderInput!) {
createPurchaseOrder(input: $input) {
id
orderNumber
status
orderCurrency
totalAmount
version
items {
productId
variantId
quantity
unitPrice
uomId
}
}
}

The exact output fields can vary by the current generated schema; open the PurchaseOrder type in the reference.

Use the purchase-order transition mutations rather than editing a status string. Approval, cancellation, and receiving have separate business rules.

Goods receipts​

A GRN records what was actually received at a warehouse:

mutation CreateGRN($input: CreateGRNInput!) {
createGRN(createGRNInput: $input) {
id
grnNumber
status
warehouseId
purchaseOrderId
items {
productId
acceptedQuantity
rejectedQuantity
}
}
}

A receipt can include partial quantities, shortages, damage, batch information, and inspection data. Use the resulting GRN and stock movement records as the source of truth for on-hand state.

Read stock​

Use the inventory queries for the current tenant and selected warehouse/variant. Do not infer available stock from the sales order list:

query Inventory($productId: ID, $warehouseId: ID) {
inventorySummary(productId: $productId, warehouseId: $warehouseId) {
totalAvailable
totalQuantity
totalValue
lowStockItems
outOfStockItems
}
}

Check the generated InventorySummary type if the operation's projection changes. Stock can be reserved, issued, transferred, adjusted, or returned; totalStock and totalAvailable are not interchangeable.

Stock movements​

Every inventory-changing workflow should result in a movement with a type such as receipt, issue, transfer, adjustment, or return. Use the movement query to reconcile an external WMS or reporting system. Do not write a compensating movement merely because an external sync was retried; use the original operation's idempotency and reconciliation state.

Integration rules​

  • Keep product, variant, UOM, warehouse, and supplier ids mapped per tenant.
  • Use idempotencyKey for import/order/payment commands that expose it.
  • Do not receive a purchase order by calling a deprecated direct-receive mutation; use createGRN as the schema description instructs.
  • Treat quantities and monetary values as decimals and keep their UOM/currency metadata.
  • Re-read after an asynchronous or bulk operation; a request accepted by a queue is not the same as a completed write.

Next: Errors, pagination, and idempotency.