Simulator update for retirement
This commit is contained in:
parent
2f2a6860f4
commit
38088ac38b
@ -216,8 +216,9 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
|
||||
projected_end_date,
|
||||
college_enrollment_status,
|
||||
currently_working,
|
||||
// The new field:
|
||||
career_goals,
|
||||
retirement_start_date,
|
||||
desired_retirement_income_monthly,
|
||||
|
||||
// planned fields
|
||||
planned_monthly_expenses,
|
||||
@ -248,7 +249,9 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
|
||||
projected_end_date,
|
||||
college_enrollment_status,
|
||||
currently_working,
|
||||
career_goals, -- ADD THIS
|
||||
career_goals,
|
||||
retirement_start_date,
|
||||
desired_retirement_income_monthly,
|
||||
planned_monthly_expenses,
|
||||
planned_monthly_debt_payments,
|
||||
planned_monthly_retirement_contribution,
|
||||
@ -257,14 +260,16 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
|
||||
planned_surplus_retirement_pct,
|
||||
planned_additional_income
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
status = VALUES(status),
|
||||
start_date = VALUES(start_date),
|
||||
projected_end_date = VALUES(projected_end_date),
|
||||
college_enrollment_status = VALUES(college_enrollment_status),
|
||||
currently_working = VALUES(currently_working),
|
||||
career_goals = VALUES(career_goals), -- ADD THIS
|
||||
career_goals = VALUES(career_goals),
|
||||
retirement_start_date = VALUES(retirement_start_date),
|
||||
desired_retirement_income_monthly = VALUES(desired_retirement_income_monthly),
|
||||
planned_monthly_expenses = VALUES(planned_monthly_expenses),
|
||||
planned_monthly_debt_payments = VALUES(planned_monthly_debt_payments),
|
||||
planned_monthly_retirement_contribution = VALUES(planned_monthly_retirement_contribution),
|
||||
@ -285,7 +290,9 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
|
||||
projected_end_date || null,
|
||||
college_enrollment_status || null,
|
||||
currently_working || null,
|
||||
career_goals || null, // pass career_goals here
|
||||
career_goals || null,
|
||||
retirement_start_date || null,
|
||||
desired_retirement_income_monthly || null,
|
||||
planned_monthly_expenses ?? null,
|
||||
planned_monthly_debt_payments ?? null,
|
||||
planned_monthly_retirement_contribution ?? null,
|
||||
|
@ -286,6 +286,13 @@ export default function ScenarioContainer({
|
||||
surplusEmergencyAllocation: scenarioOverrides.surplusEmergencyAllocation,
|
||||
surplusRetirementAllocation: scenarioOverrides.surplusRetirementAllocation,
|
||||
additionalIncome: scenarioOverrides.additionalIncome,
|
||||
retirement_start_date: localScenario.retirement_start_date
|
||||
|| localScenario.projected_end_date
|
||||
|| null,
|
||||
desired_retirement_income_monthly: parseScenarioOverride(
|
||||
localScenario.desired_retirement_income_monthly,
|
||||
0
|
||||
),
|
||||
|
||||
studentLoanAmount: collegeData.studentLoanAmount,
|
||||
interestRate: collegeData.interestRate,
|
||||
@ -931,6 +938,8 @@ export default function ScenarioContainer({
|
||||
show={showScenarioModal}
|
||||
onClose={() => setShowScenarioModal(false)}
|
||||
scenario={localScenario}
|
||||
collegeProfile={collegeProfile}
|
||||
financialProfile={financialProfile}
|
||||
onSave={handleScenarioSave}
|
||||
/>
|
||||
|
||||
|
@ -13,7 +13,8 @@ export default function ScenarioEditModal({
|
||||
show,
|
||||
onClose,
|
||||
scenario,
|
||||
collegeProfile
|
||||
collegeProfile,
|
||||
financialProfile
|
||||
}) {
|
||||
/*********************************************************
|
||||
* 1) CIP / IPEDS data states
|
||||
@ -128,53 +129,58 @@ export default function ScenarioEditModal({
|
||||
|
||||
const s = scenario || {};
|
||||
const c = collegeProfile || {};
|
||||
const safe = v =>
|
||||
v === null || v === undefined ? '' : v;
|
||||
|
||||
setFormData({
|
||||
// scenario portion
|
||||
scenario_title: s.scenario_title || '',
|
||||
career_name: s.career_name || '',
|
||||
status: s.status || 'planned',
|
||||
start_date: s.start_date || '',
|
||||
projected_end_date: s.projected_end_date || '',
|
||||
college_enrollment_status: s.college_enrollment_status || 'not_enrolled',
|
||||
currently_working: s.currently_working || 'no',
|
||||
scenario_title : safe(s.scenario_title),
|
||||
career_name : safe(s.career_name),
|
||||
status : safe(s.status || 'planned'),
|
||||
start_date : safe(s.start_date),
|
||||
projected_end_date : safe(s.projected_end_date),
|
||||
retirement_start_date: safe(
|
||||
(s.retirement_start_date || s.projected_end_date || '')
|
||||
.toString() // handles Date objects
|
||||
.substring(0, 10) // keep YYYY-MM-DD
|
||||
),
|
||||
desired_retirement_income_monthly :
|
||||
safe(s.desired_retirement_income_monthly
|
||||
?? financialProfile?.monthly_expenses),
|
||||
|
||||
planned_monthly_expenses: s.planned_monthly_expenses ?? null,
|
||||
planned_monthly_debt_payments: s.planned_monthly_debt_payments ?? null,
|
||||
planned_monthly_retirement_contribution:
|
||||
s.planned_monthly_retirement_contribution ?? null,
|
||||
planned_monthly_emergency_contribution:
|
||||
s.planned_monthly_emergency_contribution ?? null,
|
||||
planned_surplus_emergency_pct: s.planned_surplus_emergency_pct ?? null,
|
||||
planned_surplus_retirement_pct: s.planned_surplus_retirement_pct ?? null,
|
||||
planned_additional_income: s.planned_additional_income ?? null,
|
||||
planned_monthly_expenses : safe(s.planned_monthly_expenses),
|
||||
planned_monthly_debt_payments : safe(s.planned_monthly_debt_payments),
|
||||
planned_monthly_retirement_contribution: safe(s.planned_monthly_retirement_contribution),
|
||||
planned_monthly_emergency_contribution : safe(s.planned_monthly_emergency_contribution),
|
||||
planned_surplus_emergency_pct : safe(s.planned_surplus_emergency_pct),
|
||||
planned_surplus_retirement_pct : safe(s.planned_surplus_retirement_pct),
|
||||
planned_additional_income : safe(s.planned_additional_income),
|
||||
|
||||
// college portion
|
||||
college_profile_id: c.id || null,
|
||||
selected_school: c.selected_school || '',
|
||||
selected_program: c.selected_program || '',
|
||||
program_type: c.program_type || '',
|
||||
academic_calendar: c.academic_calendar || 'monthly',
|
||||
college_profile_id: safe(c.id || null),
|
||||
selected_school: safe(c.selected_school || ''),
|
||||
selected_program: safe(c.selected_program || ''),
|
||||
program_type: safe(c.program_type || ''),
|
||||
academic_calendar: safe(c.academic_calendar || 'monthly'),
|
||||
|
||||
is_in_state: !!c.is_in_state,
|
||||
is_in_district: !!c.is_in_district,
|
||||
is_online: !!c.is_online,
|
||||
college_enrollment_status_db: c.college_enrollment_status || 'not_enrolled',
|
||||
is_in_state: safe(!!c.is_in_state),
|
||||
is_in_district: safe(!!c.is_in_district),
|
||||
is_online: safe(!!c.is_online),
|
||||
college_enrollment_status_db: safe(c.college_enrollment_status || 'not_enrolled'),
|
||||
|
||||
annual_financial_aid: c.annual_financial_aid ?? '',
|
||||
existing_college_debt: c.existing_college_debt ?? '',
|
||||
tuition_paid: c.tuition_paid ?? 0,
|
||||
loan_deferral_until_graduation: !!c.loan_deferral_until_graduation,
|
||||
loan_term: c.loan_term ?? 10,
|
||||
interest_rate: c.interest_rate ?? 5,
|
||||
extra_payment: c.extra_payment ?? 0,
|
||||
credit_hours_per_year: c.credit_hours_per_year ?? '',
|
||||
hours_completed: c.hours_completed ?? '',
|
||||
program_length: c.program_length ?? '',
|
||||
credit_hours_required: c.credit_hours_required ?? '',
|
||||
enrollment_date: c.enrollment_date ? c.enrollment_date.substring(0, 10): '',
|
||||
expected_graduation: c.expected_graduation ? c.expected_graduation.substring(0, 10): '',
|
||||
expected_salary: c.expected_salary ?? ''
|
||||
annual_financial_aid : safe(c.annual_financial_aid),
|
||||
existing_college_debt : safe(c.existing_college_debt),
|
||||
tuition_paid : safe(c.tuition_paid),
|
||||
loan_term : safe(c.loan_term ?? 10),
|
||||
interest_rate : safe(c.interest_rate ?? 5),
|
||||
extra_payment : safe(c.extra_payment),
|
||||
credit_hours_per_year: safe(c.credit_hours_per_year ?? ''),
|
||||
hours_completed: safe(c.hours_completed ?? ''),
|
||||
program_length: safe(c.program_length ?? ''),
|
||||
credit_hours_required: safe(c.credit_hours_required ?? ''),
|
||||
enrollment_date: safe(c.enrollment_date ? c.enrollment_date.substring(0, 10): ''),
|
||||
expected_graduation: safe(c.expected_graduation ? c.expected_graduation.substring(0, 10): ''),
|
||||
expected_salary: safe(c.expected_salary ?? '')
|
||||
});
|
||||
|
||||
// Manual / auto tuition
|
||||
@ -479,6 +485,8 @@ async function handleSave() {
|
||||
status : s(formData.status),
|
||||
start_date : s(formData.start_date),
|
||||
projected_end_date : s(formData.projected_end_date),
|
||||
retirement_start_date : s(formData.retirement_start_date),
|
||||
desired_retirement_income_monthly : n(formData.desired_retirement_income_monthly),
|
||||
|
||||
planned_monthly_expenses : n(formData.planned_monthly_expenses),
|
||||
planned_monthly_debt_payments : n(formData.planned_monthly_debt_payments),
|
||||
@ -650,6 +658,41 @@ async function handleSave() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Retirement date */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Planned Retirement Date
|
||||
</label>
|
||||
<input
|
||||
type="date"
|
||||
name="retirement_start_date"
|
||||
value={formData.retirement_start_date}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Desired retirement income (monthly) */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Desired Retirement Income (monthly $)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="desired_retirement_income_monthly"
|
||||
value={formData.desired_retirement_income_monthly}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
{formData.desired_retirement_income_monthly && (
|
||||
<p className="text-xs text-gray-500">
|
||||
≈ $
|
||||
{(formData.desired_retirement_income_monthly*12)
|
||||
.toLocaleString()} per year
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* College Enrollment Status */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
@ -694,6 +737,7 @@ async function handleSave() {
|
||||
type="number"
|
||||
name="planned_monthly_expenses"
|
||||
value={formData.planned_monthly_expenses}
|
||||
placeholder={financialProfile?.monthly_expenses ?? ''}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
@ -706,6 +750,7 @@ async function handleSave() {
|
||||
type="number"
|
||||
name="planned_monthly_debt_payments"
|
||||
value={formData.planned_monthly_debt_payments}
|
||||
placeholder={financialProfile?.monthly_debt_payments ?? ''}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
@ -718,6 +763,7 @@ async function handleSave() {
|
||||
type="number"
|
||||
name="planned_monthly_retirement_contribution"
|
||||
value={formData.planned_monthly_retirement_contribution}
|
||||
placeholder={financialProfile?.retirement_contribution ?? ''}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
@ -730,6 +776,7 @@ async function handleSave() {
|
||||
type="number"
|
||||
name="planned_monthly_emergency_contribution"
|
||||
value={formData.planned_monthly_emergency_contribution}
|
||||
placeholder={financialProfile?.emergency_contribution ?? ''}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
@ -742,6 +789,7 @@ async function handleSave() {
|
||||
type="number"
|
||||
name="planned_surplus_emergency_pct"
|
||||
value={formData.planned_surplus_emergency_pct}
|
||||
placeholder={financialProfile?.extra_cash_emergency_pct ?? ''}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
@ -754,6 +802,7 @@ async function handleSave() {
|
||||
type="number"
|
||||
name="planned_surplus_retirement_pct"
|
||||
value={formData.planned_surplus_retirement_pct}
|
||||
placeholder={financialProfile?.extra_cash_retirement_pct ?? ''}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
@ -766,6 +815,7 @@ async function handleSave() {
|
||||
type="number"
|
||||
name="planned_additional_income"
|
||||
value={formData.planned_additional_income}
|
||||
placeholder={financialProfile?.additional_income ?? ''}
|
||||
onChange={handleFormChange}
|
||||
className="border border-gray-300 rounded p-2 w-full"
|
||||
/>
|
||||
|
@ -96,6 +96,7 @@ function calculateLoanPayment(principal, annualRate, years) {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************
|
||||
* MAIN SIMULATION FUNCTION
|
||||
***************************************************/
|
||||
@ -112,8 +113,10 @@ const {
|
||||
currentSalary: _currentSalary = 0,
|
||||
monthlyExpenses: _monthlyExpenses = 0,
|
||||
monthlyDebtPayments: _monthlyDebtPayments = 0,
|
||||
partTimeIncome: _partTimeIncome = 0,
|
||||
additionalIncome: _additionalIncome = 0,
|
||||
extraPayment: _extraPayment = 0,
|
||||
desired_retirement_income_monthly : _retSpend = 0,
|
||||
retirement_start_date : _retStart,
|
||||
|
||||
// Student-loan config ----------------------------------------
|
||||
studentLoanAmount: _studentLoanAmount = 0,
|
||||
@ -163,7 +166,7 @@ const {
|
||||
const currentSalary = num(_currentSalary);
|
||||
const monthlyExpenses = num(_monthlyExpenses);
|
||||
const monthlyDebtPayments = num(_monthlyDebtPayments);
|
||||
const partTimeIncome = num(_partTimeIncome);
|
||||
const additionalIncome = num(_additionalIncome);
|
||||
const extraPayment = num(_extraPayment);
|
||||
|
||||
const studentLoanAmount = num(_studentLoanAmount);
|
||||
@ -195,6 +198,17 @@ const randomRangeMax = num(_randomRangeMax);
|
||||
* “Safe” suffix distinguishes the sanitized values.
|
||||
* -------------------------------------------------*/
|
||||
|
||||
/***************************************************
|
||||
* 2) CLAMP THE SCENARIO START TO MONTH-BEGIN
|
||||
***************************************************/
|
||||
|
||||
const scenarioStartClamped = moment(startDate || new Date()).startOf('month');
|
||||
|
||||
const retirementSpendMonthly = num(_retSpend);
|
||||
const retirementStartISO =
|
||||
_retStart ? moment(_retStart).startOf('month')
|
||||
: scenarioStartClamped.clone().add(simulationYears,'years'); // fallback
|
||||
|
||||
/***************************************************
|
||||
* HELPER: Retirement Interest Rate
|
||||
***************************************************/
|
||||
@ -219,10 +233,27 @@ function getMonthlyInterestRate() {
|
||||
}
|
||||
|
||||
/***************************************************
|
||||
* 2) CLAMP THE SCENARIO START TO MONTH-BEGIN
|
||||
* HELPER: Retirement draw
|
||||
***************************************************/
|
||||
function simulateDrawdown(opts){
|
||||
const {
|
||||
startingBalance, monthlySpend,
|
||||
interestStrategy, flatAnnualRate,
|
||||
randomRangeMin, randomRangeMax,
|
||||
monthlyReturnSamples
|
||||
} = opts;
|
||||
let bal = startingBalance, m = 0;
|
||||
while (bal > 0 && m < 1200){
|
||||
const r = getMonthlyInterestRate(
|
||||
interestStrategy, flatAnnualRate,
|
||||
randomRangeMin, randomRangeMax,
|
||||
monthlyReturnSamples);
|
||||
bal = bal*(1+r) - monthlySpend;
|
||||
m++;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
const scenarioStartClamped = moment(startDate || new Date()).startOf('month');
|
||||
|
||||
/***************************************************
|
||||
* 3) DETERMINE PROGRAM LENGTH (credit hours)
|
||||
@ -327,6 +358,10 @@ function getMonthlyInterestRate() {
|
||||
for (let monthIndex = 0; monthIndex < maxMonths; monthIndex++) {
|
||||
const currentSimDate = scenarioStartClamped.clone().add(monthIndex, 'months');
|
||||
|
||||
const reachedRetirement =
|
||||
retirementSpendMonthly > 0 &&
|
||||
currentSimDate.isSameOrAfter(retirementStartISO);
|
||||
|
||||
// figure out if we are in the college window
|
||||
let stillInCollege = false;
|
||||
if (inCollege && enrollmentDateObj && graduationDateObj) {
|
||||
@ -359,15 +394,18 @@ for (let monthIndex = 0; monthIndex < maxMonths; monthIndex++) {
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* 7.2 BASE MONTHLY INCOME
|
||||
* 7.2 BASE MONTHLY INCOME (salary -or- retirement draw)
|
||||
************************************************/
|
||||
let baseMonthlyIncome = 0;
|
||||
if (!stillInCollege) {
|
||||
// user out of college => expected or current
|
||||
|
||||
if (reachedRetirement) {
|
||||
const withdrawal = Math.min(retirementSpendMonthly, currentRetirementSavings);
|
||||
currentRetirementSavings -= withdrawal;
|
||||
baseMonthlyIncome += withdrawal;
|
||||
} else if (!stillInCollege) {
|
||||
baseMonthlyIncome = (expectedSalary || currentSalary) / 12;
|
||||
} else {
|
||||
// in college => might have partTimeIncome + current
|
||||
baseMonthlyIncome = (currentSalary / 12) + (partTimeIncome / 12);
|
||||
baseMonthlyIncome = (currentSalary / 12) + (additionalIncome / 12);
|
||||
}
|
||||
|
||||
/************************************************
|
||||
@ -430,17 +468,25 @@ baseMonthlyIncome += salaryAdjustThisMonth; // adjust gross BEFORE tax
|
||||
stateYTDtax += monthlyStateTax;
|
||||
|
||||
/************************************************
|
||||
* 7.5 LOAN + EXPENSES
|
||||
* 7.5 WITHDRAW FROM RETIREMENT
|
||||
************************************************/
|
||||
// Check if we're now exiting college & deferral ended => recalc monthlyLoanPayment
|
||||
const nowExitingCollege = wasInDeferral && !stillInCollege;
|
||||
if (nowExitingCollege) {
|
||||
// recalc monthlyLoanPayment with the current loanBalance
|
||||
monthlyLoanPayment = calculateLoanPayment(loanBalance, interestRate, loanTerm);
|
||||
|
||||
if (reachedRetirement && retirementSpendMonthly > 0) {
|
||||
const withdrawal = Math.min(retirementSpendMonthly, currentRetirementSavings);
|
||||
currentRetirementSavings -= withdrawal;
|
||||
// Treat the withdrawal like (taxable) income so the cash-flow works out
|
||||
baseMonthlyIncome += withdrawal;
|
||||
}
|
||||
|
||||
// From here on we’ll use `livingCost` instead of `monthlyExpenses`
|
||||
const livingCost = reachedRetirement ? retirementSpendMonthly : monthlyExpenses;
|
||||
|
||||
/************************************************
|
||||
* 7.6 LOAN + EXPENSES
|
||||
************************************************/
|
||||
// sum up all monthly expenses
|
||||
let totalMonthlyExpenses = monthlyExpenses + monthlyDebtPayments + tuitionCostThisMonth + extraImpactsThisMonth;
|
||||
let totalMonthlyExpenses =
|
||||
livingCost + monthlyDebtPayments + tuitionCostThisMonth + extraImpactsThisMonth;
|
||||
|
||||
if (stillInCollege && loanDeferralUntilGraduation) {
|
||||
// accumulate interest only
|
||||
@ -460,13 +506,15 @@ baseMonthlyIncome += salaryAdjustThisMonth; // adjust gross BEFORE tax
|
||||
let leftover = netMonthlyIncome - totalMonthlyExpenses;
|
||||
|
||||
// baseline contributions
|
||||
const baselineContributions = monthlyRetirementContribution + monthlyEmergencyContribution;
|
||||
const monthlyRetContrib = reachedRetirement ? 0 : monthlyRetirementContribution;
|
||||
const monthlyEmergContrib = reachedRetirement ? 0 : monthlyEmergencyContribution;
|
||||
const baselineContributions = monthlyRetContrib + monthlyEmergContrib;
|
||||
let effectiveRetirementContribution = 0;
|
||||
let effectiveEmergencyContribution = 0;
|
||||
|
||||
if (leftover >= baselineContributions) {
|
||||
effectiveRetirementContribution = monthlyRetirementContribution;
|
||||
effectiveEmergencyContribution = monthlyEmergencyContribution;
|
||||
effectiveRetirementContribution = monthlyRetContrib;
|
||||
effectiveEmergencyContribution = monthlyEmergContrib;
|
||||
leftover -= baselineContributions;
|
||||
}
|
||||
|
||||
@ -543,9 +591,31 @@ baseMonthlyIncome += salaryAdjustThisMonth; // adjust gross BEFORE tax
|
||||
loanPaidOffMonth = scenarioStartClamped.clone().add(maxMonths, 'months').format('YYYY-MM');
|
||||
}
|
||||
|
||||
/* ---- 8) RETIREMENT DRAWDOWN ---------------------------------- */
|
||||
|
||||
const monthlySpend = retirementSpendMonthly || 0;
|
||||
|
||||
const monthsCovered = monthlySpend > 0
|
||||
? simulateDrawdown({
|
||||
startingBalance : currentRetirementSavings,
|
||||
monthlySpend,
|
||||
interestStrategy,
|
||||
flatAnnualRate,
|
||||
randomRangeMin,
|
||||
randomRangeMax,
|
||||
monthlyReturnSamples
|
||||
})
|
||||
: 0;
|
||||
|
||||
const yearsCovered = Math.round(monthsCovered / 12);
|
||||
|
||||
/* ---- 9) RETURN ------------------------------------------------ */
|
||||
|
||||
return {
|
||||
projectionData,
|
||||
loanPaidOffMonth,
|
||||
yearsCovered, // <-- add these two
|
||||
monthsCovered, // (or just yearsCovered if that’s all you need)
|
||||
finalEmergencySavings: +currentEmergencySavings.toFixed(2),
|
||||
finalRetirementSavings: +currentRetirementSavings.toFixed(2),
|
||||
finalLoanBalance: +loanBalance.toFixed(2),
|
||||
|
Loading…
Reference in New Issue
Block a user