Skip to main content

Goods receipts

Route: /org/inventory/procurement · Module: org_inv_procurement — 4 permissions Permission to view: View Inventory Procurements


1. What a goods receipt is​

The record that stock arrived from a vendor. One receipt is a batch — many lines posted together under one reference number, typically one delivery or one invoice.

Figure 1 — Four receipt batches. The type pill distinguishes raw materials from finished goods.
Raw material receiptFinished goods receipt
You areBuying inputs to make thingsBuying ready-made products to resell
Lines key onraw_material_idvariation_id
Typical forA manufacturerA trader
The chooser calls itDefaultThe alternative

2. How a receipt moves through the module​


3. Step by step​

Step 1 · Choose what you are receiving​

Figure 2 — The type chooser.

Add Receipt Batch does not open the entry form. It asks a question first.

Raw Materials carries a Default badge and is described as the manufacturer's path.

Finished Goods is for buying products you resell without making.

The choice decides which master the lines are drawn from, and it cannot be changed once you are in the grid — go back and start again.

Step 2 · Fill the grid​

Figure 3 — The entry grid. Every material is listed; a line counts only once you type a quantity.

The grid lists every material — 160 on this tenant — and the counter reads 0 of 160 filled. A row becomes part of the receipt only when it has a quantity.

ColumnNotes
MaterialRead-only
UOMFrom the material. Not editable here
CurrentThe material's current_stock today
Last costlast_purchase_price
Qty *Required per filled line. Must be > 0
Unit cost *Required. Pre-filled from Last cost; may be 0 but not negative
Batch #Optional. A vendor lot number
ExpiryOptional date

Above the grid, Reference / Invoice # and Remarks apply to the whole batch.

Step 3 · Save​

Save Receipt posts one PURCHASE ledger row per filled line, all sharing a generated batch id and your reference number.

Saving a receipt does not change the Current column

Every line writes a ledger row. Nothing writes current_stock. Come back to this screen after saving and the Current column reads exactly what it did before.

This is the module's most confusing behaviour and it is not a bug you can configure away. See How stock is counted.


4. Validation — the exact strings​

ConditionMessage
No line has a quantityAdd a quantity to at least one item
A filled line has qty ≤ 0, or a negative/non-numeric unit costEach filled line needs a positive quantity and non-negative unit cost
Save succeedsReceipt saved — N items
Save failsFailed to save receipt batch

5. Permissions — and a gap​

ActionPermissionEnforced?
See the screenprocurement.view✅
Menu entryprocurement.menu✅
Post a receiptprocurement.create✅ at the API
Approve a receiptprocurement.approve❌ never checked anywhere
procurement.approve does nothing, and the buttons are not hidden

There is no approval step in the product. The permission exists, can be granted, and is read by no route and no component.

Worse, this screen performs no permission checks in the UI at all — Add Receipt Batch renders for anyone holding procurement.view. A user without procurement.create can fill in a whole batch and only discover the problem when Save fails. See Permissions.


6. The admin contract​

Must be trueOr else
A project is selectedThe list is empty
Raw materials (or products) existThe grid has no rows to fill
last_purchase_price is setUnit cost pre-fills as 0

7. Downstream​

EffectWhere
One PURCHASE row per lineLedger
Batch groupingThe list view groups by batch id
Cost capturedThe row's unit_cost, used for valuation
current_stockUnchanged

8. Troubleshooting​

SymptomCause
Add Receipt Batch opens a dialog, not a formCorrect — the type chooser comes first
Unit cost pre-fills as 0The material's last_purchase_price is 0
Save refused with a validation toastA filled line has qty ≤ 0 or a negative unit cost
Stock did not go upExpected. Receipts write the ledger only — see §3
A colleague can open the form but not saveThey hold procurement.view but not .create; the UI does not hide the button