Estimated Selling Costs: $${fNum(data.costToSell)}
`;
outputHTML += `
Remaining Loan Balance: $${fNum(data.remainingLoanBalance)}
`;
outputHTML += `
Net Proceeds from Sale (Equity - Selling Costs): $${fNum(data.netProceedsFromSale)}
`;
outputHTML += `
Net Cost of Buying (Outflows - Net Sale Proceeds): $${fNum(data.netCostOfBuying)}
`;
outputHTML += `
Renting a Property
`;
outputHTML += `
Upfront Costs (Security Deposit): $${fNum(data.securityDeposit)}
`;
outputHTML += `
Total Rent Paid: $${fNum(data.totalRentPaid)}
`;
outputHTML += `
Total Renter's Insurance: $${fNum(data.totalRentersInsurance)}
`;
outputHTML += `
Total Outflows for Renting: $${fNum(data.totalCashOutflowsRenting - data.securityDeposit)} (excluding deposit returned)
`; // Exclude deposit as it's returned
outputHTML += `
`;
if (data.investmentPrincipalRenter > 0) {
outputHTML += `
Investment Growth (from investing upfront buying cost difference of $${fNum(data.investmentPrincipalRenter)}): $${fNum(data.futureValueOfInvestmentRenter - data.investmentPrincipalRenter)} (Total Value: $${fNum(data.futureValueOfInvestmentRenter)})
`;
} else {
outputHTML += `
No significant upfront cost difference to invest.
`;
}
outputHTML += `
Security Deposit Returned: $${fNum(data.securityDeposit)}
`;
outputHTML += `
Net Cost of Renting (Outflows - Investment Gains - Deposit Returned): $${fNum(data.netCostOfRenting)}
`;
outputHTML += `
`;
outputHTML += `
`;
if (data.netCostOfBuying < data.netCostOfRenting) {
outputHTML += `
Buying is estimated to be $${fNum(Math.abs(data.netCostOfBuying - data.netCostOfRenting))} cheaper than renting over ${data.years} years.
`;
} else if (data.netCostOfRenting < data.netCostOfBuying) {
outputHTML += `
Renting is estimated to be $${fNum(Math.abs(data.netCostOfBuying - data.netCostOfRenting))} cheaper than buying over ${data.years} years.
`;
} else {
outputHTML += `
Buying and renting have a similar estimated net cost over ${data.years} years.
`;
}
outputHTML += `
`;
this.elements.analysisOutput.innerHTML = outputHTML;
this.elements.pdfDownloadContainer.style.display = 'block';
this.elements.analysisTabButton.disabled = false;
},
downloadPdf: function() {
try {
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') {
alert('PDF generation library (jsPDF core) is not loaded.'); console.error('window.jspdf or window.jspdf.jsPDF is not defined.'); return;
}
const { jsPDF: JSPDF_CONSTRUCTOR } = window.jspdf;
const doc = new JSPDF_CONSTRUCTOR();
if (typeof doc.autoTable !== 'function') {
alert('PDF generation plugin (jsPDF-Autotable) is not loaded.'); console.error('doc.autoTable is not a function.'); return;
}
if (!this.calculatedData || Object.keys(this.calculatedData).length === 0 || !this.calculatedData.inputs) {
alert('Error: Calculation data missing.'); console.error('PDF error: this.calculatedData empty or inputs missing.'); return;
}
const data = this.calculatedData;
const inputs = data.inputs;
const primaryColor = '#1e3a8a'; const accentColor = '#3b82f6'; const textColor = '#1f2937';
const tableHeaderColor = accentColor; const lightBgColor = '#f0f9ff';
const cheaperColor = '#065f46'; const costlierColor = '#991b1b';
doc.setFontSize(18); doc.setTextColor(primaryColor);
doc.text("Rent vs. Buy Comparison Analysis", 14, 22);
doc.setFontSize(10); doc.setTextColor(textColor);
doc.text(`Comparison Period: ${data.years} Years | Report Generated: ${new Date().toLocaleDateString('en-US', {day:'2-digit', month: 'short', year: 'numeric' })}`, 14, 30);
doc.text(`Key Assumptions: Inflation ${parseFloat(inputs.rvbAnnualInflationRate).toFixed(1)}%, Investment Return ${parseFloat(inputs.rvbInvestmentReturnRate).toFixed(1)}%`, 14, 38);
let currentY = 50;
const fNum = (num, dec=0) => (typeof num !== 'number' || isNaN(num)) ? 'N/A' : num.toLocaleString(undefined, {minimumFractionDigits:dec, maximumFractionDigits:dec});
const addSection = (title, sectionData, highlightLastRow = false) => {
doc.setFontSize(13); doc.setTextColor(primaryColor); doc.setFont(undefined, 'bold');
doc.text(title, 14, currentY);
currentY += 7;
doc.autoTable({
body: sectionData, startY: currentY, theme: 'grid',
styles: {fontSize: 9, cellPadding: 2, textColor: textColor, font: 'helvetica'},
columnStyles: { 0: { fontStyle: 'normal', cellWidth: 100, fillColor: lightBgColor }, 1: { halign: 'right', cellWidth: 'auto'} },
didParseCell: (hookData) => {
if (highlightLastRow && hookData.row.index === sectionData.length -1) {
hookData.cell.styles.fontStyle = 'bold';
hookData.cell.styles.fontSize = 10;
if (data.netCostOfBuying < data.netCostOfRenting && title.includes("Buying")) hookData.cell.styles.textColor = cheaperColor;
if (data.netCostOfRenting < data.netCostOfBuying && title.includes("Renting")) hookData.cell.styles.textColor = cheaperColor;
}
}
});
currentY = doc.lastAutoTable.finalY + 10;
};
const buyingDetails = [
["Home Purchase Price:", `$${fNum(data.homePrice)}`],
["Down Payment:", `$${fNum(data.downPayment)} (${((data.downPayment/data.homePrice)*100).toFixed(1)}%)`],
["Loan Amount:", `$${fNum(data.loanAmount)}`],
["Mortgage Rate / Term:", `${parseFloat(inputs.rvbMortgageInterestRate).toFixed(2)}% / ${inputs.rvbLoanTermYears} Years`],
["Monthly P&I:", `$${fNum(data.monthlyPAndI)}`],
["Upfront Costs (DP + Closing):", `$${fNum(data.upfrontBuyingCost)}`],
["Total Mortgage Payments (P&I) over ${data.years} yrs:", `$${fNum(data.totalMortgagePayments)}`],
["Total Property Taxes over ${data.years} yrs:", `$${fNum(data.totalPropertyTaxes)}`],
["Total Home Insurance over ${data.years} yrs:", `$${fNum(data.totalHomeInsurance)}`],
["Total Maintenance over ${data.years} yrs:", `$${fNum(data.totalMaintenance)}`],
["Total HOA Fees over ${data.years} yrs:", `$${fNum(data.totalHOA)}`],
["TOTAL CASH OUTFLOWS (BUYING):", `$${fNum(data.totalCashOutflowsBuying)}`],
["Projected Home Value (end of ${data.years} yrs):", `$${fNum(data.futureHomeValue)}`],
["Remaining Loan Balance:", `$${fNum(data.remainingLoanBalance)}`],
["Estimated Selling Costs:", `$${fNum(data.costToSell)}`],
["Net Proceeds from Sale (Equity - Selling):", `$${fNum(data.netProceedsFromSale)}`],
["NET COST OF BUYING:", `$${fNum(data.netCostOfBuying)}`]
];
addSection("Buying a Home Details & Costs", buyingDetails, true);
const rentingDetails = [
["Initial Monthly Rent:", `$${fNum(parseFloat(inputs.rvbMonthlyRent))}`],
["Initial Annual Renter's Insurance:", `$${fNum(parseFloat(inputs.rvbRentersInsurance))}`],
["Security Deposit:", `$${fNum(data.securityDeposit)}`],
["Total Rent Paid over ${data.years} yrs (inflated):", `$${fNum(data.totalRentPaid)}`],
["Total Renter's Insurance over ${data.years} yrs (inflated):", `$${fNum(data.totalRentersInsurance)}`],
["TOTAL CASH OUTFLOWS (RENTING, before deposit return):", `$${fNum(data.totalCashOutflowsRenting)}`],
["Investment from Upfront Diff. ($${fNum(data.investmentPrincipalRenter > 0 ? data.investmentPrincipalRenter : 0)}):", `$${fNum(data.futureValueOfInvestmentRenter)} (Future Value)`],
["Security Deposit Returned:", `$${fNum(data.securityDeposit)}`],
["NET COST OF RENTING:", `$${fNum(data.netCostOfRenting)}`]
];
addSection("Renting a Property Details & Costs", rentingDetails, true);
doc.setFontSize(12); doc.setFont(undefined, 'bold');
let conclusionText = "";
if (data.netCostOfBuying < data.netCostOfRenting) {
conclusionText = `Buying is estimated to be $${fNum(Math.abs(data.netCostOfBuying - data.netCostOfRenting))} cheaper than renting over ${data.years} years.`;
doc.setTextColor(cheaperColor);
} else if (data.netCostOfRenting < data.netCostOfBuying) {
conclusionText = `Renting is estimated to be $${fNum(Math.abs(data.netCostOfBuying - data.netCostOfRenting))} cheaper than buying over ${data.years} years.`;
doc.setTextColor(cheaperColor); // Still "cheaper" for renting scenario
} else {
conclusionText = `Buying and renting have a similar estimated net cost over ${data.years} years.`;
doc.setTextColor(textColor);
}
if (currentY > doc.internal.pageSize.height - 40) doc.addPage(); currentY = currentY > doc.internal.pageSize.height - 40 ? 20 : currentY;
doc.text(conclusionText, doc.internal.pageSize.width / 2, currentY, { align: 'center' });
currentY += 15;
doc.setFontSize(9); doc.setTextColor('#6b7280'); doc.setFont(undefined, 'normal');
const finalNote = "This analysis is an estimate based on the inputs and assumptions provided (e.g., constant rates for inflation, appreciation, investment). Actual financial outcomes can vary significantly. This tool does not provide financial advice.";
const splitNote = doc.splitTextToSize(finalNote, doc.internal.pageSize.width - 28);
if (currentY > doc.internal.pageSize.height - 40) doc.addPage(); currentY = currentY > doc.internal.pageSize.height - 40 ? 20 : currentY;
doc.text(splitNote, 14, currentY);
doc.save("Rent_vs_Buy_Analysis.pdf");
} catch (error) {
console.error("PDF Generation Error:", error);
alert("An error occurred while generating the PDF: " + error.message + "\nPlease check the console (F12).");
}
}
};
document.addEventListener('DOMContentLoaded', function() {
rvbApp.init();
});