Stock Opname in ERP: Cycle Counts Without Freezing Ops

Photo by rob.wall on flickr
Stock opname is a full physical count of every SKU in a warehouse, usually done once a year and requiring operations to freeze for one to three days. Cycle counting rotates a smaller subset of SKUs through counts on a regular schedule, based on ABC classification, so the warehouse keeps operating normally. Most custom ERP builds use both: continuous ABC cycle counts for accuracy, plus one annual stock opname for statutory reporting.
A full count is only accurate if nothing moves while it's being taken. If a unit is counted on one shelf and then relocated before the tally is finalized, it can be counted twice or missed entirely. That's why receiving, picking, and putaway have to pause for the duration of a full physical count, unlike a cycle count, which only freezes the specific SKUs currently under count.
ABC classification ranks items by consumption value using the Pareto principle: A items are the small share of SKUs that account for most of the value and get counted most often, typically monthly. B items get a moderate frequency, often quarterly, and C items -- the bulk of the catalog by SKU count but the smallest share of value -- are counted only once or twice a year. This concentrates counting effort where errors matter most financially.
No. Cycle counting improves day-to-day accuracy and catches shrinkage earlier, but most Indonesian accounting practice under PSAK 202 still expects a documented full physical count at or near year-end to support the financial statements. Treat cycle counting as a continuous accuracy program that makes the annual stock opname faster and less disruptive, not a replacement for it.
Never the same person who performed the physical count. A proper workflow routes variances above tolerance to a supervisor for a blind recount, then to an inventory or finance manager for final approval before any adjustment is posted to the ledger. Skipping this segregation of duties is one of the most common findings auditors raise when reviewing Indonesian stock opname records.

Photo by rob.wall on flickr
Key Takeaway
This guide shows how to replace an annual stock opname freeze with ABC-scheduled cycle counting inside a custom ERP: classify inventory into A/B/C tiers, schedule counts against a frozen snapshot, route variances through a segregation-of-duties approval workflow, and post adjustments under PSAK 202 valuation rules so year-end counts become a formality.
Ask any warehouse manager what they dread most on the ERP calendar and the answer is usually the same: stock opname. A full physical count forces receiving, picking, and shipping to stop so nobody moves a box while someone else is counting it. For a business running on thin margins, one to three days of frozen operations every year is a real cost, not just an inconvenience.
This post walks through how to design stock opname inside a custom ERP so it stops being a once-a-year fire drill: the difference between snapshot counting, live counting, and ABC-scheduled cycle counts, how to route variances through an approval workflow that survives an audit, and how to post the resulting adjustments to the ledger correctly under Indonesian accounting rules.
A full stock opname only produces a trustworthy number if nothing moves during the count. That single constraint is what forces the freeze: every open pick, putaway, and goods receipt has to pause, because a unit counted on one shelf and then moved to another before the tally is summed gets counted twice, or not at all. Most teams schedule the full count once a year, right before financial statements close, and treat the frozen window as an unavoidable cost of compliance.
On a warehouse with a few thousand active SKUs, a full stock opname commonly takes one to three days of stopped picking and receiving. Businesses tolerate that cost for statutory compliance, but a count that only happens once a year is a single sample of a number that changes daily -- it tells you almost nothing about where the shrinkage is actually happening.
There are three ways to structure a count inside an ERP, and most custom builds end up using different ones for different purposes rather than picking a single method for everything.
| Method | How It Works | Best For | Trade-off |
|---|---|---|---|
| Full Physical Count (Snapshot) | Freeze all movement, count every SKU at once, reconcile the whole ledger in a single pass | The year-end statutory stock opname required for PSAK 202 reporting | Ops halted for one to three days depending on SKU count and warehouse size |
| Live / Perpetual Counting | Count continuously against a live system balance while receiving and picking keep running | High-velocity warehouses that cannot tolerate any freeze at all | Movements during the count window can make the difference meaningless unless timestamps are locked |
| ABC Cycle Counting | Rotate a scheduled subset of SKUs through counts by ABC class, using a frozen snapshot per task | Most custom ERP deployments balancing accuracy against uptime | Needs a scheduler and a disciplined variance workflow to stay trustworthy over time |
Do not try to replace the annual statutory stock opname with cycle counting alone. Run ABC cycle counts continuously for accuracy and shrinkage detection, then run one full snapshot count near year-end so the number in the financial statements has a single, defensible cutoff date.
ABC classification splits inventory into three tiers by consumption value: A items are the small share of SKUs that carry most of the value and get counted monthly, B items get counted quarterly, and C items -- the bulk of the catalog by SKU count but the smallest share of value -- get counted once or twice a year. Because only a rotating subset is ever open for counting at once, the rest of the warehouse keeps receiving, picking, and shipping normally. The scheduler below checks each item's last count date against its class frequency and opens a new cycle count task the moment it falls due, so nobody has to remember which SKUs are overdue.
// cycle_count_task
{
id: uuid,
item_id: uuid,
warehouse_id: uuid,
abc_class: 'A' | 'B' | 'C',
frequency_days: number, // A=30, B=90, C=180
scheduled_date: date,
status: 'pending' | 'counted' | 'variance_review' | 'posted',
counted_by: uuid,
counted_at: timestamp,
system_qty_snapshot: decimal, // frozen at task creation, not at count time
counted_qty: decimal,
variance_qty: decimal, // counted_qty - system_qty_snapshot
variance_value: decimal, // variance_qty * valuation_rate
}
// Scheduler picks the next batch of A-class items every 30 days,
// B every 90, C every 180 -- overlapping windows are fine because
// each item only ever has one open task at a time.
function scheduleNextCycleCount(item: InventoryItem, today: Date) {
const frequencyByClass = { A: 30, B: 90, C: 180 }
const daysSinceLastCount = diffInDays(today, item.lastCountedAt)
if (daysSinceLastCount >= frequencyByClass[item.abcClass]) {
return createCycleCountTask(item, today)
}
return null
}A cycle count is only as trustworthy as the workflow that resolves disagreements between the physical count and the system balance. The task needs to compare against a frozen snapshot, route anything outside tolerance to a second pair of eyes, and leave a clean audit trail behind every adjustment.
Never let the person who physically counted an item also approve its own variance adjustment. That missing segregation of duties is the single most common finding auditors raise when they review stock opname records in Indonesian companies, and it is trivial to design out from the start.
Once a variance is approved, the adjustment needs to land in the ledger the same way any other stock movement does -- through a proper voucher, at the correct valuation, with a timestamp that keeps the scheduler and the audit trail in agreement.
Batch adjustment postings by warehouse and by day, and have a supervisor review the batch before it hits the ledger rather than posting each variance the instant it's approved. It catches pattern-level problems, like one aisle producing variances every cycle, that a single adjustment view never surfaces.
Indonesian accounting rules under PSAK 202, the renumbered PSAK 14, require inventory to be carried at the lower of cost and net realizable value, using FIFO or weighted average -- LIFO is not permitted. A well-designed cycle count program feeds that requirement continuously instead of leaving it to a single scramble at year-end.
Teams that move from a single annual freeze to ABC-scheduled cycle counts consistently report that their year-end stock opname closes in hours instead of days, because the ledger was never far from reality in the first place.