social-security-calculator 3.0.0 → 3.1.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/lib/constants.d.ts +2 -1
- package/lib/constants.js +3 -1
- package/lib/index.d.ts +1 -10
- package/lib/index.js +32 -9
- package/lib/wage-index.js +15 -9
- package/package.json +2 -1
package/lib/constants.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const EARLY_RETIREMENT_AGE = 62;
|
|
2
|
-
export declare const WAGE_INDEX_CUTOFF =
|
|
2
|
+
export declare const WAGE_INDEX_CUTOFF = 2024;
|
|
3
3
|
export declare const MAX_RETIREMENT_AGE = 70;
|
|
4
4
|
export declare const LOOKBACK_YEARS = 40;
|
|
5
5
|
export declare const MAX_DROP_OUT_YEARS = 5;
|
|
@@ -10,6 +10,7 @@ export declare const FIRST_BEND_POINT_MULTIPLIER = 180;
|
|
|
10
10
|
export declare const SECOND_BEND_POINT_MULTIPLIER = 1085;
|
|
11
11
|
export declare const FAM_MAX_BASES: number[];
|
|
12
12
|
export declare const CHILD_SURVIVOR_BENEFIT_PERCENTAGE = 0.75;
|
|
13
|
+
export declare const DISABILITY_LAST_EARNINGS_YEARS_THRESHOLD = 5;
|
|
13
14
|
export declare const PIA_PERCENTAGES: {
|
|
14
15
|
readonly FIRST_BRACKET: 0.9;
|
|
15
16
|
readonly SECOND_BRACKET: 0.32;
|
package/lib/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Constants
|
|
2
2
|
export const EARLY_RETIREMENT_AGE = 62;
|
|
3
|
-
export const WAGE_INDEX_CUTOFF =
|
|
3
|
+
export const WAGE_INDEX_CUTOFF = 2024;
|
|
4
4
|
export const MAX_RETIREMENT_AGE = 70;
|
|
5
5
|
// LOOKBACK_YEARS This represents the working years between 22 and 62,
|
|
6
6
|
// but it does not filter out the wages of years before and after that range.
|
|
@@ -15,6 +15,8 @@ export const FIRST_BEND_POINT_MULTIPLIER = 180;
|
|
|
15
15
|
export const SECOND_BEND_POINT_MULTIPLIER = 1085;
|
|
16
16
|
export const FAM_MAX_BASES = [230, 332, 433];
|
|
17
17
|
export const CHILD_SURVIVOR_BENEFIT_PERCENTAGE = 0.75; // 75% of PIA for child survivor benefits
|
|
18
|
+
// Disability 5-year rule: if disability date is more than 5 years after last earnings year, disability benefit is zero
|
|
19
|
+
export const DISABILITY_LAST_EARNINGS_YEARS_THRESHOLD = 5;
|
|
18
20
|
export const PIA_PERCENTAGES = {
|
|
19
21
|
FIRST_BRACKET: 0.9,
|
|
20
22
|
SECOND_BRACKET: 0.32,
|
package/lib/index.d.ts
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
import { BenefitCalculationResult,
|
|
1
|
+
import { BenefitCalculationResult, Earnings } from './model';
|
|
2
2
|
export declare function calc(birthday: Date, retirementDate: Date, earnings: Earnings): BenefitCalculationResult;
|
|
3
3
|
export declare function calcRetirement(birthday: Date, retirementDate: Date, earnings: Earnings): Partial<BenefitCalculationResult>;
|
|
4
|
-
export declare function calculateRetirementDates(birthday: Date, retirementDate: Date): RetirementDates;
|
|
5
|
-
export declare function retirementDateAdjustedPayment(dates: RetirementDates, colaAdjustedPIA: number): number;
|
|
6
4
|
export declare function calculatePIA(AIME: number, baseYear?: number): number;
|
|
7
5
|
export declare function calculateAIME(earnings: Earnings, lookbackYears: number, baseYear?: number): number;
|
|
8
|
-
export declare function getLookbackYears(elapsedYears: number): number;
|
|
9
|
-
export declare function getLookbackYearsDisability(elapsedYears: number): number;
|
|
10
|
-
export declare function getFullRetirementMonths(commonLawBirthDate: Date): number;
|
|
11
|
-
/** Compute Family-Max bend points for an eligibility year given AWI_{year-2}. */
|
|
12
|
-
export declare function getFamilyMaxBendPoints(baseYear: number): [number, number, number];
|
|
13
|
-
/** Apply the family-maximum formula to a PIA using already-computed bend points. */
|
|
14
|
-
export declare function calculateFamilyMaximum(pia: number, [bp1, bp2, bp3]: [number, number, number]): number;
|
|
15
6
|
export declare function getEnglishCommonLawDate(date: Date): Date;
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { wageIndex } from './wage-index';
|
|
2
|
-
import { EARLY_RETIREMENT_AGE, WAGE_INDEX_CUTOFF, MAX_RETIREMENT_AGE, MAX_DROP_OUT_YEARS, DROP_OUT_YEARS_DIVISOR, BEND_POINT_DIVISOR, FIRST_BEND_POINT_MULTIPLIER, SECOND_BEND_POINT_MULTIPLIER, PIA_PERCENTAGES, EARLY_RETIREMENT_REDUCTION, ELAPSED_YEARS_START_AGE, LOOKBACK_YEARS, CHILD_SURVIVOR_BENEFIT_PERCENTAGE, FAM_MAX_BASES } from './constants';
|
|
2
|
+
import { EARLY_RETIREMENT_AGE, WAGE_INDEX_CUTOFF, MAX_RETIREMENT_AGE, MAX_DROP_OUT_YEARS, DROP_OUT_YEARS_DIVISOR, BEND_POINT_DIVISOR, FIRST_BEND_POINT_MULTIPLIER, SECOND_BEND_POINT_MULTIPLIER, PIA_PERCENTAGES, EARLY_RETIREMENT_REDUCTION, ELAPSED_YEARS_START_AGE, LOOKBACK_YEARS, CHILD_SURVIVOR_BENEFIT_PERCENTAGE, FAM_MAX_BASES, DISABILITY_LAST_EARNINGS_YEARS_THRESHOLD } from './constants';
|
|
3
3
|
// Main entry point
|
|
4
4
|
export function calc(birthday, retirementDate, earnings) {
|
|
5
5
|
const retirementCalc = calcRetirement(birthday, retirementDate, earnings);
|
|
@@ -13,7 +13,11 @@ export function calcRetirement(birthday, retirementDate, earnings) {
|
|
|
13
13
|
const regularBenefit = calculateBenefitPipeline(earnings, context.yearAge60, getLookbackYears(context.totalYears));
|
|
14
14
|
const disabilityBenefit = calculateBenefitPipeline(earnings, context.yearAge60, getLookbackYearsDisability(context.totalYears));
|
|
15
15
|
const monthlyBenefit = retirementDateAdjustedPayment(context.dates, regularBenefit.colaAdjustedPIA);
|
|
16
|
-
|
|
16
|
+
// Apply disability 5-year rule: if disability date is more than 5 years after last earnings year, benefit is zero
|
|
17
|
+
let disabilityPIAFloored = 0;
|
|
18
|
+
if (context.retirementDate <= context.dates.fullRetirement && !isDisabilityDateBeyondFiveYearRule(retirementDate, earnings)) {
|
|
19
|
+
disabilityPIAFloored = Math.floor(disabilityBenefit.colaAdjustedPIA);
|
|
20
|
+
}
|
|
17
21
|
return {
|
|
18
22
|
AIME: regularBenefit.aime,
|
|
19
23
|
PIA: regularBenefit.pia,
|
|
@@ -79,7 +83,7 @@ function validateInputs(birthday, retirementDate, earnings) {
|
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
85
|
// Retirement date calculations
|
|
82
|
-
|
|
86
|
+
function calculateRetirementDates(birthday, retirementDate) {
|
|
83
87
|
const eclBirthDate = getEnglishCommonLawDate(birthday);
|
|
84
88
|
const fraMonths = getFullRetirementMonths(eclBirthDate);
|
|
85
89
|
const earliestRetirement = new Date(eclBirthDate.getFullYear() + EARLY_RETIREMENT_AGE, eclBirthDate.getMonth(), eclBirthDate.getDate());
|
|
@@ -96,7 +100,7 @@ export function calculateRetirementDates(birthday, retirementDate) {
|
|
|
96
100
|
};
|
|
97
101
|
}
|
|
98
102
|
// Benefit amount calculations
|
|
99
|
-
|
|
103
|
+
function retirementDateAdjustedPayment(dates, colaAdjustedPIA) {
|
|
100
104
|
const earlyRetireMonths = monthsDifference(dates.adjusted, dates.fullRetirement);
|
|
101
105
|
let adjustedBenefits = colaAdjustedPIA;
|
|
102
106
|
if (dates.retirementDate < dates.earliestRetirement) {
|
|
@@ -174,12 +178,12 @@ export function calculateAIME(earnings, lookbackYears, baseYear) {
|
|
|
174
178
|
return Math.floor(totalEarnings / (12 * lookbackYears));
|
|
175
179
|
}
|
|
176
180
|
// Lookback year calculations
|
|
177
|
-
|
|
181
|
+
function getLookbackYears(elapsedYears) {
|
|
178
182
|
const minComputationYears = 2;
|
|
179
183
|
const adjustedLookbackYears = Math.floor(elapsedYears) - 5;
|
|
180
184
|
return Math.max(minComputationYears, adjustedLookbackYears);
|
|
181
185
|
}
|
|
182
|
-
|
|
186
|
+
function getLookbackYearsDisability(elapsedYears) {
|
|
183
187
|
const minComputationYears = 2;
|
|
184
188
|
const dropOutYears = Math.min(Math.floor(elapsedYears / DROP_OUT_YEARS_DIVISOR), MAX_DROP_OUT_YEARS);
|
|
185
189
|
const adjustedLookbackYears = elapsedYears - dropOutYears;
|
|
@@ -227,7 +231,7 @@ function getDelayedRetirementRate(birthYear) {
|
|
|
227
231
|
return 2 / 3 / 100; // 2/3 of 1%
|
|
228
232
|
}
|
|
229
233
|
// Full retirement age calculation
|
|
230
|
-
|
|
234
|
+
function getFullRetirementMonths(commonLawBirthDate) {
|
|
231
235
|
const year = commonLawBirthDate.getFullYear();
|
|
232
236
|
if (year <= 1937) {
|
|
233
237
|
return 65 * 12;
|
|
@@ -249,12 +253,12 @@ export function getFullRetirementMonths(commonLawBirthDate) {
|
|
|
249
253
|
}
|
|
250
254
|
}
|
|
251
255
|
/** Compute Family-Max bend points for an eligibility year given AWI_{year-2}. */
|
|
252
|
-
|
|
256
|
+
function getFamilyMaxBendPoints(baseYear) {
|
|
253
257
|
const [f1, f2, f3] = FAM_MAX_BASES.map(b => Math.round((b * baseYear / BEND_POINT_DIVISOR)));
|
|
254
258
|
return [f1, f2, f3];
|
|
255
259
|
}
|
|
256
260
|
/** Apply the family-maximum formula to a PIA using already-computed bend points. */
|
|
257
|
-
|
|
261
|
+
function calculateFamilyMaximum(pia, [bp1, bp2, bp3]) {
|
|
258
262
|
let max = 0;
|
|
259
263
|
max += 1.50 * Math.min(pia, bp1);
|
|
260
264
|
if (pia > bp1)
|
|
@@ -266,6 +270,25 @@ export function calculateFamilyMaximum(pia, [bp1, bp2, bp3]) {
|
|
|
266
270
|
// SSA: round total down to next lower $0.10
|
|
267
271
|
return Math.floor(max * 10) / 10;
|
|
268
272
|
}
|
|
273
|
+
// Check if disability date violates the 5-year rule
|
|
274
|
+
function isDisabilityDateBeyondFiveYearRule(disabilityDate, earnings) {
|
|
275
|
+
if (!earnings || earnings.length === 0) {
|
|
276
|
+
return true; // No earnings means no disability benefit
|
|
277
|
+
}
|
|
278
|
+
// Find the last year with non-zero earnings
|
|
279
|
+
const earningsWithValue = earnings.filter(e => e.earnings > 0);
|
|
280
|
+
if (earningsWithValue.length === 0) {
|
|
281
|
+
return true; // No non-zero earnings means no disability benefit
|
|
282
|
+
}
|
|
283
|
+
const lastEarningsYear = Math.max(...earningsWithValue.map(e => e.year));
|
|
284
|
+
// Last day of the last earnings year
|
|
285
|
+
const lastDayOfLastEarningsYear = new Date(lastEarningsYear, 11, 31); // December 31st
|
|
286
|
+
// Add exactly 5 years to the last day of last earnings year
|
|
287
|
+
const fiveYearsLater = new Date(lastDayOfLastEarningsYear);
|
|
288
|
+
fiveYearsLater.setFullYear(fiveYearsLater.getFullYear() + DISABILITY_LAST_EARNINGS_YEARS_THRESHOLD);
|
|
289
|
+
// Disability date must be after the 5-year threshold
|
|
290
|
+
return disabilityDate > fiveYearsLater;
|
|
291
|
+
}
|
|
269
292
|
// Utility functions
|
|
270
293
|
export function getEnglishCommonLawDate(date) {
|
|
271
294
|
const eclDate = new Date(date);
|
package/lib/wage-index.js
CHANGED
|
@@ -442,22 +442,28 @@ export const wageIndex = [
|
|
|
442
442
|
"year": 2024,
|
|
443
443
|
"taxMax": 168600,
|
|
444
444
|
"cola": 2.5,
|
|
445
|
-
"awi":
|
|
445
|
+
"awi": 69846.57
|
|
446
446
|
},
|
|
447
447
|
{
|
|
448
448
|
"year": 2025,
|
|
449
449
|
"taxMax": 176100,
|
|
450
|
-
"cola": 2.
|
|
451
|
-
"awi":
|
|
450
|
+
"cola": 2.8,
|
|
451
|
+
"awi": 72644.64
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
"year": 2026,
|
|
455
|
+
"taxMax": 176100,
|
|
456
|
+
"cola": 2.5,
|
|
457
|
+
"awi": 75670.13
|
|
452
458
|
}
|
|
453
459
|
];
|
|
460
|
+
// 2026 60000.00 60000.00 0.00 0.00 176100 2.5 75670.13
|
|
454
461
|
export const wageIndexFuture = [
|
|
455
|
-
{ year:
|
|
456
|
-
{ year:
|
|
457
|
-
{ year:
|
|
458
|
-
{ year:
|
|
459
|
-
{ year:
|
|
460
|
-
{ year: 2029, awi: 84736.18 },
|
|
462
|
+
{ year: 2025, awi: 72644.64 },
|
|
463
|
+
{ year: 2026, awi: 75670.13 },
|
|
464
|
+
{ year: 2027, awi: 78726.01 },
|
|
465
|
+
{ year: 2028, awi: 81961.66 },
|
|
466
|
+
{ year: 2029, awi: 85192.51 },
|
|
461
467
|
{ year: 2030, awi: 88030.45 },
|
|
462
468
|
{ year: 2031, awi: 91479.46 },
|
|
463
469
|
{ year: 2032, awi: 95090.94 },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "social-security-calculator",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Calculate estimated Social Security Benefits",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@jest/globals": "^29.7.0",
|
|
42
42
|
"@types/jest": "^29.5.8",
|
|
43
|
+
"@types/minimist": "^1.2.5",
|
|
43
44
|
"@types/xml2js": "^0.4.11",
|
|
44
45
|
"compound-calc": "^4.0.3",
|
|
45
46
|
"csvtojson": "^2.0.10",
|