Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { roundDiv } from "./math_round_div.ts"; ← from math.round-div ^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 GiftAid } from "./charity_gift_aid_types.ts";
import { BASIC_RATE, BASIC_RATE_HISTORY } from "./charity_gift_aid_data.ts"; ← this capability’s own data, compiled from data/basic-rate.json into the same file by fune build
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
function basicRate(onDate: string): number {
const row = BASIC_RATE.find((r) => r.validFrom <= onDate && (r.validTo === null || onDate <= r.validTo));
if (row !== undefined) return row.basisPoints;
// A history=current build carries only the rate in force; answering an old
// gift with today's rate would be a quiet wrong claim, so say why instead.
if (BASIC_RATE_HISTORY !== "full" && BASIC_RATE.length > 0) {
const earliest = BASIC_RATE.map((r) => r.validFrom).reduce((a, b) => (b < a ? b : a));
if (onDate < earliest) {
throw new RangeError(
`no Gift Aid basic rate on ${onDate}: this build was installed with history=${BASIC_RATE_HISTORY}, so it only carries rules from ${earliest}. Reinstall with history=full for earlier tax years.`
);
}
}
throw new RangeError(`no Gift Aid basic rate on ${onDate}`);
}
/**
* The tax a charity reclaims on a Gift Aid donation. The donor is treated as
* having given the gift after deducting basic rate tax (ITA 2007 s414), so the
* charity reclaims donation x rate / (100% - rate): 25p per £1 at 20%. It is
* always the UK basic rate, for Scottish and Welsh taxpayers too.
*/
export function giftAid(donation: Money, onDate: string): GiftAid {
if (donation.currency !== "GBP") {
throw new RangeError(`Gift Aid is claimed in GBP, received ${donation.currency}`);
}
if (donation.minor < 0) {
throw new RangeError(`donation must not be negative, received ${donation.minor}`);
}
if (!ISO_DATE.test(onDate)) {
throw new RangeError(`onDate must be an ISO date (YYYY-MM-DD), received "${onDate}"`);
}
const rate = basicRate(onDate);
// Rounded down: a claim may never exceed the tax treated as deducted.
const tax = roundDiv(donation.minor * rate, 10000 - rate, "down");
return {
donation: money(donation.minor, "GBP"),
taxReclaimable: money(tax, "GBP"),
gross: money(donation.minor + tax, "GBP"),
basicRateBasisPoints: rate,
};
}