impl/typescript.ts
1,567 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 RoundingMode, roundDiv } from "./math_round_div.ts"; ← from math.round-div ^1.0.0 · built alongside by fune
import { type Money, money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { type PricingBasis } from "./finance_markup_price_types.ts";
/**
* The selling price that hits a target margin or markup on `cost`.
*
* Margin divides by the price and markup by the cost, so the same 5000 basis
* points means 200.00 or 150.00 on a cost of 100.00; the basis is explicit.
*/
export function markupPrice(cost: Money, basisPoints: number, basis: PricingBasis, mode: RoundingMode): Money {
if (!Number.isInteger(basisPoints)) {
throw new TypeError(`basisPoints must be an integer, received ${basisPoints}`);
}
if (cost.minor < 0) {
throw new RangeError(`cost must not be negative, received ${cost.minor}`);
}
let numerator: number;
let denominator: number;
if (basis === "markup") {
if (basisPoints < -10000) {
throw new RangeError(`a markup of ${basisPoints} basis points gives a negative price`);
}
numerator = cost.minor * (10000 + basisPoints);
denominator = 10000;
} else if (basis === "margin") {
if (basisPoints >= 10000) {
throw new RangeError(`a margin of ${basisPoints} basis points has no finite price`);
}
numerator = cost.minor * 10000;
denominator = 10000 - basisPoints;
} else {
throw new RangeError(`unknown pricing basis "${basis}"`);
}
if (!Number.isSafeInteger(numerator)) {
throw new RangeError("the price calculation exceeds 2^53 - 1");
}
return money(roundDiv(numerator, denominator, mode), cost.currency);
}