adding revenue handlers and shell
This commit is contained in:
@@ -21,14 +21,23 @@ type Period struct {
|
||||
}
|
||||
|
||||
// RevenueCategory is what the revenue is broken down by
|
||||
type RevenueCategory string
|
||||
// RevenueCategory is now just a string — categories are dynamic per company
|
||||
type RevenueCategory = string
|
||||
|
||||
// Built-in reserved categories
|
||||
const (
|
||||
CategoryProduct RevenueCategory = "product"
|
||||
CategoryLocation RevenueCategory = "location"
|
||||
CategoryTotal RevenueCategory = "total"
|
||||
CategoryTotal RevenueCategory = "total" // always required, never custom
|
||||
)
|
||||
|
||||
// CategoryDef defines a custom category for a specific company
|
||||
// e.g. Company: Apple, Name: "product", Labels: ["iPhone", "Mac", "Services"]
|
||||
type CategoryDef struct {
|
||||
ID int
|
||||
Company *Company
|
||||
Name string // e.g. "product", "location", "segment"
|
||||
Labels []string // populated from category_labels table
|
||||
}
|
||||
|
||||
// Revenue is a single line in a financial report
|
||||
type Revenue struct {
|
||||
ID int
|
||||
@@ -59,11 +68,57 @@ func (r *RevenueReport) Total(category RevenueCategory) float64 {
|
||||
return sum
|
||||
}
|
||||
|
||||
// Validate checks that product + location sums match the total entry
|
||||
func (r *RevenueReport) Validate() (bool, float64, float64) {
|
||||
total := r.Total(CategoryTotal)
|
||||
products := r.Total(CategoryProduct)
|
||||
locations := r.Total(CategoryLocation)
|
||||
// both breakdowns should equal the total
|
||||
return products == total && locations == total, products, locations
|
||||
// SumByCategory returns a map of category → sum for all entries
|
||||
func (r *RevenueReport) SumByCategory() map[RevenueCategory]float64 {
|
||||
sums := make(map[RevenueCategory]float64)
|
||||
for _, e := range r.Entries {
|
||||
sums[e.Category] += e.Value
|
||||
}
|
||||
return sums
|
||||
}
|
||||
|
||||
// Validate checks every category independently sums to total
|
||||
func (r *RevenueReport) Validate() (bool, map[RevenueCategory]float64) {
|
||||
total := r.Total(CategoryTotal)
|
||||
sums := r.SumByCategory()
|
||||
delete(sums, CategoryTotal) // don't compare total to itself
|
||||
|
||||
allMatch := true
|
||||
for cat, sum := range sums {
|
||||
if sum != total {
|
||||
allMatch = false
|
||||
_ = cat // caller can inspect the map to see which failed
|
||||
}
|
||||
}
|
||||
return allMatch, sums
|
||||
}
|
||||
|
||||
// RevSum aggregates revenue across multiple reports or periods
|
||||
// Use this when you want e.g. FY = Q1+Q2+Q3+Q4
|
||||
type RevSum struct {
|
||||
Company *Company
|
||||
Categories map[RevenueCategory]float64 // category → total across all periods
|
||||
Labels map[string]float64 // label → total (e.g. "iPhone" across quarters)
|
||||
Total float64
|
||||
Periods []Period // which periods were summed
|
||||
}
|
||||
|
||||
func NewRevSum(company *Company, reports []RevenueReport) *RevSum {
|
||||
rs := &RevSum{
|
||||
Company: company,
|
||||
Categories: make(map[RevenueCategory]float64),
|
||||
Labels: make(map[string]float64),
|
||||
}
|
||||
for _, report := range reports {
|
||||
rs.Periods = append(rs.Periods, report.Period)
|
||||
for _, e := range report.Entries {
|
||||
if e.Category == CategoryTotal {
|
||||
rs.Total += e.Value
|
||||
continue
|
||||
}
|
||||
rs.Categories[e.Category] += e.Value
|
||||
rs.Labels[e.Label] += e.Value
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user