Home Loan Refinancing Budget Planner
1. Loan Details
2. Refinance Analysis
3. Budget & Amortization
Step 1: Enter Current Loan & Refinance Offer
Prepare Refinance Analysis
Previous
Next
Step 2: Refinance Cost & Savings Analysis
Complete loan details in Tab 1 and click "Prepare Refinance Analysis" to see results here.
Previous
Next
Step 3: Budget Impact, Amortization & Download
Download Full Refinance Analysis as PDF
Previous
Next
Refinancing results in lifetime savings, primarily due to factors other than monthly payment reduction (e.g., shorter term, significantly lower total interest despite higher payments).
`;
}
if (inputs.refiNewLoanAmountUser > 0 && inputs.refiNewLoanAmountUser !== currentLoan.remainingBalance) {
let cashOutOrIn = inputs.refiNewLoanAmountUser - currentLoan.remainingBalance;
analysisHTML += `
Note: New loan amount ($${inputs.effectiveNewLoanAmount.toFixed(2)}) differs from current remaining balance. This analysis primarily compares the cost of the old debt vs. an equivalent amount of new debt. ${cashOutOrIn > 0 ? `You are taking out an additional $${cashOutOrIn.toFixed(2)}.` : `You are bringing $${Math.abs(cashOutOrIn).toFixed(2)} to closing.` }
`;
}
container.innerHTML = analysisHTML;
// Also update amortization previews on Tab 3
displayAmortizationTable(refiReportData.amortization.current, 'currentLoanAmortizationPreview', "Current Loan Amortization (Remaining)");
displayAmortizationTable(refiReportData.amortization.new, 'newLoanAmortizationPreview', "New Refinanced Loan Amortization");
}
function calculateBudgetImpact() {
const gmi = getRefiNum('budgetGrossMonthlyIncome');
const otherDebts = getRefiNum('budgetOtherMonthlyDebts');
const outputDiv = document.getElementById('budgetImpactOutput');
if (gmi <= 0 || !refiReportData.currentLoanCalcs.monthlyPI || !refiReportData.newLoanCalcs.monthlyPI) {
outputDiv.innerHTML = "
Please enter Gross Monthly Income and ensure refinance analysis is complete to see budget impact.
";
return;
}
const currentHousingPITI = refiReportData.currentLoanCalcs.monthlyPI; // Simplified: Using P&I as main housing cost for this DTI.
const newHousingPITI = refiReportData.newLoanCalcs.monthlyPI; // User should add taxes/insurance if comparing full PITI
const currentFrontDTI = (currentHousingPITI / gmi) * 100;
const currentBackDTI = ((currentHousingPITI + otherDebts) / gmi) * 100;
const newFrontDTI = (newHousingPITI / gmi) * 100;
const newBackDTI = ((newHousingPITI + otherDebts) / gmi) * 100;
const cashFlowChange = refiReportData.comparison.monthlyPaymentDifference || 0; // P&I difference
refiReportData.budgetImpact = {
gmi, otherDebts,
currentFrontDTI, currentBackDTI,
newFrontDTI, newBackDTI,
cashFlowChange
};
outputDiv.innerHTML = `
Budget Impact Analysis:
Change in Monthly Cash Flow (P&I): $${Math.abs(cashFlowChange).toFixed(2)} ${cashFlowChange >= 0 ? 'Improved' : 'Reduced'}
Current Front-End DTI (Housing): ${currentFrontDTI.toFixed(1)}% | New Front-End DTI: ${newFrontDTI.toFixed(1)}%
Current Back-End DTI (Total): ${currentBackDTI.toFixed(1)}% | New Back-End DTI: ${newBackDTI.toFixed(1)}%
Note: DTI calculation here uses P&I for housing. For full PITI, add taxes & insurance to housing costs. `;
}
function displayAmortizationTable(schedule, containerId, title) {
const container = document.getElementById(containerId);
if (!schedule || schedule.length === 0) {
container.innerHTML = `
${title}: No data to display.
`;
return;
}
// Show first 5, ellipsis if more than 10, then last 5
let displaySchedule = schedule;
if (schedule.length > 12) {
displaySchedule = [
...schedule.slice(0, 5),
{ month: '...', beginningBalance: '...', payment: '...', interest: '...', principal: '...', lumpSum: '...', endingBalance: '...' },
...schedule.slice(-5)
];
}
let tableHTML = `
Mth Start Bal. Payment Interest Principal Lump Sum End Bal.
`;
displaySchedule.forEach(row => {
tableHTML += `
${row.month}
${typeof row.beginningBalance === 'number' ? row.beginningBalance.toFixed(2) : row.beginningBalance}
${typeof row.payment === 'number' ? row.payment.toFixed(2) : row.payment}
${typeof row.interest === 'number' ? row.interest.toFixed(2) : row.interest}
${typeof row.principal === 'number' ? row.principal.toFixed(2) : row.principal}
${typeof row.lumpSum === 'number' ? row.lumpSum.toFixed(2) : row.lumpSum}
${typeof row.endingBalance === 'number' ? row.endingBalance.toFixed(2) : row.endingBalance}
`;
});
tableHTML += `
Showing summarized schedule. Full schedule in PDF. `;
container.innerHTML = `
${title} ${tableHTML}`;
}
function downloadRefiPdf() {
if (!refiReportData.comparison || refiReportData.comparison.monthlyPaymentDifference === undefined) {
alert("Please prepare and view the refinance analysis first."); return;
}
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') {
alert('PDF generation library (jsPDF) is not loaded.'); return;
}
const jsPDFConstructor = window.jspdf.jsPDF;
const doc = new jsPDFConstructor();
if (typeof doc.autoTable !== 'function') {
alert('jsPDF AutoTable plugin not loaded.'); return;
}
const { inputs, currentLoanCalcs, newLoanCalcs, comparison, budgetImpact, amortization } = refiReportData;
const primaryColor = '#007bff', textColor = '#212529', tableHeaderColor = '#e9ecef';
let yPos = 22;
const pageHeight = doc.internal.pageSize.height; const margin = 20;
function checkYPdf(increment = 10) { if (yPos + increment > pageHeight - margin) { doc.addPage(); yPos = margin; } }
doc.setFontSize(18); doc.setTextColor(primaryColor);
doc.text("Home Loan Refinance Analysis", 14, yPos); yPos += 8;
doc.setFontSize(10); doc.setTextColor(textColor);
doc.text(`Report Date: ${new Date().toLocaleDateString()} | Analysis As Of: ${inputs.refiAsOfMonth}/${inputs.refiAsOfYear}`, 14, yPos); yPos += 10;
// Current Loan Summary
checkYPdf(30);
doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Current Loan Details", 14, yPos); yPos += 6;
let currentLoanBody = [
['Original Amount:', `$${inputs.currentOrigLoanAmount.toFixed(2)}`],
['Original Rate:', `${inputs.currentOrigInterestRate.toFixed(3)}%`],
['Original Term:', `${inputs.currentOrigLoanTerm} years`],
['Start Date:', `${inputs.currentLoanStartMonth}/${inputs.currentLoanStartYear}`],
[{content:'Status as of ' + inputs.refiAsOfMonth+'/'+inputs.refiAsOfYear, styles:{fontStyle:'bold', halign:'left', cellWidth:'wrap'}}],
[' Remaining Balance:', `$${currentLoanCalcs.remainingBalance.toFixed(2)}`],
[' Monthly P&I:', `$${currentLoanCalcs.monthlyPI.toFixed(2)}`],
[' Remaining Term:', `${Math.floor(currentLoanCalcs.remainingTermMonths/12)}y ${currentLoanCalcs.remainingTermMonths%12}m`]
];
doc.autoTable({startY: yPos, body: currentLoanBody, theme:'plain', styles:{fontSize:9, cellPadding:1.2}, columnStyles:{0:{fontStyle:'bold'}}});
yPos = doc.lastAutoTable.finalY + 7;
// Refinance Offer Summary
checkYPdf(30);
doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Refinance Offer Details", 14, yPos); yPos += 6;
let refiOfferBody = [
['New Loan Amount:', `$${inputs.effectiveNewLoanAmount.toFixed(2)}`],
['New Rate:', `${inputs.refiNewInterestRate.toFixed(3)}%`],
['New Term:', `${inputs.refiNewLoanTerm} years`],
['Est. Closing Costs:', `$${inputs.refiClosingCosts.toFixed(2)}`],
['New Monthly P&I:', `$${newLoanCalcs.monthlyPI.toFixed(2)}`]
];
doc.autoTable({startY: yPos, body: refiOfferBody, theme:'plain', styles:{fontSize:9, cellPadding:1.2}, columnStyles:{0:{fontStyle:'bold'}}});
yPos = doc.lastAutoTable.finalY + 10;
// Comparative Analysis
checkYPdf(40);
doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Refinance Comparison Summary", 14, yPos); yPos +=6;
const comparisonBody = [
['Monthly P&I Payment Difference:', `$${comparison.monthlyPaymentDifference.toFixed(2)} ${comparison.monthlyPaymentDifference >= 0 ? '(Savings)' : '(Increased Cost)'}`],
['Total Future Interest (Current Loan):', `$${comparison.totalRemainingInterestCurrent.toFixed(2)}`],
['Total Future Interest (New Loan):', `$${comparison.totalInterestNew.toFixed(2)}`],
['Interest Savings (New vs Current):', `$${(comparison.totalRemainingInterestCurrent - comparison.totalInterestNew).toFixed(2)}`],
['Net Lifetime Financial Impact:', `$${Math.abs(comparison.lifetimeSavings).toFixed(2)} ${comparison.lifetimeSavings >= 0 ? 'Total Savings' : 'Total Cost'}`],
['Break-Even Point for Closing Costs:', `${isFinite(comparison.breakEvenMonths) ? comparison.breakEvenMonths + ' months' : 'N/A (No monthly savings)'}`]
];
doc.autoTable({startY: yPos, body: comparisonBody, theme:'plain', styles:{fontSize:9, cellPadding:1.2}, columnStyles:{0:{fontStyle:'bold'}}});
yPos = doc.lastAutoTable.finalY + 7;
// Budget Impact
if(budgetImpact && budgetImpact.gmi > 0){
checkYPdf(30);
doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Budget Impact Analysis", 14, yPos); yPos +=6;
const budgetBody = [
['Gross Monthly Income:', `$${budgetImpact.gmi.toFixed(2)}`],
['Other Monthly Debts:', `$${budgetImpact.otherDebts.toFixed(2)}`],
['Change in Monthly Cash Flow (P&I):', `$${Math.abs(budgetImpact.cashFlowChange).toFixed(2)} ${budgetImpact.cashFlowChange >=0 ? 'Improved' : 'Reduced'}`],
['Current Front-End DTI:', `${budgetImpact.currentFrontDTI.toFixed(1)}% -> New: ${budgetImpact.newFrontDTI.toFixed(1)}%`],
['Current Back-End DTI:', `${budgetImpact.currentBackDTI.toFixed(1)}% -> New: ${budgetImpact.newBackDTI.toFixed(1)}%`]
];
doc.autoTable({startY: yPos, body: budgetBody, theme:'plain', styles:{fontSize:9, cellPadding:1.2}, columnStyles:{0:{fontStyle:'bold'}}});
yPos = doc.lastAutoTable.finalY + 10;
}
// Amortization Schedules
function addAmortToPdf(title, scheduleData) {
if (!scheduleData || scheduleData.length === 0) return;
checkYPdf(20);
doc.setFontSize(11); doc.setTextColor(primaryColor); doc.text(title, 14, yPos); yPos += 6;
const head = [['Month', 'Start Bal.', 'Payment', 'Interest', 'Principal', 'Lump Sum', 'End Bal.']];
const body = scheduleData.map(r => [
r.month, r.beginningBalance.toFixed(2), r.payment.toFixed(2), r.interest.toFixed(2),
r.principal.toFixed(2), r.lumpSum.toFixed(2), r.endingBalance.toFixed(2)
]);
doc.autoTable({
startY: yPos, head: head, body: body, theme: 'grid',
headStyles: {fillColor: tableHeaderColor, textColor:textColor, fontStyle:'bold', fontSize:8},
styles:{fontSize:7, cellPadding:1, halign:'right'},
columnStyles:{0:{halign:'center'}},
pageBreak: 'auto' // Let autotable handle page breaks for long tables
});
yPos = doc.lastAutoTable.finalY + 10;
}
addAmortToPdf("Amortization: Current Loan (Remaining Term)", amortization.current);
addAmortToPdf("Amortization: New Refinanced Loan", amortization.new);
doc.save("Home_Loan_Refinance_Analysis.pdf");
}
updateRefiNavButtons(); // Initial call