Functional Weave
Code in TypeScript

charity.payroll-giving

UK Payroll Giving for one pay period: the donation comes off pay before PAYE, and the tax it saves the employee.

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 12 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 payroll specialist review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.

What it does

Payroll Giving (Give As You Earn) for one pay period. The employer deducts the donation from pay **before income tax** (ITEPA 2003 s713) but **after National Insurance**, and passes it to a Payroll Giving agency. The relief is simply the PAYE the lower pay saves, so this runs payroll.income-tax twice, with and without the donation, and reports the difference.

That is deliberately not `donation x marginal rate`. PAYE works on taxable pay rounded down to whole pounds, so the saving each period comes in whole multiples of the rate on £1: a £5 donation at basic rate saves exactly £1, a 50p donation saves nothing that month, and a donation that straddles a band boundary gets each rate on its own part. The PAYE is the real deduction the payroll will make, cumulative codes, Scottish and Welsh bands, K codes and all.

For example

  • payrollGiving(1257L, monthly, 1, £3,000.00, £100.00, £0.00, £0.00, 2026-04-25) → taxable pay £2,900.00, tax without giving £390.20, tax with giving £370.20, tax saved £20.00, net cost £80.00 basic rate: £100 given from £3,000 saves £20 of PAYE, costing £80
  • payrollGiving(1257L, monthly, 1, £3,000.00, £5.00, £0.00, £0.00, 2026-04-25) → taxable pay £2,995.00, tax without giving £390.20, tax with giving £389.20, tax saved £1.00, net cost £4.00 £5 given saves exactly £1
  • payrollGiving(1257L, monthly, 1, £3,000.00, £0.50, £0.00, £0.00, 2026-04-25) → taxable pay £2,999.50, tax without giving £390.20, tax with giving £390.20, tax saved £0.00, net cost £0.50 50p given saves nothing: PAYE rounds taxable pay down to whole pounds (naive 20% says 10p)

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 payrollGiving(taxCode: string, frequency: PayFrequency, period: number, pay: Money, donation: Money, previousPayToDate: Money, previousTaxToDate: Money, payDate: string): PayrollGiving
taxCodestringas issued by HMRC, e.g. 1257L or S1257L
frequencyPayFrequency
periodinttax week or month of the payment, from payroll.tax-period
payMoneytaxable pay for the period before the donation, after any net pay pension deduction
donationMoneythe Payroll Giving deduction this period
previousPayToDateMoneytaxable pay already paid this tax year, after earlier Payroll Giving deductions
previousTaxToDateMoneytax already deducted this tax year
payDatedatedecides the tax year's bands
returnsPayrollGiving

The type it declares, generated into your project

/** The period's PAYE with and without the donation. */
export interface PayrollGiving {
  /** pay less the donation: what PAYE is worked out on */
  readonly taxablePay: Money;
  /** PAYE this period had there been no donation */
  readonly taxWithoutGiving: Money;
  /** PAYE this period with the donation deducted first */
  readonly taxWithGiving: Money;
  /** the relief: taxWithoutGiving less taxWithGiving */
  readonly taxSaved: Money;
  /** donation less taxSaved: what the gift costs the employee */
  readonly netCost: Money;
}

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

import { payrollGiving } from "#fune/charity.payroll-giving@^1";
impl/typescript.ts · 40 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 { type Money, money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { incomeTax } from "./payroll_income_tax.ts";  ← from payroll.income-tax ^1.0.0 · built alongside by fune
import { type PayFrequency } from "./payroll_tax_period_types.ts";
import { type PayrollGiving } from "./charity_payroll_giving_types.ts";

/**
 * Payroll Giving (ITEPA 2003 s713): the donation is deducted from pay before
 * PAYE is worked out, so the relief is whatever PAYE the lower pay saves, by
 * the same tax table routines the payroll uses. That is not simply the
 * donation times the marginal rate: PAYE rounds taxable pay down to whole
 * pounds, so a 50p donation can save nothing and a £5 one exactly £1.
 */
export function payrollGiving(
  taxCode: string,
  frequency: PayFrequency,
  period: number,
  pay: Money,
  donation: Money,
  previousPayToDate: Money,
  previousTaxToDate: Money,
  payDate: string
): PayrollGiving {
  if (donation.currency !== "GBP") {
    throw new RangeError(`payroll amounts must be in GBP, received ${donation.currency} for donation`);
  }
  if (donation.minor < 0) throw new RangeError(`donation must not be negative, received ${donation.minor}`);
  if (donation.minor > pay.minor) {
    throw new RangeError(`donation of ${donation.minor} is more than pay of ${pay.minor}`);
  }
  const taxablePay = money(pay.minor - donation.minor, "GBP");
  const without = incomeTax(taxCode, frequency, period, pay, previousPayToDate, previousTaxToDate, payDate).tax.minor;
  const withGiving = incomeTax(taxCode, frequency, period, taxablePay, previousPayToDate, previousTaxToDate, payDate).tax.minor;
  return {
    taxablePay,
    taxWithoutGiving: money(without, "GBP"),
    taxWithGiving: money(withGiving, "GBP"),
    taxSaved: money(without - withGiving, "GBP"),
    netCost: money(donation.minor - (without - withGiving), "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 3 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 charity.payroll-giving
Download for TypeScript charity.payroll-giving-1.0.0-typescript.fune · 13,175 bytes sha256 275659946460d922db0282ea577abe096e5302ed9860d6d21f6fb77f9c6dbbcc

The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./charity.payroll-giving-1.0.0-typescript.fune, or fetch it from a terminal with fune pull charity.payroll-giving@1.0.0:typescript.

The whole function, every language, is one file too: charity.payroll-giving-1.0.0.fune, 17,773 bytes, sha256 6b0c4d385fdbed84474164b57fe3c3f23e9d39a2757335f3e7ea1641e055b2ce. 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 charity.payroll-giving

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

// fune: after charity.payroll-giving

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 money.amount in charity.payroll-giving
// fune: replace payroll.income-tax in charity.payroll-giving
// fune: replace payroll.tax-period in charity.payroll-giving

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 charity.payroll-giving --steps.

// fune: step charity.payroll-giving 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
basic rate: £100 given from £3,000 saves £20 of PAYE, costing £80 1257L, monthly, 1, £3,000.00, £100.00, £0.00, £0.00, 2026-04-25 → taxable pay £2,900.00, tax without giving £390.20, tax with giving £370.20, tax saved £20.00, net cost £80.00
£5 given saves exactly £1 1257L, monthly, 1, £3,000.00, £5.00, £0.00, £0.00, 2026-04-25 → taxable pay £2,995.00, tax without giving £390.20, tax with giving £389.20, tax saved £1.00, net cost £4.00
50p given saves nothing: PAYE rounds taxable pay down to whole pounds (naive 20% says 10p) 1257L, monthly, 1, £3,000.00, £0.50, £0.00, £0.00, 2026-04-25 → taxable pay £2,999.50, tax without giving £390.20, tax with giving £390.20, tax saved £0.00, net cost £0.50
higher rate: £200 given from £5,000 saves £80 1257L, monthly, 1, £5,000.00, £200.00, £0.00, £0.00, 2026-04-25 → taxable pay £4,800.00, tax without giving £952.06, tax with giving £872.06, tax saved £80.00, net cost £120.00
Scottish intermediate rate: £100 saves £21 S1257L, monthly, 1, £3,000.00, £100.00, £0.00, £0.00, 2026-04-25 → taxable pay £2,900.00, tax without giving £392.27, tax with giving £371.27, tax saved £21.00, net cost £79.00
cumulative month 6: £100 saves £20 against tax to date 1257L, monthly, 6, £3,000.00, £100.00, £15,000.00, £1,951.60, 2026-09-25 → taxable pay £2,900.00, tax without giving £390.40, tax with giving £370.40, tax saved £20.00, net cost £80.00
no donation: nothing saved 1257L, monthly, 1, £3,000.00, £0.00, £0.00, £0.00, 2026-04-25 → taxable pay £3,000.00, tax without giving £390.20, tax with giving £390.20, tax saved £0.00, net cost £0.00
pay under the allowance: the donation saves no tax at all 1257L, monthly, 1, £1,000.00, £1,000.00, £0.00, £0.00, 2026-04-25 → taxable pay £0.00, tax without giving £0.00, tax with giving £0.00, tax saved £0.00, net cost £1,000.00
a donation above pay is an error 1257L, monthly, 1, £100.00, £100.01, £0.00, £0.00, 2026-04-25 → error: donation of 10001 is more than pay of 10000
a negative donation is an error 1257L, monthly, 1, £100.00, -£0.01, £0.00, £0.00, 2026-04-25 → error: donation must not be negative
Show the other 2 tests
CaseArgumentsExpected
a euro donation is an error 1257L, monthly, 1, £100.00, €1.00, £0.00, £0.00, 2026-04-25 → error: payroll amounts must be in GBP, received EUR for donation
a bad period is refused by payroll.income-tax 1257L, monthly, 13, £3,000.00, £1.00, £0.00, £0.00, 2026-04-25 → error: period 13 is not a tax period for monthly pay

More from the author

- `pay` is the period's taxable pay before the donation (after any net pay pension deduction); `previousPayToDate` should already be net of earlier Payroll Giving deductions, as it is on the payroll record. - National Insurance is not affected by the donation and is not computed here (payroll.national-insurance on the full pay). - A charity cannot claim Gift Aid on Payroll Giving donations (s416(4) Condition C excludes them), and agency fees are the caller's to handle. - The tax years are those payroll.income-tax has bands for (2023-24 on).

## Sources

- Income Tax (Earnings and Pensions) Act 2003 s713, https://www.legislation.gov.uk/ukpga/2003/1/section/713 - GOV.UK, "Payroll Giving" ("taken from your employees' pay before tax but after National Insurance"; "You can't claim Gift Aid on Payroll Giving donations"), https://www.gov.uk/payroll-giving - Income Tax Act 2007 s416(4), https://www.legislation.gov.uk/ukpga/2007/3/section/416

Files

PathBytes
README.md1,812
impl/python.py1,740
impl/rust.rs2,674
impl/typescript.ts1,792
vectors.json5,382