Loan Affordability Estimate
Desired Loan Term: ${estimatorData.loanTermYears} years
Annual Interest Rate: ${estimatorData.annualInterestRate !== undefined ? estimatorData.annualInterestRate.toFixed(2) : 'N/A'}%
Estimated Affordable Monthly Loan Payment: ${formatCurrency(estimatorData.affordableMonthlyPayment)}
(This is conservatively based on your Off-Peak Monthly Net Income to ensure year-round affordability)
Estimated Total Loan Amount You Might Afford: ${formatCurrency(estimatorData.estimatedLoanAmount)}
`;
if (estimatorData.affordableMonthlyPayment <= 0 && estimatorData.offPeakMonthlyNetIncome <= 0) {
displayError(errorTab4El, "Note: The estimated affordable loan amount is zero because the off-peak monthly net income is not positive. This suggests that based on the provided off-peak figures, covering loan payments might be challenging during those months.");
}
}
if (nextButton) {
nextButton.addEventListener('click', () => {
let canProceed = false;
if (currentTab === 0) {
canProceed = validateTab1();
} else if (currentTab === 1) {
canProceed = validateAndCalculateTab2();
} else if (currentTab === 2) {
canProceed = validateAndCalculateTab3();
if (canProceed) populateSummary();
}
if (canProceed && currentTab < tabContents.length - 1) {
showTab(currentTab + 1);
}
});
}
if (prevButton) {
prevButton.addEventListener('click', () => {
if (currentTab > 0) {
showTab(currentTab - 1);
if (currentTab === 1) clearError(errorTab1El);
if (currentTab === 2) clearError(errorTab2El);
if (currentTab === 3) clearError(errorTab3El);
clearError(errorTab4El);
}
});
}
if (tabButtons) {
tabButtons.forEach((button, index) => {
button.addEventListener('click', () => {
if (index <= currentTab) {
showTab(index);
} else {
let canReachTarget = true;
for (let i = currentTab; i < index; i++) {
let validationPassed = false;
if (i === 0) validationPassed = validateTab1();
else if (i === 1) validationPassed = validateAndCalculateTab2();
else if (i === 2) {
validationPassed = validateAndCalculateTab3();
if(validationPassed) populateSummary();
}
if (!validationPassed) {
showTab(i);
// Trigger error display on the failed tab by simulating next button if possible or directly calling displayError
if (i === 0 && nextButton) nextButton.click();
else if (i === 1 && nextButton) nextButton.click();
else if (i === 2 && nextButton) nextButton.click();
canReachTarget = false;
break;
}
}
if (canReachTarget) {
showTab(index);
}
}
});
});
}
if (downloadPdfButton) {
downloadPdfButton.addEventListener('click', () => {
clearError(errorTab4El);
if (typeof window.jspdf === 'undefined') {
displayError(errorTab4El, 'PDF generation library (jsPDF) is not loaded. Namespace `window.jspdf` undefined. Check internet connection or script loading.');
console.error('window.jspdf is not defined.');
return;
}
if (typeof window.jspdf.jsPDF === 'undefined') {
displayError(errorTab4El, 'PDF constructor (jsPDF.jsPDF) is not found within the library. Script might be corrupted or wrong version.');
console.error('window.jspdf.jsPDF is not defined.');
return;
}
const JSPDFConstructor = window.jspdf.jsPDF;
const doc = new JSPDFConstructor();
if (typeof doc.autoTable !== 'function') {
displayError(errorTab4El, 'PDF table plugin (jsPDF-AutoTable) is not functional. `doc.autoTable` is not a function.');
console.error('doc.autoTable is not a function. jsPDF-AutoTable plugin might have failed.');
return;
}
if (Object.keys(estimatorData).length < 7 || estimatorData.estimatedLoanAmount === undefined) {
displayError(errorTab4El, 'Please complete all calculation steps before downloading the PDF.');
return;
}
const formatCurrencyForPdf = (num) => {
if (typeof num !== 'number' || isNaN(num)) return '$0.00';
return `$${num.toFixed(2)}`;
};
doc.setFontSize(18);
doc.text("Seasonal Business Loan Affordability Estimate", 14, 20);
doc.setFontSize(10);
doc.setTextColor(100); // Light grey for timestamp
doc.text(`Report Generated: ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}`, 14, 27);
doc.setTextColor(0); // Reset text color to black
const tableColumnStyles = {
0: { cellWidth: 65, fontStyle: 'bold', halign: 'left' },
1: { cellWidth: 'auto', halign: 'right' }
};
const headStyles = { fillColor: [0, 115, 170], textColor: 255, fontStyle: 'bold' };
const theme = 'grid';
const inputData = [
["Peak Season Monthly Revenue:", formatCurrencyForPdf(estimatorData.peakRevenue)],
["Number of Peak Months:", estimatorData.peakMonths.toString()],
["Off-Peak Season Monthly Revenue:", formatCurrencyForPdf(estimatorData.offPeakRevenue)],
["Number of Off-Peak Months:", estimatorData.offPeakMonths.toString()],
["Peak Season Monthly Expenses:", formatCurrencyForPdf(estimatorData.peakExpenses)],
["Off-Peak Season Monthly Expenses:", formatCurrencyForPdf(estimatorData.offPeakExpenses)],
["Desired Loan Term (Years):", estimatorData.loanTermYears.toString()],
["Annual Interest Rate (%):", estimatorData.annualInterestRate !== undefined ? estimatorData.annualInterestRate.toFixed(2) : 'N/A']
];
doc.autoTable({
startY: 35,
head: [['User Inputs', 'Value']],
body: inputData,
theme: theme,
headStyles: headStyles,
columnStyles: tableColumnStyles,
tableWidth: 'auto',
margin: { left: 14, right: 14 }
});
let lastTableY = doc.lastAutoTable.finalY;
const financialSummaryData = [
["Total Annual Revenue:", formatCurrencyForPdf(estimatorData.totalAnnualRevenue)],
["Total Annual Expenses:", formatCurrencyForPdf(estimatorData.totalAnnualExpenses)],
["Net Annual Income:", formatCurrencyForPdf(estimatorData.netAnnualIncome)],
["Average Monthly Net Income:", formatCurrencyForPdf(estimatorData.averageMonthlyNetIncome)],
["Peak Season Monthly Net Income:", formatCurrencyForPdf(estimatorData.peakMonthlyNetIncome)],
["Off-Peak Season Monthly Net Income:", formatCurrencyForPdf(estimatorData.offPeakMonthlyNetIncome)]
];
doc.autoTable({
startY: lastTableY + 8,
head: [['Business Financials Summary', 'Value']],
body: financialSummaryData,
theme: theme,
headStyles: headStyles,
columnStyles: tableColumnStyles,
tableWidth: 'auto',
margin: { left: 14, right: 14 }
});
lastTableY = doc.lastAutoTable.finalY;
const loanAffordabilityData = [
["Est. Affordable Monthly Payment:", formatCurrencyForPdf(estimatorData.affordableMonthlyPayment)],
["Est. Total Affordable Loan Amount:", formatCurrencyForPdf(estimatorData.estimatedLoanAmount)]
];
doc.autoTable({
startY: lastTableY + 8,
head: [['Loan Affordability Estimate', 'Value']],
body: loanAffordabilityData,
theme: theme,
headStyles: headStyles,
columnStyles: tableColumnStyles,
tableWidth: 'auto',
margin: { left: 14, right: 14 }
});
lastTableY = doc.lastAutoTable.finalY;
doc.setFontSize(9);
doc.setTextColor(100);
const noteText = "The Estimated Affordable Monthly Loan Payment is conservatively based on your Off-Peak Monthly Net Income to help ensure year-round affordability. This estimator provides guidance and does not constitute financial advice or a guarantee of loan approval. All figures are estimates.";
// Calculate text height properly: doc.getTextDimensions(text, { maxWidth: X }).h
const noteMaxWidth = doc.internal.pageSize.width - 28;
const splitNoteText = doc.splitTextToSize(noteText, noteMaxWidth);
const noteTextHeight = doc.getTextDimensions(splitNoteText, {maxWidth: noteMaxWidth}).h;
doc.text(splitNoteText, 14, lastTableY + 7);
let finalY = lastTableY + 7 + noteTextHeight;
if (estimatorData.affordableMonthlyPayment <= 0 && estimatorData.offPeakMonthlyNetIncome <= 0) {
doc.setTextColor(200, 0, 0);
const warningText = "Warning: The estimated affordable loan amount is zero because the off-peak monthly net income is not positive. This suggests that based on the provided off-peak figures, covering loan payments might be challenging during those months.";
const splitWarningText = doc.splitTextToSize(warningText, noteMaxWidth);
const warningTextHeight = doc.getTextDimensions(splitWarningText, {maxWidth: noteMaxWidth}).h;
doc.text(splitWarningText, 14, finalY + 4); // Add some space after previous note
}
doc.save("Seasonal_Loan_Affordability_Estimate.pdf");
});
}
// Initialize
if (tabContents.length > 0 && tabButtons.length > 0) {
showTab(0);
}
updateOffPeakMonthsDisplay();
});