Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { roundDiv } from "./math_round_div.ts"; ← from math.round-div ^1.0.0 · built alongside by fune
import { subtractMoney } from "./money_add.ts"; ← from money.add ^1.0.0 · built alongside by fune
import type { Money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { type MarginBreakdown } from "./finance_margin_types.ts";
/**
* Profit on a sale, expressed both ways.
*
* Margin divides by the price, markup divides by the cost, and the gap between
* them is wide enough to price a whole range wrongly: 10.00 to 15.00 is a 50%
* markup and a 33.33% margin. Returning only one of them invites the caller to
* use it as the other, so both are returned and neither is called "the rate".
*
* Both ratios are in basis points and come out of integer division, so no
* float ever touches the money.
*/
export function margin(cost: Money, price: Money): MarginBreakdown {
// subtractMoney also rejects a GBP cost against a EUR price, which is the
// only way this function can be called with genuinely unanswerable inputs.
const profit = subtractMoney(price, cost);
return {
cost,
price,
profit,
// A zero denominator is not an error here: a giveaway (price 0) and a free
// sample (cost 0) are real trade. The ratio is undefined, not wrong, and
// the rest of the breakdown is still correct.
marginBasisPoints: price.minor === 0 ? null : roundDiv(profit.minor * 10000, price.minor, "half-up"),
markupBasisPoints: cost.minor === 0 ? null : roundDiv(profit.minor * 10000, cost.minor, "half-up"),
};
}
/** Convert a margin to the markup that produces it. Undefined at 100% margin. */
export function marginToMarkupBasisPoints(marginBasisPoints: number): number {
if (marginBasisPoints >= 10000) {
throw new RangeError(`a margin of ${marginBasisPoints} basis points has no finite markup`);
}
return roundDiv(marginBasisPoints * 10000, 10000 - marginBasisPoints, "half-up");
}
/** Convert a markup to the margin it produces. */
export function markupToMarginBasisPoints(markupBasisPoints: number): number {
return roundDiv(markupBasisPoints * 10000, 10000 + markupBasisPoints, "half-up");
}