package model import "time" // PeriodType defines the granularity of the revenue entry type PeriodType string const ( PeriodQuarter PeriodType = "Q" PeriodHalfYear PeriodType = "H" PeriodYear PeriodType = "Y" ) // Period holds the actual time range for a revenue entry type Period struct { Type PeriodType Year int Index int // Q1=1 Q2=2 Q3=3 Q4=4 | H1=1 H2=2 | FY=1 Start time.Time End time.Time } // RevenueCategory is what the revenue is broken down by type RevenueCategory string const ( CategoryProduct RevenueCategory = "product" CategoryLocation RevenueCategory = "location" CategoryTotal RevenueCategory = "total" ) // Revenue is a single line in a financial report type Revenue struct { ID int Company *Company Currency *Currency Category RevenueCategory Label string // e.g. "North America", "iPhone", "Total" Value float64 Period Period } // RevenueReport groups revenue lines for a company/period // and verifies that sub-categories sum to total type RevenueReport struct { Company *Company Period Period Entries []Revenue } // Total returns the sum of all entries matching a category func (r *RevenueReport) Total(category RevenueCategory) float64 { var sum float64 for _, e := range r.Entries { if e.Category == category { sum += e.Value } } 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 }