Functional Weave
Code in Rust

logistics.duty-estimate@1.0.1

impl/typescript.ts

1,973 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 { vatRate } from "./finance_tax_vat_rate.ts";  ← from finance.tax.vat-rate ^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 { addMoney } 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 { applyRate } from "./money_apply_rate.ts";  ← from money.apply-rate ^1.0.0 · built alongside by fune
import { type DutyEstimate } from "./logistics_duty_estimate_types.ts";

function nonNegative(name: string, amount: Money): void {
  if (amount.minor < 0) throw new RangeError(`${name} must not be negative, received ${amount.minor}`);
}

/**
 * Import duty on the CIF customs value, then import VAT on the customs value
 * plus duty plus the VAT-only additions, at the rate in force on importDate.
 */
export function dutyEstimate(
  goods: Money,
  freight: Money,
  insurance: Money,
  dutyBasisPoints: number,
  vatAdditions: Money,
  jurisdiction: string,
  vatCategory: string,
  importDate: string,
  rounding: RoundingMode,
): DutyEstimate {
  nonNegative("goods", goods);
  nonNegative("freight", freight);
  nonNegative("insurance", insurance);
  nonNegative("vatAdditions", vatAdditions);
  if (!Number.isInteger(dutyBasisPoints)) {
    throw new TypeError(`dutyBasisPoints must be an integer, received ${dutyBasisPoints}`);
  }
  if (dutyBasisPoints < 0) throw new RangeError(`dutyBasisPoints must not be negative, received ${dutyBasisPoints}`);

  // CIF: duty is charged on what it cost to get the goods to the border.
  const customsValue = addMoney(addMoney(goods, freight), insurance);
  const duty = applyRate(customsValue, dutyBasisPoints, rounding);
  // VAT is charged on the duty too, and on costs after the border.
  const vatValue = addMoney(addMoney(customsValue, duty), vatAdditions);
  const rate = vatRate(jurisdiction, vatCategory, importDate);
  const importVat = applyRate(vatValue, rate.basisPoints, rounding);
  return {
    customsValue,
    dutyBasisPoints,
    duty,
    vatValue,
    vatBasisPoints: rate.basisPoints,
    importVat,
    totalPayable: addMoney(duty, importVat),
  };
}