add core revenue to model and allow to tweak params
This commit is contained in:
parent
a07788cb74
commit
491dbacda4
7 changed files with 476 additions and 118 deletions
|
|
@ -78,12 +78,17 @@ class ROICalculator {
|
|||
const graceElement = document.getElementById('grace-period');
|
||||
const gracePeriod = graceElement ? parseInt(graceElement.value) || 6 : 6;
|
||||
|
||||
// Get core service revenue with validation
|
||||
const coreRevenueElement = document.getElementById('core-service-revenue');
|
||||
const coreServiceRevenue = coreRevenueElement ? parseFloat(coreRevenueElement.value) || 0 : 0;
|
||||
|
||||
return {
|
||||
investmentAmount,
|
||||
timeframe,
|
||||
investmentModel,
|
||||
loanInterestRate,
|
||||
revenuePerInstance,
|
||||
coreServiceRevenue,
|
||||
servalaShare,
|
||||
gracePeriod
|
||||
};
|
||||
|
|
@ -96,6 +101,7 @@ class ROICalculator {
|
|||
investmentModel: 'direct',
|
||||
loanInterestRate: 0.05,
|
||||
revenuePerInstance: 50,
|
||||
coreServiceRevenue: 0,
|
||||
servalaShare: 0.25,
|
||||
gracePeriod: 6
|
||||
};
|
||||
|
|
@ -204,7 +210,10 @@ class ROICalculator {
|
|||
monthlyRevenue = monthlyLoanPayment;
|
||||
} else {
|
||||
// Direct investment model: Revenue based on instances with performance incentives
|
||||
monthlyRevenue = currentInstances * inputs.revenuePerInstance;
|
||||
// Service revenue (shared with Servala) + Core service revenue (100% to CSP)
|
||||
const serviceRevenue = currentInstances * inputs.revenuePerInstance;
|
||||
const coreRevenue = currentInstances * inputs.coreServiceRevenue;
|
||||
monthlyRevenue = serviceRevenue;
|
||||
|
||||
// Calculate performance bonus if CSP exceeds baseline expectations
|
||||
if (baselineInstances > 0 && month > 6) { // Start performance tracking after 6 months
|
||||
|
|
@ -217,11 +226,13 @@ class ROICalculator {
|
|||
|
||||
// Determine revenue split based on dynamic grace period
|
||||
if (month <= effectiveGracePeriod) {
|
||||
cspRevenue = monthlyRevenue;
|
||||
// During grace period: CSP keeps all service revenue + core revenue
|
||||
cspRevenue = serviceRevenue + coreRevenue;
|
||||
servalaRevenue = 0;
|
||||
} else {
|
||||
cspRevenue = monthlyRevenue * (1 - adjustedServalaShare);
|
||||
servalaRevenue = monthlyRevenue * adjustedServalaShare;
|
||||
// After grace period: CSP keeps share of service revenue + all core revenue
|
||||
cspRevenue = (serviceRevenue * (1 - adjustedServalaShare)) + coreRevenue;
|
||||
servalaRevenue = serviceRevenue * adjustedServalaShare;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,6 +255,18 @@ class ROICalculator {
|
|||
breakEvenMonth = month;
|
||||
}
|
||||
|
||||
// Calculate revenue components for data tracking
|
||||
let serviceRevenueForData, coreRevenueForData, totalRevenueForData;
|
||||
if (inputs.investmentModel === 'loan') {
|
||||
serviceRevenueForData = monthlyLoanPayment;
|
||||
coreRevenueForData = 0;
|
||||
totalRevenueForData = monthlyLoanPayment;
|
||||
} else {
|
||||
serviceRevenueForData = currentInstances * inputs.revenuePerInstance;
|
||||
coreRevenueForData = currentInstances * inputs.coreServiceRevenue;
|
||||
totalRevenueForData = serviceRevenueForData + coreRevenueForData;
|
||||
}
|
||||
|
||||
monthlyData.push({
|
||||
month,
|
||||
scenario: scenario.name,
|
||||
|
|
@ -251,7 +274,10 @@ class ROICalculator {
|
|||
churnedInstances,
|
||||
totalInstances: currentInstances,
|
||||
baselineInstances,
|
||||
monthlyRevenue,
|
||||
serviceRevenue: serviceRevenueForData,
|
||||
coreRevenue: coreRevenueForData,
|
||||
totalRevenue: totalRevenueForData,
|
||||
monthlyRevenue: serviceRevenueForData, // Keep for backward compatibility
|
||||
cspRevenue,
|
||||
servalaRevenue,
|
||||
cumulativeCSPRevenue,
|
||||
|
|
|
|||
|
|
@ -230,7 +230,9 @@ class ExportManager {
|
|||
const params = [
|
||||
['Investment Amount', this.formatCHF(inputs.investmentAmount)],
|
||||
['Investment Timeframe', `${inputs.timeframe} years`],
|
||||
['Revenue per Instance', `${this.formatCHF(inputs.revenuePerInstance)} / month`],
|
||||
['Service Revenue per Instance', `${this.formatCHF(inputs.revenuePerInstance)} / month`],
|
||||
['Core Service Revenue per Instance', `${this.formatCHF(inputs.coreServiceRevenue)} / month`],
|
||||
['Total Revenue per Instance', `${this.formatCHF(inputs.revenuePerInstance + inputs.coreServiceRevenue)} / month`],
|
||||
['Loan Interest Rate', `${(inputs.loanInterestRate * 100).toFixed(1)}%`],
|
||||
['Direct Investment Share', `${(inputs.servalaShare * 100).toFixed(0)}% to Servala`],
|
||||
['Grace Period', `${inputs.gracePeriod} months`]
|
||||
|
|
@ -526,7 +528,9 @@ class ExportManager {
|
|||
if (inputs.investmentModel === 'loan') {
|
||||
csvContent += `Loan Interest Rate (%),${(inputs.loanInterestRate * 100).toFixed(1)}\n`;
|
||||
}
|
||||
csvContent += `Revenue per Instance,${inputs.revenuePerInstance}\n`;
|
||||
csvContent += `Service Revenue per Instance,${inputs.revenuePerInstance}\n`;
|
||||
csvContent += `Core Service Revenue per Instance,${inputs.coreServiceRevenue}\n`;
|
||||
csvContent += `Total Revenue per Instance,${inputs.revenuePerInstance + inputs.coreServiceRevenue}\n`;
|
||||
if (inputs.investmentModel === 'direct') {
|
||||
csvContent += `Servala Share (%),${(inputs.servalaShare * 100).toFixed(0)}\n`;
|
||||
csvContent += `Grace Period (months),${inputs.gracePeriod}\n`;
|
||||
|
|
|
|||
|
|
@ -198,6 +198,18 @@ class ROICalculatorApp {
|
|||
}
|
||||
}
|
||||
|
||||
updateCoreServiceRevenue(value) {
|
||||
try {
|
||||
const element = document.getElementById('core-service-revenue');
|
||||
if (element) {
|
||||
element.value = value;
|
||||
this.updateCalculations();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating core service revenue:', error);
|
||||
}
|
||||
}
|
||||
|
||||
updateScenarioChurn(scenarioKey, churnRate) {
|
||||
try {
|
||||
if (this.calculator && this.calculator.scenarios[scenarioKey]) {
|
||||
|
|
|
|||
|
|
@ -183,7 +183,9 @@ class UIManager {
|
|||
<td><span style="color: ${scenarioColor}" class="fw-bold">${data.scenario}</span></td>
|
||||
<td>${modelBadge}</td>
|
||||
<td class="text-end">${data.totalInstances ? data.totalInstances.toLocaleString() : '0'}</td>
|
||||
<td class="text-end">${this.formatCurrencyDetailed(data.monthlyRevenue || 0)}</td>
|
||||
<td class="text-end">${this.formatCurrencyDetailed(data.serviceRevenue || data.monthlyRevenue || 0)}</td>
|
||||
<td class="text-end">${this.formatCurrencyDetailed(data.coreRevenue || 0)}</td>
|
||||
<td class="text-end fw-bold">${this.formatCurrencyDetailed(data.totalRevenue || data.monthlyRevenue || 0)}</td>
|
||||
<td class="text-end fw-bold">${this.formatCurrencyDetailed(data.cspRevenue || 0)}</td>
|
||||
<td class="text-end text-muted">${this.formatCurrencyDetailed(data.servalaRevenue || 0)}</td>
|
||||
<td class="text-end fw-bold ${netPositionClass}">${this.formatCurrencyDetailed(data.netPosition || 0)}</td>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue