Yearly Spending Habit Analysis Tool

Step 1: Your Annual Income & Savings Goals

Savings Goal (Optional)

Step 2: Enter Your Annual Spending by Category

Enter your total actual spending for the year in each category. Optionally, add your budgeted amount for comparison.

Step 3: Spending Analysis & Insights

Enter income (Tab 1) and spending (Tab 2), then click "Refresh Analysis".

Step 4: Overall Summary & Download Report

Complete the analysis on previous tabs to view the summary.

Please enter your income and spending details on previous tabs.

"; document.getElementById('downloadSpendingPdfButton').disabled = true; return; } const { annualNetIncome, totals, targetSavings } = spendingReportData; let targetSavingsAmountText = "N/A"; if (targetSavings.value > 0) { targetSavingsAmountText = targetSavings.type === "Amount" ? `$${targetSavings.value.toFixed(2)}` : `${targetSavings.value.toFixed(1)}% of net income`; } let summaryHTML = `

Analysis Period: ${spendingReportData.analysisPeriodLabel}

Annual Net Income: $${annualNetIncome.toFixed(2)}


Total Actual Annual Spending: $${totals.actualSpending.toFixed(2)}

${totals.budgetedSpending !== null ? `

Total Budgeted Annual Spending: $${totals.budgetedSpending.toFixed(2)}

Overall Budget Variance: $${totals.budgetVariance.toFixed(2)}


` : ''}

Actual Annual Savings: $${totals.actualSavings.toFixed(2)}

Actual Savings Rate: ${totals.actualSavingsRate.toFixed(1)}%

Target Annual Savings Goal: ${targetSavingsAmountText}

`; const topCats = [...spendingReportData.categories].sort((a,b) => b.actualSpending - a.actualSpending).slice(0,3); if(topCats.length > 0 && totals.actualSpending > 0) { summaryHTML += `

Top Spending Categories:

    `; topCats.forEach(cat => { summaryHTML += `
  • ${cat.name}: $${cat.actualSpending.toFixed(2)} (${((cat.actualSpending / totals.actualSpending) * 100).toFixed(1)}%)
  • `; }); summaryHTML += `
`; } container.innerHTML = summaryHTML; document.getElementById('downloadSpendingPdfButton').disabled = false; } function downloadSpendingPdf() { if (!spendingReportData.totals || (spendingReportData.annualNetIncome === 0 && spendingReportData.totals.actualSpending === 0) ) { alert("Please enter income and spending data 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 { analysisPeriodLabel, annualNetIncome, targetSavings, categories, totals } = spendingReportData; 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(`Yearly Spending Analysis: ${analysisPeriodLabel}`, 14, yPos); yPos += 8; doc.setFontSize(10); doc.setTextColor(textColor); doc.text(`Report Date: ${new Date().toLocaleDateString()}`, 14, yPos); yPos += 10; checkYPdf(25); doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Income & Savings Summary", 14, yPos); yPos += 6; let incomeBody = [ ['Annual Net Income:', `$${annualNetIncome.toFixed(2)}`], ['Target Savings:', `${targetSavings.type === "Amount" ? '$'+targetSavings.value.toFixed(2) : targetSavings.value.toFixed(1)+'% of Net Income'}`], ['Actual Annual Savings:', `$${totals.actualSavings.toFixed(2)}`], ['Actual Savings Rate:', `${totals.actualSavingsRate.toFixed(1)}%`] ]; doc.autoTable({startY: yPos, body: incomeBody, theme:'plain', styles:{fontSize:9, cellPadding:1.5}, columnStyles:{0:{fontStyle:'bold'}}}); yPos = doc.lastAutoTable.finalY + 7; checkYPdf(15); doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Overall Spending Summary", 14, yPos); yPos += 6; let overallSpendingBody = [ ['Total Actual Spending:', `$${totals.actualSpending.toFixed(2)}`] ]; if (totals.budgetedSpending !== null) { overallSpendingBody.push(['Total Budgeted Spending:', `$${totals.budgetedSpending.toFixed(2)}`]); overallSpendingBody.push(['Overall Budget Variance:', `$${totals.budgetVariance.toFixed(2)} (${totals.budgetVariance > 0 ? 'Over Budget' : totals.budgetVariance < 0 ? 'Under Budget' : 'On Budget'})`]); } doc.autoTable({startY: yPos, body: overallSpendingBody, theme:'plain', styles:{fontSize:9, cellPadding:1.5}, columnStyles:{0:{fontStyle:'bold'}}}); yPos = doc.lastAutoTable.finalY + 10; checkYPdf(20 + categories.length * 6); doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Detailed Spending by Category", 14, yPos); yPos += 6; let tableHead = [['Category', 'Actual ($)', '% of Total']]; if (totals.budgetedSpending !== null) { tableHead[0].push('Budgeted ($)', 'Variance ($)', 'Variance (%)'); } const tableBody = categories.map(cat => { const percent = totals.actualSpending > 0 ? (cat.actualSpending / totals.actualSpending) * 100 : 0; let row = [cat.name, cat.actualSpending.toFixed(2), `${percent.toFixed(1)}%`]; if (totals.budgetedSpending !== null) { const budget = cat.budgetedSpending !== null ? cat.budgetedSpending : 0; const variance = cat.actualSpending - budget; const variancePercent = budget > 0 ? (variance / budget) * 100 : (cat.actualSpending > 0 ? (budget === 0 ? 'Infinite' : 100) : 0); row.push(cat.budgetedSpending !== null ? budget.toFixed(2) : 'N/A', variance.toFixed(2), cat.budgetedSpending !== null ? (variancePercent === 'Infinite' ? 'Infinite' : variancePercent.toFixed(1) + '%') : 'N/A'); } return row; }); doc.autoTable({ startY: yPos, head: tableHead, body: tableBody, theme: 'grid', headStyles: {fillColor: tableHeaderColor, textColor:textColor, fontStyle:'bold', fontSize:9}, styles:{fontSize:8, cellPadding:1.5}, columnStyles: { 1:{halign:'right'}, 2:{halign:'right'}, 3:{halign:'right'}, 4:{halign:'right'}, 5:{halign:'right'} } }); yPos = doc.lastAutoTable.finalY + 7; const topCategoriesPdf = [...categories].sort((a,b) => b.actualSpending - a.actualSpending).slice(0,5); if(topCategoriesPdf.length > 0 && totals.actualSpending > 0) { checkYPdf(15 + topCategoriesPdf.length * 5); doc.setFontSize(10); doc.setTextColor(textColor); doc.text("Top Spending Categories:", 14, yPos); yPos +=5; topCategoriesPdf.forEach(cat => { checkYPdf(5); doc.text(`- ${cat.name}: $${cat.actualSpending.toFixed(2)} (${((cat.actualSpending / totals.actualSpending) * 100).toFixed(1)}%)`, 16, yPos); yPos += 5; }); } doc.save("Yearly_Spending_Analysis.pdf"); } // Initialize updateSHANavButtons();
Scroll to Top