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
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue