From e3b779ea915cf4f79b1e0e9c43f3b44e5350c163 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 7 Jul 2025 17:31:00 +0000 Subject: [PATCH] Fixed simulation when salary = 0, stale college profile/grad date existed --- src/utils/FinancialProjectionService.js | 46 +++++++++++++++++++------ 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/utils/FinancialProjectionService.js b/src/utils/FinancialProjectionService.js index 29a99b1..9b896a3 100644 --- a/src/utils/FinancialProjectionService.js +++ b/src/utils/FinancialProjectionService.js @@ -126,6 +126,8 @@ const { // College ---------------------------------------------------- inCollege = false, + collegeEnrollmentStatus = 'none', + programType, hoursCompleted: _hoursCompleted = 0, creditHoursPerYear: _creditHoursPerYear, @@ -172,6 +174,9 @@ const extraPayment = num(_extraPayment); const studentLoanAmount = num(_studentLoanAmount); const interestRate = num(_interestRate); const loanTerm = num(_loanTerm); +const isProgrammeActive = + ['enrolled', 'graduated'].includes(collegeEnrollmentStatus); + const hoursCompleted = num(_hoursCompleted); const creditHoursPerYear = num(_creditHoursPerYear); @@ -360,6 +365,11 @@ function simulateDrawdown(opts){ for (let monthIndex = 0; monthIndex < maxMonths; monthIndex++) { const currentSimDate = scenarioStartClamped.clone().add(monthIndex, 'months'); + const hasGraduated = + isProgrammeActive && + gradDate && + currentSimDate.isSameOrAfter(moment(gradDate).startOf('month')); + if (!reachedRetirement && currentSimDate.isSameOrAfter(retirementStartISO)) { reachedRetirement = true; firstRetirementBalance = currentRetirementSavings; // capture once @@ -409,11 +419,21 @@ for (let monthIndex = 0; monthIndex < maxMonths; monthIndex++) { const withdrawal = Math.min(retirementSpendMonthly, currentRetirementSavings); currentRetirementSavings -= withdrawal; baseMonthlyIncome += withdrawal; - } else if (!stillInCollege) { - baseMonthlyIncome = (expectedSalary || currentSalary) / 12; - } else { - baseMonthlyIncome = (currentSalary / 12) + (additionalIncome / 12); - } + } else if (!stillInCollege) { + // Use expectedSalary **only** once the user has graduated + const monthlyFromJob = ( + hasGraduated && expectedSalary > 0 + ? expectedSalary // kicks in the month *after* gradDate + : currentSalary + ) / 12; + + baseMonthlyIncome = + monthlyFromJob + (additionalIncome / 12); + +} else { // stillInCollege branch + baseMonthlyIncome = + (currentSalary / 12) + (additionalIncome / 12); +} /************************************************ * 7.3 MILESTONE IMPACTS – safe number handling @@ -512,9 +532,13 @@ baseMonthlyIncome += salaryAdjustThisMonth; // adjust gross BEFORE tax let leftover = netMonthlyIncome - totalMonthlyExpenses; + const canSaveThisMonth = leftover > 0; + // baseline contributions - const monthlyRetContrib = isRetiredThisMonth ? 0 : monthlyRetirementContribution; - const monthlyEmergContrib = isRetiredThisMonth ? 0 : monthlyEmergencyContribution; + const monthlyRetContrib = + canSaveThisMonth && !isRetiredThisMonth ? monthlyRetirementContribution : 0; + const monthlyEmergContrib = + canSaveThisMonth && !isRetiredThisMonth ? monthlyEmergencyContribution : 0; const baselineContributions = monthlyRetContrib + monthlyEmergContrib; let effectiveRetirementContribution = 0; let effectiveEmergencyContribution = 0; @@ -536,7 +560,7 @@ baseMonthlyIncome += salaryAdjustThisMonth; // adjust gross BEFORE tax } // Surplus => leftover - if (leftover > 0) { + if (canSaveThisMonth && leftover > 0) { const totalPct = surplusEmergencyAllocation + surplusRetirementAllocation; const emergPortion = leftover * (surplusEmergencyAllocation / totalPct); const retPortion = leftover * (surplusRetirementAllocation / totalPct); @@ -546,9 +570,9 @@ baseMonthlyIncome += salaryAdjustThisMonth; // adjust gross BEFORE tax } const monthlyReturnRate = getMonthlyInterestRate(); - if (monthlyReturnRate !== 0) { - currentRetirementSavings *= (1 + monthlyReturnRate); - } + if (monthlyReturnRate !== 0 && leftover > 0) { + currentRetirementSavings *= (1 + monthlyReturnRate); +} const netSavings = netMonthlyIncome - actualExpensesPaid; // (UPDATED) add inCollege, stillInCollege, loanDeferralUntilGraduation to the result