Purchase Orders
Purchase Orders is where you formally order stock from suppliers, track the order through approval and shipping, receive the goods into inventory (with batch/expiry/serial capture), and handle any damaged or returned items.
Overview
A Purchase Order moves through a status lifecycle: Draft → Pending Approval → Approved → Sent to Supplier (Ordered) → Partially Received / Fully Received, and can be Cancelled at most stages.
Every PO carries items (product, quantity, unit cost, tax rate), optional Landed Costs (freight, customs duty, handling, insurance) that get proportionally allocated across item costs on receipt, and optional multi-currency support with a manual exchange rate.
The Auto-Generate PO wizard scans every product whose stock has fallen to or below its reorder point and lets you build one or more POs from those suggestions in a few clicks, grouped by supplier.
Receiving stock against a PO updates inventory quantities, can update product cost price (three modes: keep existing, use PO cost, or blend with weighted average), and supports capturing batch numbers, expiry dates and serial numbers per line item.
This page requires the Advanced plan tier and the purchase_orders feature, same as Suppliers.
Before You Start
- At least one Supplier must exist (add one from the Suppliers page) before you can create a PO.
- You need Create permission to raise or receive a PO, Edit permission to change its status, and Delete permission to cancel or delete a Draft/Pending PO.
- Reverse Charge Mechanism (RCM) sections are fetched from GST settings — only relevant if your purchase is from an unregistered supplier, GTA, or other RCM-liable category.
Step-by-Step Guide
1 Create a Purchase Order manually
- Click New PO.
- Select a Supplier (required) and optionally a Reference (the supplier's own invoice/PO number) and Expected Delivery date.
- Choose a Currency — defaults to INR; selecting a foreign currency reveals an Exchange Rate field (1 [currency] = ? INR).
- If this purchase is subject to Reverse Charge Mechanism, check RCM Applicable and pick the relevant RCM Section from the dropdown (required once RCM is checked).
- Add one or more Items: pick a Product (its cost price auto-fills the Unit Cost), set Quantity, adjust Unit Cost if needed, and pick a Tax % (0/5/12/18/28). Use + Add Item for more lines.
- Optionally add Landed Costs — Freight, Import Duty, Customs, Handling, Insurance or Other, each with an amount and note; these get proportionally spread across item unit costs once the PO is received.
- Review the live Subtotal / Tax / Landed Costs / Total at the bottom.
- Choose how to save: Save as Draft (no approval needed yet, fully editable later), Submit for Approval (moves straight to Pending Approval), or Place Order (moves straight to Ordered/sent-to-supplier, skipping approval).
2 Move a PO through the approval workflow
- Open a PO (click its row) to see the status-specific action buttons at the bottom.
- From Draft, click Submit for Approval to send it for review.
- From Pending Approval, click Approve to move it forward, or Return to Draft to send it back for edits.
- From Approved, click Send to Supplier to mark it Ordered — this is the point at which you'd actually place the order with your vendor.
- At any stage before Received, click Cancel PO (with Delete permission) to abandon the order.
3 Auto-generate Purchase Orders from low-stock suggestions
- Click Auto-Generate PO.
- Step 1 lists every product at or below its reorder point, with current stock, reorder point, unit cost and a suggested order quantity — untick any you don't want to order, and adjust quantities inline.
- Products with no default supplier show an amber — Assign — dropdown; you must assign a supplier to every selected item before continuing.
- Click Review Orders to move to Step 2, which groups the selected items into one draft PO per supplier and shows an estimated total.
- Click Create N POs to generate all of them at once as Draft purchase orders — Step 3 confirms how many were created (and reports any that failed).
4 Receive stock against a PO
- Open a PO with status Ordered or Partially Received and click Receive Items.
- Optionally tap Scan Barcode to scan each incoming carton — matching products auto-increment their Receiving Now quantity.
- If you use multiple warehouses, pick a destination under Receive Into Warehouse (or leave "No specific location").
- Choose a Cost Price Update mode: Keep existing, Use PO cost, or Weighted average (recommended — blends current stock cost with the new incoming cost).
- For each pending line item, enter the quantity Receiving Now, and optionally a Damaged quantity (damaged units are automatically filed as a return and never added to sellable stock), a Unit Cost override, Batch #, Expiry Date, and — for serial-tracked products — one serial number per line in the textarea.
- The progress bar at the top shows units received so far out of units ordered.
- Click Confirm Receipt to update inventory. The PO status becomes Partially Received or Fully Received depending on whether everything ordered has now arrived.
5 Record a return to supplier
- Open a PO with status Partial or Received and click Return Items.
- Pick a global Return Reason (Damaged, Incorrect item, Expired, Excess quantity, Quality issue, Other) — you can override the reason per line item if needed.
- For each received item, enter the Return Qty (capped at what was actually received), an optional Batch #, and an optional per-item reason override.
- Add an optional Notes field for context (e.g. "Driver confirmed damage on delivery").
- Click Submit Return — this creates a Purchase Return record and adjusts inventory back down.
6 Adjust landed costs after a PO is placed
- Open a PO that is not yet Received or Cancelled.
- In the Landed Costs panel, click + Add (or Edit if costs already exist).
- Add/edit rows with Type (Freight, Import Duty, Customs, Handling, Insurance, Other), Amount and an optional note.
- Click Save & Allocate — the amounts are proportionally spread across each item's unit cost so your true landed cost per unit is reflected when you receive stock.
7 Search, filter, sort and export the PO list
- Use the search box to match PO number or supplier name.
- Filter by Status (all 7 lifecycle states) or by a specific Supplier using the dropdowns.
- Click any of the 8 stat cards at the top (Draft, Pending Approval, Approved, Sent to Supplier, Partially Received, Fully Received, Cancelled, Total Spent) to instantly filter the list to that status — click again to clear.
- Sort by clicking column headers (PO Number, Date, Expected date, Total, Status) — click again to reverse direction.
- Switch between Table and Grid card views with the view toggle.
- Click the export icon (Export permission required) to download the current filtered list as a spreadsheet, or use the print/share bar inside an open PO to email or print a formatted PO document.
Every Field & Button, Explained
| Field / Button | What it does |
|---|---|
Supplier * | The vendor this PO is placed with; required to save any PO. |
Reference | Free-text field for the supplier's own invoice/PO number. |
Expected Delivery | Optional target delivery date, shown on the PO document and in the list. |
Currency / Exchange Rate | Defaults to INR; choosing a foreign currency (USD, EUR, GBP, AED, CNY, JPY) reveals a manual exchange-rate field used to compute an approximate INR total. |
RCM Applicable / RCM Section | Marks the purchase as Reverse Charge Mechanism liable and records which GST RCM section applies. |
Items (Product, Qty, Unit Cost, Tax %) | One row per product ordered; selecting a product auto-fills its current cost price which you can override. |
Landed Costs (Type, Amount, Note) | Freight/duty/handling/insurance/other costs that get proportionally allocated to item unit costs when stock is received. |
Save as Draft / Submit for Approval / Place Order | Three ways to save a new PO, corresponding to DRAFT, PENDING_APPROVAL, and ORDERED status respectively. |
Status badge | One of Draft, Pending Approval, Approved, Sent to Supplier, Partially Received, Fully Received, Cancelled — colour-coded throughout the page. |
Auto-Generate PO | Opens the reorder-suggestion wizard that builds draft POs from products at or below their reorder point. |
Receive Items | Opens the stock-receiving modal for POs in Ordered or Partially Received status. |
Return Items | Opens the supplier-return modal for POs in Partial or Received status. |
Cost Price Update mode | Keep existing / Use PO cost / Weighted average — controls how receiving stock affects the product's stored cost price. |
Damaged (receiving) | Units received in damaged condition; still counts toward the received quantity for progress tracking but is auto-filed as a return rather than added to sellable stock. |
Batch #, Expiry Date, Serial Numbers | Optional per-line traceability fields captured at receiving time, most relevant for perishable or serial-tracked products. |
Total Spent (stat card) | Running total of PO spending across all statuses shown in the page header stats. |
Tips & Best Practices
- Use "Weighted average" cost mode when receiving unless you have a specific reason not to — it keeps your product cost price realistic even when the same item arrives at different prices over time.
- Run Auto-Generate PO at the start of your day so nothing runs out before you notice — it only surfaces products that have already dropped to their configured reorder point.
- Add landed costs before receiving stock, not after, so the allocation reflects in your very first cost-price update from that PO.
- Use the Scan Barcode option in the Receive Items modal for large deliveries — it is much faster and less error-prone than typing quantities by hand.
Troubleshooting & FAQ
Why can't I select a supplier for one of my auto-generated reorder suggestions?
I received stock but the cost price on the product didn't change.
The RCM Section dropdown is empty.
Can I delete a Purchase Order?
My foreign-currency PO total looks different from what I expect.
🧑💻 Developer Notes
Source component(s):
frontend-app/packages/pos-react-lib/src/pages/PurchaseOrdersPage.jsx
Backend endpoints used:
GET /purchase-ordersPOST /purchase-ordersGET /purchase-orders/:idDELETE /purchase-orders/:idGET /purchase-orders/statsGET /purchase-orders/suggested-reorderPOST /purchase-orders/auto-generatePOST /purchase-orders/:id/receivePOST /purchase-orders/:id/returnsGET /purchase-orders/:id/returnsPOST /purchase-orders/:id/landed-costsPATCH /purchase-orders/:id/statusGET /rcm/sectionsGET /warehousesGET /suppliersGET /products
Related tables (db-core repositories):
PurchaseOrderPurchaseOrderItemPurchaseReturnSupplierProductWarehouse
Redux slices:
authscope (selectCurrentScope / getScopeQueryParams)
Feature flag key: purchase_orders (requires advanced plan tier or above)
PO documents (for print/email/WhatsApp share) are built client-side as a standalone HTML string via buildPOHtml() rather than server-rendered. Damaged units on receipt are unioned from two separate quantity maps (good vs damaged) because a line can be fully damaged with zero good units — see the comment above handleConfirm in ReceiveItemsModal. Landed cost allocation and weighted-average costing are computed server-side in the /receive and /landed-costs endpoints.