KEY INSIGHT
Cost allocation in Nigerian SaaS must account for NGN/USD exchange rate volatility, with infrastructure costs tracked per-tenant while aggregating shared platform expenses for accurate unit economics.
Accurate cost allocation enables proper pricing decisions and helps identify unprofitable tenants, critical for sustainable operations in markets with significant currency fluctuation.
```python
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
from typing import Optional
import requests
class CostAllocationService:
"""Track and allocate costs across tenants."""
def __init__(self, db_session, aws_cost_client, azure_cost_client):
self.db = db_session
self.aws_costs = aws_cost_client
self.azure_costs = azure_cost_client
self.exchange_rate_service = ExchangeRateService()
def calculate_monthly_costs(self, billing_month: datetime) -> dict:
"""Calculate total platform costs for a billing month."""
start_date = billing_month.replace(day=1)
if billing_month.month == 12:
end_date = billing_month.replace(year=billing_month.year + 1, month=1, day=1)
else:
end_date = billing_month.replace(month=billing_month.month + 1, day=1)
aws_costs = self._get_aws_costs(start_date, end_date)
azure_costs = self._get_azure_costs(start_date, end_date)
ai_costs = self._get_ai_provider_costs(start_date, end_date)
third_party = self._get_third_party_costs(start_date, end_date)
ngn_rate = self.exchange_rate_service.get_rate('USD', 'NGN', start_date)
total_usd = aws_costs + azure_costs + ai_costs + third_party
total_ngn = Decimal(str(total_usd)) * Decimal(str(ngn_rate))
return {
'period': {'start': start_date, 'end': end_date},
'costs_usd': {
'aws': aws_costs,
'azure': azure_costs,
'ai_providers': ai_costs,
'third_party': third_party,
'total': total_usd
},
'costs_ngn': float(total_ngn),
'exchange_rate': ngn_rate
}
def allocate_costs_to_tenants(self, billing_month: datetime) -> list[dict]:
"""Allocate platform costs to individual tenants."""
platform_costs = self.calculate_monthly_costs(billing_month)
tenants = self.db.query(Tenant).filter(
Tenant.status == 'active'
).all()
allocations = []
shared_costs = self._calculate_shared_costs(platform_costs)
direct_costs = self._calculate_direct_costs(tenants, billing_month)
for tenant in tenants:
tenant_direct = direct_costs.get(tenant.id, {})
tenant_share = self._calculate_tenant_share(
tenant,
shared_costs,
platform_costs
)
allocations.append({
'tenant_id': tenant.id,
'tenant_name': tenant.name,
'plan': tenant.plan,
'direct_costs': tenant_direct,
'allocated_shared': tenant_share,
'total_costs_ngn': sum(tenant_direct.values()) + tenant_share
})
return allocations
```
**Per-Tenant Cost Tracking:**
```python
class TenantCostTracker:
"""Track detailed costs per tenant."""
def __init__(self, db_session, metrics_collector):
self.db = db_session
self.metrics = metrics_collector
def track_computation_cost(
self,
tenant_id: str,
service: str,
resource_usage: dict,
pricing: dict
) -> dict:
"""Track and calculate computation cost for tenant."""
cost_breakdown = {}
total_cost = Decimal('0')
if 'compute_seconds' in resource_usage:
compute_cost = Decimal(str(resource_usage['compute_seconds'])) * Decimal(str(pricing['compute_per_second']))
cost_breakdown['compute'] = float(compute_cost)
total_cost += compute_cost
if 'storage_gb_months' in resource_usage:
storage_cost = Decimal(str(resource_usage['storage_gb_months'])) * Decimal(str(pricing['storage_per_gb']))
cost_breakdown['storage'] = float(storage_cost)
total_cost += storage_cost
if 'api_calls' in resource_usage:
api_cost = Decimal(str(resource_usage['api_calls'])) * Decimal(str(pricing['api_per_call']))
cost_breakdown['api_calls'] = float(api_cost)
total_cost += api_cost
if 'ai_tokens' in resource_usage:
tokens_cost = self._calculate_ai_cost(resource_usage['ai_tokens'], pricing)
cost_breakdown['ai_tokens'] = float(tokens_cost)
total_cost += tokens_cost
cost_record = TenantCostRecord(
tenant_id=tenant_id,
service=service,
billing_month=datetime.utcnow().replace(day=1),
costs=cost_breakdown,
total_ngn=float(total_cost),
exchange_rate=self._get_current_rate(),
created_at=datetime.utcnow()
)
self.db.add(cost_record)
self.db.commit()
return cost_breakdown
def _calculate_ai_cost(self, token_usage: dict, pricing: dict) -> Decimal:
"""Calculate AI provider costs by model."""
total = Decimal('0')
model_prices = {
'gpt-4': {'prompt': 0.03, 'completion': 0.06}, # per 1K tokens USD
'gpt-3.5-turbo': {'prompt': 0.0015, 'completion': 0.002}
}
for model, usage in token_usage.items():
if model in model_prices:
prices = model_prices[model]
cost = (
Decimal(str(usage.get('prompt_tokens', 0))) * Decimal(str(prices['prompt'])) / 1000 +
Decimal(str(usage.get('completion_tokens', 0))) * Decimal(str(prices['completion'])) / 1000
)
total += cost
return total
```
**Unit Economics Calculation:**
```python
class UnitEconomicsCalculator:
"""Calculate unit economics for SaaS operations."""
def __init__(self, db_session):
self.db = db_session
def calculate_unit_economics(self, period_start: datetime, period_end: datetime) -> dict:
"""Calculate thorough unit economics."""
tenants = self._get_active_tenants(period_start, period_end)
revenue_per_tenant = self._calculate_revenue_per_tenant(tenants)
cost_per_tenant = self._calculate_cost_per_tenant(tenants, period_start, period_end)
gross_margin = (revenue_per_tenant - cost_per_tenant) / revenue_per_tenant if revenue_per_tenant > 0 else 0
cac = self._calculate_customer_acquisition_cost(period_start, period_end)
ltv = self._calculate_lifetime_value(tenants)
ltv_cac_ratio = ltv / cac if cac > 0 else 0
months_to_recover = cac / (revenue_per_tenant / 12) if revenue_per_tenant > 0 else 0
return {
'revenue_per_tenant_monthly': revenue_per_tenant,
'cost_per_tenant_monthly': cost_per_tenant,
'gross_margin_percent': gross_margin * 100,
'cac_ngn': cac,
'ltv_ngn': ltv,
'ltv_cac_ratio': ltv_cac_ratio,
'months_to_recover': months_to_recover
}
def _calculate_cost_per_tenant(self, tenants: list, period_start: datetime, period_end: datetime) -> Decimal:
"""Calculate average cost per tenant."""
total_costs = Decimal('0')
for tenant in tenants:
costs = self.db.query(TenantCostRecord).filter(
TenantCostRecord.tenant_id == tenant.id,
TenantCostRecord.billing_month >= period_start,
TenantCostRecord.billing_month <= period_end
).all()
tenant_total = sum(Decimal(str(c.total_ngn)) for c in costs)
total_costs += tenant_total
return total_costs / Decimal(str(len(tenants))) if tenants else Decimal('0')
```
**Common Failure Modes:**
Cost allocation that doesn't account for currency fluctuations creates billing disputes. Always lock exchange rates at the time of billing cycle creation and store historical rates with each cost record.
```python
def store_cost_with_exchange_rate(tenant_id: str, cost_usd: float, billing_month: datetime):
"""Store costs with exchange rate for future reconciliation."""
rate = get_exchange_rate('USD', 'NGN', billing_month)
cost_record = {
'tenant_id': tenant_id,
'cost_usd': cost_usd,
'exchange_rate': rate,
'cost_ngn': cost_usd * rate,
'rate_locked_at': billing_month,
'billing_month': billing_month
}
return cost_record
```