Stock Counts
A stock count — also known as a cycle count — is a targeted physical audit of a location or bin that snapshots expected quantities, collects your physical counts, and applies the differences as inventory adjustments.
A stock count is how you reconcile what Knowledge ERP thinks is on the shelf against what's actually there. Rather than shutting down an entire warehouse for a full physical inventory, you run focused counts — one location at a time, one bin at a time — so stock accuracy stays high without interrupting operations.
Counting little and often, rather than shutting the warehouse for one annual count, is a practice in its own right. This page is about running a count in Knowledge ERP; for the practice itself — how to choose what to count and how often — see cycle counting.
If you know this as a cycle count, that's the same thing. Accounting literature calls a rolling programme of small audits cycle counting; the app says Stock Count because what you create here is one count of one place, with no schedule attached. The class names, database tables, permission slug and API paths all still read
cycle_count/cycle-counts— nothing about an existing integration or role changes.
When you create a count, the system automatically generates a count line for every unit in scope, snapshotting its current recorded quantity as the expected quantity. You then walk the shelves, enter what you actually see, and finish the count — at which point any discrepancies are written back as Adjustment movements on the affected units.
There is no "unpost". Once a count is finished the count itself is locked, and the adjustment movements it wrote are permanent — they form part of each unit's movement history. Deleting the count afterwards removes the paperwork and leaves the stock changes in place. Stock is still correctable: run another count, or adjust the unit directly. Either way the correction is recorded as a new change on top, not as an undo of the count.
Creating a stock count #
New counts are created from Inventory → Stock Counts → New Stock Count.
- Location — the warehouse, room, or vehicle to count. Leave blank to include all locations (single-location accounts hide this field entirely). The form defaults to your active location.
- Bin — optionally narrow the scope to a single bin within the location. The dropdown shows every bin's full hierarchical path (e.g. Warehouse A → Rack 5 → Shelf 3). See Locations & Bins.
- Notes — free-text memo for the count (who requested it, why, etc.).
Saving the form creates the count in Draft status and immediately generates count lines for every unit in the chosen scope.
Statuses #
A stock count moves through four statuses:
- Draft — just created; lines have been generated but counting hasn't begun.
- In Progress — counting is underway. A count transitions here automatically the moment you open the Scan Mode page.
- Posted — the count has been finished and its adjustments applied to stock. Final state; the count is read-only. (This is what Finish Count & Update Stock produces — "posted" is the accounting term the status and the API keep.)
- Cancelled — abandoned without finishing. No adjustments were written.
Entering counts #
There are two ways to record what you physically counted for each line.
Table view #
On the count's Edit page, the Units to Count table lists every line with a Counted column reading Counted or To count, its Product name, barcode, bin, expected quantity, counted quantity, and variance. Click Enter Count on a line and answer the question the product admits of, plus an optional note:
- Something you count (units of
ea,item,pcs— the default) asks "Is it on the shelf?" — Yes or No. There is no half an angle grinder, so there is no number to get wrong. - Something you measure (
gal,lb,ft, …) asks "How much is left?" in the product's own units, and tells you what a full one holds. Two and a half gallons out of a five-gallon drum is entered as2.5.
The variance badge updates immediately, reading in the same terms — Matches
or Missing for a counted thing, −2.5 gal for a measured one. Green for a
surplus, red for a shortage, gray for no difference.
Leave a line blank to skip it. A skipped line is neither counted nor adjusted.
The Mark All as Expected header action bulk-sets every uncounted line to its expected quantity (i.e. variance of zero), useful when you've confirmed a large section matches and want to close out the remaining lines quickly.
Scan Mode #
For faster floor counting, open Scan Mode from the Edit page header. The dedicated scan interface works like this:
- Scan a bin barcode to set an active bin filter — subsequent unit scans are matched only against units in that bin.
- Scan a unit barcode to pull up the matching count line.
- Answer the same question the table view asks. For something you count, scanning it is the evidence it is there, so one key — Yes — it is here — records it; No — it is missing is next to it. For something you measure, type how much is left in the product's own units and save. The last five scanned units appear as a recent-activity list so you can spot errors quickly.
- Clear the bin filter at any time to scan across all bins again.
Opening Scan Mode transitions a Draft count to In Progress automatically.
Finishing the count and updating stock #
When all lines are entered — or when you're satisfied with what's been counted — click Finish Count & Update Stock on the Edit page. The confirmation dialog spells out what the action does and what can still be changed afterwards.
Finishing the count does the following for every line that has a
counted_quantity:
- Compares
counted_quantitytoexpected_quantity. - If they differ, sets the unit's
quantity_remainingto the counted value and writes an Adjustment movement attributed to the stock count. - A line answered No — it is missing additionally marks the unit Missing and takes it out of its bin.
- A line answered Yes — it is here for a unit that was already Missing puts it back In Stock, in the bin the count was walking.
- Lines without a
counted_quantity(still blank) are skipped — they neither create an adjustment nor flag an error.
The count's status becomes Posted and posted_at is stamped with the
current time.
Lines where
counted_quantityequalsexpected_quantitydo not produce an adjustment movement — only genuine discrepancies touch unit records.
The count is read-only from this point. The REST endpoint behind the button is
still POST /api/v1/inventory/cycle-counts/{id}/post, and it refuses a count
that has already been finished.
Empty is not the same as missing #
Only the Present/Missing question can report a unit gone, and only for a unit that was supposed to be on that shelf in the first place:
- A measured product counted at zero is an empty five-gallon drum. It is still sitting exactly where it should be, so it stays In Stock in its bin at 0% — it is not missing.
- A countable unit that was already part-used is asked for a number rather than Present/Missing, so counting it down to zero means the roll of tape ran out. Also not missing.
- A unit that is checked out or in transit is legitimately not on the shelf. Answering "no" for one describes where it already is, so its status is left alone.
Marking a unit missing also releases anything it was promised to — a sales-order reservation or an appointment hold — so the order finds out it is a unit short rather than keeping a promise against something nobody can find.
Cancelling a count #
If you started a count by mistake or it's no longer needed, Cancel Count from the Edit page header sets the status to Cancelled without writing any adjustments. Cancelled counts are retained for audit purposes.
Count lines #
Each line in the Units to Count table corresponds to one physical unit. A line records:
- Product and unit barcode — for identification.
- Bin — where the unit was located when the count was created.
- Expected quantity — the system's recorded
quantity_remainingat the moment lines were generated, stored as an integer (where10000= 100%). - Counted quantity — what you physically observed, same scale. Null until entered.
- Variance —
counted − expected.
The
0–10000scale is how these are stored, and it is what the API reads and writes. It is not what the screens ask you for: a person is asked Present/Missing or an amount in the product's own units, and the app converts. Entering a count of1on a screen never means one basis point.
- Notes — any remarks about this specific unit's count.
Relationship to units and movements #
Stock counts work entirely through inventory units.
When a count is finished, each adjusted unit gains a new Adjustment movement in
its Movements tab, with the note "Stock count" (plus your line note if
provided). This keeps the unit's movement history complete — anyone looking at
a unit can see exactly when an adjustment was made and trace it back to the
count that caused it. Movements written before the rename still read
"Cycle count"; stored history is not rewritten.
Stock counts respect locations and bins: a count scoped to a bin only touches units in that bin; a count scoped to a location touches all units in that location regardless of bin; and a count with no location scopes every unit in the account.
Doing it from the API #
API note:
location_idis required when creating a stock count via the API. The UI allows leaving it blank to count all locations, but the API does not support that mode.
# Create a stock count (lines are generated automatically)
curl -X POST "https://your-domain.com/api/v1/inventory/cycle-counts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"location_id": "<location-id>", "bin_id": "<bin-id>", "notes": "Q2 shelf audit"}'
# Submit counted quantities for specific lines
curl -X PUT "https://your-domain.com/api/v1/inventory/cycle-counts/{cycleCount}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"counts": [
{"line_id": "<line-id>", "counted_quantity": 10000},
{"line_id": "<line-id>", "counted_quantity": 7500}
]
}'
# Post all adjustments — irreversible
curl -X POST "https://your-domain.com/api/v1/inventory/cycle-counts/{cycleCount}/post" \
-H "Authorization: Bearer $TOKEN"
Quantities are integers on a
0–10000scale over the API (10000= 100%,5000= 50%), matching the samequantity_remainingscale used on inventory units. Filter the list endpoint with?status=draftor?location_id=<id>to narrow results.