Reading a movement
How to take one ledger row and work out what actually happened.
1. What every row carries
| Field | Always present | Notes |
|---|---|---|
transaction_type | ✅ | One of the six |
quantity | ✅ | Signed |
| Item key | ✅ | Either variation_id or raw_material_id, never both |
created_at | ✅ | |
created_by | ✅ stored | Never displayed — Audit Info reads N/A |
reference_no | receipts, sales, maintenance | Production writes none |
batch_id | receipts, sales, maintenance | Production writes none |
reference_id | production only | The production order's id |
unit_cost | receipts, sales | Cost on a receipt, selling price on a sale |
batch_number, expiry_date | receipts only | Vendor lot data |
remarks | usually | Encoded, and shown undecoded here |
A database check constraint enforces it: exactly one of variation_id and raw_material_id is set.
There is no row that moves both.
2. Identifying the item
| Row keys on | Item Details shows |
|---|---|
| A variation | Product name, SKU, variation — correctly |
| A raw material | Nothing |
The API returns raw_material_name on every row. The ledger page renders only product_name, so
the name is dropped for materials.
To identify a raw-material movement you have to work sideways:
- Read the unit in the Movement column —
KG,LT,PCSnarrows it - Read the Remarks — a receipt's remarks name the vendor and the delivery
- Match the reference number back to the receipt or maintenance entry, which do show names
- Or query it through the external API, which returns the name
3. Tracing each type back to its source
| Type | Trace by |
|---|---|
PURCHASE | reference_no → the goods receipt batch |
SALE | reference_no → the invoice |
ADJUSTMENT / WASTAGE | reference_no → the maintenance batch |
PRODUCTION_IN | The remark Production Order #xxxxxxxx completion |
CONSUMPTION_OUT | The remark Consumption for Production Order #xxxxxxxx |
Recognising a production run
A completed run always looks the same:
PRODUCTION_IN +18 <product> "Production Order #36385abd completion"
CONSUMPTION_OUT −7.6 (blank) "Consumption for Production Order #36385abd"
CONSUMPTION_OUT −0.1 (blank) "Consumption for Production Order #36385abd"
…one per ingredient
One positive row naming the product, followed by N anonymous negative rows sharing the same 8-character id. That id is the first 8 characters of the production order's UUID, which is also what the order list displays.
4. Decoding the remarks yourself
The ledger prints the tags raw. They mean:
| Tag | From | Meaning |
|---|---|---|
[CUST:…] | Sales | Customer name |
[PH:…] | Sales | Customer phone |
[PAY:…] | Sales | cash · card · upi · bank · credit |
[DISC:…] | Sales | Discount percent applied to the cart |
[TAX:…] | Sales | Tax percent, typed by the operator |
[REASON:…] | Maintenance | Free-text reason |
[REASON_ID:…] | Maintenance | A master reason id — never written, because the master list cannot be populated |
[BATCH:…] / [REF:…] | Legacy | An older receipt encoding, superseded by the real batch_id and reference_no columns |
[REASON:Expired stock] Past shelf life at morning check — the reason is Expired stock, the note is
the rest.
5. Working out a balance
There is no balance column. The balance for a variation is SUM(quantity) over its rows — which is
what the webhook payload's new_level uses.
The catalogue Stock column and the
raw material Stock Status read current_stock, which no ledger
operation writes.
The ledger sum and the displayed stock are independent and routinely disagree. Read How stock is counted.
The sum can be negative — nothing blocks a production run or a sale that exceeds what has been received.
6. Worked example
A row reading:
Jul 29, 2026 20:00 | (blank) | WASTAGE | ↙ 3.5 KG | N/A Ref: Manual
[REASON:Spoilage — cold chain break] Chiller failure overnight; batch discarded
Tells you:
- A raw material was written off — blank Item Details means material, not product
- 3.5 KG — so a material measured in kilograms
- It was wastage, not a counting correction: the stock existed and was destroyed
- The reason was a cold-chain failure, with the operator's own note
- Who did it is unrecoverable from this screen — Audit Info is always
N/A
To find out which material, open the maintenance batch with the same reference number, where the names are shown.
7. Troubleshooting
| Symptom | Cause |
|---|---|
| Cannot tell what item a row refers to | Raw-material rows do not render their name |
| No reference number on a row | It is a production movement; trace via the remark |
| Two rows look identical | They are separate lines of the same batch on different items |
| Who posted this? | Not answerable from the UI — created_by is stored but never resolved |
| The sum does not match the stock column | Two independent systems |