Suppliers & purchase orders
The buying side of the shop: who you buy from, what you have on order, what actually turned up, and whether it cost what it was supposed to.
Everything here lives under Purchasing — "Suppliers → draft & approve POs → receive goods into stock." It is an add-on: if the page is missing, turn Purchasing on in Settings → Add-ons.
The page has four tabs: Overview, Suppliers, Purchase orders and Receive. The Orders and Receive tabs carry a number badge — how many orders need action, and how many are ready to receive. Three tiles sit above them: Suppliers, Orders and Ready to receive.
Suppliers
Nothing else works until there is a supplier: the empty state says so — "Suppliers are required before you can create purchase orders or receive goods."
- Open the Suppliers tab and press Add supplier.
- Fill in Name and Code (both required — "Short unique code for lists and reports", e.g. ACME).
- Add Contact, Email and Phone if you have them.
- Set Payment terms (days) — "Net days until payment is due". It defaults to 30.
- Press Add supplier.
The table shows Name, Code, Contact, Email, Phone, Terms, Status and Actions. Each row has Edit, Deactivate/Activate and Delete. Deleting asks "Remove this supplier?" and hides the record rather than destroying history.
Terms are stored and displayed, but nothing tracks what you owe a supplier or when it is due. There are no supplier invoices, statements, payments, or accounts-payable ageing in the product today.
Importing a supplier list
Import CSV opens Import suppliers —
"Download template → fill in Excel → upload file. Dry-run first, then
commit." The columns are name, code,
contact_name, email, phone,
payment_terms_days, currency,
is_active.
name and code are required, and
code is the matching key: "Matching code updates the existing
supplier; new codes create rows." That makes a re-import safe — it updates
rather than duplicating. Press Dry run, read the result
("Dry run — N valid of M"), then Commit import.
Raise a purchase order
- Go to the Purchase orders tab. The create panel is titled "Create purchase order" — "Pick a supplier and products, then save as draft. Approve from the list to receive."
- Pick a Supplier. Only active suppliers are listed.
- Type a PO number (required, e.g. PO-1001) and an Expected date.
- Add lines with Add product ("Search name / SKU…") or Or pick from stocked items.
- For each line set Qty and Unit cost. Unit cost is pre-filled from the item's average cost; the panel keeps a running Est. subtotal.
- Press Create draft PO.
You get "Created draft PO-1001 with 4 line(s). Approve it from the PO list to receive."
The statuses, and what moves between them
| Status | Meaning | What moves it on |
|---|---|---|
| draft | Just created. Every new PO starts here. | Approve |
| pending approval | Accepted by the system but never set by the app. | Approve |
| approved | Signed off. Can be received against. | Send, or receive directly |
| sent | Marked as sent to the supplier. | Receiving goods |
| partially received | Some lines came in, some are outstanding. | Receiving the rest |
| received | Every line received in full. | — |
| cancelled | Displayed, but nothing in the app ever sets it. | — |
The list's filter chips group these as All, Needs action, In transit and Done. Approving out of turn is refused in plain words — "Cannot send purchase order in status 'draft' (approve first)".
Send only flips the status to sent so you can see what has gone out. It does not email or print the order — there is no PO email, PDF or print in the product. Send the order to your supplier the way you do now, then press Send to record it.
Once created there is no edit, no line add or remove, no delete and no cancel button. A wrong PO stays on the list. Raise a correct one and simply do not receive against the wrong one.
Receive goods against a PO
The Receive tab is titled "Receive goods (GRN)" — "Receive against approved or sent POs. Edit qty per line for partial receipts."
- Pick the Purchase order from the list (only approved, sent, or partially received orders appear).
- Type a GRN number — the delivery note reference.
- Check each line. It shows "Ordered 24 · Received 0 · Remaining 24". Receive now is pre-filled with the remaining quantity — change it to what actually turned up. Leave a line at 0 to skip it.
- If the supplier charged a different price, change Unit cost. The variance for that line appears beside it.
- If you are taking more than was ordered, tick Acknowledge over-receipt (when receiving more than remaining).
- Press Receive & post.
You get "Posted GRN GRN-1001 — stock updated. Cost variance total: 12.40." Stock rises immediately, and the PO becomes partially received or received depending on whether anything is outstanding. Come back and receive the rest later against the same PO.
Receiving posts straight to stock. There is no draft goods receipt in the app, no reversal and no void. A mis-keyed receipt has to be corrected with a stock adjustment (see Stock & holds), which leaves the purchase record showing the wrong quantity received.
Goods-receipt variance
"Variance" here means cost variance, and only that:
(the unit cost you typed on the receipt − the unit cost on the PO line) × the quantity received.
A positive figure means you were charged more than agreed; a negative one means less. It appears in three places: live beside each line as you type, in a Posted cost variance list after you post, and totalled in the confirmation message. Variance is only calculated for a receipt line that is linked to a PO line.
Across the year, Reports → GRN variance — "Cost and quantity variance on posted receipts (YTD)" — totals the same figures by supplier and lists the worst lines. It counts posted receipts only.
Despite the report's title, only cost variance is stored. Receiving 20 of 24 simply leaves the PO partially received — there is no shortage or damage figure, no reject quantity, and no per-line quantity variance anywhere.
Chasing late orders — PO ageing
Ageing lives in Reports, not on the Purchasing page. Pick the Purchasing category, then Open PO aging — "Open purchase orders by days past expected date."
- It counts orders in approved, sent and partially received. Drafts, cancelled and fully-received orders are left out.
- Ageing is measured on what is still outstanding — ordered quantity minus received quantity, never below zero.
- The buckets are current, 1-30, 31-60, 61-90 and 91+ days past the expected date. Empty buckets are hidden.
- The tiles are Open POs, Open value and Remaining qty; the detail table shows PO #, Supplier, Status, Open value, Days and Bucket.
Two neighbours are worth knowing: Receipts / GRN lists what was received in a date range, and Supplier spend YTD totals ordered and received value by supplier for the year.
The Overview tab
A 30-day dashboard: Open POs ("Not fully received"), Received value ("GRNs in range"), Late receipts ("Past expected date"), Open by status, Supplier spend, Received by day and a Late PO list ordered by days past expected.
Supplier returns — not built
Supplier returns, debit notes and return-to-vendor do not exist anywhere in the product — not in the UI, not in the API. If a delivery has to go back, the only truthful thing the system can record is a stock adjustment or waste entry with a reason and a note. The purchase record will still show the goods as received, and the supplier credit has to be tracked outside the system.
Other limits worth knowing before you plan around them
- No PO detail screen — you see the header row on the list; the lines are only visible in the Receive tab.
- No receiving without a PO from the app.
- No barcode scanning while receiving, and no lot or expiry date entry on the receive form.
- No freight, landed cost, tax or discount on a purchase order — the order tax total is always zero.
- No supplier product catalogue, supplier SKU, pack size, minimum order quantity, lead time or cost history.
- No suggested reorder — the Low stock report tells you what is short, you raise the PO.
- No approval limits: anyone with purchasing rights can approve their own order.
- The over-receipt policy exists in the database (block / warn / allow with approval) but there is no screen to change it, and the app behaves as "warn" — tick the acknowledgement and it goes through.