Purchase Orders
A purchase order records a request to procure goods from a vendor and tracks the full lifecycle from draft through line-item receiving to vendor invoice creation.
A purchase order (PO) is the formal record of a request to buy goods from a vendor. It holds the vendor, the receiving location, an ordered date, an expected delivery date, and one or more line items — each pairing a Product with a quantity and unit cost. When units arrive, you receive them against those lines and the resulting inventory units land directly in stock. After receiving, a vendor invoice can be generated from the PO in a single step.
POs connect purchasing to inventory. Receiving a line creates real inventory units and writes a Purchase movement on each one, so your stock levels and movement history are always accurate.
Creating a purchase order #
New purchase orders are created from Purchasing → Purchase Orders → New Purchase Order.
- Vendor — the supplier you are ordering from. Required. Pulling up a vendor also surfaces any vendor SKU links you have recorded, which the line-item form can draw on for default costs.
- PO Number — auto-generated as
PO-0001,PO-0002, … if left blank, or you can type your own. - Status — defaults to Draft; see the lifecycle section below.
- Receiving Location — which warehouse or site the stock should land in (hidden in single-location accounts). Defaults to the active location and sets the initial default receiving bin on the receive page.
- Order Date and Expected Delivery — informational dates shown in the list and on the PDF.
- Notes — free-text field visible on the PO PDF sent to the vendor.
- Tax Rate — an optional tax rate applied to the
subtotal; the resulting
tax_amountandtotal_amountare recomputed automatically whenever the rate or any line item changes. - Custom fields — any extra attributes defined for purchase orders appear at the bottom of the form.
Line items #
After saving the PO header, add lines on the Items tab (or in the relation manager on the edit page).
Each line captures:
- Product — which product you are ordering. Required.
- Vendor Part # — the vendor's own catalog/part number for this line. Auto-filled from the Product's vendor record for this PO's vendor when you pick the Product, but freely editable per line and printed on the PO PDF sent to the vendor.
- Quantity Ordered — how many units you expect.
- Unit Cost — what you are paying per unit. Stored and returned as an
integer in cents (
5000= $50.00). - Notes — optional per-line notes.
The PO's total amount refreshes every time a line is added, edited, or
deleted: it sums quantity_ordered × unit_cost across all lines, then adds
the tax amount if a tax rate is set.
The purchase order lifecycle #
A PO moves through five statuses:
- Draft — editable; not yet sent to the vendor.
- Submitted — sent to the vendor. The Submit to Vendor action on the view page marks the PO submitted and, if the vendor has an email address, queues an email with a PDF attachment. Submitting is subject to spend approval: a PO whose approval status is Pending is refused, and no email goes out.
- Partially Received — at least one line item has stock received against it but not all lines are fully received. Status advances automatically when you receive the first unit.
- Received — every line's
quantity_receivedhas reachedquantity_ordered. Status advances automatically when the last receipt closes the last open line. - Cancelled — the PO is closed with no further receiving allowed.
You can also Download PDF from the view page at any status — useful for sending the PO manually or keeping a paper record.
Spend approval #
A PO carries an approval status alongside its lifecycle status, so an order can be (say) Approved and Partially Received at the same time. It is one of Not Required, Pending, Approved, or Rejected, and only Not Required or Approved may be sent to the vendor.
Two per-person limits, both in cents, decide what happens on submit:
- Purchase limit — what that person may commit on their own. Empty means unlimited, so nobody is blocked until you set limits.
- Purchase approval limit — what that person may approve for others.
0means no approval authority at all, which is what every existing user starts with; empty means unlimited.
When you press Submit to Vendor, the total is recomputed from the order's lines. Within your purchase limit, the PO submits with approval status Not Required. Above it — or if an automation rule with a Require Approval action matches — the PO becomes Pending and is not sent to the vendor.
Approving and rejecting #
A pending PO can be actioned by any user who holds the
approve_purchase_orders permission and whose purchase approval limit covers
the total. There is no supervisor chain: approvers are a pool, not a tree.
- Approve records who approved it and when; the PO can then be submitted.
- Reject requires a reason and returns the PO to an editable Draft. The rejection — who, when, and why — stays on the record permanently, so the history of a turned-down order is never erased.
If nobody can approve it, the PO shows a warning naming the amount and saying that no user in the account has a high enough purchase approval limit. The order is never silently approved and never left looking ordinary; raise an approver's limit, or reduce the order.
An approved PO is locked #
An approval authorises a specific commitment: this much money, to this vendor. So once a PO is Approved, that commitment is frozen. Approving $900 and then raising the order to $9,000 would be an approval nobody actually gave.
Locked — the vendor, the tax rate, and the line items. Lines cannot be added, edited, removed, or moved between orders, whether from the PO screen, the API, or an automation.
Still editable — notes, the PO number, order and expected-delivery dates, the receiving location, and custom fields. A buyer chasing a late delivery should not need a fresh approval to write down a new date. Receiving stock against the order is unaffected.
This is not a setting, and there is nothing to switch on. Some systems ship the equivalent lock turned off by default, which means out of the box a confirmed order's amount can be raised with nobody re-approving it.
Unlock for Editing re-opens a locked PO. It is a deliberate action, never a side effect of an edit: it asks why, discards the approval, and returns the order to a Pending draft that must be approved — and submitted — again. Who unlocked it, when, and why are kept on the order. It is available while the PO is a Draft or Submitted, and not once stock has been received against it.
Unlocking needs the update purchase orders permission — the permission to
edit an order, since that is what unlocking is for. It deliberately does not
need approve_purchase_orders: the person fixing a line is usually the buyer,
not an approver. Unlocking cannot spend anything (it removes the approval, and a
resubmission is re-checked against the submitter's own purchase limit), but it
does discard an approval someone else gave, so it is not open to everyone.
Receiving stock #
Click Receive Items on the PO's view page (visible for Submitted and Partially Received POs) to open the dedicated receive screen. There is one receiving screen and this is it — the Receive button beside an individual line on the Line Items tab opens the same screen with that line already selected, rather than a second form of its own.
The receive screen shows each line with its ordered and remaining quantities. For each line you want to receive:
- Select the line — click it in the list, or scan a Product barcode to jump straight to the matching line.
- Set the destination — choose the location and bin where the stock should land. The screen pre-fills the location's default receiving bin — or, if that location has none set, wherever the Product was last put away. You can also scan a bin barcode to switch destinations on the fly.
- Enter quantity — defaults to the remaining quantity on the line.
- Optionally enter a lot number and expiration date if the Product's category tracks them.
- Click Receive (or press Enter).
Each receipt creates one inventory unit per unit received and writes a
Purchase movement against that unit, carrying that unit's share of the
receipt — so the movements for one receipt add up to the quantity received,
not to the quantity multiplied by the number of units. The line's
quantity_received increments and the PO status updates automatically. A scrollable Recent Receipts log shows the
last five receipts so you can catch mistakes quickly.
Creating a vendor invoice from a PO #
Once a PO reaches Received status, the Record Invoice action appears on the view page. Clicking it opens a small form where you confirm the vendor's invoice number, invoice date, due date, and amount due (pre-filled from the PO total). Saving creates a vendor invoice with one line per PO line — quantities and costs are carried over automatically — and takes you straight to the new invoice.
The purchase order list #
Purchasing → Purchase Orders shows each PO with its PO number, vendor, status badge, ordered date, expected delivery date, and total. Filter by status (multi-select), Product (finds any PO whose lines include that Product), or use the Trashed filter to view soft-deleted orders.
What lives on the PO page #
Open a PO to see its header infolist and tabs for everything attached to it:
- Line Items — the line items; new lines cannot be added once the PO is fully received.
- Received Inventory — every inventory unit created by receiving against this PO, with its current location and status.
- Vendor Invoices — vendor invoices linked to this PO.
- Attachments — any files uploaded against the PO.
- Changes — a full audit trail of edits.
Doing it from the API #
# Create a purchase order
curl -X POST "https://your-domain.com/api/v1/purchasing/purchase-orders" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"vendor_id": "...",
"ordered_at": "2026-06-02",
"expected_at": "2026-06-16",
"notes": "Please ship via ground freight."
}'
# Add a line item (unit_cost in cents)
curl -X POST "https://your-domain.com/api/v1/purchase-order-items" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"purchase_order_id": "...",
"product_id": "...",
"quantity_ordered": 10,
"unit_cost": 4500
}'
# List POs for a specific vendor
curl "https://your-domain.com/api/v1/purchasing/purchase-orders?vendor_id=..." \
-H "Authorization: Bearer $TOKEN"
Money fields are integers in cents over the API —
unit_cost: 4500means $45.00, andtotal_amountis returned the same way.