Sustainable Business Practices Budget Estimator

Estimation Setup

Sustainability Initiatives & Costs

Add New Sustainability Initiative

Current Initiatives & Estimated Costs

Sustainability Area Initiative Name One-Time Cost ($) Recurring Cost Total Est. Cost for Period ($) Actions

Cost Summary & Report

Project Name:

Company Name:

Estimation Period:


Total Estimated One-Time Costs: $0.00

Total Estimated Recurring Costs (for period): $0.00

Grand Total Estimated Cost: $0.00

Estimated Costs by Sustainability Area

Sustainability AreaTotal Estimated Cost ($)% of Grand Total

Cost Distribution by Sustainability Area

Chart library not loaded.

"; return; } const ctx = chartCanvas.getContext('2d'); const chartColors = ['#28a745', '#17a2b8', '#007bff', '#ffc107', '#6f42c1', '#fd7e14', '#6c757d', '#dc3545', '#20c997']; if (sbpAreaCostChartInstance) sbpAreaCostChartInstance.destroy(); if (labels.length > 0 && data.some(d => d > 0)) { sbpAreaCostChartInstance = new Chart(ctx, { type: 'pie', data: { labels: labels, datasets: [{ label: 'Cost by Sustainability Area', data: data, backgroundColor: labels.map((_,i)=>chartColors[i % chartColors.length]), borderColor: varGet('--sbp-light-text'), borderWidth:1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' }, tooltip: {callbacks: {label: c=>`${c.label}: $${c.parsed.toFixed(2)}`}} } } }); } else { ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); ctx.textAlign='center'; ctx.fillStyle = varGet('--sbp-secondary-color'); ctx.font = "16px Arial"; ctx.fillText("No cost data by area to display chart.", ctx.canvas.width/2, ctx.canvas.height/2); sbpAreaCostChartInstance = null; } } function varGet(varName) { return getComputedStyle(document.documentElement).getPropertyValue(varName).trim(); } // --- PDF Download --- window.sbpDownloadPDF = function() { if (typeof jsPDF === 'undefined' || typeof autoTable === 'undefined') { alert("PDF libraries not loaded."); return; } const { jsPDF: JSPDF } = window.jspdf; const localAutoTable = window.jspdf.autoTable; const doc = new JSPDF(); sbpUpdateSummaryReport(); const projectName = sbpEstimationSettings.projectName; const companyName = sbpEstimationSettings.companyName; let periodText = sbpEstimationSettings.estimationPeriodType; if(sbpEstimationSettings.estimationPeriodType === "Custom") periodText = `${sbpEstimationSettings.customPeriodMonths} Months`; const generationDate = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) + " (Reference Time: Friday, May 16, 2025)"; doc.setFontSize(18); doc.setTextColor(varGet('--sbp-primary-color')); doc.text(projectName, doc.internal.pageSize.getWidth() / 2, 15, { align: 'center' }); doc.setFontSize(11); doc.setTextColor(varGet('--sbp-dark-text')); doc.text(`Company: ${companyName || 'N/A'}`, 14, 25); doc.text(`Estimation Period: ${periodText}`, doc.internal.pageSize.getWidth() - 14, 25, { align: 'right' }); doc.setFontSize(9); doc.setTextColor(varGet('--sbp-secondary-color')); doc.text(`Report Generated: ${generationDate}`, 14, 31); let yPos = 40; const totalOneTime = sbpInitiativesList.reduce((sum, init) => sum + init.oneTimeCost, 0); const totalRecurringForPeriod = sbpInitiativesList.reduce((sum, init) => sum + init.totalRecurringForPeriod, 0); const grandTotal = sbpInitiativesList.reduce((sum, init) => sum + init.totalCostForPeriod, 0); doc.setFontSize(12); doc.setTextColor(varGet('--sbp-primary-color')); doc.text("Overall Cost Summary", 14, yPos); yPos += 7; doc.setFontSize(10); doc.setTextColor(varGet('--sbp-dark-text')); doc.text(`Total Estimated One-Time Costs: $${totalOneTime.toFixed(2)}`, 14, yPos); yPos += 5; doc.text(`Total Estimated Recurring Costs (for period): $${totalRecurringForPeriod.toFixed(2)}`, 14, yPos); yPos += 5; doc.setFont(undefined, 'bold'); doc.text(`Grand Total Estimated Cost for Sustainability Initiatives: $${grandTotal.toFixed(2)}`, 14, yPos); yPos += 10; doc.setFont(undefined, 'normal'); doc.setFontSize(12); doc.setTextColor(varGet('--sbp-primary-color')); doc.text("Itemized Initiatives & Costs", 14, yPos); yPos += 7; const initHead = [['Area', 'Initiative', 'Description', 'One-Time ($)', 'Recurring', `Total/Period ($)`]]; const initBody = sbpInitiativesList.map(init => [ init.area, init.name, init.description || '-', init.oneTimeCost.toFixed(2), init.recurringCost > 0 ? `$${init.recurringCost.toFixed(2)} / ${init.recurringCostFrequency.replace('ly','')}` : '-', init.totalCostForPeriod.toFixed(2) ]); localAutoTable(doc, { head: initHead, body: initBody, startY: yPos, theme: 'grid', headStyles: { fillColor: varGet('--sbp-primary-color'), textColor: varGet('--sbp-light-text')}, styles: { fontSize: 7, cellPadding: 1.5, overflow: 'linebreak' }, columnStyles: { 0:{cellWidth:35}, 1:{cellWidth:40}, 2:{cellWidth:55}, 3:{halign:'right', cellWidth:20}, 4:{cellWidth:25}, 5:{halign:'right', cellWidth:25, fontStyle:'bold'} }, didDrawPage: data => yPos = data.cursor.y }); yPos = doc.lastAutoTable.finalY + 10; const chartCanvas = document.getElementById('sbpAreaCostChart'); if (chartCanvas && sbpAreaCostChartInstance && sbpAreaCostChartInstance.data.datasets[0].data.some(d => d > 0)) { if (yPos > doc.internal.pageSize.getHeight() - 80) { doc.addPage(); yPos = 20;} doc.setFontSize(12); doc.setTextColor(varGet('--sbp-primary-color')); doc.text("Cost Distribution by Sustainability Area", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 7; try { const chartImage = chartCanvas.toDataURL('image/png', 1.0); const imgProps = doc.getImageProperties(chartImage); const pdfChartWidth = doc.internal.pageSize.getWidth() * 0.65; const chartHeight = (imgProps.height * pdfChartWidth) / imgProps.width; const xOffset = (doc.internal.pageSize.getWidth() - pdfChartWidth) / 2; if (yPos + chartHeight > doc.internal.pageSize.getHeight() -10 ) { doc.addPage(); yPos = 20; doc.text("Cost Distribution (cont.)", doc.internal.pageSize.getWidth() / 2, yPos, {align: 'center'}); yPos += 7; } doc.addImage(chartImage, 'PNG', xOffset, yPos, pdfChartWidth, chartHeight); } catch(e) { console.error("Error adding chart to PDF:", e); } } doc.save(`${projectName.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_sustainability_estimate.pdf`); } // --- Initialization --- sbpRenderSustainabilityAreaOptions(); sbpRenderInitiativesTable(); sbpShowTab(0); sbpUpdateSummaryReport(); });
Scroll to Top