Planning software TCO calculator

What a planning system really costs, and how long before it pays.

Pick your business, the modules you're evaluating and your size. You'll see software, implementation, services and internal team costs, the time to full value, and what a TCO model can't capture.

1Type of business
2Software under consideration
3Business size
4Situation
Adjust assumptions
$50K$300K
$50$500
Use a vendor quote to replace the typical software estimate.
Leave blank to keep the typical implementation estimate.
Total cost of ownership
–
Months before full value
–
Software fees paid before full value
–

Cost breakdown

Shaded band: where most deals landTypical

Time to value

Selection happens before you sign; the contract clock starts at signing. Hover a phase for detail.

What TCO doesn't capture

The real cost of a poor fit is that people stop using it.

Accuracy is one measure, not the measure. A poor fit doesn't show up as a line item. It shows up as work around the system, and it isn't in the totals above.

Lack of trust

Planners can't see why it made a call, so they override it.

Ask: Can a planner see why, in their terms?
Adoption stalls, work doubles

The system stays the book of record, but planning moves to Excel: export, rework, import back.

Ask: Which spreadsheets does this retire?
Breaks when conditions change

Fine in the demo. Under shortages, promotions or new items, it makes calls a planner wouldn't.

Ask: Show it on our data, under disruption.
The clock resets

A replacement restarts selection, implementation and adoption, with a team that's now skeptical.

Ask: What evidence beyond the demo says it will stick?
Hard to extend

Supplier collaboration, allocation, pricing, S&OP and execution don't connect cleanly. Each becomes a workaround or another vendor.

Ask: What does adding the next capability cost?

METRAI tests how candidate systems behave on standardized scenarios, including whether planners would trust their calls, before you sign.

Schedule a Conversation

Save this estimate

Download a shareable worksheet with your assumptions, estimate, caveats, and questions to ask vendors.

Illustrative planning estimate — not a vendor quote or independent assessment.

Independent estimates based on METRAI’s operating experience and publicly available third-party ranges. Actual pricing depends on SKU and location counts, modules, hosting and negotiation. METRAI is vendor-neutral and takes no referral fees or commissions.
+(n/1e9).toFixed(1)+'B'; if(a>=1e6)return ' +(n/1e6).toFixed(a>=1e7?0:1)+'M'; if(a>=1e3)return ' +Math.round(n/1e3)+'K'; return ' +Math.round(n); } function scenario(i){ const sz=SIZES[S.size], ty=TYPES[S.type], term=+$('term').value, rep=$('sit').value==='replace'; const fteCost=(+$('fte').value||150)*1e3; const n=S.mods.size, row=PRICE[S.size][PRICE_ROW(S.type)]; const list=[...S.mods].reduce((s,m)=>s+(row[m]||0),0); const swQuote=+$('swQuote').value; const annualBase=list*BUNDLE[n]; const annual=(Number.isFinite(swQuote)&&swQuote>0?swQuote:annualBase)*PRICE_RANGE[i]; const implMo=sz.impl[i]+(n-1)*EXTRA_MOD_MO[i]+(rep?REPLACE_MO[i]:0); const termMo=term*12, liveMo=Math.max(0,termMo-implMo); let sw=0; for(let y=0;y0?implQuote*IMPL_SPREAD[i]:annual*sz.implX*IMPL_SPREAD[i]*(rep?1.2:1)); const vRate=(+$('vrate').value||200)*1800; const psMo=Math.min(liveMo,12)+0.5*Math.max(0,liveMo-12); const ps=VPS_FTE[S.size]*vRate*psMo/12*PS_SPREAD[i]; const int=(sz.fte*fteCost*implMo/12 + sz.ong*fteCost*liveMo/12)*INT_SPREAD[i]; return {annual,sw,impl,ps,int,total:sw+impl+ps+int,sel:sz.sel[i],implMo,adopt:sz.adopt[i],termMo,liveMo}; } const tip=$('tip'); function bindTip(el,html){ el.addEventListener('mousemove',e=>{tip.innerHTML=html;tip.style.opacity=1; const x=Math.min(e.clientX+14,innerWidth-280);tip.style.left=x+'px';tip.style.top=(e.clientY+14)+'px';}); el.addEventListener('mouseleave',()=>tip.style.opacity=0); } function syncMods(){ const row=PRICE[S.size][PRICE_ROW(S.type)]; document.querySelectorAll('#mods input').forEach(inp=>{ const ok=row[inp.value]!=null; inp.disabled=!ok; inp.parentElement.title=ok?'':'Not typically offered for '+TYPES[S.type].label.toLowerCase(); if(!ok){inp.checked=false;S.mods.delete(inp.value);} }); if(!S.mods.size){S.mods.add('forecast');document.querySelector('#mods input[value=forecast]').checked=true;} } function calc(){ syncMods(); $('sizeNote').textContent=S.size==='ent'?'Above ~$10B, pricing is usually bespoke; typical figures here skew low.':''; const ty=TYPES[S.type], term=+$('term').value; const sc=[0,1,2].map(scenario), T=sc[1]; // module notes const notes=MOD_NOTES[S.type]||{}; const msgs=[...S.mods].filter(m=>notes[m]).map(m=>notes[m]); if(TYPE_NOTES[S.type])msgs.push(TYPE_NOTES[S.type]); $('modNote').textContent=msgs.join(' '); $('modNote').className='note'; // KPIs $('kTcoLab').textContent=`${term}-year total cost of ownership`; $('kTco').textContent=money(T.total); $('kTcoR').textContent=`Software, implementation, services and internal team over ${term} years.`; const toValue=T.implMo+T.adopt; $('kTime').textContent=toValue; $('kTimeR').textContent=`after signing, plus ~${T.sel} months of selection. ${Math.round(toValue/T.termMo*100)}% of the term.`; // fees before full value const preFees=T.annual*(T.implMo+T.adopt)/12; $('kPre').textContent=money(preFees); $('kPreR').textContent=`Fees during implementation and change management, assuming planners adopt it.`; // cost ranges (typical shown; band = low-high spread, no numbers) const keys=['sw','impl','ps','int']; const nMods=S.mods.size, disc=Math.round((1-BUNDLE[nMods])*100); $('costSub').innerHTML=`${term}-year costs for ${[...S.mods].map(m=>MODS[m].label).join(' + ')}, ${SIZES[S.size].label.toLowerCase()} ${ty.label.toLowerCase()}. Annual software fee ${money(T.annual)}${nMods>1?' after multi-module discounts':''}.`; const R=$('ranges'); R.innerHTML=''; function row(name,sub,color,vals,scaleMax,total){ const d=document.createElement('div');d.className='rr'+(total?' total':''); const L=vals[0]/scaleMax*100, H=vals[2]/scaleMax*100, M=vals[1]/scaleMax*100; d.innerHTML=`
${total?'':``}
${name}${sub}
${money(vals[1])}
`; bindTip(d.querySelector('.rtrack'),`${name}: ${money(vals[1])} typicalMost deals land between ${money(vals[0])} and ${money(vals[2])}, depending on scope and negotiation.`); R.append(d); } row(`Total cost of ownership`,`${term} years`, '#0F172A', sc.map(s=>s.total), sc[2].total*1.05, true); const lineMax=Math.max(...keys.map(k=>sc[2][k]))*1.05; keys.forEach(k=>row(LABELS[k],BASIS[k],COLORS[k],sc.map(s=>s[k]),lineMax,false)); // timeline (typical) const segs=[ {k:'sel',l:'Selection',mo:T.sel,c:'',hatch:true,d:'Requirements, RFP, demos, negotiation'}, {k:'impl',l:'Implementation',mo:T.implMo,c:'#0891B2',d:'Data, integration, configuration, testing, go-live'}, {k:'adopt',l:'Change management',mo:T.adopt,c:'#A855F7',d:'Planner adoption, tuning, trust in the numbers'}, {k:'val',l:'Full value',mo:Math.max(0,T.termMo-T.implMo-T.adopt),c:'#4F46E5',d:'Remaining contract term at full value'} ]; const tl=$('tl');tl.innerHTML=''; const totMo=segs.reduce((s,x)=>s+x.mo,0); segs.forEach(x=>{if(!x.mo)return;const d=document.createElement('div');d.className='seg'+(x.hatch?' hatch':''); if(x.c)d.style.background=x.c;d.style.flex=`${x.mo} 0 0`; d.textContent=(x.mo/totMo>0.09)?`${x.mo} mo`:''; bindTip(d,`${x.l} · ${x.mo} months${x.d}`);tl.append(d);}); tl.setAttribute('aria-label',segs.map(x=>`${x.l} ${x.mo} months`).join(', ')); $('tlAxis').innerHTML=`Start selectionSign · month ${T.sel}Contract ends · month ${totMo}`; $('tlLegend').innerHTML=segs.map(x=>`${x.l} · ${x.mo} mo`).join(''); const valYrs=Math.max(0,T.termMo-T.implMo-T.adopt)/12; $('tlCallout').innerHTML=`That leaves ${valYrs.toFixed(1)} of ${term} years at full value, and it assumes planners adopt the system and stop planning around it. What TCO doesn't capture ↓`; } if(['3','5','7'].includes(QP.get('term')))$('term').value=QP.get('term'); if(['replace','first'].includes(QP.get('start')))$('sit').value=QP.get('start'); calc(); })();