Skip to main content

Correcting COGS: revalue vs rebuild vs recalculate

When a product's cost is wrong — a landed cost arrived late, a supplier invoice corrected a unit price, an early receipt was costed from the wrong source — you correct it by changing the cost on the underlying FIFO layers. SKU.io gives you three distinct ways to do that, and the difference between them is the single most common point of confusion in COGS management. This page explains what each path does, why they're different, and the machinery every one of them shares: a single writer that records each cost change as a dated, chained ledger row so your internal COGS stays reproducible and ties back to the general ledger.

Most cost changes need none of these — they apply automatically

Editing the unit cost on a purchase order line, or adding, changing, or removing a landed cost (freight, duty, or a supplier bill) on a purchase order, automatically re-prices the FIFO layers that receipt created — even after the order is received, closed, and invoiced. The change writes a dated revaluation row, refreshes the product's average cost, and re-prices the sales and movements that already drew from those layers, all through the single writer described below. You do not need to run Recalculate, Rebuild, or Revalue for an ordinary cost correction on a purchase order. (To edit a unit cost after an order has closed, reopen it first with More → Reopen — see Correct a cost on a received purchase order; a landed cost applies without reopening.) Reach for the three paths on this page when you want to deliberately re-derive a cost, re-sync after an unusual change, or fix one specific layer by hand.

New to FIFO costing?

Read FIFO layers & how COGS is realized first. This page assumes you know what a layer is, that COGS is realized when a negative movement consumes a layer, and that each layer carries an avg_cost (its per-unit cost). Field-level definitions live in the FIFO layer fields, statuses & enums reference.

Three paths, one difference that matters

All three corrections change what a layer costs. What separates them is scope — how many layers, chosen how — and where the new number comes from.

PathScopeWhere the new cost comes fromWhere you run it
RevalueOne layer (or a hand-picked set)A COGS source you choose, or a value you typeA product's SKU COGS Health page (per-layer Revalue COGS, or Bulk Revalue COGS)
RebuildEvery layer of a productEach layer's best-available source, re-applied in date orderSKU COGS Health (single product), or Rebuild All COGS across products
RecalculateA product's layers (or hand-picked layers), re-priced in placeEach layer's own source document, re-read (a receipt layer re-runs base cost + landed-cost proration from the current PO)Recalculate COGS on a product's SKU COGS Health page, or Recalculate Costs on the FIFO Layers list

Think of it as a widening lens. Revalue is a scalpel on one layer. Rebuild re-prices the whole product from scratch. Recalculate re-reads each layer's own source document and re-prices it in place — for a receipt layer, that re-runs the base cost and landed-cost proration from the current purchase order.

Revalue — correct one layer deliberately

A revaluation sets a single layer's new per-unit cost. You open the layer on the product's SKU COGS Health page, pick where the new cost should come from — a linked purchase order line, a weighted-average transfer cost, an assembly cost, or Manual to type an exact figure — and apply it. SKU.io validates that the source you chose is legal for that layer's origin (you can't, for example, price a purchase receipt from an assembly line), then writes the change.

Revalue is the right tool when you know this specific layer is wrong and you know what it should be. Bulk revalue applies the same deliberate change to several selected layers at once. See Revalue COGS (single, bulk & the SKU COGS Health page) for the steps.

Rebuild — re-price a product from its sources

A rebuild walks every layer of a product in chronological order and re-prices each one from its best-available COGS source — the same priority hierarchy SKU.io uses when a layer is first created. It's the tool for "this product's costs drifted and I want them all re-derived from first principles," not "I know the exact number for one layer."

Rebuild has three behaviors worth knowing:

  • It skips initial-count stock takes. A layer whose origin is a stock take marked initial count is a costing basis — the number you seeded the system with — so rebuild leaves it untouched rather than trying to re-derive it.
  • It rebuilds kit components first. For a kit (bundle), SKU.io rebuilds each component product's COGS before the kit itself, then recomputes the kit's unit cost as the sum of its components. This keeps a bundle's cost consistent with the parts it's made of.
  • It only writes where the cost actually changed. Layers whose best-available cost already matches are left alone — no ledger noise.

Run a rebuild for one product from its SKU COGS Health page, or rebuild many at once (with a progress tray) via Rebuild All COGS on the SKU COGS Health list. See Rebuild COGS & the SKU COGS Health dashboard.

Recalculate — re-price layers from their source documents

Recalculate is narrower than rebuild. It walks the layers you point it at and re-prices each one in place from its own source document — no chronological replay, no best-available hierarchy (those are rebuild's job). For a purchase-receipt layer that means re-reading the current PO line's unit cost and re-running the landed-cost proration — the calculation that spreads freight, duty, and other landed costs across received units. Layers whose source cost hasn't moved are left untouched (a no-op writes no ledger row).

Because a purchase-order cost edit or a landed-cost change already re-prices its receipt layers automatically (see the note at the top of this page), you rarely need to run Recalculate by hand. Use it as a manual re-sync — to confirm a product's layers reflect the latest source data, or after an unusual change you're not certain propagated. Run it for a whole product via Recalculate COGS on its SKU COGS Health page (this covers all of the product's warehouse layers, whatever created them), or on hand-picked layers via the Recalculate Costs bulk action on the FIFO Layers list.

These aren't competing tools — they're different blast radii

A late landed cost on one PO applies on its own the moment you add it — no tool needed; run recalculate only to re-sync if you're unsure it propagated. A single mispriced receipt you know the number for: revalue. A product whose costs are broadly off and you want re-derived: rebuild. When unsure, start narrow.

The single writer: applyRevaluation

Here is the important part. However you kick off a correction — revalue, rebuild, recalculate, or an automatic re-price behind a purchase-order cost edit, a landed-cost change, a landed-cost split, or a stock-take resize — every cost change funnels through one internal writer. That writer is what makes the correction auditable, and it's why an automatic re-price and a hand-run revaluation leave the same kind of dated ledger row.

For each layer whose cost actually changes, the writer does the following, atomically, under a row lock on the layer:

  1. Locks the layer so two corrections on the same layer can't race. It reads the layer's authoritative current cost and its cost version from the locked row.
  2. Skips silently if nothing changed. If the new per-unit cost rounds to the same value as the old one, no row is written — corrections that are no-ops leave no trace.
  3. Writes exactly one dated ledger row to the inventory revaluation ledger, capturing the old and new unit cost, the old and new total cost, the units on hand and consumed at the time, the COGS source, a reason, and the effective date (see the locked-period policy below).
  4. Advances the cost version so each row chains to the last: every ledger row's old_unit_cost equals the previous row's new_unit_cost. A unique index on the version rejects duplicates, and deadlocks retry up to three times.
  5. Brings the layer to the new cost — unless the caller already restructured the layer (a landed-cost split or a stock-take resize supplies its own old/new totals), in which case the writer records the row without re-mutating the layer.
  6. Propagates the change downstream. It touches the layer's source event so its accounting transaction updates, and re-prices every active negative movement that consumed the layer — so realized COGS on the sales and transfers that drew from it moves with the correction.
Why the chained ledger matters

Before this single-writer design (SKU-8068), four separate paths could mutate a layer's cost silently — no ledger row, no dated event, no GL entry. That's exactly why a valuation report could drift on re-run and stop tying to QuickBooks. Now every cost change is one dated, chained event. Don't look for a way to change a layer's cost "directly" — there isn't one, by design.

The chained, dated ledger is what powers two things a raw "current cost" number never could: valuing historical on-hand at the cost that was in effect on a past date, and posting a matching GL journal for each correction. Both are covered below.

The locked-period policy

Accounting periods close. Once you set an accounting lock date, the books on or before that date are filed and must not be rewritten. Cost corrections respect this automatically.

When a correction's natural effective date falls in a locked period, SKU.io doesn't stamp the ledger row with that date. Instead it lands the revaluation on the first open-period boundary — the day after the lock date — so the correction takes effect in the earliest period that's still open, and the closed books are left exactly as filed.

The SKU COGS Health page reflects the same policy in reverse. Each layer carries an accounting impact flag: a layer is marked locked when its oldest related accounting transaction falls on or before the lock date. That flag warns you that the layer already touched closed books — a signal to correct forward rather than expect the original period to change.

A stronger guard applies to FIFO re-dating

The locked-period policy reroutes a revaluation's date. The FIFO re-date reallocation below goes further and blocks outright — it won't replay historical consumption into a locked period at all. Re-pricing forward is safe; re-writing which layer a past sale consumed inside closed books isn't.

How corrected cost reaches accounting

Correcting a layer's cost is only half the job — the corrected number has to reach your COGS reporting and reconcile to the general ledger.

The COGS accounting report

Realized COGS flows into an accounting subledger you read at Reporting → Accounting COGS (/reporting/accounting-cogs), with row-level drilldown and CSV export. The report is served from a SQL view, and which view depends on the tenant's accounting engine:

  • Tenants on the legacy ledger read a view backed by accounting transaction lines.
  • Tenants on the v2 ledger read an equivalent view backed by journal lines.

Both share the same cached batch layer, so the report reads identically regardless of engine — you don't choose or configure this; SKU.io resolves it by the tenant's active ledger feature. Because every cost correction re-prices the negative movements that consumed the layer (step 6 above), the realized COGS this report shows always reflects your latest corrections, grouped by sales channel, country, brand, and SKU.

GL reconciliation

The dated revaluation ledger exists so inventory valuation ties to the GL. Two guarantees follow from it:

  • Point-in-time valuation is reproducible. Asking "what was this on-hand worth as of a past date?" values the stock at the cost that was in effect on that date — read from the dated ledger — not today's live cost. Re-running the same as-of date returns the same number even after later corrections.
  • Every correction posts a matching GL journal. Each revaluation contributes a dated, monthly batched journal entry against the Inventory control account, so the change in valuation over a period equals the net Inventory-account journal entries for that period. That identity is what an Inventory GL Reconciliation report checks — and what closes the gap when a customer's valuation doesn't match their accounting system.

In short: the ledger row is the bridge. No silent cost mutation means no unexplained drift between what SKU.io says inventory is worth and what the GL says.

Two corrections that also move dates

Most corrections change cost and leave a layer's date alone. Two situations affect a layer's position in the FIFO queue — its fifo_layer_date — and both are worth understanding, because date order drives which layer a sale consumes.

FIFO re-dating goes further than re-pricing

Re-dating a finalized stock take moves its layer earlier or later in the FIFO queue. Because FIFO consumption is ordered strictly by layer date, moving a layer means past outbound movements should have consumed different layers than they actually did.

The revalue-usages rebuild described above doesn't fix this — it only re-prices layers; for a pure date move with unchanged cost it does effectively nothing, because it never re-assigns which layer a sale consumed. Correcting that requires a FIFO reallocation replay: SKU.io replays historical consumption against the re-ordered layers, recomputes COGS, and propagates the result into the consuming documents and accounting. That replay runs as a tracked background job with a dry-run preview (how many usages would be reassigned, the net COGS delta) and a hard block on any locked period. See Amend, re-date, reverse & delete a stock take for where re-dating lives.

Warehouse transfers preserve the source date

When you transfer stock between warehouses, the destination layer keeps the source layer's original fifo_layer_date (and its cost) rather than being stamped with the receipt date. Without this, transferred stock would "rejuvenate" — old inventory would appear brand-new in the destination warehouse, corrupting aging reports and breaking FIFO order for future sales.

Because a single transfer can draw from several source layers of different dates, SKU.io groups the received quantity by source date and creates a separate destination layer per distinct date, so each lot's true age carries across the move. This is automatic — there's nothing to configure — and it's covered in How warehouse transfers work.

At a glance

RevalueRebuildRecalculate
ScopeOne layer or a chosen setEvery layer of a productA product's layers, or hand-picked ones, in place
New cost fromChosen source / manual valueBest-available source, in date orderEach layer's own source, re-read
Skips initial-count layersYou chooseYesNo — re-prices each layer from its source (unchanged costs no-op)
Handles kitsPer layerRebuilds components firstn/a
Writes the dated ledger rowYesYesYes
Respects the lock dateYesYesYes
Typical triggerOne known-wrong layerBroad drift on a productManual re-sync (PO/landed-cost edits apply automatically)

All three write through the same single writer, so all three produce the same chained, dated, GL-reconcilable ledger. The choice between them is only about how wide a net you cast and where the new number comes from.

Next steps

Last verified: