Workplace Safety & Compliance Budget Tool

General Settings

Safety & Compliance Expense Items

Training & Education

No Training items added.

Personal Protective Equipment (PPE)

No PPE items added.

Safety Equipment & Infrastructure

No Equipment items added.

Inspections, Audits & Certifications

No Inspection/Audit items added.

Health & Environmental

No Health/Environmental items added.

Consultancy & Legal

No Consultancy/Legal items added.

Software & Technology

No Software/Tech items added.

Emergency Preparedness

No Emergency Prep items added.

Custom/Other Expenses

No Custom items added.

Safety & Compliance Budget Summary

Complete expense details in Tab 2 to see the summary.

Average Cost Per Employee (${overallBudgetPeriod}): ${currency}${(totalEmployees > 0 ? grandTotalCostForPeriod / totalEmployees : 0).toFixed(2)}

`; if (totalLocations > 0) { html += `

Average Cost Per Location (${overallBudgetPeriod}): ${currency}${(grandTotalCostForPeriod / totalLocations).toFixed(2)}

`; } html += '
'; html += '

Detailed Breakdown:

'; html += ''; if (allItems.length === 0) { html += ''; } else { const groupedByCategory = allItems.reduce((acc, item) => { (acc[item.category] = acc[item.category] || []).push(item); return acc; }, {}); for (const category in groupedByCategory) { html += ``; let categorySubtotal = 0; groupedByCategory[category].forEach(item => { html += ``; categorySubtotal += item.calculatedCost; }); html += ``; } } html += ``; html += '
CategoryExpense ItemBasis & FrequencyEst. Cost for Period
No expenses entered.
${category}
${item.name} ${item.basisDetails} ${currency}${item.calculatedCost.toFixed(2)}
Subtotal for ${category}:${currency}${categorySubtotal.toFixed(2)}
GRAND TOTAL:${currency}${grandTotalCostForPeriod.toFixed(2)}
'; summaryOutput.innerHTML = html; downloadPdfButton.disabled = allItems.length === 0; } // PDF Download downloadPdfButton.addEventListener('click', function () { if (typeof jspdf === 'undefined' || typeof jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library is not loaded.'); return; } const { jsPDF } = jspdf; const doc = new jsPDF('p'); const currency = currencySymbolInput.value || '$'; const overallBudgetPeriod = getVal('wscb-budget-period'); const totalEmployees = getNum('wscb-num-employees', true); const totalLocations = getNum('wscb-num-locations', true); const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--wscb-primary-color').trim(); const secondaryColor = getComputedStyle(document.documentElement).getPropertyValue('--wscb-secondary-color').trim(); const textColor = getComputedStyle(document.documentElement).getPropertyValue('--wscb-text-color').trim(); const buttonTextColor = getComputedStyle(document.documentElement).getPropertyValue('--wscb-button-text-color').trim(); doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text('Workplace Safety & Compliance Budget', doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' }); doc.setFontSize(12); doc.setTextColor(textColor); let lastY = 30; doc.text(`Business Name: ${getVal('wscb-business-name') || 'N/A'}`, 14, lastY); lastY += 7; doc.text(`Budget Period: ${overallBudgetPeriod}`, 14, lastY); lastY += 7; doc.text(`Total Employees: ${totalEmployees}`, 14, lastY); lastY += 7; if (totalLocations > 0) { doc.text(`Total Locations: ${totalLocations}`, 14, lastY); lastY += 7; } doc.text(`Currency: ${currency}`, 14, lastY); lastY += 7; const industry = getVal('wscb-industry-type'); if(industry) { doc.text(`Industry: ${industry}`, 14, lastY); lastY+=7;} lastY += 3; // Extra space before summary totals const allItemsPdf = []; // Re-collect for PDF let grandTotalCostForPeriodPdf = 0; // Simplified re-collection logic - assuming generateBudgetSummary() populates 'allItems' correctly // For a truly robust PDF, recalculate everything here like in generateBudgetSummary() document.querySelectorAll('.wscb-expense-item').forEach(itemEl => { const itemName = itemEl.querySelector('.wscb-item-name').value || 'Unnamed Item'; const itemCategory = itemEl.dataset.itemCategory; const costAmount = getNum(itemEl.querySelector('.wscb-item-cost-amount').value); const costBasis = itemEl.querySelector('.wscb-item-cost-basis').value; const itemFrequency = itemEl.querySelector('.wscb-item-frequency').value; let unitsParticipantsInput = itemEl.querySelector('.wscb-item-units-participants').value; let numUnitsOrRate = 0; let isRate = false; if (costBasis === 'perUnitParticipant') { if (unitsParticipantsInput.includes('%')) { numUnitsOrRate = getNum(unitsParticipantsInput.replace('%','')) / 100; isRate = true; } else { numUnitsOrRate = getNum(unitsParticipantsInput); } } let baseMultiplier = 1; if (costBasis === 'perEmployee') baseMultiplier = totalEmployees; else if (costBasis === 'perLocation') baseMultiplier = totalLocations; else if (costBasis === 'perUnitParticipant') baseMultiplier = isRate ? totalEmployees * numUnitsOrRate : numUnitsOrRate; let itemAnnualCost = costAmount; if (itemFrequency === 'Monthly') itemAnnualCost = costAmount * 12; else if (itemFrequency === 'Quarterly') itemAnnualCost = costAmount * 4; else if (itemFrequency === 'Annually') itemAnnualCost = costAmount * 1; else if (itemFrequency === 'Biennially') itemAnnualCost = costAmount / 2; else if (itemFrequency === 'Triennially') itemAnnualCost = costAmount / 3; else if (itemFrequency === 'OneTime') { if (overallBudgetPeriod === 'Monthly') itemAnnualCost = costAmount * 12; else if (overallBudgetPeriod === 'Quarterly') itemAnnualCost = costAmount * 4; else itemAnnualCost = costAmount; } let costForThisItemForPeriod = 0; if (overallBudgetPeriod === 'Monthly') costForThisItemForPeriod = (itemAnnualCost / 12) * baseMultiplier; else if (overallBudgetPeriod === 'Quarterly') costForThisItemForPeriod = (itemAnnualCost / 4) * baseMultiplier; else if (overallBudgetPeriod === 'Annually') costForThisItemForPeriod = itemAnnualCost * baseMultiplier; if (itemFrequency === 'OneTime' && costBasis === 'totalSetAmount') { costForThisItemForPeriod = costAmount; } let basisDetails = `${currency}${costAmount.toFixed(2)}`; if(costBasis !== 'totalSetAmount') basisDetails += ` / ${costBasis.replace('per','').toLowerCase()}`; basisDetails += ` (${itemFrequency})`; if(costBasis === 'perUnitParticipant') basisDetails += ` for ${unitsParticipantsInput}${isRate ? ' of emp.' : ' items'}`; allItemsPdf.push({ category: itemCategory, name: itemName, basisDetails: basisDetails, calculatedCost: costForThisItemForPeriod }); grandTotalCostForPeriodPdf += costForThisItemForPeriod; }); doc.setFontSize(10); doc.text(`Total Estimated Budget (${overallBudgetPeriod}): ${currency}${grandTotalCostForPeriodPdf.toFixed(2)}`, 14, lastY); lastY += 7; doc.text(`Average Cost Per Employee (${overallBudgetPeriod}): ${currency}${(totalEmployees > 0 ? grandTotalCostForPeriodPdf / totalEmployees : 0).toFixed(2)}`, 14, lastY); lastY += 7; if (totalLocations > 0) { doc.text(`Average Cost Per Location (${overallBudgetPeriod}): ${currency}${(grandTotalCostForPeriodPdf / totalLocations).toFixed(2)}`, 14, lastY); lastY += 7; } lastY += 3; const tableBody = []; const groupedByCategoryPdf = allItemsPdf.reduce((acc, item) => { (acc[item.category] = acc[item.category] || []).push(item); return acc; }, {}); for (const category in groupedByCategoryPdf) { tableBody.push([{ content: category, colSpan: 3, styles: { fontStyle: 'bold', fillColor: '#f0f0f0' } }]); let categorySubtotalPdf = 0; groupedByCategoryPdf[category].forEach(item => { tableBody.push([item.name, item.basisDetails, `${currency}${item.calculatedCost.toFixed(2)}`]); categorySubtotalPdf += item.calculatedCost; }); tableBody.push([ { content: `Subtotal for ${category}`, colSpan: 2, styles: { halign: 'right', fontStyle: 'bold'} }, { content: `${currency}${categorySubtotalPdf.toFixed(2)}`, styles: { halign: 'right', fontStyle: 'bold' } } ]); } tableBody.push([ { content: `GRAND TOTAL`, colSpan: 2, styles: { halign: 'right', fontStyle: 'bold', fillColor: primaryColor, textColor: buttonTextColor } }, { content: `${currency}${grandTotalCostForPeriodPdf.toFixed(2)}`, styles: { halign: 'right', fontStyle: 'bold', fillColor: primaryColor, textColor: buttonTextColor } } ]); doc.autoTable({ startY: lastY, head: [['Expense Item', 'Basis & Frequency', `Est. Cost for ${overallBudgetPeriod}`]], body: tableBody, theme: 'grid', headStyles: { fillColor: secondaryColor, textColor: buttonTextColor, fontSize: 9 }, styles: { fontSize: 8, cellPadding: 2 }, columnStyles: { 2: { halign: 'right' } }, didDrawCell: (data) => { if (data.cell.raw && data.cell.raw.colSpan === 3) { // Category Header Row // Handled by autotable's colSpan styling } } }); doc.save(`Workplace_Safety_Budget_${getVal('wscb-business-name').replace(/\s+/g, '_') || 'Report'}.pdf`); }); // Initial setup for empty states const initialCategoriesContainers = [ "wscb-training-items-container", "wscb-ppe-items-container", "wscb-equipment-items-container", "wscb-inspections-items-container", "wscb-health-env-items-container", "wscb-consultancy-items-container", "wscb-software-items-container", "wscb-emergency-items-container", "wscb-custom-items-container" ]; initialCategoriesContainers.forEach(id => checkEmptyState(id)); showTab(0); updateAllCurrencyPrefixes(); });
Scroll to Top