Functional Weave
Code in TypeScript

payroll.tax-code-parse@1.0.2

impl/typescript.ts

3,047 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 { money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { type TaxCode, type TaxCodeKind, type TaxRegion } from "./payroll_tax_code_parse_types.ts";

// The whitespace every language trims alike: trim() and \s also take \ufeff,
// which Python's strip() and Rust's trim() keep, and those take \x85, which
// trim() keeps. So each implementation names the same set instead.
const EDGE_SPACE = /^[ \t\n\r\v\f\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+|[ \t\n\r\v\f\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+$/g;

// The basis marker may be written with or without a space: "1257L W1", "1257LM1", "1257L X".
const BASIS = /^(.*?)[ \t\n\r\v\f\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]*(W1|M1|X)$/;
const ALLOWANCE = /^(0|[1-9][0-9]{0,5})([LMNT])$/;
const K_CODE = /^K([1-9][0-9]{0,5})$/;
const D_CODE = /^D([0-9])$/;

/**
 * Take a UK PAYE tax code apart into its region, kind, allowance and basis.
 *
 * The annual allowance is number x £10 + £9, not number x £10: a code covers a
 * £10 band of allowances and HMRC's routines give the employee the top of it,
 * so 1257L is worth £12,579 a year to the payroll.
 */
export function parseTaxCode(code: string): TaxCode {
  const original = code;
  let text = code.replace(EDGE_SPACE, "").toUpperCase();
  if (text === "") throw new RangeError(`unrecognised tax code "${original}"`);

  let cumulative = true;
  const basis = BASIS.exec(text);
  if (basis !== null && basis[1] !== "") {
    text = basis[1];
    cumulative = false;
  }

  let region: TaxRegion = "rest-of-uk";
  let body = text;
  if (text.startsWith("S")) {
    region = "scotland";
    body = text.slice(1);
  } else if (text.startsWith("C")) {
    region = "wales";
    body = text.slice(1);
  }

  let kind: TaxCodeKind;
  let number = 0;
  let suffix: string | null = null;
  let allowancePounds = 0;

  const allowance = ALLOWANCE.exec(body);
  const k = K_CODE.exec(body);
  const d = D_CODE.exec(body);
  if (body === "NT") {
    if (region !== "rest-of-uk") {
      throw new RangeError(`there is no Scottish or Welsh NT code: "${original}"`);
    }
    kind = "no-tax";
  } else if (body === "BR") {
    kind = "basic-rate";
  } else if (d !== null) {
    kind = "d-rate";
    number = Number(d[1]);
  } else if (k !== null) {
    kind = "negative-allowance";
    number = Number(k[1]);
    allowancePounds = -(number * 10 + 9);
  } else if (allowance !== null) {
    number = Number(allowance[1]);
    suffix = allowance[2];
    if (number === 0 && suffix !== "T") {
      throw new RangeError(`unrecognised tax code "${original}": a zero allowance is only issued as 0T`);
    }
    kind = "allowance";
    allowancePounds = number === 0 ? 0 : number * 10 + 9;
  } else {
    throw new RangeError(`unrecognised tax code "${original}"`);
  }

  const prefix = region === "scotland" ? "S" : region === "wales" ? "C" : "";
  return {
    code: prefix + body,
    region,
    kind,
    number,
    suffix,
    annualAllowance: money(allowancePounds * 100, "GBP"),
    cumulative,
  };
}