Business Technology Upgrade Cost Calculator

Upgrade Project Overview

Technology Cost Details

Sections below appear based on "Scope of Upgrade" from Tab 1. For recurring costs like subscriptions, enter the cost for its typical billing cycle; the tool will estimate the first year's impact on the upgrade budget.

Hardware Costs

$
$
$
$
$
$

Software Costs

$
$
$
$
$
$

Network Infrastructure

$
$
$

Cloud Services

$
$
$

IT Security Enhancements

$
$
$

Implementation & Project Costs

$
$
$

Decommissioning & Disposal

$

Custom Upgrade Costs

No custom costs added.

Contingency Planning

%

Upgrade Project Budget Summary

Complete project setup and cost details in previous tabs to see the summary.

Number of Users/Employees Directly Affected must be at least 1.

"; document.getElementById('btuc-download-pdf').disabled = true; return; } const allCosts = []; let subtotalDirectCosts = 0; document.querySelectorAll('#costs .btuc-cost-category-section').forEach(section => { if (section.style.display === 'block' || section.classList.contains('btuc-visible')) { // Only process visible sections // One-time costs section.querySelectorAll('.btuc-cost-item').forEach(input => { const cost = getNum(input.value); if (cost > 0) { allCosts.push({ category: input.dataset.category, name: input.dataset.itemName, amount: cost, type: 'One-Time' }); subtotalDirectCosts += cost; } }); // Recurring costs (calculate first year) section.querySelectorAll('.btuc-cost-item-recurring-val').forEach(input => { const costPerCycle = getNum(input.value); const cycleSelect = section.querySelector(`.btuc-cost-item-recurring-cycle[data-target-id="${input.id}"]`) || section.querySelector(`#${input.id.replace('-cost', '-cycle')}`); // Find corresponding cycle select const cycle = cycleSelect ? cycleSelect.value : 'Annually'; if (costPerCycle > 0) { let firstYearCost = costPerCycle; if (cycle === 'Monthly') firstYearCost *= 12; // If Annually, it's already the first year cost. allCosts.push({ category: input.dataset.category, name: input.dataset.itemName + ` (${currency}${costPerCycle.toFixed(2)}/${cycle})`, amount: firstYearCost, type: 'First Year Recurring' }); subtotalDirectCosts += firstYearCost; } }); } }); document.querySelectorAll('.btuc-custom-cost-item').forEach(item => { const name = item.querySelector('.btuc-custom-name').value || 'Custom Cost'; const amount = getNum(item.querySelector('.btuc-custom-amount').value); const type = item.querySelector('.btuc-custom-type').value; // OneTime or RecurringFirstYear if (amount > 0) { allCosts.push({ category: 'Custom Upgrade Costs', name, amount, type }); subtotalDirectCosts += amount; } }); const contingencyType = CONFIG_INPUTS.contingencyType.value; const contingencyValue = getNum(CONFIG_INPUTS.contingencyValue.value); let contingencyAmount = 0; if (contingencyType === 'percentage') { contingencyAmount = subtotalDirectCosts * (contingencyValue / 100); } else { // fixed contingencyAmount = contingencyValue; } const grandTotal = subtotalDirectCosts + contingencyAmount; html += `

Upgrade Project Budget Summary:

`; html += `

Project Name: ${CONFIG_INPUTS.projectName.value || 'N/A'}

`; const selectedScopesText = Array.from(CONFIG_INPUTS.scopeCheckboxes).filter(cb=>cb.checked).map(cb=>cb.labels[0].textContent.trim()).join(', '); html += `

Scope: ${selectedScopesText || 'N/A'}

`; html += `

Users Affected: ${numUsers}

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

Estimated Costs Breakdown:

'; html += ''; if (allCosts.length === 0 && contingencyAmount === 0) { html += ''; } else { const groupedByCategory = allCosts.reduce((acc, item) => { (acc[item.category] = acc[item.category] || []).push(item); return acc; }, {}); for (const category in groupedByCategory) { html += ``; groupedByCategory[category].forEach(item => { html += ``; }); } html += ``; html += ``; } html += ``; html += '
CategoryItemTypeEstimated Cost
No costs entered.
${category}
${item.name} ${item.type} ${currency}${item.amount.toFixed(2)}
Subtotal of Direct Costs:${currency}${subtotalDirectCosts.toFixed(2)}
Contingency (${contingencyType === 'percentage' ? contingencyValue + '%' : 'Fixed'}):${currency}${contingencyAmount.toFixed(2)}
GRAND TOTAL ESTIMATED UPGRADE COST:${currency}${grandTotal.toFixed(2)}
'; html += `

Estimated Cost Per Affected User: ${currency}${(numUsers > 0 ? grandTotal / numUsers : 0).toFixed(2)}

`; summaryOutput.innerHTML = html; document.getElementById('btuc-download-pdf').disabled = allCosts.length === 0 && contingencyAmount === 0; } // PDF Download document.getElementById('btuc-download-pdf').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 = CONFIG_INPUTS.currencySymbol.value || '$'; const numUsers = getNum(CONFIG_INPUTS.numUsers.value); const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--btuc-primary-color').trim(); const secondaryColor = getComputedStyle(document.documentElement).getPropertyValue('--btuc-secondary-color').trim(); const textColor = getComputedStyle(document.documentElement).getPropertyValue('--btuc-text-color').trim(); const buttonTextColor = getComputedStyle(document.documentElement).getPropertyValue('--btuc-button-text-color').trim(); doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text('Business Technology Upgrade Cost Estimate', doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' }); doc.setFontSize(12); doc.setTextColor(textColor); let lastY = 30; doc.text(`Business Name: ${CONFIG_INPUTS.businessName.value || 'N/A'}`, 14, lastY); lastY += 7; doc.text(`Project Name: ${CONFIG_INPUTS.projectName.value || 'N/A'}`, 14, lastY); lastY += 7; const selectedScopesText = Array.from(CONFIG_INPUTS.scopeCheckboxes).filter(cb=>cb.checked).map(cb=>cb.labels[0].textContent.trim()).join(', '); doc.text(`Scope: ${selectedScopesText || 'N/A'}`, 14, lastY); lastY += 7; doc.text(`Users Affected: ${numUsers}`, 14, lastY); lastY += 7; doc.text(`Project Duration: ${CONFIG_INPUTS.projectDuration.value || 'N/A'}`, 14, lastY); lastY += 7; doc.text(`Currency: ${currency}`, 14, lastY); lastY += 10; const allCostsPdf = []; let subtotalDirectCostsPdf = 0; // Re-collect for PDF document.querySelectorAll('#costs .btuc-cost-category-section').forEach(section => { if (section.style.display === 'block' || section.classList.contains('btuc-visible')) { section.querySelectorAll('.btuc-cost-item').forEach(input => { const cost = getNum(input.value); if (cost > 0) { allCostsPdf.push({ category: input.dataset.category, name: input.dataset.itemName, type: 'One-Time', amount: cost }); subtotalDirectCostsPdf += cost; } }); section.querySelectorAll('.btuc-cost-item-recurring-val').forEach(input => { const costPerCycle = getNum(input.value); const cycleSelect = section.querySelector(`.btuc-cost-item-recurring-cycle[data-target-id="${input.id}"]`) || section.querySelector(`#${input.id.replace('-cost', '-cycle')}`); const cycle = cycleSelect ? cycleSelect.value : 'Annually'; if (costPerCycle > 0) { let firstYearCost = (cycle === 'Monthly') ? costPerCycle * 12 : costPerCycle; allCostsPdf.push({ category: input.dataset.category, name: input.dataset.itemName + ` (${currency}${costPerCycle.toFixed(2)}/${cycle})`, type: '1st Year Recurring', amount: firstYearCost }); subtotalDirectCostsPdf += firstYearCost; } }); } }); document.querySelectorAll('.btuc-custom-cost-item').forEach(item => { const name = item.querySelector('.btuc-custom-name').value || 'Custom Cost'; const amount = getNum(item.querySelector('.btuc-custom-amount').value); const type = item.querySelector('.btuc-custom-type').value; if (amount > 0) { allCostsPdf.push({ category: 'Custom Upgrade Costs', name, type, amount }); subtotalDirectCostsPdf += amount; } }); const contingencyTypePdf = CONFIG_INPUTS.contingencyType.value; const contingencyValuePdf = getNum(CONFIG_INPUTS.contingencyValue.value); let contingencyAmountPdf = (contingencyTypePdf === 'percentage') ? subtotalDirectCostsPdf * (contingencyValuePdf / 100) : contingencyValuePdf; const grandTotalPdf = subtotalDirectCostsPdf + contingencyAmountPdf; const tableBody = []; const groupedByCategoryPdf = allCostsPdf.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' } }]); groupedByCategoryPdf[category].forEach(item => { tableBody.push([item.name, item.type, `${currency}${item.amount.toFixed(2)}`]); }); } tableBody.push([ { content: 'Subtotal of Direct Costs:', colSpan: 2, styles: { halign: 'right', fontStyle: 'bold'} }, { content: `${currency}${subtotalDirectCostsPdf.toFixed(2)}`, styles: { halign: 'right', fontStyle: 'bold' } } ]); tableBody.push([ { content: `Contingency (${contingencyTypePdf === 'percentage' ? contingencyValuePdf + '%' : 'Fixed'}):`, colSpan: 2, styles: { halign: 'right', fontStyle: 'bold'} }, { content: `${currency}${contingencyAmountPdf.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}${grandTotalPdf.toFixed(2)}`, styles: { halign: 'right', fontStyle: 'bold', fillColor: primaryColor, textColor: buttonTextColor } } ]); doc.autoTable({ startY: lastY, head: [['Item/Category', 'Type', 'Estimated Cost']], 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 */ } } }); lastY = doc.lastAutoTable.finalY + 10; doc.setFontSize(10); doc.setTextColor(textColor); doc.text(`Estimated Cost Per Affected User: ${currency}${(numUsers > 0 ? grandTotalPdf / numUsers : 0).toFixed(2)}`, 14, lastY); doc.save(`Tech_Upgrade_Budget_${CONFIG_INPUTS.projectName.value.replace(/\s+/g, '_') || 'Report'}.pdf`); }); // Initial setup checkEmptyState(COST_CONTAINERS.customCosts, 'btuc-no-items-text'); toggleCostSections(); // Initial call to set visibility based on default scope toggleContingencyInputType(); // Set initial contingency input style showTab(0); updateAllCurrencyPrefixes(); });
Scroll to Top