Employee Relocation Budget Planner

Relocation Overview & Settings

Relocation Expense Details (Per Employee)

All standard costs below are entered as 'per employee'. Custom expenses can be 'per employee' or 'total for event'.

Pre-Move & Planning

$
$
$

Moving & Transportation

$
$
$

Travel to New Location

$

Temporary Living

$
$

Housing Assistance (New Location)

$
$
$

Settling-In Services

$
$
$

Financial Allowances/Bonuses

$
$
$

Custom Expenses

No custom expenses added.

Relocation Budget Summary

Complete relocation and expense details in previous tabs to see the summary.

Average Estimated Cost Per Employee: ${currency}${(numEmployees > 0 ? grandTotalCost / numEmployees : 0).toFixed(2)}

`; summaryOutput.innerHTML = html; downloadPdfButton.disabled = allExpenses.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 numEmployees = getNum('erb-num-employees', true); const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--erb-primary-color').trim(); const secondaryColor = getComputedStyle(document.documentElement).getPropertyValue('--erb-secondary-color').trim(); const textColor = getComputedStyle(document.documentElement).getPropertyValue('--erb-text-color').trim(); const buttonTextColor = getComputedStyle(document.documentElement).getPropertyValue('--erb-button-text-color').trim(); doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text('Employee Relocation Budget', doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' }); doc.setFontSize(12); doc.setTextColor(textColor); let lastY = 30; doc.text(`Business Name: ${getVal('erb-business-name') || 'N/A'}`, 14, lastY); lastY += 7; doc.text(`Event Name: ${getVal('erb-event-name') || 'N/A'}`, 14, lastY); lastY += 7; doc.text(`Origin: ${getVal('erb-origin') || 'N/A'} -> Destination: ${getVal('erb-destination') || 'N/A'}`, 14, lastY); lastY += 7; doc.text(`Number of Employees: ${numEmployees}`, 14, lastY); lastY += 7; doc.text(`Relocation Type: ${getVal('erb-relocation-type')}`, 14, lastY); lastY += 10; const allExpensesPdf = []; let grandTotalCostPdf = 0; const costsByCategoryPdf = {}; document.querySelectorAll('#expenses .erb-cost-item').forEach(input => { const fieldGroup = input.closest('.erb-form-group'); if (fieldGroup.style.display === 'none' && !(getVal('erb-relocation-type') === 'International' && fieldGroup.classList.contains('erb-international-field'))) return; const perEmpCost = getNum(input.value, true); if (perEmpCost > 0) { const totalForThisItem = perEmpCost * numEmployees; allExpensesPdf.push({ category: input.dataset.category, name: input.dataset.itemName, perEmpCost, totalCost: totalForThisItem }); costsByCategoryPdf[input.dataset.category] = (costsByCategoryPdf[input.dataset.category] || 0) + totalForThisItem; grandTotalCostPdf += totalForThisItem; } }); document.querySelectorAll('.erb-custom-expense-item').forEach(item => { const name = item.querySelector('.erb-custom-name').value || 'Custom Expense'; const cost = getNum(item.querySelector('.erb-custom-amount').value, true); const type = item.querySelector('.erb-custom-cost-type').value; let perEmpCostVal = 0, totalCostVal = 0; if (type === 'perEmployee') { perEmpCostVal = cost; totalCostVal = cost * numEmployees; } else { perEmpCostVal = (numEmployees > 0 ? cost / numEmployees : 0); totalCostVal = cost; } if (totalCostVal > 0) { allExpensesPdf.push({ category: 'Custom Expenses', name, perEmpCost: perEmpCostVal, totalCost: totalCostVal }); costsByCategoryPdf['Custom Expenses'] = (costsByCategoryPdf['Custom Expenses'] || 0) + totalCostVal; grandTotalCostPdf += totalCostVal; } }); const tableBody = []; const sortedCategoriesPdf = Object.keys(costsByCategoryPdf).sort(); sortedCategoriesPdf.forEach(category => { tableBody.push([{ content: category, colSpan: 3, styles: { fontStyle: 'bold', fillColor: '#f0f0f0' } }]); allExpensesPdf.filter(exp => exp.category === category).forEach(exp => { tableBody.push([exp.name, `${currency}${exp.perEmpCost.toFixed(2)}`, `${currency}${exp.totalCost.toFixed(2)}`]); }); tableBody.push([ { content: `Subtotal for ${category}`, colSpan: 2, styles: { halign: 'right', fontStyle: 'bold' } }, { content: `${currency}${costsByCategoryPdf[category].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}${grandTotalCostPdf.toFixed(2)}`, styles: { halign: 'right', fontStyle: 'bold', fillColor: primaryColor, textColor: buttonTextColor } } ]); doc.autoTable({ startY: lastY, head: [['Expense Item', 'Cost Per Employee', 'Total Estimated Cost']], body: tableBody, theme: 'grid', headStyles: { fillColor: secondaryColor, textColor: buttonTextColor, fontSize: 10 }, styles: { fontSize: 9, cellPadding: 2 }, columnStyles: { 1: { halign: 'right' }, 2: { halign: 'right' } }, didDrawCell: (data) => { if (data.cell.raw && data.cell.raw.colSpan === 3) { // Category Header Row // Styling handled by autotable itself with colSpan } } }); lastY = doc.lastAutoTable.finalY + 10; doc.setFontSize(10); doc.setTextColor(textColor); doc.text(`Average Estimated Cost Per Employee: ${currency}${(numEmployees > 0 ? grandTotalCostPdf / numEmployees : 0).toFixed(2)}`, 14, lastY); doc.save(`Employee_Relocation_Budget_${getVal('erb-event-name').replace(/\s+/g, '_') || 'Report'}.pdf`); }); // Initial setup checkEmptyState('#erb-custom-expenses-container', 'erb-no-items-text'); toggleInternationalFields(); // Initial check for relocation type showTab(0); updateAllCurrencyPrefixes(); // Initial call });
Scroll to Top