`;
// Switch to results tab
const resultsTabButton = Array.from(document.getElementsByClassName("rvb-tab-button")).find(btn => btn.textContent.includes("Comparison Results"));
if (resultsTabButton && !resultsTabButton.classList.contains('active')) {
openRvbTab({currentTarget: resultsTabButton}, 'rvbResultsTab');
}
document.getElementById('rvbDownloadPdfButton').style.display = 'block';
}
function downloadRvbPDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
// --- Retrieve all input values again for PDF (or pass them if refactoring) ---
const homePrice = getRvbNumValue('rvbHomePrice');
const downPaymentType = document.getElementById('rvbDownPaymentType').value;
let downPaymentInputVal = downPaymentType === 'percent' ? getRvbNumValue('rvbDownPaymentPercent') : getRvbNumValue('rvbDownPaymentAmount');
let downPaymentAmount;
if (downPaymentType === 'percent') {
downPaymentAmount = homePrice * (downPaymentInputVal / 100);
} else {
downPaymentAmount = downPaymentInputVal;
}
if (downPaymentAmount > homePrice) downPaymentAmount = homePrice;
const loanAmount = homePrice - downPaymentAmount;
const loanTermYears = getRvbNumValue('rvbLoanTerm');
const annualInterestRate = getRvbNumValue('rvbInterestRate');
const propertyTaxType = document.getElementById('rvbPropertyTaxType').value;
let propertyTaxInputVal = propertyTaxType === 'percent' ? getRvbNumValue('rvbPropertyTaxPercent') : getRvbNumValue('rvbPropertyTaxAmount');
let annualPropertyTax;
if (propertyTaxType === 'percent') {
annualPropertyTax = homePrice * (propertyTaxInputVal / 100);
} else {
annualPropertyTax = propertyTaxInputVal;
}
const annualHomeInsurance = getRvbNumValue('rvbHomeInsurance');
const monthlyHoaFees = getRvbNumValue('rvbHoaFees');
const maintenanceType = document.getElementById('rvbMaintenanceType').value;
let maintenanceInputVal = maintenanceType === 'percent' ? getRvbNumValue('rvbMaintenancePercent') : getRvbNumValue('rvbMaintenanceAmount');
let annualMaintenance;
if (maintenanceType === 'percent') {
annualMaintenance = homePrice * (maintenanceInputVal / 100);
} else {
annualMaintenance = maintenanceInputVal;
}
const upfrontBuyingCostsPercent = getRvbNumValue('rvbUpfrontCostsPercent');
const upfrontBuyingCosts = homePrice * (upfrontBuyingCostsPercent / 100);
const initialMonthlyRent = getRvbNumValue('rvbMonthlyRent');
const annualRentersInsurance = getRvbNumValue('rvbRentersInsurance');
const comparisonPeriodYears = getRvbNumValue('rvbComparisonPeriod');
const propertyAppreciationRate = getRvbNumValue('rvbPropertyAppreciation');
const rentIncreaseRate = getRvbNumValue('rvbRentIncrease');
const investmentReturnRate = getRvbNumValue('rvbInvestmentReturn');
const sellingCostsPercent = getRvbNumValue('rvbSellingCostsPercent');
const marginalTaxRate = getRvbNumValue('rvbTaxRate');
// Re-run calculations for PDF data consistency
const monthlyMortgagePayment = calculateMonthlyMortgage(loanAmount, annualInterestRate, loanTermYears);
let totalPrincipalPaid = 0, totalInterestPaid = 0, remainingLoanBalance = loanAmount, cumulativeTaxSavings = 0;
for (let i = 0; i < comparisonPeriodYears * 12; i++) {
if (remainingLoanBalance <= 0) break;
let interestForMonth = remainingLoanBalance * (annualInterestRate / 12 / 100);
let principalForMonth = monthlyMortgagePayment - interestForMonth;
if (principalForMonth > remainingLoanBalance) {principalForMonth = remainingLoanBalance; interestForMonth = monthlyMortgagePayment - principalForMonth; if(interestForMonth < 0) interestForMonth = 0;}
totalInterestPaid += interestForMonth; totalPrincipalPaid += principalForMonth; remainingLoanBalance -= principalForMonth;
if ((i + 1) % 12 === 0) {
let annualInterestForYear = 0; let tempBalance = (i === 11) ? loanAmount : (remainingLoanBalance + principalForMonth);
for(let m=0; m<12; m++){ if (tempBalance <=0) break; let intM = tempBalance * (annualInterestRate / 12 / 100); let priM = monthlyMortgagePayment - intM; if (priM > tempBalance) priM = tempBalance; annualInterestForYear += intM; tempBalance -= priM;}
cumulativeTaxSavings += (annualInterestForYear + annualPropertyTax) * (marginalTaxRate/100);
}
}
remainingLoanBalance = Math.max(0, remainingLoanBalance);
const totalMortgagePayments = totalPrincipalPaid + totalInterestPaid;
const totalPropertyTaxesPaid = annualPropertyTax * comparisonPeriodYears;
const totalHomeInsurancePaid = annualHomeInsurance * comparisonPeriodYears;
const totalHoaFeesPaid = monthlyHoaFees * 12 * comparisonPeriodYears;
const totalMaintenancePaid = annualMaintenance * comparisonPeriodYears;
const futureHomeValue = homePrice * Math.pow(1 + (propertyAppreciationRate/100), comparisonPeriodYears);
const sellingCostsAmount = futureHomeValue * (sellingCostsPercent/100);
const netProceedsFromSale = futureHomeValue - sellingCostsAmount - remainingLoanBalance;
const buyerNetWorthEffect = netProceedsFromSale - (downPaymentAmount + upfrontBuyingCosts + totalMortgagePayments + totalPropertyTaxesPaid + totalHomeInsurancePaid + totalHoaFeesPaid + totalMaintenancePaid - cumulativeTaxSavings);
let cumulativeRentPaid = 0; let currentAnnualRent = initialMonthlyRent * 12;
for (let i = 0; i < comparisonPeriodYears; i++) { cumulativeRentPaid += currentAnnualRent; currentAnnualRent *= (1 + (rentIncreaseRate/100)); }
const totalRentersInsurancePaid = annualRentersInsurance * comparisonPeriodYears;
const totalCashOutflowsRenting = cumulativeRentPaid + totalRentersInsurancePaid;
const initialSumNotSpent = downPaymentAmount + upfrontBuyingCosts;
const futureValueOfSavingsRenting = initialSumNotSpent * Math.pow(1 + (investmentReturnRate/100), comparisonPeriodYears);
const earningsFromInvestment = futureValueOfSavingsRenting - initialSumNotSpent;
const renterNetWorthEffect = futureValueOfSavingsRenting - totalCashOutflowsRenting;
const difference = renterNetWorthEffect - buyerNetWorthEffect;
let comparisonMessagePDF = "";
if (difference > 0) comparisonMessagePDF = `Renting is financially better by approx. $${Math.abs(difference).toFixed(2)}`;
else if (difference < 0) comparisonMessagePDF = `Buying is financially better by approx. $${Math.abs(difference).toFixed(2)}`;
else comparisonMessagePDF = `Renting and Buying have a similar financial outcome`;
comparisonMessagePDF += ` over ${comparisonPeriodYears} years.`;
let yPos = 20;
doc.setFontSize(20); doc.setTextColor(RvbPrimaryColor);
doc.text("Rent vs. Buy Comparison Report", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' });
yPos += 12;
doc.setFontSize(10); doc.setTextColor(RvbTextColor);
doc.text(`Comparison Period: ${comparisonPeriodYears} years`, 14, yPos); yPos += 6;
doc.text(`Report Generated: ${new Date().toLocaleDateString()}`, 14, yPos); yPos += 10;
// Inputs Summary Table
doc.setFontSize(14); doc.setTextColor(RvbPrimaryColor);
doc.text("Key Input Assumptions", 14, yPos); yPos += 7;
const inputData = [
["Home Price:", `$${homePrice.toFixed(2)}`],
["Down Payment:", `$${downPaymentAmount.toFixed(2)} (${downPaymentType === 'percent' ? downPaymentInputVal + '%' : 'fixed'})`],
["Loan Term:", `${loanTermYears} years`],
["Interest Rate:", `${annualInterestRate.toFixed(2)}%`],
["Annual Property Tax:", `$${annualPropertyTax.toFixed(2)} (${propertyTaxType === 'percent' ? propertyTaxInputVal + '%' : 'fixed'})`],
["Annual Home Insurance:", `$${annualHomeInsurance.toFixed(2)}`],
["Monthly HOA Fees:", `$${monthlyHoaFees.toFixed(2)}`],
["Annual Maintenance:", `$${annualMaintenance.toFixed(2)} (${maintenanceType === 'percent' ? maintenanceInputVal + '%' : 'fixed'})`],
["Upfront Buying Costs:", `${upfrontBuyingCostsPercent.toFixed(1)}% ($${upfrontBuyingCosts.toFixed(2)})`],
["Monthly Rent (Initial):", `$${initialMonthlyRent.toFixed(2)}`],
["Annual Renter's Insurance:", `$${annualRentersInsurance.toFixed(2)}`],
["Property Appreciation:", `${propertyAppreciationRate.toFixed(1)}% p.a.`],
["Rent Increase:", `${rentIncreaseRate.toFixed(1)}% p.a.`],
["Investment Return:", `${investmentReturnRate.toFixed(1)}% p.a.`],
["Home Selling Costs:", `${sellingCostsPercent.toFixed(1)}%`],
["Marginal Tax Rate:", `${marginalTaxRate.toFixed(1)}%`],
];
doc.autoTable({
startY: yPos,
head: [['Parameter', 'Value']],
body: inputData,
theme: 'grid',
headStyles: { fillColor: RvbPrimaryColor, textColor: '#FFFFFF', fontStyle: 'bold' },
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: { 1: { halign: 'right' } }
});
yPos = doc.autoTable.previous.finalY + 10;
// Buying Scenario
doc.setFontSize(14); doc.setTextColor(RvbPrimaryColor);
doc.text("Buying Scenario Financial Summary", 14, yPos); yPos += 7;
const buyingData = [
["Total Mortgage Principal Paid", `$${totalPrincipalPaid.toFixed(2)}`],
["Total Mortgage Interest Paid", `$${totalInterestPaid.toFixed(2)}`],
["Total Property Taxes Paid", `$${totalPropertyTaxesPaid.toFixed(2)}`],
["Total Home Insurance Paid", `$${totalHomeInsurancePaid.toFixed(2)}`],
["Total HOA Fees Paid", `$${totalHoaFeesPaid.toFixed(2)}`],
["Total Maintenance Costs Paid", `$${totalMaintenancePaid.toFixed(2)}`],
["Initial Outlay (Down Payment + Upfront Costs)", `$${(downPaymentAmount + upfrontBuyingCosts).toFixed(2)}`],
["---", "---"],
["Estimated Future Home Value", `$${futureHomeValue.toFixed(2)}`],
["Less: Selling Costs", `-$${sellingCostsAmount.toFixed(2)}`],
["Less: Remaining Loan Balance", `-$${remainingLoanBalance.toFixed(2)}`],
["Net Proceeds from Sale", `$${netProceedsFromSale.toFixed(2)}`],
["Plus: Potential Tax Savings", `$${cumulativeTaxSavings.toFixed(2)}`],
["Net Financial Impact (Buying)", `$${buyerNetWorthEffect.toFixed(2)}`, {fontStyle: 'bold'}]
];
doc.autoTable({
startY: yPos,
head: [['Item', 'Amount (USD)']],
body: buyingData,
theme: 'striped',
headStyles: { fillColor: RvbPrimaryColor, textColor: '#FFFFFF' },
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: { 1: { halign: 'right' } },
didParseCell: function (data) {
if (data.row.raw[0] === "Net Financial Impact (Buying)") {
data.cell.styles.fontStyle = 'bold';
data.cell.styles.textColor = RvbPrimaryColor;
}
}
});
yPos = doc.autoTable.previous.finalY + 10;
// Renting Scenario
doc.setFontSize(14); doc.setTextColor(RvbPrimaryColor);
doc.text("Renting Scenario Financial Summary", 14, yPos); yPos += 7;
const rentingData = [
["Initial Funds Available for Investment", `$${initialSumNotSpent.toFixed(2)}`],
["Future Value of Invested Funds", `$${futureValueOfSavingsRenting.toFixed(2)}`],
["Total Earnings from Investment", `$${earningsFromInvestment.toFixed(2)}`],
["---", "---"],
["Total Rent Paid", `-$${cumulativeRentPaid.toFixed(2)}`],
["Total Renter's Insurance Paid", `-$${totalRentersInsurancePaid.toFixed(2)}`],
["Net Financial Impact (Renting)", `$${renterNetWorthEffect.toFixed(2)}`, {fontStyle: 'bold'}]
];
doc.autoTable({
startY: yPos,
head: [['Item', 'Amount (USD)']],
body: rentingData,
theme: 'striped',
headStyles: { fillColor: RvbPrimaryColor, textColor: '#FFFFFF' },
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: { 1: { halign: 'right' } },
didParseCell: function (data) {
if (data.row.raw[0] === "Net Financial Impact (Renting)") {
data.cell.styles.fontStyle = 'bold';
data.cell.styles.textColor = RvbPrimaryColor;
}
}
});
yPos = doc.autoTable.previous.finalY + 15;
// Overall Comparison
doc.setFontSize(16); doc.setTextColor(RvbPrimaryColor);
doc.text("Overall Comparison Result", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' });
yPos += 8;
doc.setFontSize(11); doc.setTextColor(RvbTextColor);
doc.text(comparisonMessagePDF, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center', maxWidth: doc.internal.pageSize.getWidth() - 28 });
yPos += 10;
doc.setFontSize(8); doc.setTextColor('#777777');
doc.text("This calculator provides a financial estimate based on the inputs and assumptions provided. It does not constitute financial advice.", 14, doc.internal.pageSize.getHeight() - 10);
doc.save(`Rent_vs_Buy_Analysis_${new Date().toISOString().slice(0,10)}.pdf`);
}
