Stock movements
stock_movementsRead the STOCK MOVEMENT LEDGER in bulk — every recorded change to on-hand quantity, across products, in one call. stock_level says how many you have RIGHT NOW for one product; this says WHAT MOVED, WHEN, WHY and WHAT IT LEFT BEHIND. Use it for stock-turn analysis, shrinkage hunting, reorder timing, or reconstructing how a balance got where it is. Every filter is optional and a bare call is a legitimate "what moved lately" pull: narrow with product (case-insensitive contains against the product name OR SKU), from/to (a window on each row's docDate) and sourceType (exactly one cause — see the list). Each row: ts (when it was posted), docDate (the document's own date, YYYY-MM-DD — a voided or edited bill's reversal keeps the bill's date, while a voided credit or debit note's reversal is dated the day of the void, like its journal entry; where no document survives, the Malaysian day it was posted), product {sku, name}, qtyDelta (signed — negative took stock out), balanceAfter (on-hand immediately after that move, as the posting path recorded it), unitCost, reason (free text the person typed, where there was one), sourceType and sourceId (the id of the document that moved it — pair it with search_documents to see which one). ⚠ WHICH MOVEMENTS EXIST AT ALL DEPENDS ON THIS COMPANY'S STOCK MODE, and the answer says which mode it is in (stockMode) with a note. In modified_periodic — the mode of companies created before 28 Sep 2026 unless they switched; newer companies start perpetual — selling does NOT move stock: invoices, delivery orders and credit notes write no movement row, and stock is trued up at stock take. Seeing no 'sale' rows there means the company is periodic; it does NOT mean nothing was sold, and it is NOT shrinkage. Only a perpetual company ORIGINATES sale / credit_note rows — but a periodic company can still HOLD them, and can still gain new ones: rows written while it ran perpetual stay, and a VOID takes its truth from the document's own movement rows rather than from today's setting, so voiding a perpetually-booked invoice or supplier return writes a fresh sale_void / debit_note_void row in a company that is periodic now. So a periodic company with movement rows is not a contradiction and not a bug. Read the mode before you interpret the rows, and read a row's own sourceType before you attribute it to the mode. balanceAfter is the total across the whole company, not per location; on a multi-location company each row also carries location. Rows come back newest first, capped at 200 with total, shown and more — when more is true, narrow by date and pull the periods in turn rather than treating a partial page as the whole. Values are verbatim as recorded at the time, never re-derived. ⛔ WHAT IT WILL NOT DO: it does not value your inventory (balance_sheet does), does not compute COGS or margin (income_statement, profit_drivers), and does not tell you what to reorder (low_stock). Nothing is written, and no draft is created.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Latest docDate (YYYY-MM-DD; the document date, else the Malaysian posting day). | |
| sku | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. | |
| date | No | NOT a filter on this tool. Pass a date WINDOW as `from` and/or `to` (YYYY-MM-DD). A single date is `from` and `to` set to the same day. | |
| from | No | Earliest docDate (YYYY-MM-DD; the document date, else the Malaysian posting day). The main way to break a >200-row pull into honest slices. | |
| item | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. | |
| name | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. | |
| type | No | NOT a filter on this tool. Pass the movement cause as `sourceType`. | |
| limit | No | NOT a filter on this tool. The page size is fixed at 200 movements. Narrow with from/to, product or sourceType and pull the periods in turn. | |
| query | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. | |
| dateTo | No | NOT a filter on this tool. Pass the end of the window as `to`. | |
| reason | No | NOT a filter on this tool. `reason` is free text the person typed and is RETURNED on each row, not a filter. To narrow by cause use `sourceType` (e.g. 'adjustment'). | |
| source | No | NOT a filter on this tool. Pass the movement cause as `sourceType`. | |
| product | No | Case-insensitive contains matched against the product NAME or SKU. Omit for every product. | |
| dateFrom | No | NOT a filter on this tool. Pass the start of the window as `from`. | |
| productId | No | NOT a filter on this tool. This search matches product TEXT, not ids — pass the name or SKU as `product`. | |
| sourceType | No | Exactly one movement cause. One of: purchase, purchase_void, debit_note, debit_note_void, adjustment, stock_take, import_opening, assembly_out, assembly_in, disassembly_out, disassembly_in, sale, sale_void, credit_note, transfer_out, transfer_in. Note that sale, sale_void, credit_note are only ever CREATED by a perpetual company — a company that has since switched to periodic still holds the ones it wrote (and a void of one of those documents still writes its reversal leg). | |
| productName | No | NOT a filter on this tool. Pass it as `product` — one case-insensitive contains matched against BOTH the product name and the SKU. |