package spread import "math" func round(x float64, places int) float64 { p := math.Pow(10, float64(places)) return math.Round(x*p) / p } // spreadPrice returns our unit price for a line and whether it was an exact // catalog match. It never returns a price above the customer's current price. func spreadPrice(li LineItem, cat Catalog) (price float64, matched bool) { if p, ok := cat.Lookup(li.PartNumber); ok { matched = true price = p } else { price = li.UnitPrice * (1 - modelDiscount(li)) } price = round(price, 4) if price > li.UnitPrice || li.UnitPrice <= 0 { price = li.UnitPrice } return price, matched } // Analyze re-quotes every line against the built-in catalog (public demo). func Analyze(ref string, items []LineItem) Report { return AnalyzeWith(ref, items, BuiltinCatalog{}) } // AnalyzeWith re-quotes every line against the given catalog and rolls up the // totals. Parts not found in the catalog are priced by the category model. func AnalyzeWith(ref string, items []LineItem, cat Catalog) Report { rep := Report{BOMRef: ref} rep.Totals.CutRate = CutRate for _, li := range items { price, matched := spreadPrice(li, cat) unitSave := round(li.UnitPrice-price, 4) if unitSave < 0 { unitSave = 0 } al := AnalyzedLine{ LineItem: li, SpreadPrice: price, UnitSaving: unitSave, LineMarket: round(li.UnitPrice*li.Quantity, 2), LineSpread: round(price*li.Quantity, 2), Matched: matched, } al.LineSaving = round(al.LineMarket-al.LineSpread, 2) if al.LineSaving < 0 { al.LineSaving = 0 } rep.Lines = append(rep.Lines, al) rep.Totals.Market += al.LineMarket rep.Totals.Spread += al.LineSpread rep.Totals.Savings += al.LineSaving } rep.Totals.Lines = len(rep.Lines) rep.Totals.Market = round(rep.Totals.Market, 2) rep.Totals.Spread = round(rep.Totals.Spread, 2) rep.Totals.Savings = round(rep.Totals.Savings, 2) rep.Totals.OurCut = round(rep.Totals.Savings*CutRate, 2) rep.Totals.NetSavings = round(rep.Totals.Savings-rep.Totals.OurCut, 2) return rep }