mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
- Initialize git repository - Add comprehensive .gitignore for Go projects - Install golangci-lint v2.6.0 (latest v2) globally - Configure .golangci.yml with appropriate linters and formatters - Fix all formatting issues (gofmt) - Fix all errcheck issues (unchecked errors) - Adjust complexity threshold for validation functions - All checks passing: build, test, vet, lint
153 lines
4.9 KiB
Go
153 lines
4.9 KiB
Go
package unit
|
||
|
||
import (
|
||
"github.com/damirmukimov/city_resource_graph/models/customer"
|
||
"github.com/damirmukimov/city_resource_graph/models/params"
|
||
"github.com/damirmukimov/city_resource_graph/models/revenue"
|
||
)
|
||
|
||
// UnitEconomics contains all unit economics metrics for a year.
|
||
type UnitEconomics struct {
|
||
LTV TierMetrics `json:"ltv"` // Lifetime Value by tier
|
||
CAC float64 `json:"cac"` // Customer Acquisition Cost
|
||
LTVToCACRatio float64 `json:"ltv_cac_ratio"` // LTV/CAC ratio
|
||
ChurnRate float64 `json:"churn_rate"` // Blended annual churn rate
|
||
PaybackPeriod float64 `json:"payback_period"` // Months to recover CAC
|
||
}
|
||
|
||
// TierMetrics contains metrics for each tier.
|
||
type TierMetrics struct {
|
||
Basic float64 `json:"basic"`
|
||
Business float64 `json:"business"`
|
||
Enterprise float64 `json:"enterprise"`
|
||
Blended float64 `json:"blended"`
|
||
}
|
||
|
||
// CalculateUnitEconomics computes unit economics for a given year.
|
||
func CalculateUnitEconomics(year int, custMetrics customer.CustomerMetrics, tierDist customer.TierDistribution, revBreakdown revenue.RevenueBreakdown, p *params.Params) UnitEconomics {
|
||
uep := p.UnitEconomics
|
||
|
||
// Calculate LTV for each tier
|
||
ltv := calculateLTVByTier(p)
|
||
|
||
// Calculate blended LTV based on tier distribution
|
||
totalCustomers := float64(custMetrics.PayingOrgs)
|
||
if totalCustomers == 0 {
|
||
totalCustomers = 1 // Avoid division by zero
|
||
}
|
||
|
||
blendedLTV := (ltv.Basic*float64(tierDist.Basic) +
|
||
ltv.Business*float64(tierDist.Business) +
|
||
ltv.Enterprise*float64(tierDist.Enterprise)) / totalCustomers
|
||
|
||
// Calculate CAC (marketing & sales costs / new customers)
|
||
marketingSalesCosts := p.Costs.MarketingSales.GetYear(year)
|
||
cac := marketingSalesCosts / float64(custMetrics.PayingOrgs)
|
||
if custMetrics.PayingOrgs == 0 {
|
||
cac = 0
|
||
}
|
||
|
||
// Calculate LTV/CAC ratio
|
||
ltvToCACRatio := 0.0
|
||
if cac > 0 {
|
||
ltvToCACRatio = blendedLTV / cac
|
||
}
|
||
|
||
// Calculate blended churn rate (weighted by tier distribution)
|
||
blendedChurn := (uep.ChurnRates.Basic*float64(tierDist.Basic) +
|
||
uep.ChurnRates.Business*float64(tierDist.Business) +
|
||
uep.ChurnRates.Enterprise*float64(tierDist.Enterprise)) / totalCustomers
|
||
|
||
// Calculate payback period (months to recover CAC)
|
||
avgMonthlyRevenue := revBreakdown.Total / float64(custMetrics.PayingOrgs) / 12
|
||
paybackPeriod := 0.0
|
||
if avgMonthlyRevenue > 0 {
|
||
paybackPeriod = cac / avgMonthlyRevenue
|
||
}
|
||
|
||
return UnitEconomics{
|
||
LTV: TierMetrics{
|
||
Basic: ltv.Basic,
|
||
Business: ltv.Business,
|
||
Enterprise: ltv.Enterprise,
|
||
Blended: blendedLTV,
|
||
},
|
||
CAC: cac,
|
||
LTVToCACRatio: ltvToCACRatio,
|
||
ChurnRate: blendedChurn,
|
||
PaybackPeriod: paybackPeriod,
|
||
}
|
||
}
|
||
|
||
// calculateLTVByTier calculates Lifetime Value for each tier.
|
||
func calculateLTVByTier(p *params.Params) TierMetrics {
|
||
uep := p.UnitEconomics
|
||
|
||
// Get blended monthly subscription prices (including transaction uplifts)
|
||
basicMonthly := p.Pricing.Basic * (1.0 + p.Pricing.BlendedUplift.Basic)
|
||
businessMonthly := p.Pricing.Business * (1.0 + p.Pricing.BlendedUplift.Business)
|
||
enterpriseMonthly := p.Pricing.Enterprise * (1.0 + p.Pricing.BlendedUplift.Enterprise)
|
||
|
||
// Calculate LTV for Basic tier
|
||
basicLTV := calculateTierLTV(
|
||
basicMonthly,
|
||
uep.RetentionMonths.Basic,
|
||
uep.TransactionFees.Basic,
|
||
0, // No upsell revenue for basic (it's the entry tier)
|
||
uep.PlatformCosts,
|
||
)
|
||
|
||
// Calculate LTV for Business tier
|
||
businessUpsellRevenue := uep.UpsellRates.BusinessToEnterprise * calculateTierLTV(
|
||
enterpriseMonthly,
|
||
uep.RetentionMonths.Enterprise,
|
||
uep.TransactionFees.Enterprise,
|
||
0, // No further upsell
|
||
uep.PlatformCosts,
|
||
)
|
||
businessLTV := calculateTierLTV(
|
||
businessMonthly,
|
||
uep.RetentionMonths.Business,
|
||
uep.TransactionFees.Business,
|
||
businessUpsellRevenue,
|
||
uep.PlatformCosts,
|
||
)
|
||
|
||
// Calculate LTV for Enterprise tier
|
||
enterpriseExpansionRevenue := uep.EnterpriseExpansion.MultiSiteRate *
|
||
uep.EnterpriseExpansion.AdditionalSites *
|
||
uep.EnterpriseExpansion.SiteRevenue * 12 * uep.RetentionMonths.Enterprise // Annual expansion revenue
|
||
|
||
enterpriseLTV := calculateTierLTV(
|
||
enterpriseMonthly,
|
||
uep.RetentionMonths.Enterprise,
|
||
uep.TransactionFees.Enterprise,
|
||
enterpriseExpansionRevenue,
|
||
uep.PlatformCosts,
|
||
)
|
||
|
||
return TierMetrics{
|
||
Basic: basicLTV,
|
||
Business: businessLTV,
|
||
Enterprise: enterpriseLTV,
|
||
}
|
||
}
|
||
|
||
// calculateTierLTV calculates LTV for a specific tier.
|
||
func calculateTierLTV(monthlyRevenue, retentionMonths, transactionFees, upsellRevenue, platformCosts float64) float64 {
|
||
// Gross subscription revenue over lifetime
|
||
grossSubscriptionRevenue := monthlyRevenue * retentionMonths
|
||
|
||
// Transaction revenue over lifetime (annual fees × years)
|
||
years := retentionMonths / 12
|
||
grossTransactionRevenue := transactionFees * years
|
||
|
||
// Total gross revenue
|
||
totalGrossRevenue := grossSubscriptionRevenue + grossTransactionRevenue + upsellRevenue
|
||
|
||
// Net of platform costs
|
||
totalNetRevenue := totalGrossRevenue * (1.0 - platformCosts)
|
||
|
||
return totalNetRevenue
|
||
}
|