Time Saved: ${timeSavedStr}
`;
const origAmortTableBody = document.querySelector('#blis-original-amortization-table tbody');
origAmortTableBody.innerHTML = '';
originalSchedule.forEach(row => {
const tr = origAmortTableBody.insertRow();
tr.insertCell().textContent = row.month;
tr.insertCell().textContent = row.date;
tr.insertCell().textContent = blis_formatCurrency(row.totalPayment);
tr.insertCell().textContent = blis_formatCurrency(row.principal);
tr.insertCell().textContent = blis_formatCurrency(row.interest);
tr.insertCell().textContent = blis_formatCurrency(row.balance);
});
const accAmortTableBody = document.querySelector('#blis-accelerated-amortization-table tbody');
accAmortTableBody.innerHTML = '';
acceleratedSchedule.forEach(row => {
const tr = accAmortTableBody.insertRow();
tr.insertCell().textContent = row.month;
tr.insertCell().textContent = row.date;
tr.insertCell().textContent = blis_formatCurrency(row.basePayment);
tr.insertCell().textContent = blis_formatCurrency(row.extraPayment);
tr.insertCell().textContent = blis_formatCurrency(row.totalPayment);
tr.insertCell().textContent = blis_formatCurrency(row.principal);
tr.insertCell().textContent = blis_formatCurrency(row.interest);
tr.insertCell().textContent = blis_formatCurrency(row.balance);
});
// Reset amortization toggle to show original first
const origAmortButton = document.getElementById('blis-show-original-amortization');
if (origAmortButton) origAmortButton.click();
blis_navigateToTab(2);
}
function blis_generatePDF() {
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') {
alert('PDF generation library (jsPDF) is not loaded.'); return;
}
const JSPDF_CONSTRUCTOR = window.jspdf.jsPDF;
const tempDoc = new JSPDF_CONSTRUCTOR();
if (typeof tempDoc.autoTable !== 'function') {
alert('jsPDF AutoTable plugin is not loaded.'); return;
}
if (Object.keys(blis_loanDataForPdf).length === 0) {
alert('Please calculate savings first.'); return;
}
const doc = new JSPDF_CONSTRUCTOR();
const data = blis_loanDataForPdf;
let yPos = 15;
const pageMargin = 15;
const pageWidth = doc.internal.pageSize.getWidth();
const primaryColor = [0, 123, 255]; // Blue
const accentColor = [40, 167, 69]; // Green
function addHeader(text) {
doc.setFontSize(16);
doc.setTextColor(primaryColor[0], primaryColor[1], primaryColor[2]);
doc.text(text, pageWidth / 2, yPos, { align: 'center' });
yPos += 10;
}
function addSubHeader(text) {
doc.setFontSize(12);
doc.setFont(undefined, 'bold');
doc.setTextColor(51, 51, 51);
doc.text(text, pageMargin, yPos);
yPos += 7;
}
addHeader("Business Loan Interest Savings Report");
// Inputs
addSubHeader("Loan & Extra Payment Inputs:");
const inputData = [
["Original Loan Amount:", blis_formatCurrency(data.loanAmount)],
["Annual Interest Rate:", `${data.annualInterestRate.toFixed(2)}%`],
["Original Loan Term:", `${data.loanTermYears} years`],
["Loan Start Date:", data.startDateString ? blis_formatDate(new Date(data.startDateString.replace(/-/g, '\/'))) : 'N/A'],
["Additional Monthly Payment:", blis_formatCurrency(data.additionalMonthlyPayment)],
];
doc.autoTable({
startY: yPos, body: inputData, theme: 'grid',
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: { 0: { fontStyle: 'bold' } }
});
yPos = doc.lastAutoTable.finalY + 10;
// Savings Summary
if (yPos > doc.internal.pageSize.getHeight() - 70) { doc.addPage(); yPos = pageMargin; }
addSubHeader("Savings Summary:");
const summaryData = [
["Original Monthly Payment (P&I):", blis_formatCurrency(data.originalMonthlyPayment)],
["Effective Monthly Payment (with Extra):", blis_formatCurrency(data.originalMonthlyPayment + data.additionalMonthlyPayment)],
["Original Total Interest Paid:", blis_formatCurrency(data.originalTotalInterest)],
["New Total Interest Paid (with extra):", blis_formatCurrency(data.newTotalInterest)],
[{content: "TOTAL INTEREST SAVED:", styles:{fontStyle:'bold', fillColor: [209,250,223]}}, // Light Green BG
{content: blis_formatCurrency(data.interestSaved), styles:{fontStyle:'bold', textColor: accentColor, fillColor: [209,250,223]}}],
["Original Payoff:", `${Math.floor(data.originalPayoffMonths/12)} yrs, ${data.originalPayoffMonths%12} mths` + (data.startDateString ? ` (${data.originalPayoffDate})` : '')],
["New Payoff (with extra):", `${Math.floor(data.newPayoffMonths/12)} yrs, ${data.newPayoffMonths%12} mths` + (data.startDateString ? ` (${data.newPayoffDate})` : '')],
["Time Saved:", data.timeSavedStr],
];
doc.autoTable({
startY: yPos, body: summaryData, theme: 'grid',
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: { 0: { fontStyle: 'bold' } },
didParseCell: function(hookData) {
if (hookData.row.index === 4) { // Total Interest Saved row
hookData.cell.styles.fontStyle = 'bold';
hookData.cell.styles.fillColor = [209,250,223]; // Light green
if(hookData.column.index === 1) hookData.cell.styles.textColor = accentColor;
}
}
});
yPos = doc.lastAutoTable.finalY + 10;
// Accelerated Amortization Schedule
if (data.acceleratedSchedule && data.acceleratedSchedule.length > 0) {
if (yPos > doc.internal.pageSize.getHeight() - 40) { doc.addPage(); yPos = pageMargin; }
addSubHeader("Accelerated Amortization Schedule (with Extra Payments):");
const amortBody = data.acceleratedSchedule.map(row => [
row.month, row.date,
blis_formatCurrency(row.basePayment), blis_formatCurrency(row.extraPayment), blis_formatCurrency(row.totalPayment),
blis_formatCurrency(row.principal), blis_formatCurrency(row.interest), blis_formatCurrency(row.balance)
]);
doc.autoTable({
startY: yPos,
head: [['Month', 'Date', 'Base Pmt', 'Extra Pmt', 'Total Pmt', 'Principal', 'Interest', 'Balance']],
body: amortBody,
theme: 'striped',
headStyles: { fillColor: primaryColor, textColor: [255,255,255] },
styles: { fontSize: 7, cellPadding: 1.5, halign: 'right' }, // Smaller font for more columns
columnStyles: { 0: { halign: 'center' }, 1: { halign: 'left' } }
});
}
doc.save("Loan_Interest_Savings_Report.pdf");
}