manufacturing.variance
Standard costing variances: material price and usage, labour rate and efficiency, favourable or adverse.
1.0.0 · published 2026-10-03 by charlie · Anterra
Pinned by 15 tests, run in TypeScript, Python and Rust.
What it does
The four basic standard-costing variances for a period, flexed to the output actually produced:
material price = actual material cost - AQ x SP material usage = (AQ - SQ) x SP SQ = standard quantity per unit x actual output labour rate = actual labour cost - AH x SR labour efficiency = (AH - SH) x SR SH = standard hours per unit x actual output
For example
costVariances(standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up)→ material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £36,000.00, actual cost £36,610.00 the textbook case: 2 kg at £3 and 3 h at £10 a unit, 1000 made with 2200 kg for £6160 and 2900 h for £30450costVariances(standard price £1.25, standard quantity …, actual quantity …, actual cost £42.00, standard rate £11.50, standard hours …, actual hours …, actual cost £300.00, 100, half-up)→ material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £329.17, actual cost £342.00 fractional standards: 1/3 kg at £1.25 and 1/4 h at £11.50 a unit, 100 made using 33.5 kg; each flexed cost is rounded once and the variances still add upcostVariances(standard price £1.25, standard quantity …, actual quantity …, actual cost £42.00, standard rate £11.50, standard hours …, actual hours …, actual cost £300.00, 100, down)→ material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £329.16, actual cost £342.00 the same rounded down: 4187.5p and 4166.67p become 4187p and 4166p
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
export function costVariances(material: MaterialActual, labour: LabourActual, actualOutput: number, mode: RoundingMode): CostVariances
| material | MaterialActual | standard and actual material for the period |
| labour | LabourActual | standard and actual labour for the period |
| actualOutput | int | units actually produced, which the standard is flexed to |
| mode | RoundingMode | how each flexed cost becomes whole minor units |
| returns | CostVariances |
The types it declares, generated into your project
/** Material standard per unit of output, and what was actually used and paid. */
export interface MaterialActual {
/** per unit of material (per kg, per metre) */
readonly standardPrice: Money;
/** material per unit of output */
readonly standardQuantity: Rational;
/** material actually used */
readonly actualQuantity: Rational;
/** what the material used actually cost */
readonly actualCost: Money;
}
/** Labour standard per unit of output, and what was actually worked and paid. */
export interface LabourActual {
/** per hour */
readonly standardRate: Money;
/** hours per unit of output */
readonly standardHours: Rational;
/** hours actually worked */
readonly actualHours: Rational;
/** what those hours actually cost */
readonly actualCost: Money;
}
/** One variance: how big, and which way. */
export interface Variance {
/** never negative */
readonly amount: Money;
readonly effect: VarianceEffect;
}
export type VarianceEffect = "favourable" | "adverse" | "none";
/** The four variances, their total, and the costs they reconcile. */
export interface CostVariances {
/** actual cost - actual quantity x standard price */
readonly materialPrice: Variance;
/** (actual quantity - standard quantity for actual output) x standard price */
readonly materialUsage: Variance;
/** actual cost - actual hours x standard rate */
readonly labourRate: Variance;
/** (actual hours - standard hours for actual output) x standard rate */
readonly labourEfficiency: Variance;
/** actualCost - standardCost; always the sum of the four */
readonly total: Variance;
/** standard material and labour for the actual output */
readonly standardCost: Money;
/** actual material and labour cost */
readonly actualCost: Money;
}
Your code names it in one line, in the file that uses it
import { costVariances } from "#fune/manufacturing.variance@^1";
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { type Rational, multiplyRational, rational, rationalToInteger } from "./math_rational.ts"; ← from math.rational ^1.0.0 · built alongside by fune
import { type RoundingMode } from "./math_round_div.ts"; ← from math.round-div ^1.0.0 · built alongside by fune
import { type Money, assertSameCurrency, money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { type CostVariances, type LabourActual, type MaterialActual, type Variance } from "./manufacturing_variance_types.ts";
function notNegative(name: string, value: Rational): Rational {
const r = rational(value.numerator, value.denominator);
if (r.numerator < 0) {
throw new RangeError(`${name} must not be negative`);
}
return r;
}
function costOf(name: string, value: Money, currency: Money): number {
assertSameCurrency(currency, value);
if (value.minor < 0) {
throw new RangeError(`${name} must not be negative, received ${value.minor}`);
}
return value.minor;
}
function variance(signed: number, currency: string): Variance {
return {
amount: money(Math.abs(signed), currency),
effect: signed > 0 ? "adverse" : signed < 0 ? "favourable" : "none",
};
}
/**
* Material price and usage, labour rate and efficiency variances, flexed to
* actual output. The four flexed costs are each rounded once and every
* variance is a difference of them, so the variances always sum exactly.
*/
export function costVariances(material: MaterialActual, labour: LabourActual, actualOutput: number, mode: RoundingMode): CostVariances {
if (!Number.isSafeInteger(actualOutput) || actualOutput < 0) {
throw new RangeError(`actualOutput must be a whole number, not negative, received ${actualOutput}`);
}
const unit = material.standardPrice;
const currency = unit.currency;
const sp = costOf("material standardPrice", material.standardPrice, unit);
const matActual = costOf("material actualCost", material.actualCost, unit);
const sr = costOf("labour standardRate", labour.standardRate, unit);
const labActual = costOf("labour actualCost", labour.actualCost, unit);
const sq = notNegative("material standardQuantity", material.standardQuantity);
const aq = notNegative("material actualQuantity", material.actualQuantity);
const sh = notNegative("labour standardHours", labour.standardHours);
const ah = notNegative("labour actualHours", labour.actualHours);
const output = rational(actualOutput, 1);
const actualAtStandard = rationalToInteger(multiplyRational(aq, rational(sp, 1)), mode);
const standardMaterial = rationalToInteger(multiplyRational(multiplyRational(sq, output), rational(sp, 1)), mode);
const hoursAtStandard = rationalToInteger(multiplyRational(ah, rational(sr, 1)), mode);
const standardLabour = rationalToInteger(multiplyRational(multiplyRational(sh, output), rational(sr, 1)), mode);
const standardCost = standardMaterial + standardLabour;
const actualCost = matActual + labActual;
return {
materialPrice: variance(matActual - actualAtStandard, currency),
materialUsage: variance(actualAtStandard - standardMaterial, currency),
labourRate: variance(labActual - hoursAtStandard, currency),
labourEfficiency: variance(hoursAtStandard - standardLabour, currency),
total: variance(actualCost - standardCost, currency),
standardCost: money(standardCost, currency),
actualCost: money(actualCost, currency),
};
}Install
fune build
With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 3 dependencies, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:
fune add manufacturing.variance
The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./manufacturing.variance-1.0.0-typescript.fune, or fetch it from a terminal with fune pull manufacturing.variance@1.0.0:typescript.
The whole function, every language, is one file too: manufacturing.variance-1.0.0.fune, 33,167 bytes, sha256 891beb9988a990d3a686a6a4e0993bc51814dd5d685d087f0e5ebdcb2c68455c. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
// fune: before manufacturing.variance
after — your function gets the result and the arguments, and returns the final result.
// fune: after manufacturing.variance
replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.
// fune: replace math.rational in manufacturing.variance
// fune: replace math.round-div in manufacturing.variance
// fune: replace money.amount in manufacturing.variance
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show manufacturing.variance --steps.
// fune: step manufacturing.variance after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| the textbook case: 2 kg at £3 and 3 h at £10 a unit, 1000 made with 2200 kg for £6160 and 2900 h for £30450 | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up | → | material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £36,000.00, actual cost £36,610.00 |
| fractional standards: 1/3 kg at £1.25 and 1/4 h at £11.50 a unit, 100 made using 33.5 kg; each flexed cost is rounded once and the variances still add up | standard price £1.25, standard quantity …, actual quantity …, actual cost £42.00, standard rate £11.50, standard hours …, actual hours …, actual cost £300.00, 100, half-up | → | material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £329.17, actual cost £342.00 |
| the same rounded down: 4187.5p and 4166.67p become 4187p and 4166p | standard price £1.25, standard quantity …, actual quantity …, actual cost £42.00, standard rate £11.50, standard hours …, actual hours …, actual cost £300.00, 100, down | → | material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £329.16, actual cost £342.00 |
| exactly on standard: every variance is none | standard price £5.00, standard quantity …, actual quantity …, actual cost £50.00, standard rate £20.00, standard hours …, actual hours …, actual cost £100.00, 10, half-up | → | material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £150.00, actual cost £150.00 |
| nothing made: everything used is usage and efficiency variance | standard price £5.00, standard quantity …, actual quantity …, actual cost £20.00, standard rate £20.00, standard hours …, actual hours …, actual cost £40.00, 0, half-up | → | material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £0.00, actual cost £60.00 |
| dear material used sparingly and cheap labour working fast: favourable overall | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,045.00, standard rate £10.00, standard hours …, actual hours …, actual cost £27,440.00, 1,000, half-up | → | material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £36,000.00, actual cost £33,485.00 |
| hours in minutes as a fraction: 90 minutes is 3/2 hours | standard price €10.00, standard quantity …, actual quantity …, actual cost €10.00, standard rate €24.00, standard hours …, actual hours …, actual cost €36.00, 1, half-up | → | material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost €34.00, actual cost €46.00 |
| costs in two currencies are an error | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate €10.00, standard hours …, actual hours …, actual cost €30,450.00, 1,000, half-up | → | error: currency mismatch |
| negative output is an error | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, -1, half-up | → | error: actualOutput must be a whole number, not negative |
| fractional output is an error | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 2.5, half-up | → | error: actualOutput must be a whole number, not negative |
Show the other 5 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| a negative actual quantity is an error | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up | → | error: material actualQuantity must not be negative |
| negative standard hours are an error | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up | → | error: labour standardHours must not be negative |
| a negative standard price is an error | standard price -£3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half… | → | error: material standardPrice must not be negative |
| a negative actual labour cost is an error | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost -£0.01, 1,000, half-up | → | error: labour actualCost must not be negative |
| a zero denominator is an error | standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up | → | error: denominator must not be zero |
More from the author
A variance is **adverse** when actual cost is higher than standard and **favourable** when it is lower; `amount` is always the size and `effect` the direction (`none` for exactly zero). The textbook case: a standard of 2 kg at £3/kg and 3 hours at £10/h, 1,000 units made using 2,200 kg for £6,160 and 2,900 hours for £30,450, gives material price £440 F, usage £600 A, labour rate £1,450 A, efficiency £1,000 F, and £610 A in total.
**The variances always add up.** Quantities and hours are exact fractions (`Rational`: 1/3 kg, 1/4 hour), so the flexed costs (AQ x SP, SQ x SP, AH x SR, SH x SR) are exact fractions of a minor unit. Each of those four is rounded once by `mode`, and every variance is a difference of whole amounts, so price plus usage is exactly actual material cost less standard material cost, and the four together are exactly `actualCost - standardCost`. Rounding each variance on its own would leave a penny or two unexplained.
**Price variance on usage.** The material price variance is computed on the quantity used, not the quantity purchased; where stock is held at standard cost and the price variance is taken on purchase, pass purchases in a separate call with the purchase quantity as `actualQuantity`.
**Rules.** All money must be in the currency of `material.standardPrice`. Prices, rates, quantities, hours and costs may not be negative; output is a whole number, not negative. With zero output, all material and labour used is usage and efficiency variance. Overhead variances are not included.
Source: CIMA Official Terminology (2005), "variance" entries for direct material price, direct material usage, direct labour rate and direct labour efficiency variances; the same definitions appear in ACCA Performance Management (PM) study material on standard costing.
Files
| Path | Bytes |
|---|---|
| README.md | 2,242 |
| impl/python.py | 3,284 |
| impl/rust.rs | 5,042 |
| impl/typescript.ts | 3,265 |
| vectors.json | 12,134 |