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"),
};
}