add core revenue to model and allow to tweak params

This commit is contained in:
Tobias Brunner 2025-07-23 14:29:54 +02:00
parent a07788cb74
commit 491dbacda4
Signed by: tobru
SSH key fingerprint: SHA256:kOXg1R6c11XW3/Pt9dbLdQvOJGFAy+B2K6v6PtRWBGQ
7 changed files with 476 additions and 118 deletions

View file

@ -387,6 +387,116 @@
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* Enhanced main configuration styling */
.main-config-section {
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.main-config-section .form-label {
color: #495057;
font-weight: 600;
}
.main-config-section .input-group-text {
background-color: #e9ecef;
border-color: #ced4da;
font-weight: 500;
}
/* Advanced controls styling */
.advanced-controls-section {
background: #f8f9fa;
border-top: 3px solid #007bff;
}
.advanced-controls-section .card {
transition: all 0.2s ease;
border: none;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.advanced-controls-section .card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.advanced-controls-section .card-header {
font-weight: 600;
border-bottom: 1px solid rgba(255,255,255,0.2);
}
/* Slider styling improvements */
.form-range {
height: 6px;
background: #e9ecef;
border-radius: 3px;
}
.form-range::-webkit-slider-thumb {
width: 18px;
height: 18px;
background: #007bff;
border: 3px solid #fff;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
cursor: pointer;
}
.form-range::-moz-range-thumb {
width: 18px;
height: 18px;
background: #007bff;
border: 3px solid #fff;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
cursor: pointer;
}
/* Real-time results cards */
.results-card {
transition: all 0.2s ease;
border: 2px solid transparent;
}
.results-card:hover {
border-color: #007bff;
transform: translateY(-1px);
}
/* Growth scenario checkboxes */
.form-check-input:checked {
background-color: #007bff;
border-color: #007bff;
}
.form-check-label {
cursor: pointer;
user-select: none;
transition: all 0.2s ease;
}
.form-check-label:hover {
color: #007bff;
}
/* Responsive improvements */
@media (max-width: 768px) {
.main-config-section {
margin: 0 -15px;
border-radius: 0;
}
.advanced-controls-section .card {
margin-bottom: 1rem;
}
.advanced-controls-section .card-body {
padding: 1rem;
}
}
/* Model comparison box */
.model-comparison-box {
border: 1px solid #dee2e6;

View file

@ -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,

View file

@ -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`;

View file

@ -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]) {

View file

@ -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>

View file

@ -32,6 +32,7 @@ function updateRevenuePerInstance(value) { window.ROICalculatorApp?.updateRevenu
function updateServalaShare(value) { window.ROICalculatorApp?.updateServalaShare(value); }
function updateGracePeriod(value) { window.ROICalculatorApp?.updateGracePeriod(value); }
function updateLoanRate(value) { window.ROICalculatorApp?.updateLoanRate(value); }
function updateCoreServiceRevenue(value) { window.ROICalculatorApp?.updateCoreServiceRevenue(value); }
function updateScenarioChurn(scenarioKey, churnRate) { window.ROICalculatorApp?.updateScenarioChurn(scenarioKey, churnRate); }
function updateScenarioPhase(scenarioKey, phaseIndex, newInstancesPerMonth) { window.ROICalculatorApp?.updateScenarioPhase(scenarioKey, phaseIndex, newInstancesPerMonth); }
function resetAdvancedParameters() { window.ROICalculatorApp?.resetAdvancedParameters(); }
@ -124,60 +125,65 @@ document.addEventListener('DOMContentLoaded', function() {
</div>
</div>
<!-- Organized Controls Section -->
<div class="py-3">
<!-- Primary Controls Row -->
<div class="row align-items-end mb-3">
<!-- Main Configuration Section -->
<div class="py-4">
<!-- Investment & Timeframe Row -->
<div class="row mb-4">
<!-- Investment Amount -->
<div class="col-lg-3 col-md-4 mb-2">
<label class="form-label small fw-semibold mb-1">Initial Investment</label>
<div class="input-group input-group-sm">
<div class="col-lg-4 col-md-6 mb-3">
<label class="form-label fw-semibold mb-2">Initial Investment</label>
<div class="input-group">
<span class="input-group-text">CHF</span>
<input type="text" class="form-control" id="investment-amount"
data-value="500000" value="500,000"
oninput="handleInvestmentAmountInput(this)"
onchange="updateCalculations()">
</div>
<input type="range" class="form-range mt-1" id="investment-slider"
<input type="range" class="form-range mt-2" id="investment-slider"
min="100000" max="2000000" step="50000" value="500000"
onchange="updateInvestmentAmount(this.value)">
</div>
<!-- Time & Revenue -->
<div class="col-lg-3 col-md-4 mb-2">
<div class="row">
<div class="col-6">
<label class="form-label small fw-semibold mb-1">Years</label>
<select class="form-select form-select-sm" id="timeframe" onchange="updateCalculations()">
<option value="1">1</option>
<option value="2">2</option>
<option value="3" selected>3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<div class="col-6">
<label class="form-label small fw-semibold mb-1">Revenue/Instance</label>
<div class="input-group input-group-sm">
<input type="number" class="form-control" id="revenue-per-instance"
min="20" max="200" step="5" value="50" onchange="updateCalculations()">
<span class="input-group-text">CHF</span>
</div>
<input type="range" class="form-range mt-1" id="revenue-slider"
min="20" max="200" step="5" value="50"
onchange="updateRevenuePerInstance(this.value)">
</div>
<div class="d-flex justify-content-between">
<small class="text-muted">CHF 100K</small>
<small class="text-muted">CHF 2M</small>
</div>
</div>
<!-- Timeframe -->
<div class="col-lg-2 col-md-3 mb-3">
<label class="form-label fw-semibold mb-2">Analysis Period</label>
<select class="form-select" id="timeframe" onchange="updateCalculations()">
<option value="1">1 Year</option>
<option value="2">2 Years</option>
<option value="3" selected>3 Years</option>
<option value="4">4 Years</option>
<option value="5">5 Years</option>
</select>
</div>
<!-- Service Revenue -->
<div class="col-lg-3 col-md-6 mb-3">
<label class="form-label fw-semibold mb-2">Service Revenue per Instance</label>
<div class="input-group">
<input type="number" class="form-control" id="revenue-per-instance"
min="20" max="200" step="5" value="50" onchange="updateCalculations()">
<span class="input-group-text">CHF/month</span>
</div>
<input type="range" class="form-range mt-2" id="revenue-slider"
min="20" max="200" step="5" value="50"
onchange="updateRevenuePerInstance(this.value)">
<div class="d-flex justify-content-between">
<small class="text-muted">CHF 20</small>
<small class="text-muted">CHF 200</small>
</div>
</div>
<!-- Actions -->
<div class="col-lg-2 col-md-4 mb-2">
<div class="d-flex justify-content-end gap-2">
<button class="btn btn-outline-info btn-sm" type="button" onclick="toggleAdvancedControls()" id="advancedToggleBtn">
<i class="bi bi-gear"></i> Advanced
<div class="col-lg-3 col-md-3 mb-3 d-flex align-items-end">
<div class="d-flex gap-2 w-100">
<button class="btn btn-outline-info" type="button" onclick="toggleAdvancedControls()" id="advancedToggleBtn">
<i class="bi bi-gear"></i> Advanced Settings
</button>
<a href="{% url 'services:roi_calculator_help' %}" class="btn btn-outline btn-sm" target="_blank">
<a href="{% url 'services:roi_calculator_help' %}" class="btn btn-outline-secondary" target="_blank">
<i class="bi bi-question-circle"></i> Help
</a>
</div>
@ -185,49 +191,54 @@ document.addEventListener('DOMContentLoaded', function() {
</div>
<!-- Scenarios & Results Row -->
<div class="row align-items-center">
<!-- Scenario Selection -->
<div class="col-lg-6 col-md-8 mb-2">
<label class="form-label small fw-semibold mb-2">Growth Scenarios</label>
<div class="d-flex gap-3 justify-content-center">
<div class="row">
<!-- Growth Scenarios -->
<div class="col-lg-6 mb-3">
<label class="form-label fw-semibold mb-2">Growth Scenarios</label>
<div class="d-flex gap-4 flex-wrap">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="conservative-enabled" checked onchange="toggleScenario('conservative')">
<label class="form-check-label small fw-medium" for="conservative-enabled" data-bs-toggle="tooltip" title="Conservative: 2% churn, steady growth">
<span class="text-success"></span> Conservative
<label class="form-check-label fw-medium" for="conservative-enabled" data-bs-toggle="tooltip" title="Conservative: 2% churn, steady growth">
<span class="text-success fs-5"></span> Conservative
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="moderate-enabled" checked onchange="toggleScenario('moderate')">
<label class="form-check-label small fw-medium" for="moderate-enabled" data-bs-toggle="tooltip" title="Moderate: 3% churn, balanced growth">
<span class="text-warning"></span> Moderate
<label class="form-check-label fw-medium" for="moderate-enabled" data-bs-toggle="tooltip" title="Moderate: 3% churn, balanced growth">
<span class="text-warning fs-5"></span> Moderate
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="aggressive-enabled" checked onchange="toggleScenario('aggressive')">
<label class="form-check-label small fw-medium" for="aggressive-enabled" data-bs-toggle="tooltip" title="Aggressive: 5% churn, rapid growth">
<span class="text-danger"></span> Aggressive
<label class="form-check-label fw-medium" for="aggressive-enabled" data-bs-toggle="tooltip" title="Aggressive: 5% churn, rapid growth">
<span class="text-danger fs-5"></span> Aggressive
</label>
</div>
</div>
</div>
<!-- Model Results -->
<div class="col-lg-6 col-md-4 mb-2">
<div class="row">
<div class="col-lg-6 mb-3">
<label class="form-label fw-semibold mb-2">Real-Time Results</label>
<div class="row g-2">
<div class="col-6">
<div class="text-center p-2 border rounded model-result-box">
<div class="text-success fw-semibold small mb-1">Direct Investment</div>
<div class="fw-bold" id="net-position-direct" style="font-size: 1.1rem; line-height: 1.2;">CHF 0</div>
<div class="fw-bold text-primary" id="roi-percentage-direct" style="font-size: 0.9rem; line-height: 1.2;">0%</div>
<div style="font-size: 0.7rem;" class="text-muted">Net Position / ROI</div>
<div class="card h-100">
<div class="card-body text-center p-3">
<div class="text-success fw-bold mb-2">Direct Investment</div>
<div class="h5 mb-1" id="net-position-direct">CHF 0</div>
<div class="text-primary fw-bold" id="roi-percentage-direct">0%</div>
<small class="text-muted">Net Profit / ROI</small>
</div>
</div>
</div>
<div class="col-6">
<div class="text-center p-2 border rounded model-result-box">
<div class="text-warning fw-semibold small mb-1">Loan Model</div>
<div class="fw-bold" id="net-position-loan" style="font-size: 1.1rem; line-height: 1.2;">CHF 0</div>
<div class="fw-bold text-primary" id="roi-percentage-loan" style="font-size: 0.9rem; line-height: 1.2;">0%</div>
<div style="font-size: 0.7rem;" class="text-muted">Net Position / ROI</div>
<div class="card h-100">
<div class="card-body text-center p-3">
<div class="text-warning fw-bold mb-2">Loan Model</div>
<div class="h5 mb-1" id="net-position-loan">CHF 0</div>
<div class="text-primary fw-bold" id="roi-percentage-loan">0%</div>
<small class="text-muted">Net Profit / ROI</small>
</div>
</div>
</div>
</div>
@ -235,53 +246,232 @@ document.addEventListener('DOMContentLoaded', function() {
</div>
</div>
<!-- Collapsible Advanced Controls -->
<!-- Advanced Controls Section -->
<div class="collapse" id="advancedControls">
<div class="row py-2 bg-white border-top">
<!-- Loan Rate (for loan model calculations) -->
<div class="col-md-2" id="loan-rate-section">
<label class="form-label small mb-1">Loan Rate (%)</label>
<input type="number" class="form-control form-control-sm" id="loan-interest-rate"
min="3" max="8" step="0.1" value="5.0" onchange="updateCalculations()">
<input type="range" class="form-range mt-1" id="loan-rate-slider"
min="3" max="8" step="0.1" value="5.0" onchange="updateLoanRate(this.value)">
</div>
<!-- Servala Share -->
<div class="col-md-2">
<label class="form-label small mb-1">Servala Share (%)</label>
<input type="number" class="form-control form-control-sm" id="servala-share"
min="10" max="40" step="1" value="25" onchange="updateCalculations()">
<input type="range" class="form-range mt-1" id="share-slider"
min="10" max="40" step="1" value="25" onchange="updateServalaShare(this.value)">
</div>
<!-- Grace Period -->
<div class="col-md-2">
<label class="form-label small mb-1">Grace Period (Mo)</label>
<input type="number" class="form-control form-control-sm" id="grace-period"
min="0" max="24" step="1" value="6" onchange="updateCalculations()">
<input type="range" class="form-range mt-1" id="grace-slider"
min="0" max="24" step="1" value="6" onchange="updateGracePeriod(this.value)">
</div>
<!-- Scenario Tuning -->
<div class="col-md-6">
<div class="bg-light border-top py-4">
<div class="container-fluid">
<h6 class="text-primary mb-4"><i class="bi bi-gear"></i> Advanced Configuration</h6>
<!-- Investment Model Parameters -->
<div class="row mb-4">
<div class="col-12">
<h6 class="text-secondary mb-3">Investment Model Settings</h6>
</div>
<!-- Loan Rate -->
<div class="col-lg-3 col-md-6 mb-3">
<label class="form-label fw-semibold mb-2">Loan Interest Rate</label>
<div class="input-group">
<input type="number" class="form-control" id="loan-interest-rate"
min="3" max="8" step="0.1" value="5.0" onchange="updateCalculations()">
<span class="input-group-text">% annual</span>
</div>
<input type="range" class="form-range mt-2" id="loan-rate-slider"
min="3" max="8" step="0.1" value="5.0" onchange="updateLoanRate(this.value)">
<div class="d-flex justify-content-between">
<small class="text-muted">3%</small>
<small class="text-muted">8%</small>
</div>
</div>
<!-- Servala Share -->
<div class="col-lg-3 col-md-6 mb-3">
<label class="form-label fw-semibold mb-2">Servala Revenue Share</label>
<div class="input-group">
<input type="number" class="form-control" id="servala-share"
min="10" max="40" step="1" value="25" onchange="updateCalculations()">
<span class="input-group-text">%</span>
</div>
<input type="range" class="form-range mt-2" id="share-slider"
min="10" max="40" step="1" value="25" onchange="updateServalaShare(this.value)">
<div class="d-flex justify-content-between">
<small class="text-muted">10%</small>
<small class="text-muted">40%</small>
</div>
</div>
<!-- Grace Period -->
<div class="col-lg-3 col-md-6 mb-3">
<label class="form-label fw-semibold mb-2">Grace Period</label>
<div class="input-group">
<input type="number" class="form-control" id="grace-period"
min="0" max="24" step="1" value="6" onchange="updateCalculations()">
<span class="input-group-text">months</span>
</div>
<input type="range" class="form-range mt-2" id="grace-slider"
min="0" max="24" step="1" value="6" onchange="updateGracePeriod(this.value)">
<div class="d-flex justify-content-between">
<small class="text-muted">0</small>
<small class="text-muted">24</small>
</div>
</div>
<!-- Core Service Revenue -->
<div class="col-lg-3 col-md-6 mb-3">
<label class="form-label fw-semibold mb-2">Core Service Revenue</label>
<div class="input-group">
<input type="number" class="form-control" id="core-service-revenue"
min="0" max="500" step="5" value="0" onchange="updateCalculations()">
<span class="input-group-text">CHF/month</span>
</div>
<input type="range" class="form-range mt-2" id="core-revenue-slider"
min="0" max="500" step="5" value="0" onchange="updateCoreServiceRevenue(this.value)">
<div class="d-flex justify-content-between">
<small class="text-muted">CHF 0</small>
<small class="text-muted">CHF 500</small>
</div>
<small class="text-muted">Additional compute/storage revenue per instance</small>
</div>
</div>
<!-- Scenario Parameters -->
<div class="row mb-4">
<div class="col-12">
<h6 class="text-secondary mb-3">Growth Scenario Tuning</h6>
</div>
<!-- Conservative Scenario -->
<div class="col-lg-4 mb-4">
<div class="card h-100">
<div class="card-header bg-success text-white">
<h6 class="mb-0"><i class="bi bi-shield-check"></i> Conservative Scenario</h6>
</div>
<div class="card-body">
<!-- Churn Rate -->
<div class="mb-3">
<label class="form-label fw-semibold mb-2">Monthly Churn Rate</label>
<div class="input-group input-group-sm">
<input type="number" class="form-control" id="conservative-churn"
min="0" max="10" step="0.1" value="2.0" onchange="updateScenarioChurn('conservative', this.value)">
<span class="input-group-text">%</span>
</div>
</div>
<!-- Phase Growth Parameters -->
<label class="form-label fw-semibold mb-2">Instance Growth per Phase</label>
<div class="row g-2">
<div class="col-6">
<label class="form-label small">Phase 1 (6mo)</label>
<input type="number" class="form-control form-control-sm" id="conservative-phase-0"
min="10" max="200" value="50" onchange="updateScenarioPhase('conservative', 0, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 2 (6mo)</label>
<input type="number" class="form-control form-control-sm" id="conservative-phase-1"
min="10" max="200" value="75" onchange="updateScenarioPhase('conservative', 1, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 3 (12mo)</label>
<input type="number" class="form-control form-control-sm" id="conservative-phase-2"
min="10" max="300" value="100" onchange="updateScenarioPhase('conservative', 2, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 4 (12mo)</label>
<input type="number" class="form-control form-control-sm" id="conservative-phase-3"
min="10" max="300" value="150" onchange="updateScenarioPhase('conservative', 3, this.value)">
</div>
</div>
</div>
</div>
</div>
<!-- Moderate Scenario -->
<div class="col-lg-4 mb-4">
<div class="card h-100">
<div class="card-header bg-warning text-dark">
<h6 class="mb-0"><i class="bi bi-speedometer2"></i> Moderate Scenario</h6>
</div>
<div class="card-body">
<!-- Churn Rate -->
<div class="mb-3">
<label class="form-label fw-semibold mb-2">Monthly Churn Rate</label>
<div class="input-group input-group-sm">
<input type="number" class="form-control" id="moderate-churn"
min="0" max="10" step="0.1" value="3.0" onchange="updateScenarioChurn('moderate', this.value)">
<span class="input-group-text">%</span>
</div>
</div>
<!-- Phase Growth Parameters -->
<label class="form-label fw-semibold mb-2">Instance Growth per Phase</label>
<div class="row g-2">
<div class="col-6">
<label class="form-label small">Phase 1 (6mo)</label>
<input type="number" class="form-control form-control-sm" id="moderate-phase-0"
min="20" max="300" value="100" onchange="updateScenarioPhase('moderate', 0, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 2 (6mo)</label>
<input type="number" class="form-control form-control-sm" id="moderate-phase-1"
min="20" max="400" value="200" onchange="updateScenarioPhase('moderate', 1, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 3 (12mo)</label>
<input type="number" class="form-control form-control-sm" id="moderate-phase-2"
min="20" max="500" value="300" onchange="updateScenarioPhase('moderate', 2, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 4 (12mo)</label>
<input type="number" class="form-control form-control-sm" id="moderate-phase-3"
min="20" max="600" value="400" onchange="updateScenarioPhase('moderate', 3, this.value)">
</div>
</div>
</div>
</div>
</div>
<!-- Aggressive Scenario -->
<div class="col-lg-4 mb-4">
<div class="card h-100">
<div class="card-header bg-danger text-white">
<h6 class="mb-0"><i class="bi bi-rocket"></i> Aggressive Scenario</h6>
</div>
<div class="card-body">
<!-- Churn Rate -->
<div class="mb-3">
<label class="form-label fw-semibold mb-2">Monthly Churn Rate</label>
<div class="input-group input-group-sm">
<input type="number" class="form-control" id="aggressive-churn"
min="0" max="15" step="0.1" value="5.0" onchange="updateScenarioChurn('aggressive', this.value)">
<span class="input-group-text">%</span>
</div>
</div>
<!-- Phase Growth Parameters -->
<label class="form-label fw-semibold mb-2">Instance Growth per Phase</label>
<div class="row g-2">
<div class="col-6">
<label class="form-label small">Phase 1 (6mo)</label>
<input type="number" class="form-control form-control-sm" id="aggressive-phase-0"
min="50" max="500" value="200" onchange="updateScenarioPhase('aggressive', 0, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 2 (6mo)</label>
<input type="number" class="form-control form-control-sm" id="aggressive-phase-1"
min="50" max="600" value="400" onchange="updateScenarioPhase('aggressive', 1, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 3 (12mo)</label>
<input type="number" class="form-control form-control-sm" id="aggressive-phase-2"
min="50" max="800" value="600" onchange="updateScenarioPhase('aggressive', 2, this.value)">
</div>
<div class="col-6">
<label class="form-label small">Phase 4 (12mo)</label>
<input type="number" class="form-control form-control-sm" id="aggressive-phase-3"
min="50" max="1000" value="800" onchange="updateScenarioPhase('aggressive', 3, this.value)">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Reset Button -->
<div class="row">
<div class="col-4">
<label class="form-label small mb-1">Conservative Churn (%)</label>
<input type="number" class="form-control form-control-sm" id="conservative-churn"
min="0" max="10" step="0.1" value="2.0" onchange="updateScenarioChurn('conservative', this.value)">
</div>
<div class="col-4">
<label class="form-label small mb-1">Moderate Churn (%)</label>
<input type="number" class="form-control form-control-sm" id="moderate-churn"
min="0" max="10" step="0.1" value="3.0" onchange="updateScenarioChurn('moderate', this.value)">
</div>
<div class="col-4">
<label class="form-label small mb-1">Aggressive Churn (%)</label>
<input type="number" class="form-control form-control-sm" id="aggressive-churn"
min="0" max="15" step="0.1" value="5.0" onchange="updateScenarioChurn('aggressive', this.value)">
<div class="col-12 text-center">
<button type="button" class="btn btn-outline-secondary" onclick="resetAdvancedParameters()">
<i class="bi bi-arrow-clockwise"></i> Reset to Defaults
</button>
</div>
</div>
</div>
@ -445,7 +635,9 @@ document.addEventListener('DOMContentLoaded', function() {
<th>Scenario</th>
<th>Model</th>
<th>Instances</th>
<th>Monthly Revenue</th>
<th>Service Revenue</th>
<th>Core Revenue</th>
<th>Total Revenue</th>
<th>Your Share</th>
<th>Servala Share</th>
<th>Cumulative Net Position</th>

View file

@ -189,6 +189,14 @@ html {
<h3>How It Works</h3>
<p>Invest directly in Servala's operations and earn returns through revenue sharing that scales with performance and investment size.</p>
<h3>Total Revenue Potential</h3>
<div class="model-card direct-model">
<h5><i class="bi bi-cash-stack"></i> Dual Revenue Streams</h5>
<p><strong>Service Revenue:</strong> Monthly fees from Servala managed services (shared with Servala based on your investment terms)</p>
<p><strong>Core Service Revenue:</strong> Additional revenue from selling compute, storage, and infrastructure to support each managed service instance (100% retained by CSP)</p>
<p class="text-muted"><small>Example: CHF 50 service fee + CHF 100 compute/storage = CHF 150 total revenue per instance per month</small></p>
</div>
<h4>Progressive Scaling Benefits</h4>
<div class="table-responsive">
@ -300,7 +308,7 @@ html {
<ul>
<li><strong>Initial Investment:</strong> CHF 100K - 2M (with slider)</li>
<li><strong>Timeframe:</strong> 1-5 years</li>
<li><strong>Revenue/Instance:</strong> Monthly income per managed service (CHF 20-200)</li>
<li><strong>Service Revenue/Instance:</strong> Monthly Servala service fee per managed instance (CHF 20-200)</li>
<li><strong>Growth Scenarios:</strong> Conservative, Moderate, Aggressive</li>
</ul>
</div>
@ -310,6 +318,7 @@ html {
<li><strong>Loan Rate:</strong> Annual interest (3-8%) - affects loan model calculations</li>
<li><strong>Servala Share:</strong> Revenue split percentage (10-40%) for direct investment</li>
<li><strong>Grace Period:</strong> 100% revenue retention period (0-24 months, direct investment)</li>
<li><strong>Core Revenue/Instance:</strong> Additional monthly revenue from selling compute/storage per instance (CHF 0-500)</li>
<li><strong>Churn Rates:</strong> Customer loss percentages by scenario (0-15%)</li>
</ul>
</div>
@ -419,8 +428,11 @@ html {
<h4>What is the Performance Multiplier?</h4>
<p>The Performance Multiplier is an automatically calculated metric showing how your actual results compare to baseline expectations. For example, 1.5x means you're performing 50% better than the baseline scenario. <strong>This cannot be manually configured</strong> - it's calculated based on your investment scaling factors.</p>
<h4>What is Core Service Revenue?</h4>
<p>Core Service Revenue represents the additional monthly income CSPs earn by selling compute, storage, and infrastructure resources required to run each Servala managed service instance. This revenue stream is <strong>100% retained by the CSP</strong> and is not shared with Servala, providing additional profit potential beyond the service fees.</p>
<h4>What happens during the grace period?</h4>
<p>You keep 100% of revenue during this period. Grace periods are longer for larger investments (6-12 months).</p>
<p>You keep 100% of service revenue during this period, plus all core service revenue. Grace periods are longer for larger investments (6-12 months).</p>
<h4>How accurate are the projections?</h4>
<p>Projections are based on industry benchmarks and Servala's historical data, but actual results may vary based on market conditions and your sales performance.</p>