ZuloOne
English
Sign in
Menu

🧮 Cost of stock and of sales

Updated

The costing method is Costing method. The benchmark (standard cost) is a price type — a dated price in price history; the sales margin is a service in Sales because Costing cannot depend on revenue — Sales already depends on Costing.

← Business process wiki

What Costing computes#

  • A receipt (ReceiptFifoTx / ReceiptCostTx) creates ItemCostFifo lots and raises InventoryValue. Non-recoverable input VAT is part of that amount.
  • An issue is written off by the CostingIssue driver against the net negative on Stock: a sale, a write-off, a release to production. A transfer between cells nets to zero and does not touch cost.
  • A surplus with no purchase price is valued by ISurplusCostingService at the current average.

Costing has no user documents.

How an issue is valued, and how to read Inventory value and Gross margin, is Costing method.

The benchmark#

The standard cost is the planned cost of one unit of an item. It is a price type, not a dictionary of its own: a price in price history (LT_PriceTypeHistory) under the type chosen in CostingSettings.StandardPriceType, per the item's base unit. So it has everything a price has — [from, to) windows that close when the price changes, a column in the item list, inline editing in the «Prices» section of the item card, and calculated types on top of it («standard + 10 %»). See pricing.

Setting Where Values Default
StandardPriceType Costing → Settings → Costing settings, field «Standard cost price type» any price type empty: the oldest active Base type with direction Internal

The Inventory/price-types package seeds that type: «Standard cost» (Нормативная себестоимость in Russian), direction Internal, kind Base. Direction Internal is neither a sale nor a purchase, so InventorySettings never picks it as the default sale or purchase type, and no customer or supplier is priced by it. The nightly MoveStandardCosts job creates the type when a tenant has none.

IStandardCostService:

  • PriceTypeAsync() — the type in use, or null.
  • OfAsync(item, date) — the unit standard on the date, 0 when no window covers it.
  • VarianceAsync(item, qty, actual, date) — qty × standard − actual, rounded to 2 decimals. Positive means cheaper than planned.
  • EnsurePriceTypeAsync() — the type in use, created in the tenant's default language when there is none.

Example. The item «Flour 1 kg» has the standard 8.00 from 2026-01-01, changed to 9.00 on 2026-04-01. The history then has two rows: 8.00 for [2026-01-01, 2026-04-01) and 9.00 from 2026-04-01. On 2026-03-31 OfAsync returns 8.00, on 2026-04-01 it returns 9.00. Ten bags bought for 85.00 on 2026-03-15 give the variance 10 × 8 − 85 = −5.00 (dearer than planned).

Surplus without lots. A surplus found by a stock adjustment or a stock count is valued at the item's current average over its open lots. When the item has no lots at all — it was never bought — the surplus is valued at the standard on the movement date (SurplusCostingService). Five units found, standard 6.00: the new lot is 5 units for 30.00, and the inventory value grows by 30.00. No lots and no standard: the lot is created at zero.

This does not change FIFO or AVG. The standard is the plan; the actual is still the lots.

Plan against actual: the StandardCostVariance report. Costing → Reports → Standard cost variance. One row per document and item that wrote off stock value — a sale, scrap, an issue to production. The columns are quantity, Actual cost (the InventoryValue write-off), Standard cost (quantity × the standard on the write-off date, in the item's base unit) and Variance = standard − actual. Positive means cheaper than planned. The flour example above: the ten bags written off on 2026-03-15 at an actual 85.00 show standard 80.00 and variance −5.00. A write-off with no standard window on its date shows standard 0 and variance equal to minus the actual. The report takes the type from CostingSettings.StandardPriceType even when that type is disabled; IStandardCostService falls back to the Internal type in that case.

The nightly MoveStandardCosts job copies leftover dictionary-style standard-cost rows into price history under the standard type: a row's last day becomes the day after, because price windows are half-open. The job skips a window that already has a price row of that type, item, unit and start, so a second run moves nothing.

The sales margin#

IGrossMarginService (Sales): for a [from, to) window and an item —

  • RevenueOfAsync — the sum of Revenue.Amount;
  • CogsOfAsync — the magnitude of the negative InventoryValue.Value on the documents that also have revenue in that window;
  • MarginOfAsync — the difference.

Breakage and releases to production with no revenue line are not part of that number: they have their own account in the ledger, not cost of sales.

The same figures appear in the Sales margin report (GrossMarginReport, Sales → Reports). A row is document and item. The numbers, Revenue 1000 and Cost 400 giving Margin 600, are on Costing method. The item in the register is an analytic, not a column, so the query joins movements to MetaAnalyticSetValues for the Item analytic. The report lives in Sales: Costing cannot depend on revenue.

The labour share#

Labour reaches the cost not through Costing — Costing does not depend on HR. The production order absorbs it: marked route operations, priced at the operator's position rate (Position.HourlyRate) or the work center rate (WorkCenter.HourlyRate), are added to the product's ItemCostFifo lot and to InventoryValue — see production routing.

The difference from materials is fundamental: Costing moves materials (InventoryValue stays value-neutral) whereas labour adds — payroll has already been accrued and only part of it travels into stock. In the books that is Dr inventory / Cr labour absorbed.

Outside production there is no labour share in the cost: for a purchased item there is nowhere to take it from.

Why the margin is not in Costing#

Sales → Costing already exists. The reverse dependency would be a cycle. The benchmark catalogue stays in Costing; the question "how much did we make on this sale" is asked by Sales.

What's not there yet#

  • Costing has no user document of its own; valuation follows receipts, issues and production movements.
  • Outside production there is no labour share in stock cost.
  • The sales margin report excludes breakage, write-offs and production releases that have no matching revenue line.
  • Actual valuation is FIFO or average; the standard cost is only a benchmark and does not replace the inventory layers.

Where next#

Was this page helpful?