Functional Weave
Code in TypeScript

finance.tax.vat-flat-rate

UK VAT Flat Rate Scheme: VAT due on gross turnover at the sector's dated rate, limited cost rate and first-year 1% off.

1.0.0 (not the latest) · published 2026-10-03 by charlie · Anterra

Pinned by 23 tests, run in TypeScript, Python and Rust.

Not professional advice. This capability calculates tax figures from published rules. It is a software component for developers, not tax advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have a chartered accountant or tax adviser review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.

What it does

Under the VAT Flat Rate Scheme a small business pays HMRC a fixed percentage of its VAT-inclusive turnover instead of output tax less input tax. The percentage depends on the trade sector and changes over time, so it is dated data here, and the function takes the supply date.

**Which rate.** The sector's rate in force on the supply date (tax point), from `data/flat-rates.json`. A limited cost business uses 16.5% whatever its sector, from 1 April 2017 (VAT Notice 733, para 4.4); asking for it before that date is an error, since the rate did not exist. Whether a business is a limited cost business (relevant goods under 2% of turnover, or under GBP 1,000 a year, GBP 250 a quarter) is decided per return period by the caller and passed in; the sector is still checked so that a typo is caught.

For example

  • flatRateVat(£12,000.00, accountancy-or-book-keeping, false, —, 2026-05-01) → sector accountancy-or-book-keeping, limited cost business false, first year reduction false, rate 14.5%, vat due £1,740.00 accountancy at 14.5% on 12,000.00 gross turnover
  • flatRateVat(£12,000.00, accountancy-or-book-keeping, true, —, 2026-05-01) → sector accountancy-or-book-keeping, limited cost business true, first year reduction false, rate 16.5%, vat due £1,980.00 a limited cost business pays 16.5% whatever its sector
  • flatRateVat(£12,000.00, accountancy-or-book-keeping, false, 2026-01-15, 2026-05-01) → sector accountancy-or-book-keeping, limited cost business false, first year reduction true, rate 13.5%, vat due £1,620.00 first year of registration: 1% off

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

export function flatRateVat(turnover: Money, sector: string, limitedCostBusiness: boolean, registrationDate: string | null, supplyDate: string): FlatRateVat
turnoverMoneyVAT-inclusive flat rate turnover (standard, reduced, zero-rated and exempt supplies), in GBP
sectorstringHMRC trade sector as a key, e.g. "accountancy-or-book-keeping"; see data/flat-rates.json
limitedCostBusinessboolwhether the business is a limited cost business for the period (16.5% from 1 April 2017)
registrationDatedate?date of VAT registration when entitled to the first-year 1% reduction; null otherwise
supplyDatedatetax point of the supplies; the rate in force that day applies
returnsFlatRateVat

The type it declares, generated into your project

/** The rate that applied and the VAT it gives. */
export interface FlatRateVat {
  readonly sector: string;
  readonly limitedCostBusiness: boolean;
  /** whether the 1% first-year reduction was taken off */
  readonly firstYearReduction: boolean;
  /** the flat rate applied, after any reduction */
  readonly basisPoints: number;
  /** turnover at the flat rate, rounded half-up to the penny */
  readonly vatDue: Money;
}

Your code names it in one line, in the file that uses it

import { flatRateVat } from "#fune/finance.tax.vat-flat-rate@^1";
impl/typescript.ts · 63 lines · open · 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"),
  };
}

Install

fune build

With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 4 dependencies, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add finance.tax.vat-flat-rate
Download for TypeScript finance.tax.vat-flat-rate-1.0.0-typescript.fune · 31,167 bytes sha256 56e7dfe9de493d8470c2bfeaf4c209d7276cd66164983256bb2a0d71a59edac4

The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./finance.tax.vat-flat-rate-1.0.0-typescript.fune, or fetch it from a terminal with fune pull finance.tax.vat-flat-rate@1.0.0:typescript.

The whole function, every language, is one file too: finance.tax.vat-flat-rate-1.0.0.fune, 37,466 bytes, sha256 130d7218e0fa3eabb3d522d1d9aad759474a9875b55f362c48d04f35cf78e34e. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before finance.tax.vat-flat-rate

after — your function gets the result and the arguments, and returns the final result.

// fune: after finance.tax.vat-flat-rate

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

// fune: replace dates.add-days in finance.tax.vat-flat-rate
// fune: replace dates.add-months in finance.tax.vat-flat-rate
// fune: replace money.amount in finance.tax.vat-flat-rate
// fune: replace money.apply-rate in finance.tax.vat-flat-rate

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show finance.tax.vat-flat-rate --steps.

// fune: step finance.tax.vat-flat-rate after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
accountancy at 14.5% on 12,000.00 gross turnover £12,000.00, accountancy-or-book-keeping, false, —, 2026-05-01 → sector accountancy-or-book-keeping, limited cost business false, first year reduction false, rate 14.5%, vat due £1,740.00
a limited cost business pays 16.5% whatever its sector £12,000.00, accountancy-or-book-keeping, true, —, 2026-05-01 → sector accountancy-or-book-keeping, limited cost business true, first year reduction false, rate 16.5%, vat due £1,980.00
first year of registration: 1% off £12,000.00, accountancy-or-book-keeping, false, 2026-01-15, 2026-05-01 → sector accountancy-or-book-keeping, limited cost business false, first year reduction true, rate 13.5%, vat due £1,620.00
first year off the limited cost rate: 15.5%, as in Notice 733 Example 1 £12,000.00, accountancy-or-book-keeping, true, 2017-04-01, 2018-03-31 → sector accountancy-or-book-keeping, limited cost business true, first year reduction true, rate 15.5%, vat due £1,860.00
the reduction runs to the day before the first anniversary £10,000.00, accountancy-or-book-keeping, false, 2025-06-01, 2026-05-31 → sector accountancy-or-book-keeping, limited cost business false, first year reduction true, rate 13.5%, vat due £1,350.00
and ends on the anniversary £10,000.00, accountancy-or-book-keeping, false, 2025-06-01, 2026-06-01 → sector accountancy-or-book-keeping, limited cost business false, first year reduction false, rate 14.5%, vat due £1,450.00
catering the day before the covid cut £10,000.00, catering-services-including-restaurants-and-takeaways, false, —, 2020-07-14 → sector catering-services-including-restaurants-and-takeaways, limited cost business false, first year reduction false, rate 12.5%, vat due £1,250.00
catering during the covid cut: 4.5% £10,000.00, catering-services-including-restaurants-and-takeaways, false, —, 2020-12-01 → sector catering-services-including-restaurants-and-takeaways, limited cost business false, first year reduction false, rate 4.5%, vat due £450.00
catering during the taper: 8.5% £10,000.00, catering-services-including-restaurants-and-takeaways, false, —, 2021-12-01 → sector catering-services-including-restaurants-and-takeaways, limited cost business false, first year reduction false, rate 8.5%, vat due £850.00
catering back at 12.5% from 1 April 2022 £10,000.00, catering-services-including-restaurants-and-takeaways, false, —, 2022-04-01 → sector catering-services-including-restaurants-and-takeaways, limited cost business false, first year reduction false, rate 12.5%, vat due £1,250.00
Show the other 13 tests
CaseArgumentsExpected
the first-year reduction cannot take the 0% hotel rate below zero £10,000.00, hotel-or-accommodation, false, 2021-01-01, 2021-03-01 → sector hotel-or-accommodation, limited cost business false, first year reduction true, rate 0%, vat due £0.00
pubs at 1% in 2021 £10,000.00, pubs, false, —, 2021-06-30 → sector pubs, limited cost business false, first year reduction false, rate 1%, vat due £100.00
the VAT is rounded half-up once: 14.5% of 1.00 is 14.5p, so 15p £1.00, accountancy-or-book-keeping, false, —, 2026-05-01 → sector accountancy-or-book-keeping, limited cost business false, first year reduction false, rate 14.5%, vat due £0.15
14.5% of 123.45 is 17.90 £123.45, accountancy-or-book-keeping, false, —, 2026-05-01 → sector accountancy-or-book-keeping, limited cost business false, first year reduction false, rate 14.5%, vat due £17.90
a credit note reduces the VAT due -£100.00, accountancy-or-book-keeping, false, —, 2026-05-01 → sector accountancy-or-book-keeping, limited cost business false, first year reduction false, rate 14.5%, vat due -£14.50
the first day of the 2011 table £10,000.00, retailing-food-confectionery-tobacco-newspapers-or-childrens-clothing, false, —, 2011-01-04 → sector retailing-food-confectionery-tobacco-newspapers-or-childrens-clothing, limited cost business false, first year reduction false, rate 4%, vat due £400.00
the limited cost rate did not exist before 1 April 2017 £10.00, accountancy-or-book-keeping, true, —, 2017-03-31 → error: no flat rate for limited-cost-business on 2017-03-31
dates before the 2011 table are refused £10.00, accountancy-or-book-keeping, false, —, 2011-01-03 → error: no flat rate for accountancy-or-book-keeping on 2011-01-03
an unknown sector is refused £10.00, accountancy, false, —, 2026-05-01 → error: unknown flat rate sector "accountancy"
the limited cost rate is not a sector £10.00, limited-cost-business, false, —, 2026-05-01 → error: unknown flat rate sector "limited-cost-business"
turnover must be in GBP €10.00, accountancy-or-book-keeping, false, —, 2026-05-01 → error: the flat rate scheme is for GBP turnover, received EUR
a supply before registration is refused £10.00, accountancy-or-book-keeping, false, 2026-06-01, 2026-05-31 → error: supply date 2026-05-31 is before the VAT registration date 2026-06-01
an impossible supply date is refused £10.00, accountancy-or-book-keeping, false, —, 2026-02-29 → error: not a real calendar date

More from the author

**First-year reduction.** In the first year of VAT registration 1 percentage point comes off the flat rate "until the day before your first anniversary of becoming VAT registered" (para 4.7), including off the limited cost rate (the Notice's Example 1: 16.5% becomes 15.5%). Pass the registration date when the business is entitled to it and null when it is not (registering late, or more than a year before joining the scheme, loses it). A supply date before the registration date is an error. The reduction cannot take a rate below 0%: the 0% hotel rate of 2020-21 stays 0%.

**Rate changes inside a period.** Notice 733 para 4.12 requires two calculations when a rate changes during a VAT period; call this once for each part of the turnover. The same goes for a period that straddles the end of the first year.

**Rounding.** The VAT due is the turnover times the rate, rounded half-up to the penny once. Notice 733 does not state a rounding rule; this should be confirmed by a VAT adviser. Negative turnover (credit notes) gives negative VAT.

## Data and sources

- Sector rates: HMRC, "VAT Flat Rate Scheme: how much you pay", https://www.gov.uk/vat-flat-rate-scheme/how-much-you-pay, read on 23 September 2026, including the temporary hospitality rates: catering 4.5% and hotels 0% and pubs 1% from 15 July 2020 to 30 September 2021, then 8.5%, 5.5% and 4% to 31 March 2022. - The current table dates from 4 January 2011, when it was substituted by SI 2010/2940 with the rise of VAT to 20% (VAT Regulations 1995, reg 55K, https://www.legislation.gov.uk/uksi/1995/2518/regulation/55K). Earlier rates are not included, so a supply date before 4 January 2011 is refused. - Limited cost business rate and first-year reduction: VAT Notice 733, "Flat Rate Scheme for small businesses", paras 4.4, 4.7 and 4.12, https://www.gov.uk/guidance/flat-rate-scheme-for-small-businesses-vat-notice-733--2 - Reg 55K on legislation.gov.uk also lists "Dealing in waste or scrap" at 10.5%, which gov.uk's table does not; it is left out until confirmed.

Sector keys are HMRC's sector names in lower case with punctuation turned into hyphens ("Retailing food, confectionery, tobacco, newspapers or children's clothing" is `retailing-food-confectionery-tobacco-newspapers-or-childrens-clothing`). When a rate changes, publish a new version with a new row. The data declares its effective dates, so a `history=current` build refuses older supply dates with an explanation instead of answering them at today's rate.

Files

PathBytes
README.md3,350
data/flat-rates.json12,138
impl/python.py2,568
impl/rust.rs3,493
impl/typescript.ts2,504
vectors.json7,336