Skip to main content

Cost calculations

STP calculates the cost of every movement, not only its quantity. That is what allows an accounting or reporting system to work out gross profit per sale: the sale carries both what the customer paid and what the goods cost to acquire.

Two independent decisions

Costing a movement involves two settings that are easy to conflate, and in STP they are independent:

DecisionSettingEffect
Which batch physically leavesStp.StockHandlingMethoddrives expiry handling and batch traceability
What cost is attributed to the movementStp.UseAverageWeightedCostPricedrives gross profit

Most systems bundle these into a single "costing method" choice. Here you set them separately — so FEFO selection with weighted average costing is a normal and sensible combination: ship the soonest-expiring goods, book them at the average.

Stock is held as batches, one per arrival, and each batch keeps its own cost taken from the delivery that created it. That per-batch cost is what the second decision either uses or averages over.

Which batch is drawn down

Configured per item, with the Stp.StockHandlingMethod additional property:

MethodWhich batch is drawn down
FIFO — First In, First Outthe oldest batch with a remaining quantity. This is the default.
LIFO — Last In, First Outthe newest batch with a remaining quantity
FEFO — First Expires, First Outthe batch that expires first, among those with a remaining quantity

FEFO requires the goods to carry a best-before or expiration date; it is the right choice for perishables, where the oldest arrival is not necessarily the one that should leave first.

Note: if the selected batch cannot cover the movement, STP draws from other batches rather than rejecting the movement — keeping the total quantity correct takes priority over batch attribution. See The stock model.

What cost is attributed — weighted average by default

Rather than costing an outgoing movement at the cost of the specific batch it drew from, the item is costed at a weighted average across its stock, recalculated whenever a delivery arrives and weighted by the remaining quantity of each batch. This is the default.

It is the prevailing expectation in grocery and general retail: purchase prices move constantly with supplier deals, goods are fungible, and per-batch margins are noise nobody reconciles line by line. It also means two units of the same item leave the store at the same cost, whichever delivery they physically came from.

Batch selection is unaffected — the batch chosen above is still the one whose quantity is reduced.

Turning it off, per item

Set the Stp.UseAverageWeightedCostPrice additional property to false on the item. Movements for that item are then costed from the batch they draw down, as described below.

PropertyMember ofValid valuesDescription
Stp.UseAverageWeightedCostPriceAdditionalPropertytrue, falseWhether the item is costed at a weighted average across its stock (default: true) rather than at the cost of the batch a movement draws from.

Every published transaction carries useAverageWeightedCostPrice, so a consumer can always tell which model produced the costAmount it is looking at.

Costing from the batch, when weighted average is off

Normally, from the batch itself. When a movement draws down a batch that has stock, the batch's own cost is reduced in proportion to the quantity taken — so a sale of 3 from a batch of 10 carries three tenths of that batch's cost. Two units of the same item can then leave the store at different costs, because they arrived in different deliveries at different prices.

When there is no batch cost to draw from, STP falls back. That happens when a batch is being created — an arriving delivery — or when the movement draws against a batch that is already depleted or negative. In those cases the cost is resolved in this order:

  1. The cost supplied with the movement, if there is one. A goods-received line states what the delivery cost — that is authoritative, and it becomes the new batch's cost.
  2. The item's purchase price, multiplied by the quantity.

🛑 An item with no cost source on a fallback path is costed at 0. If a movement carries no cost of its own and the item has no purchase price in PnP, the movement is recorded with a cost of zero — silently, because there is nothing to fail on. The stock quantity is still correct; the cost is not, and any gross-profit figure derived from it will be wrong.

The case that bites in practice is a sale against stock the system thinks is exhausted: there is no batch cost to draw from, and no cost on a POS sale line, so the purchase price is the only thing left.

Maintaining a purchase price in PnP for every stock-handled item is therefore a prerequisite, not an optimisation. See Data sources.

Adjusting cost without moving quantity

A depreciation writes down the cost of stock still on hand without changing the quantity — typically to take a loss up front on goods that will have to be discounted. Subsequent sales are then costed at the reduced figure.


Return