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.

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