diff --git a/docs/apicalls.rst b/docs/apicalls.rst index 5c2cf0f47..cf448a297 100644 --- a/docs/apicalls.rst +++ b/docs/apicalls.rst @@ -57,4 +57,4 @@ resetvolumediscountseconds addrecievedcallseconds :Example: curl "http://127.0.0.1:8000/addrecievedcallseconds?cstmid=vdf&subj=rif&dest=0257@amount=100" resetuserbudget - :Example: curl "http://127.0.0.1:8000/resetuserbudget?cstmid=vdf&subj=rif&dest=0257" \ No newline at end of file + :Example: curl "http://127.0.0.1:8000/resetuserbudget?cstmid=vdf&subj=rif&dest=0257" diff --git a/docs/intro.rst b/docs/intro.rst index 93065e49a..ff20f8432 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -2,7 +2,7 @@ Introduction ============ CGRates is a very fast and easy scalable rating engine targeted especially for telecom providers. -It is written in go (http://golang.net) and accesible from any language via JSON RPC. The code is well documented (go doc compliant api docs) and heavily tested. +It is written in go (http://golang.net) and accessible from any language via JSON RPC. The code is well documented (go doc compliant API docs) and heavily tested. Supported databases: kyoto_ cabinet, redis_, mongodb_. @@ -20,4 +20,28 @@ Features + Fully/Easy configurable + Very fast (5000+ req/sec) + Good documentation -+ Commercial support available \ No newline at end of file ++ Commercial support available + +How does CGRates work? +---------------------- +Let's start with the most important function: finding the cost of a certain call. The call information comes to CGRates as the following values: subject, destination, start time and end time. The engine will lookup in the database for the activation periods applicable to the received subject and destination. What are the activation periods? + +The activation period is a structure describing different prices for a call on different intervals of time. This structure has an activation time, from this time on the activation period is in effect and one ore more (usually more than one) intervals with prices. An interval is looking like this: + +:: + + type Interval struct { + Month time.Month + MonthDay int + WeekDays []time.Weekday + StartTime, EndTime string // ##:##:## format + Ponder, ConnectFee, Price, BillingUnit float64 + } + +It specifies the Month, the MonthDay, the WeekDays and the StartTime and the EndTime when the Interval's Price is in effect. + +For example the interval {"Month": 1, "WeekDays":[1,2,3,4,5]. "StartTime":"18:00:00", "Price":0.1, "BillingUnit": 1} specifies that the Price for the first month of each year from Monday to Friday starting 18:00 is 0.1 cents per second. Most structure elements are optional and they can be combined in any way it makes sense. If an element is omitted it means it is zero ore any. + +The ConnectFee specifies the connection price for the call if this interval is the first one from the call and the Ponder will establishes which interval will set the price for a call segment if more then one applies to it. + +The other functions relay on a user budget structure to manage the postpaid and prepaid different quotas. diff --git a/timespans/destinations.go b/timespans/destinations.go index 556924f38..fc976cc5a 100644 --- a/timespans/destinations.go +++ b/timespans/destinations.go @@ -33,15 +33,15 @@ type Destination struct { Serializes the destination for the storage. Used for key-value storages. */ func (d *Destination) store() (result string) { - for _, p := range d.Prefixes { - result += p + "," - } - result = strings.TrimRight(result, ",") - return + for _, p := range d.Prefixes { + result += p + "," + } + result = strings.TrimRight(result, ",") + return } func (d *Destination) restore(input string) { - d.Prefixes = strings.Split(input, ",") + d.Prefixes = strings.Split(input, ",") } /* diff --git a/timespans/tariff_plans.go b/timespans/tariff_plans.go index 8872deceb..2e9f55016 100644 --- a/timespans/tariff_plans.go +++ b/timespans/tariff_plans.go @@ -18,9 +18,9 @@ along with this program. If not, see package timespans import ( -// "log" - "strings" + // "log" "strconv" + "strings" ) /* @@ -41,56 +41,56 @@ type TariffPlan struct { Serializes the tariff plan for the storage. Used for key-value storages. */ func (tp *TariffPlan) store() (result string) { - result += strconv.FormatFloat(tp.SmsCredit, 'f', -1, 64) + ";" - result += strconv.FormatFloat(tp.Traffic, 'f', -1, 64) + ";" - result += strconv.FormatFloat(tp.ReceivedCallSecondsLimit, 'f', -1, 64) + ";" - if tp.RecivedCallBonus == nil { - tp.RecivedCallBonus = &RecivedCallBonus{} - } - result += tp.RecivedCallBonus.store() + ";" - for i, mb := range tp.MinuteBuckets { - if i > 0 { - result += "," - } - result += mb.store() - } - if tp.VolumeDiscountThresholds != nil { - result += ";" - } - for i, vd := range tp.VolumeDiscountThresholds { - if i > 0 { - result += "," - } - result += strconv.FormatFloat(vd.Volume, 'f', -1, 64) + "|" + strconv.FormatFloat(vd.Discount, 'f', -1, 64) - } - result = strings.TrimRight(result, ";") - return + result += strconv.FormatFloat(tp.SmsCredit, 'f', -1, 64) + ";" + result += strconv.FormatFloat(tp.Traffic, 'f', -1, 64) + ";" + result += strconv.FormatFloat(tp.ReceivedCallSecondsLimit, 'f', -1, 64) + ";" + if tp.RecivedCallBonus == nil { + tp.RecivedCallBonus = &RecivedCallBonus{} + } + result += tp.RecivedCallBonus.store() + ";" + for i, mb := range tp.MinuteBuckets { + if i > 0 { + result += "," + } + result += mb.store() + } + if tp.VolumeDiscountThresholds != nil { + result += ";" + } + for i, vd := range tp.VolumeDiscountThresholds { + if i > 0 { + result += "," + } + result += strconv.FormatFloat(vd.Volume, 'f', -1, 64) + "|" + strconv.FormatFloat(vd.Discount, 'f', -1, 64) + } + result = strings.TrimRight(result, ";") + return } /* De-serializes the tariff plan for the storage. Used for key-value storages. */ func (tp *TariffPlan) restore(input string) { - elements := strings.Split(input, ";") - tp.SmsCredit, _ = strconv.ParseFloat(elements[0], 64) - tp.Traffic, _ = strconv.ParseFloat(elements[1], 64) - tp.ReceivedCallSecondsLimit, _ = strconv.ParseFloat(elements[2], 64) - tp.RecivedCallBonus = &RecivedCallBonus{} - tp.RecivedCallBonus.restore(elements[3]) - for _, mbs := range strings.Split(elements[4], ",") { - mb := &MinuteBucket{} - mb.restore(mbs) - tp.MinuteBuckets = append(tp.MinuteBuckets, mb) - } - if len(elements) > 5 { - for _, vdss := range strings.Split(elements[5], ",") { - vd := &VolumeDiscount{} - vds := strings.Split(vdss, "|") - vd.Volume, _ = strconv.ParseFloat(vds[0], 64) - vd.Discount, _ = strconv.ParseFloat(vds[1], 64) - tp.VolumeDiscountThresholds = append(tp.VolumeDiscountThresholds, vd) - } - } + elements := strings.Split(input, ";") + tp.SmsCredit, _ = strconv.ParseFloat(elements[0], 64) + tp.Traffic, _ = strconv.ParseFloat(elements[1], 64) + tp.ReceivedCallSecondsLimit, _ = strconv.ParseFloat(elements[2], 64) + tp.RecivedCallBonus = &RecivedCallBonus{} + tp.RecivedCallBonus.restore(elements[3]) + for _, mbs := range strings.Split(elements[4], ",") { + mb := &MinuteBucket{} + mb.restore(mbs) + tp.MinuteBuckets = append(tp.MinuteBuckets, mb) + } + if len(elements) > 5 { + for _, vdss := range strings.Split(elements[5], ",") { + vd := &VolumeDiscount{} + vds := strings.Split(vdss, "|") + vd.Volume, _ = strconv.ParseFloat(vds[0], 64) + vd.Discount, _ = strconv.ParseFloat(vds[1], 64) + tp.VolumeDiscountThresholds = append(tp.VolumeDiscountThresholds, vd) + } + } } /* @@ -115,26 +115,26 @@ type RecivedCallBonus struct { Serializes the tariff plan for the storage. Used for key-value storages. */ func (rcb *RecivedCallBonus) store() (result string) { - result += strconv.FormatFloat(rcb.Credit, 'f', -1, 64) + "," - result += strconv.FormatFloat(rcb.SmsCredit, 'f', -1, 64) + "," - result += strconv.FormatFloat(rcb.Traffic, 'f', -1, 64) - if rcb.MinuteBucket != nil { - result += "," - result += rcb.MinuteBucket.store() - } - return + result += strconv.FormatFloat(rcb.Credit, 'f', -1, 64) + "," + result += strconv.FormatFloat(rcb.SmsCredit, 'f', -1, 64) + "," + result += strconv.FormatFloat(rcb.Traffic, 'f', -1, 64) + if rcb.MinuteBucket != nil { + result += "," + result += rcb.MinuteBucket.store() + } + return } /* De-serializes the tariff plan for the storage. Used for key-value storages. */ func (rcb *RecivedCallBonus) restore(input string) { - elements := strings.Split(input, ",") - rcb.Credit, _ = strconv.ParseFloat(elements[0], 64) - rcb.SmsCredit, _ = strconv.ParseFloat(elements[1], 64) - rcb.Traffic, _ = strconv.ParseFloat(elements[2], 64) - if len(elements) > 3 { - rcb.MinuteBucket = &MinuteBucket{} - rcb.MinuteBucket.restore(elements[3]) - } + elements := strings.Split(input, ",") + rcb.Credit, _ = strconv.ParseFloat(elements[0], 64) + rcb.SmsCredit, _ = strconv.ParseFloat(elements[1], 64) + rcb.Traffic, _ = strconv.ParseFloat(elements[2], 64) + if len(elements) > 3 { + rcb.MinuteBucket = &MinuteBucket{} + rcb.MinuteBucket.restore(elements[3]) + } } diff --git a/timespans/timespans.go b/timespans/timespans.go index 8600a563c..e2f428553 100644 --- a/timespans/timespans.go +++ b/timespans/timespans.go @@ -79,7 +79,7 @@ func (ts *TimeSpan) Contains(t time.Time) bool { } /* -will set ne interval as spans's interval if new ponder is greater then span's interval ponder +will set the interval as spans's interval if new ponder is greater then span's interval ponder or if the ponders are equal and new price is lower then spans's interval price */ func (ts *TimeSpan) SetInterval(i *Interval) { diff --git a/timespans/userbudget.go b/timespans/userbudget.go index 8de90f29f..951bf22d5 100644 --- a/timespans/userbudget.go +++ b/timespans/userbudget.go @@ -20,9 +20,9 @@ package timespans import ( // "log" "sort" - "sync" "strconv" "strings" + "sync" ) /* @@ -74,44 +74,44 @@ func (bs bucketsorter) Less(j, i int) bool { Serializes the user budget for the storage. Used for key-value storages. */ func (ub *UserBudget) store() (result string) { - result += strconv.FormatFloat(ub.Credit, 'f', -1, 64) + ";" - result += strconv.FormatFloat(ub.SmsCredit, 'f', -1, 64) + ";" - result += strconv.FormatFloat(ub.Traffic, 'f', -1, 64) + ";" - result += strconv.FormatFloat(ub.VolumeDiscountSeconds, 'f', -1, 64) + ";" - result += strconv.FormatFloat(ub.ReceivedCallSeconds, 'f', -1, 64) + ";" - result += strconv.Itoa(ub.ResetDayOfTheMonth) + ";" - result += ub.TariffPlanId - if ub.MinuteBuckets != nil { - result += ";" - } - for i, mb := range ub.MinuteBuckets { - if i > 0 { - result += "," - } - result += mb.store() - } - return + result += strconv.FormatFloat(ub.Credit, 'f', -1, 64) + ";" + result += strconv.FormatFloat(ub.SmsCredit, 'f', -1, 64) + ";" + result += strconv.FormatFloat(ub.Traffic, 'f', -1, 64) + ";" + result += strconv.FormatFloat(ub.VolumeDiscountSeconds, 'f', -1, 64) + ";" + result += strconv.FormatFloat(ub.ReceivedCallSeconds, 'f', -1, 64) + ";" + result += strconv.Itoa(ub.ResetDayOfTheMonth) + ";" + result += ub.TariffPlanId + if ub.MinuteBuckets != nil { + result += ";" + } + for i, mb := range ub.MinuteBuckets { + if i > 0 { + result += "," + } + result += mb.store() + } + return } /* De-serializes the user budget for the storage. Used for key-value storages. */ func (ub *UserBudget) restore(input string) { - elements := strings.Split(input, ";") - ub.Credit, _ = strconv.ParseFloat(elements[0], 64) - ub.SmsCredit, _ = strconv.ParseFloat(elements[1], 64) - ub.Traffic, _ = strconv.ParseFloat(elements[2], 64) - ub.VolumeDiscountSeconds, _ = strconv.ParseFloat(elements[3], 64) - ub.ReceivedCallSeconds, _ = strconv.ParseFloat(elements[4], 64) - ub.ResetDayOfTheMonth, _ = strconv.Atoi(elements[5]) - ub.TariffPlanId = elements[6] - if len(elements) > 7 { - for _, mbs := range strings.Split(elements[7], ",") { - mb := &MinuteBucket{} - mb.restore(mbs) - ub.MinuteBuckets = append(ub.MinuteBuckets, mb) - } - } + elements := strings.Split(input, ";") + ub.Credit, _ = strconv.ParseFloat(elements[0], 64) + ub.SmsCredit, _ = strconv.ParseFloat(elements[1], 64) + ub.Traffic, _ = strconv.ParseFloat(elements[2], 64) + ub.VolumeDiscountSeconds, _ = strconv.ParseFloat(elements[3], 64) + ub.ReceivedCallSeconds, _ = strconv.ParseFloat(elements[4], 64) + ub.ResetDayOfTheMonth, _ = strconv.Atoi(elements[5]) + ub.TariffPlanId = elements[6] + if len(elements) > 7 { + for _, mbs := range strings.Split(elements[7], ",") { + mb := &MinuteBucket{} + mb.restore(mbs) + ub.MinuteBuckets = append(ub.MinuteBuckets, mb) + } + } } /*