Skip to main content

Cost updates

Cost updates are one of the most important — and most dangerous — inventory operations. They affect: margin accuracy valuation reporting purchasing decisions accounting If you design this wrong, every report becomes unreliable. Here’s the breakdown for Purchasing / Replenishment → Cost updates. What “Cost update” means (business meaning) Cost changes when: new purchase price arrives supplier price increases/decreases freight / import costs added manual correction currency FX changes retroactive invoice adjustment Important distinction: 👉 Cost ≠ Price (sale price) 👉 Cost is internal and historical. You must treat cost as versioned history, never overwrite blindly. The 3 cost concepts you need

  1. Last cost (operational) Most recent purchase cost. Used for: quick margin display default purchasing suggestion
  2. Average cost (valuation) Weighted average cost (WAC). Used for: inventory valuation COGS in accounting most retail POS
  3. Purchase cost history (audit) Every cost change event. Used for: audits margin investigations supplier analysis You need all three. What you must track for each cost update product/variant location (optional — depends on strategy) previous cost new cost reason/source (purchase, adjustment, FX, manual) reference (PO, receiving, invoice) effective date who changed it Database additions (recommended)
  4. product_cost_history (core table) This is mandatory. Fields: id business_id product_id / variant_id location_id (nullable — if cost is global) cost_amount cost_method_snapshot (optional) source_type: purchase | adjustment | manual | fx | import reference_type + reference_id previous_cost_amount effective_at created_by created_at Never delete rows.
  5. Current cost snapshot (fast reads) You don’t want to query history every time. Add snapshot fields (choose one place): Option A (simple MVP) product_variant.last_cost product_variant.avg_cost Option B (more scalable multi-location) inventory_balance.avg_cost inventory_balance.last_cost Most modern POS do cost at variant + location level.
  6. Inventory ledger cost fields Your ledger should capture cost snapshot per movement: unit_cost_snapshot total_cost_snapshot Why: COGS must reflect cost at time of sale, not current cost. This is critical. Cost calculation logic (important) When receiving PO You usually: set last_cost = purchase cost recalculate average cost Weighted average example: new_avg = ((old_qty old_avg) + (received_qty received_cost)) / (old_qty + received_qty) This must run inside a transaction. When manual cost update happens Rules: do NOT change historical sales may affect valuation going forward record history row optionally recalc avg cost (configurable) Backend endpoints you need Automatic cost update (from receiving) Handled inside: POST /receivings/:id/commit This triggers: cost history insert snapshot update avg cost recalculation Manual cost update POST /products/:id/cost-update With validations: role permission (important) require reason optional approval Query endpoints cost history per product margin preview endpoints Frontend screens (PWA) MVP screens Cost history tab (product page) timeline of cost changes show source + reference Manual cost update form new cost reason effective date warning about valuation impact Receiving screen show cost per line show margin preview (huge UX win) Reports (Metabase) Cost unlocks serious analytics: margin by product / category / brand cost changes over time supplier price trends margin erosion alerts valuation report cost vs selling price gap This is high-value owner insight. MVP vs Phase 2 ✅ MVP cost history table last cost snapshot avg cost calculation on receiving ledger unit_cost_snapshot cost history UI 🚀 Phase 2 landed cost allocation (freight/import) multi-currency cost handling retroactive cost adjustments FIFO costing option (advanced) cost simulations before commit Biggest edge cases (critical) Receiving partial PO at different costs Currency changes between PO and invoice Returns to supplier (cost reversal) Manual cost change after stock already exists Zero stock → cost update behavior Negative stock scenarios These must be defined explicitly. Important architecture insight (for FlowPOS) This ties directly to your earlier “price everywhere” question. Same rule: 👉 Cost lives in history + snapshots 👉 Never overwrite blindly 👉 Ledger must capture cost snapshot If you do this, reporting stays correct forever. If you want, I can next give you: Exact minimal Kysely schema for cost history + snapshot ⭐ recommended How cost interacts with inventory ledger (very important) Landed cost design (apparel import scenario — very relevant) Multi-currency cost architecture (future-proof) Supplier price lists vs actual cost (big distinction)