Functional Weave
Code in Rust

finance.tax.vat-flat-rate@1.0.1

impl/typescript.ts

2,504 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 { addDays } from "./dates_add_days.ts";  ← from dates.add-days ^1.0.0 · built alongside by fune
import { addMonths } from "./dates_add_months.ts";  ← from dates.add-months ^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 { applyRate } from "./money_apply_rate.ts";  ← from money.apply-rate ^1.0.0 · built alongside by fune
import { FLAT_RATES, FLAT_RATES_HISTORY } from "./finance_tax_vat_flat_rate_data.ts";  ← this capability’s own data, compiled from data/flat-rates.json into the same file by fune build
import { type FlatRateVat } from "./finance_tax_vat_flat_rate_types.ts";

const LIMITED_COST = "limited-cost-business";

function rateOn(sector: string, onDate: string): number {
  const rows = FLAT_RATES.filter((r) => r.sector === sector);
  for (const row of rows) {
    if (onDate >= row.validFrom && (row.validTo === null || onDate <= row.validTo)) return row.basisPoints;
  }
  if (FLAT_RATES_HISTORY !== "full" && rows.length > 0) {
    const earliest = rows.map((r) => r.validFrom).reduce((a, b) => (b < a ? b : a));
    if (onDate < earliest) {
      throw new RangeError(
        `no flat rate for ${sector} on ${onDate}: this build was installed with history=${FLAT_RATES_HISTORY}, so it only carries rates from ${earliest}. Reinstall with history=full for older periods.`,
      );
    }
  }
  throw new RangeError(`no flat rate for ${sector} on ${onDate}`);
}

/**
 * VAT due under the Flat Rate Scheme on a slice of VAT-inclusive turnover.
 *
 * The rate is the one in force on the supply date; the first-year reduction
 * runs to the day before the first anniversary of registration.
 */
export function flatRateVat(
  turnover: Money,
  sector: string,
  limitedCostBusiness: boolean,
  registrationDate: string | null,
  supplyDate: string,
): FlatRateVat {
  if (turnover.currency !== "GBP") {
    throw new RangeError(`the flat rate scheme is for GBP turnover, received ${turnover.currency}`);
  }
  addDays(supplyDate, 0);
  if (sector === LIMITED_COST || !FLAT_RATES.some((r) => r.sector === sector)) {
    throw new RangeError(`unknown flat rate sector "${sector}"`);
  }
  const base = rateOn(limitedCostBusiness ? LIMITED_COST : sector, supplyDate);

  let reduced = false;
  if (registrationDate !== null) {
    if (supplyDate < addDays(registrationDate, 0)) {
      throw new RangeError(`supply date ${supplyDate} is before the VAT registration date ${registrationDate}`);
    }
    reduced = supplyDate < addMonths(registrationDate, 12);
  }
  const basisPoints = reduced ? Math.max(base - 100, 0) : base;
  return {
    sector,
    limitedCostBusiness,
    firstYearReduction: reduced,
    basisPoints,
    vatDue: money(applyRate(turnover, basisPoints, "half-up").minor, "GBP"),
  };
}