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.
@@ -1,5 +1,5 @@
1
1
  export declare const EARLY_RETIREMENT_AGE = 62;
2
- export declare const WAGE_INDEX_CUTOFF = 2023;
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 = 2023;
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, RetirementDates, Earnings } from './model';
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
- const disabilityPIAFloored = context.retirementDate > context.dates.fullRetirement ? 0 : Math.floor(disabilityBenefit.colaAdjustedPIA);
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
- export function calculateRetirementDates(birthday, retirementDate) {
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
- export function retirementDateAdjustedPayment(dates, colaAdjustedPIA) {
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
- export function getLookbackYears(elapsedYears) {
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
- export function getLookbackYearsDisability(elapsedYears) {
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
- export function getFullRetirementMonths(commonLawBirthDate) {
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
- export function getFamilyMaxBendPoints(baseYear) {
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
- export function calculateFamilyMaximum(pia, [bp1, bp2, bp3]) {
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": 69472.44
445
+ "awi": 69846.57
446
446
  },
447
447
  {
448
448
  "year": 2025,
449
449
  "taxMax": 176100,
450
- "cola": 2.7,
451
- "awi": 72255.52
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: 2024, awi: 69472.44 },
456
- { year: 2025, awi: 72255.52 },
457
- { year: 2026, awi: 75264.81 },
458
- { year: 2027, awi: 78304.32 },
459
- { year: 2028, awi: 81522.64 },
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.0.0",
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",