Skip to content

Instantly share code, notes, and snippets.

@alanthai
Last active August 9, 2024 22:45
Show Gist options
  • Save alanthai/2ec6b14452d696d7302a9031e3854537 to your computer and use it in GitHub Desktop.
Save alanthai/2ec6b14452d696d7302a9031e3854537 to your computer and use it in GitHub Desktop.
const INCOME = 100_000;
// ----- 2022 ----- //
// https://www.canada.ca/en/revenue-agency/services/forms-publications/payroll/t4032-payroll-deductions-tables/t4032on-jan/t4032on-january-general-information.html
const CPP_BRACKETS = [
[ 3_500, 0 ], // exemption
[ 61_400, 0.0570], // cap
[Infinity, 0 ],
];
// Max contribution 3,499.80
const EI_BRACKETS = [
[ 60_300, 0.0158], // cap
[Infinity, 0 ]
];
// Max contribution 952.74
const FEDERAL_TAX_BRACKETS = [
// [MAX amount, % tax rate]
[ 14_398, 0 ],
[ 50_197, 0.15 ],
[ 100_392, 0.205],
[ 155_625, 0.26 ],
[ 221_708, 0.29 ],
[Infinity, 0.33 ],
];
const ON_TAX_BRACKETS = [
[ 46_226, 0.0505],
[ 92_454, 0.0915],
[ 150_000, 0.1116],
[ 220_000, 0.1216],
[Infinity, 0.1316],
];
// Tax on top of tax
const ON_SURTAX_BRACKETS = [
[ 4_991, 0 ],
[ 6_387, 0.20],
[Infinity, 0.56]
];
// ----- BRACKETS ----- //
// transform bracket caps into bracket increments that the bracket rate works on
// @return Array<[cap, rate]> => Array<[increment, rate]>
// Eg, [[10, 0.15], [20, 0.30]] => [[10, 0.15], [10, 0.30]];
function getBracketIncrements(brackets) {
let lastCap = 0;
return brackets.map(([cap, rate]) => {
const bracket = [cap - lastCap, rate];
lastCap = cap;
return bracket;
});
}
// @result Array<Tuple<rate, amount>>
function calcBracketBreakdown(brackets) {
return (amount) => {
let remainder = amount;
const result = [];
for (let [increment, rate] of getBracketIncrements(brackets)) {
const incrementAmount = Math.min(increment, remainder);
result.push([rate, incrementAmount * rate]);
remainder -= increment;
if (remainder <= 0) {
break;
}
}
return result;
};
}
function calcBracketsTotal(bracketBreakdown) {
return bracketBreakdown
.map(([_, amount]) => amount)
.reduce((sum, amount) => sum + amount, 0);
}
// ----- DISPLAY ----- //
// Adds comma separators, and 2 decimal places
function formatDollars(n) {
return '$' + n.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function formatPercent(p) {
return (p * 100).toFixed(2) + '%';
}
function displayBracketBreakdown(bracketBreakdown, spacing = 2) {
bracketBreakdown.forEach(([rate, amount]) => {
const formattedAmount = `${formatDollars(amount)}`;
const rateInPercent = `${formatPercent(rate)}`;
if (amount) {
console.log(`${' '.repeat(spacing)}${formattedAmount} at ${rateInPercent}`);
}
});
}
function displayDetailedBreakdown(income) {
const cpp = calcBracketsTotal(calcBracketBreakdown(CPP_BRACKETS)(income));
const ei = calcBracketsTotal(calcBracketBreakdown(EI_BRACKETS)(income));
const federalTaxBreakdown = calcBracketBreakdown(FEDERAL_TAX_BRACKETS)(income);
const federalTax = calcBracketsTotal(federalTaxBreakdown);
const onTaxBreakdown = calcBracketBreakdown(ON_TAX_BRACKETS)(income);
const onTax = calcBracketsTotal(onTaxBreakdown);
const totalTax = federalTax + onTax;
const surtax = calcBracketsTotal(calcBracketBreakdown(ON_SURTAX_BRACKETS)(onTax));
const totalOwing = cpp + ei + totalTax + surtax;
console.log('CPP:', formatDollars(cpp));
console.log('EI:', formatDollars(ei));
console.log(''); // break
console.log('Federal Tax:', formatDollars(federalTax));
displayBracketBreakdown(federalTaxBreakdown);
console.log(''); // break
console.log('Provincial Tax:', formatDollars(onTax));
displayBracketBreakdown(onTaxBreakdown);
if (surtax) {
console.log('Surtax:', formatDollars(surtax));
}
console.log(''); // break
console.log('Total:', formatDollars(totalOwing));
console.log('Average rate:', formatPercent(totalOwing / income));
}
displayDetailedBreakdown(INCOME);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment