@seethruhead/cra-payroll 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cra-payroll.js +66 -8
- package/package.json +2 -2
package/dist/cra-payroll.js
CHANGED
|
@@ -79630,7 +79630,7 @@ import { existsSync as existsSync4, renameSync, unlinkSync, chmodSync } from "fs
|
|
|
79630
79630
|
// package.json
|
|
79631
79631
|
var package_default = {
|
|
79632
79632
|
name: "@seethruhead/cra-payroll",
|
|
79633
|
-
version: "0.
|
|
79633
|
+
version: "0.6.0",
|
|
79634
79634
|
description: "Calculate Canadian payroll deductions using CRA's Payroll Deductions Online Calculator",
|
|
79635
79635
|
type: "module",
|
|
79636
79636
|
bin: {
|
|
@@ -79644,7 +79644,7 @@ var package_default = {
|
|
|
79644
79644
|
build: "bun build --compile src/cli.ts --outfile cra-payroll --external electron",
|
|
79645
79645
|
"build:npm": "bun build src/cli.ts --outfile dist/cra-payroll.js --target=node",
|
|
79646
79646
|
release: "bash release.sh",
|
|
79647
|
-
test: "bun test src/unit.test.ts src/cache.test.ts src/monthly.test.ts src/views.test.ts",
|
|
79647
|
+
test: "bun test src/unit.test.ts src/cache.test.ts src/monthly.test.ts src/views.test.ts src/brackets.test.ts",
|
|
79648
79648
|
"test:integration": "bun test src/integration.test.ts --timeout 120000 --max-concurrency 1",
|
|
79649
79649
|
"test:all": "bun test --timeout 120000 --max-concurrency 1"
|
|
79650
79650
|
},
|
|
@@ -79826,6 +79826,55 @@ ${line("═", W)}
|
|
|
79826
79826
|
Net Pay: $${f(r2.net)}${when(totalEmployeeRrsp, ` (after RRSP): $${f(r2.net - totalEmployeeRrsp)}`)}`;
|
|
79827
79827
|
};
|
|
79828
79828
|
|
|
79829
|
+
// src/brackets.ts
|
|
79830
|
+
var brackets = (...bands) => (income) => (bands.find(([upTo]) => income <= upTo) ?? bands[bands.length - 1])[1];
|
|
79831
|
+
var cumulativeTax = (bands) => (income) => bands.reduce(({ tax, prev }, [cap, rate]) => ({
|
|
79832
|
+
tax: income > prev ? tax + (Math.min(income, cap) - prev) * rate : tax,
|
|
79833
|
+
prev: cap
|
|
79834
|
+
}), { tax: 0, prev: 0 }).tax;
|
|
79835
|
+
var withSurtax = (baseFn, taxFn, threshold1, threshold2) => (income) => {
|
|
79836
|
+
const rate = baseFn(income);
|
|
79837
|
+
const tax = taxFn(income);
|
|
79838
|
+
let mult = 1;
|
|
79839
|
+
if (tax > threshold1)
|
|
79840
|
+
mult += 0.2;
|
|
79841
|
+
if (tax > threshold2)
|
|
79842
|
+
mult += 0.36;
|
|
79843
|
+
return rate * mult;
|
|
79844
|
+
};
|
|
79845
|
+
var on2025bands = [[52886, 0.0505], [105775, 0.0915], [150000, 0.1116], [220000, 0.1216], [Infinity, 0.1316]];
|
|
79846
|
+
var on2026bands = [[54000, 0.0505], [108000, 0.0915], [150000, 0.1116], [220000, 0.1216], [Infinity, 0.1316]];
|
|
79847
|
+
var RATES = {
|
|
79848
|
+
2025: {
|
|
79849
|
+
federal: brackets([57375, 0.15], [114750, 0.205], [158468, 0.26], [220000, 0.29], [Infinity, 0.33]),
|
|
79850
|
+
provincial: {
|
|
79851
|
+
Ontario: withSurtax(brackets(...on2025bands), cumulativeTax(on2025bands), 4991, 6387),
|
|
79852
|
+
Alberta: brackets([148269, 0.1], [177922, 0.12], [237230, 0.13], [355845, 0.14], [Infinity, 0.15]),
|
|
79853
|
+
"British Columbia": brackets([47937, 0.0506], [95875, 0.077], [110076, 0.105], [133664, 0.1229], [181232, 0.147], [252752, 0.168], [Infinity, 0.205])
|
|
79854
|
+
}
|
|
79855
|
+
},
|
|
79856
|
+
2026: {
|
|
79857
|
+
federal: brackets([59000, 0.15], [118000, 0.205], [163000, 0.26], [227000, 0.29], [Infinity, 0.33]),
|
|
79858
|
+
provincial: {
|
|
79859
|
+
Ontario: withSurtax(brackets(...on2026bands), cumulativeTax(on2026bands), 5100, 6525),
|
|
79860
|
+
Alberta: brackets([152000, 0.1], [182000, 0.12], [243000, 0.13], [365000, 0.14], [Infinity, 0.15]),
|
|
79861
|
+
"British Columbia": brackets([49000, 0.0506], [98000, 0.077], [113000, 0.105], [137000, 0.1229], [186000, 0.147], [259000, 0.168], [Infinity, 0.205])
|
|
79862
|
+
}
|
|
79863
|
+
}
|
|
79864
|
+
};
|
|
79865
|
+
var supportedYears = () => Object.keys(RATES).map(Number);
|
|
79866
|
+
var marginalRate = (year, province, taxableIncome) => {
|
|
79867
|
+
const yearRates = RATES[year];
|
|
79868
|
+
if (!yearRates)
|
|
79869
|
+
return $err(`No tax bracket data for ${year}. Supported years: ${supportedYears().join(", ")}`);
|
|
79870
|
+
const provFn = yearRates.provincial[province];
|
|
79871
|
+
if (!provFn)
|
|
79872
|
+
return $err(`No provincial bracket data for ${province} in ${year}. Supported: ${Object.keys(yearRates.provincial).join(", ")}`);
|
|
79873
|
+
const fed = yearRates.federal(taxableIncome);
|
|
79874
|
+
const prov = provFn(taxableIncome);
|
|
79875
|
+
return $ok({ federal: fed, provincial: prov, combined: fed + prov });
|
|
79876
|
+
};
|
|
79877
|
+
|
|
79829
79878
|
// src/views/table.ts
|
|
79830
79879
|
var col = (v, w) => typeof v === "number" ? money(v).padStart(w) : v.padStart(w);
|
|
79831
79880
|
var renderRow = (r2) => `${r2.label} │ ${col(r2.gross, 10)} │ ${col(r2.fedTax, 10)} │ ${col(r2.provTax, 10)} │ ${col(r2.cpp, 10)} │ ${col(r2.cpp2, 6)} │ ${col(r2.ei, 10)} │ ${col(r2.netPay, 10)}${r2.rrspYou !== undefined ? ` │ ${col(r2.rrspYou, 10)} │ ${col(r2.rrspEr, 10)} │ ${col(r2.takeHome, 10)}` : ""} │ ${col(r2.cumCppEi, 10)}${r2.suffix ?? ""}`;
|
|
@@ -79871,7 +79920,7 @@ var totalsRow = (totals, rrsp) => ({
|
|
|
79871
79920
|
...rrspFields(rrsp, { rrspYou: totalEmployeeRrspTotals(totals), rrspEr: totals.rrspEmployer, takeHome: totals.netPay - totalEmployeeRrspTotals(totals) }),
|
|
79872
79921
|
cumCppEi: ""
|
|
79873
79922
|
});
|
|
79874
|
-
var renderTable = (yearly, periodsPerYear, year = 2026) => t3(yearly, (ctx) => ({ ...ctx, rrsp: showRrsp(ctx.totals) }), (ctx) => ({ ...ctx, header: renderRow(headerRow(ctx.rrsp)) }), (ctx) => ({ ...ctx, bodyRows: ctx.rows.map((r2) => renderRow(toRow(r2, ctx.rows[0], ctx.rrsp))) }), (ctx) => ({ ...ctx, totalsStr: renderRow(totalsRow(ctx.totals, ctx.rrsp)), W: ctx.header.length }), (ctx) => {
|
|
79923
|
+
var renderTable = (yearly, periodsPerYear, year = 2026, province = "Ontario", annualSalary = 0, rrspPercent = 0) => t3(yearly, (ctx) => ({ ...ctx, rrsp: showRrsp(ctx.totals) }), (ctx) => ({ ...ctx, header: renderRow(headerRow(ctx.rrsp)) }), (ctx) => ({ ...ctx, bodyRows: ctx.rows.map((r2) => renderRow(toRow(r2, ctx.rows[0], ctx.rrsp))) }), (ctx) => ({ ...ctx, totalsStr: renderRow(totalsRow(ctx.totals, ctx.rrsp)), W: ctx.header.length }), (ctx) => {
|
|
79875
79924
|
const t8 = ctx.totals;
|
|
79876
79925
|
const empTotal = totalEmployeeRrspTotals(t8);
|
|
79877
79926
|
const rrspSummary = !ctx.rrsp ? "" : `
|
|
@@ -79879,6 +79928,9 @@ var renderTable = (yearly, periodsPerYear, year = 2026) => t3(yearly, (ctx) => (
|
|
|
79879
79928
|
RRSP Er: $${money(t8.rrspEmployer)}/yr ($${money(t8.rrspEmployer / periodsPerYear)}/period)
|
|
79880
79929
|
RRSP Total (You + Er): $${money(empTotal + t8.rrspEmployer)}/yr`;
|
|
79881
79930
|
const taxRate = t8.grossIncome > 0 ? t8.totalDeductions / t8.grossIncome * 100 : 0;
|
|
79931
|
+
const taxableIncome = annualSalary * (1 - rrspPercent / 100);
|
|
79932
|
+
const mr = marginalRate(year, province, taxableIncome);
|
|
79933
|
+
const marginalLine = mr.isOk() ? ` Marginal tax rate: ${pct(mr.value.combined * 100)} (fed ${pct(mr.value.federal * 100)} + prov ${pct(mr.value.provincial * 100)})` : "";
|
|
79882
79934
|
return `Per-Paycheck Table (${year})
|
|
79883
79935
|
${line("═", ctx.W)}
|
|
79884
79936
|
${ctx.header}
|
|
@@ -79888,7 +79940,8 @@ ${ctx.bodyRows.join(`
|
|
|
79888
79940
|
${line("─", ctx.W)}
|
|
79889
79941
|
${ctx.totalsStr}
|
|
79890
79942
|
${line("═", ctx.W)}
|
|
79891
|
-
Effective tax rate: ${pct(taxRate)}${
|
|
79943
|
+
Effective tax rate: ${pct(taxRate)}${marginalLine ? `
|
|
79944
|
+
${marginalLine}` : ""}${rrspSummary}`;
|
|
79892
79945
|
});
|
|
79893
79946
|
|
|
79894
79947
|
// src/views/monthlyTable.ts
|
|
@@ -79931,7 +79984,7 @@ var totalsRow2 = (totals, rrsp) => ({
|
|
|
79931
79984
|
netPay: totals.netPay,
|
|
79932
79985
|
...rrspFields2(rrsp, { rrspYou: totalEmployeeRrspTotals2(totals), rrspEr: totals.rrspEmployer, takeHome: totals.netPay - totalEmployeeRrspTotals2(totals) })
|
|
79933
79986
|
});
|
|
79934
|
-
var renderMonthlyTable = (monthly, year = 2026) => t3(monthly, (ctx) => ({ ...ctx, rrsp: showRrsp2(ctx.totals) }), (ctx) => ({ ...ctx, header: renderRow2(headerRow2(ctx.rrsp)) }), (ctx) => ({ ...ctx, bodyRows: ctx.rows.map((r2) => renderRow2(toRow2(r2, ctx.rrsp))) }), (ctx) => ({ ...ctx, totalsStr: renderRow2(totalsRow2(ctx.totals, ctx.rrsp)), W: ctx.header.length }), (ctx) => {
|
|
79987
|
+
var renderMonthlyTable = (monthly, year = 2026, province = "Ontario", annualSalary = 0, rrspPercent = 0) => t3(monthly, (ctx) => ({ ...ctx, rrsp: showRrsp2(ctx.totals) }), (ctx) => ({ ...ctx, header: renderRow2(headerRow2(ctx.rrsp)) }), (ctx) => ({ ...ctx, bodyRows: ctx.rows.map((r2) => renderRow2(toRow2(r2, ctx.rrsp))) }), (ctx) => ({ ...ctx, totalsStr: renderRow2(totalsRow2(ctx.totals, ctx.rrsp)), W: ctx.header.length }), (ctx) => {
|
|
79935
79988
|
const t8 = ctx.totals;
|
|
79936
79989
|
const empTotal = totalEmployeeRrspTotals2(t8);
|
|
79937
79990
|
const rrspSummary = !ctx.rrsp ? "" : `
|
|
@@ -79939,6 +79992,9 @@ var renderMonthlyTable = (monthly, year = 2026) => t3(monthly, (ctx) => ({ ...ct
|
|
|
79939
79992
|
RRSP Er: $${money(t8.rrspEmployer)}/yr
|
|
79940
79993
|
RRSP Total (You + Er): $${money(empTotal + t8.rrspEmployer)}/yr`;
|
|
79941
79994
|
const taxRate = t8.grossIncome > 0 ? t8.totalDeductions / t8.grossIncome * 100 : 0;
|
|
79995
|
+
const taxableIncome = annualSalary * (1 - rrspPercent / 100);
|
|
79996
|
+
const mr = marginalRate(year, province, taxableIncome);
|
|
79997
|
+
const marginalLine = mr.isOk() ? ` Marginal tax rate: ${pct(mr.value.combined * 100)} (fed ${pct(mr.value.federal * 100)} + prov ${pct(mr.value.provincial * 100)})` : "";
|
|
79942
79998
|
return `Monthly Table (${year})
|
|
79943
79999
|
${line("═", ctx.W)}
|
|
79944
80000
|
${ctx.header}
|
|
@@ -79948,7 +80004,8 @@ ${ctx.bodyRows.join(`
|
|
|
79948
80004
|
${line("─", ctx.W)}
|
|
79949
80005
|
${ctx.totalsStr}
|
|
79950
80006
|
${line("═", ctx.W)}
|
|
79951
|
-
Effective tax rate: ${pct(taxRate)}${
|
|
80007
|
+
Effective tax rate: ${pct(taxRate)}${marginalLine ? `
|
|
80008
|
+
${marginalLine}` : ""}${rrspSummary}`;
|
|
79952
80009
|
});
|
|
79953
80010
|
|
|
79954
80011
|
// src/monthly.ts
|
|
@@ -80306,11 +80363,12 @@ var runYearlyMode = async (config2, headless, svc, flags) => {
|
|
|
80306
80363
|
}
|
|
80307
80364
|
const yearly = yearlyResult.value;
|
|
80308
80365
|
const periodsPerYear = PAY_PERIODS[config2.payPeriod];
|
|
80366
|
+
const rrspPercent = config2.rrspMatchPercent + config2.rrspUnmatchedPercent;
|
|
80309
80367
|
if (flags.table)
|
|
80310
|
-
console.log(renderTable(yearly, periodsPerYear, config2.year));
|
|
80368
|
+
console.log(renderTable(yearly, periodsPerYear, config2.year, config2.province, config2.annualSalary, rrspPercent));
|
|
80311
80369
|
if (flags.monthTable) {
|
|
80312
80370
|
const monthly = groupByMonth(yearly, config2.year, config2.payPeriod, periodsPerYear);
|
|
80313
|
-
console.log(renderMonthlyTable(monthly, config2.year));
|
|
80371
|
+
console.log(renderMonthlyTable(monthly, config2.year, config2.province, config2.annualSalary, rrspPercent));
|
|
80314
80372
|
}
|
|
80315
80373
|
if (flags.annual)
|
|
80316
80374
|
console.log(renderAnnual(yearly.totals));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seethruhead/cra-payroll",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Calculate Canadian payroll deductions using CRA's Payroll Deductions Online Calculator",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"build": "bun build --compile src/cli.ts --outfile cra-payroll --external electron",
|
|
15
15
|
"build:npm": "bun build src/cli.ts --outfile dist/cra-payroll.js --target=node",
|
|
16
16
|
"release": "bash release.sh",
|
|
17
|
-
"test": "bun test src/unit.test.ts src/cache.test.ts src/monthly.test.ts src/views.test.ts",
|
|
17
|
+
"test": "bun test src/unit.test.ts src/cache.test.ts src/monthly.test.ts src/views.test.ts src/brackets.test.ts",
|
|
18
18
|
"test:integration": "bun test src/integration.test.ts --timeout 120000 --max-concurrency 1",
|
|
19
19
|
"test:all": "bun test --timeout 120000 --max-concurrency 1"
|
|
20
20
|
},
|