← All notes

Inventory

Purchase orders need partial receipts from day one

Suppliers short-ship, back-order, and split deliveries. A PO that is either open or received cannot describe what actually arrived.

  • Invexa Technologies
  • 2 min read

The first purchase order model has a status: draft, sent, received. Receiving marks it done and adds the stock. It survives until the first delivery where the supplier sends 80 of 100 units and promises the rest on Thursday.

Receipts are events against lines

The PO is the intent. What arrives is a separate record, and there can be several:

po_lines
  po_id, sku, quantity_ordered, unit_cost_minor

po_receipts
  po_id, received_at, supplier_note, received_by

po_receipt_lines
  receipt_id, po_line_id, quantity_received, batch_id

Outstanding quantity is ordered minus the sum of received. The PO has no status column at all — its state is derived: nothing received is open, some received is partial, all received is closed, and “closed short” is a decision someone records rather than a state the system guesses.

The stock movement is written on receipt, not on order

An obvious point that gets broken constantly, usually to make the “incoming stock” number look right. Ordering stock does not create stock. If you need to show what is on order, that is a query over open PO lines, not a movement.

The same discipline as the transfer: goods exist in a location when they physically arrive there, and at no other moment.

Cost is captured per receipt

Suppliers change prices between order and delivery, and split deliveries can arrive at different costs. The batch created by each receipt carries the cost from that receipt line. Averaging at the product level loses the link between what you paid and what you sold, and margin reporting quietly stops meaning anything.

Over-receipt is a real case

Suppliers send more than ordered, sometimes deliberately. Blocking it forces warehouse staff to either refuse stock that is physically present or create a fake PO — both worse than accepting it. Allow it, flag it on the receipt, and let purchasing resolve the difference. The system should describe reality, not argue with it.

What this costs

Two extra tables instead of a status column. The alternative is discovering mid-build that “received” cannot express what happened, and retrofitting receipts into code that assumed one delivery per order.

Next step

Have a project in mind?

A 30-minute call is usually enough to know whether we are the right team for it. If we are not, we will say so.

Start a project

Replies within one working day