turash/models/revenue/revenue.go
Damir Mukimov 4a2fda96cd
Initial commit: Repository setup with .gitignore, golangci-lint v2.6.0, and code quality checks
- 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
2025-11-01 07:36:22 +01:00

163 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package revenue
import (
"github.com/damirmukimov/city_resource_graph/models/customer"
"github.com/damirmukimov/city_resource_graph/models/params"
)
// RevenueBreakdown represents all revenue streams for a given year.
// Revenue paths are separated into distinct components as specified.
type RevenueBreakdown struct {
// Path 1: SaaS Subscription Revenue
Subscription SubscriptionRevenue `json:"subscription"`
// Path 2: Market/Transaction Revenue
Transaction TransactionRevenue `json:"transaction"`
// Path 3: Public/Municipal Revenue
Municipal MunicipalRevenue `json:"municipal"`
// Path 4: Implementation Services Revenue
Implementation ImplementationRevenue `json:"implementation"`
// Total across all paths
Total float64 `json:"total"`
}
// SubscriptionRevenue represents SaaS subscription revenue.
type SubscriptionRevenue struct {
BasicARR float64 `json:"basic_arr"` // Annual recurring revenue from Basic tier
BusinessARR float64 `json:"business_arr"` // Annual recurring revenue from Business tier
EnterpriseARR float64 `json:"enterprise_arr"` // Annual recurring revenue from Enterprise tier
TotalARR float64 `json:"total_arr"` // Total subscription ARR
}
// TransactionRevenue represents marketplace and transaction revenue.
type TransactionRevenue struct {
IntroRevenue float64 `json:"intro_revenue"` // Introduction fees revenue
ServiceRevenue float64 `json:"service_revenue"` // Service marketplace commission
GroupRevenue float64 `json:"group_revenue"` // Group buying commission
Total float64 `json:"total"` // Total transaction revenue
}
// MunicipalRevenue represents municipal license and data licensing revenue.
type MunicipalRevenue struct {
LicenseRevenue float64 `json:"license_revenue"` // Municipal license fees
DataLicensing float64 `json:"data_licensing"` // Data licensing revenue
Total float64 `json:"total"` // Total municipal revenue
}
// ImplementationRevenue represents implementation services revenue.
type ImplementationRevenue struct {
Matches float64 `json:"matches"` // Total matches (derived)
PaidImpls float64 `json:"paid_impls"` // Paid implementations (derived)
Total float64 `json:"total"` // Total implementation revenue
}
// CalculateRevenue computes all revenue streams for a given year.
func CalculateRevenue(year int, custMetrics customer.CustomerMetrics, tierDist customer.TierDistribution, p *params.Params) RevenueBreakdown {
// Path 1: SaaS Subscription Revenue
subRev := calculateSubscriptionRevenue(year, tierDist, p)
// Path 2: Market/Transaction Revenue
txRev := calculateTransactionRevenue(year, p)
// Path 3: Public/Municipal Revenue
munRev := calculateMunicipalRevenue(year, p)
// Path 4: Implementation Services Revenue
implRev := calculateImplementationRevenue(custMetrics, p)
total := subRev.TotalARR + txRev.Total + munRev.Total + implRev.Total
return RevenueBreakdown{
Subscription: subRev,
Transaction: txRev,
Municipal: munRev,
Implementation: implRev,
Total: total,
}
}
// calculateSubscriptionRevenue computes SaaS subscription revenue.
// Formula: ARR_tier = Customers_tier × Monthly_Price × 12 × (1 + Blended_Uplift)
func calculateSubscriptionRevenue(year int, tierDist customer.TierDistribution, p *params.Params) SubscriptionRevenue {
basicARR := float64(tierDist.Basic) * p.Pricing.Basic * 12.0 * (1.0 + p.Pricing.BlendedUplift.Basic)
businessARR := float64(tierDist.Business) * p.Pricing.Business * 12.0 * (1.0 + p.Pricing.BlendedUplift.Business)
enterpriseARR := float64(tierDist.Enterprise) * p.Pricing.Enterprise * 12.0 * (1.0 + p.Pricing.BlendedUplift.Enterprise)
return SubscriptionRevenue{
BasicARR: basicARR,
BusinessARR: businessARR,
EnterpriseARR: enterpriseARR,
TotalARR: basicARR + businessARR + enterpriseARR,
}
}
// calculateTransactionRevenue computes marketplace and transaction revenue.
// Formula:
// - Intro Revenue = Intros × Avg_Fee × Conversion_Rate
// - Service Revenue = Service_GMV × Commission_Rate
// - Group Revenue = Group_GMV × Commission_Rate
func calculateTransactionRevenue(year int, p *params.Params) TransactionRevenue {
txp := p.Transactions
introRev := float64(txp.IntrosPerYear.GetYear(year)) * txp.AvgIntroFee * txp.IntroConversion.GetYear(year)
serviceRev := txp.ServiceGMV.GetYear(year) * txp.ServiceCommission
groupRev := txp.GroupGMV.GetYear(year) * txp.GroupCommission
return TransactionRevenue{
IntroRevenue: introRev,
ServiceRevenue: serviceRev,
GroupRevenue: groupRev,
Total: introRev + serviceRev + groupRev,
}
}
// calculateMunicipalRevenue computes municipal license and data licensing revenue.
// Formula:
// - License Revenue = Cities × Avg_License
// - Data Licensing = Data_Licensing_Value
func calculateMunicipalRevenue(year int, p *params.Params) MunicipalRevenue {
mp := p.Municipal
licenseRev := float64(mp.Cities.GetYear(year)) * mp.AvgLicense.GetYear(year)
dataLicensing := mp.DataLicensing.GetYear(year)
return MunicipalRevenue{
LicenseRevenue: licenseRev,
DataLicensing: dataLicensing,
Total: licenseRev + dataLicensing,
}
}
// calculateImplementationRevenue computes implementation services revenue.
// Formula:
// - Matches = Total_Orgs × Matches_Per_Org
// - Paid_Impls = Matches × Paid_Share
// - Revenue = Paid_Impls × Avg_Fee
func calculateImplementationRevenue(custMetrics customer.CustomerMetrics, p *params.Params) ImplementationRevenue {
ip := p.ImplServices
matches := float64(custMetrics.TotalOrgs) * ip.MatchesPerOrg
paidImpls := matches * ip.PaidShare
revenue := paidImpls * ip.AvgFee
return ImplementationRevenue{
Matches: matches,
PaidImpls: paidImpls,
Total: revenue,
}
}
// GetPlatformOnlyRevenue returns revenue from SaaS + Municipal only.
// This excludes transaction and implementation revenue.
func GetPlatformOnlyRevenue(rev RevenueBreakdown) float64 {
return rev.Subscription.TotalARR + rev.Municipal.Total
}
// GetFullRevenue returns total revenue including all paths.
func GetFullRevenue(rev RevenueBreakdown) float64 {
return rev.Total
}