Functional Weave
Code in Rust

agri.gross-margin@1.0.0

impl/typescript.ts

2,467 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { type Money, money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { sumMoney } from "./money_sum.ts";  ← from money.sum ^1.0.0 · built alongside by fune
import { subtractMoney } from "./money_add.ts";  ← from money.add ^1.0.0 · built alongside by fune
import { roundDiv } from "./math_round_div.ts";  ← from math.round-div ^1.0.0 · built alongside by fune
import { type GrossMargin, type MarginBasis, type MarginLine } from "./agri_gross_margin_types.ts";

const MAX_MINOR = 900000000000;

/**
 * Gross margin: enterprise output less its variable costs, per hectare or head.
 *
 * Each per-unit figure is its own total divided once, half up. Taking output
 * per head less costs per head instead can be a penny out: 3 head with 10000
 * output and 5000 costs is 3333 - 1667 = 1666 that way, but 5000 / 3 = 1667.
 */
export function grossMargin(
  outputs: readonly MarginLine[],
  variableCosts: readonly MarginLine[],
  per: MarginBasis,
  units: number,
  currency: string,
): GrossMargin {
  let divisor: number;
  let scale: number;
  if (per === "hectare") {
    if (typeof units !== "number" || !Number.isFinite(units) || units <= 0 || units > 10000000) {
      throw new RangeError(`units must be a finite number of hectares greater than 0 and at most 10000000, received ${units}`);
    }
    divisor = Math.round(units * 10000);
    if (divisor < 1) throw new RangeError(`units must be at least 0.0001 hectares, received ${units}`);
    scale = 10000;
  } else if (per === "head") {
    if (typeof units !== "number" || !Number.isInteger(units) || units < 1 || units > 10000000) {
      throw new RangeError(`units must be a whole number of head from 1 to 10000000, received ${units}`);
    }
    divisor = units;
    scale = 1;
  } else {
    throw new RangeError(`unknown basis "${per}": use hectare or head`);
  }

  const output = sumMoney(outputs.map((line) => line.amount), currency);
  const costs = sumMoney(variableCosts.map((line) => line.amount), currency);
  const margin = subtractMoney(output, costs);
  for (const m of [output, costs, margin]) {
    if (Math.abs(m.minor) > MAX_MINOR) {
      throw new RangeError("totals must be within 900000000000 minor units either side of zero");
    }
  }
  const perUnit = (m: Money): Money => money(roundDiv(m.minor * scale, divisor, "half-up"), currency);
  return {
    output,
    variableCosts: costs,
    grossMargin: margin,
    outputPerUnit: perUnit(output),
    variableCostsPerUnit: perUnit(costs),
    grossMarginPerUnit: perUnit(margin),
    marginBasisPoints: output.minor === 0 ? null : roundDiv(margin.minor * 10000, output.minor, "half-up"),
  };
}