Functional Weave
Code in TypeScript

construction.day-rate-quote@1.0.0

impl/typescript.ts

1,847 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 { markupPrice } from "./finance_markup_price.ts";  ← from finance.markup-price ^1.0.0 · built alongside by fune
import { addVat } from "./finance_tax_add_vat.ts";  ← from finance.tax.add-vat ^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 { addMoney } from "./money_add.ts";  ← from money.add ^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 DayRateQuote } from "./construction_day_rate_quote_types.ts";

/**
 * A quote from labour days, a day rate, marked-up materials and VAT.
 *
 * The markup goes on the materials only: the day rate already is the price of
 * labour. Labour is rounded once, to the penny, from days x rate; VAT is one
 * rounding on the net total, as on the invoice that follows.
 */
export function dayRateQuote(
  labourDaysHundredths: number,
  dayRate: Money,
  materialsCost: Money,
  materialsMarkupBasisPoints: number,
  vatCategory: string | null,
  onDate: string
): DayRateQuote {
  if (!Number.isInteger(labourDaysHundredths) || labourDaysHundredths < 0) {
    throw new RangeError(`labourDaysHundredths must be a whole number of hundredths, not negative, received ${labourDaysHundredths}`);
  }
  if (dayRate.minor < 0) {
    throw new RangeError(`dayRate must not be negative, received ${dayRate.minor}`);
  }
  const product = dayRate.minor * labourDaysHundredths;
  if (!Number.isSafeInteger(product)) {
    throw new RangeError("the labour calculation exceeds 2^53 - 1");
  }
  const labour = money(roundDiv(product, 100, "half-up"), dayRate.currency);
  const materials = markupPrice(materialsCost, materialsMarkupBasisPoints, "markup", "half-up");
  const net = addMoney(labour, materials);
  if (vatCategory === null) {
    return { labour, materials, net, vat: money(0, net.currency), gross: net, vatBasisPoints: 0 };
  }
  const vat = addVat(net, "GB", vatCategory, onDate);
  return { labour, materials, net, vat: vat.tax, gross: vat.gross, vatBasisPoints: vat.basisPoints };
}