diff --git a/cmd/cgr-loader/cgr-mediator.go b/cmd/cgr-loader/cgr-mediator.go deleted file mode 100644 index 1e4af7936..000000000 --- a/cmd/cgr-loader/cgr-mediator.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2012 Radu Ioan Fericean - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package main - -import ( - "bufio" - "database/sql" - "encoding/csv" - "flag" - "fmt" - _ "github.com/bmizerany/pq" - "github.com/cgrates/cgrates/timespans" - "log" - "net/rpc/jsonrpc" - "os" - "time" -) - -var ( - cdrFile = flag.String("freeswitchcdr", "Master.csv", "Freeswitch Master CSV CDR file.") - resultFile = flag.String("resultfile", "out.csv", "Generated file containing CDR and price info.") - host = flag.String("host", "localhost", "The host to connect to. Values that start with / are for UNIX domain sockets.") - port = flag.String("port", "5432", "The port to bind to.") - dbName = flag.String("dbname", "cgrates", "The name of the database to connect to.") - user = flag.String("user", "", "The user to sign in as.") - password = flag.String("password", "", "The user's password.") -) - -func readDbRecord(db *sql.DB, searchedUUID string) (cc *timespans.CallCost, timespansText string, err error) { - row := db.QueryRow(fmt.Sprintf("SELECT * FROM callcosts WHERE uuid='%s'", searchedUUID)) - var uuid string - cc = ×pans.CallCost{} - err = row.Scan(&uuid, &cc.Direction, &cc.Tenant, &cc.TOR, &cc.Subject, &cc.Destination, &cc.Cost, &cc.ConnectFee, ×pansText) - return -} - -func main() { - flag.Parse() - useDB := true - file, err := os.Open(*cdrFile) - defer file.Close() - if err != nil { - log.Fatal(err) - } - db, err := sql.Open("postgres", fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=disable", *host, *port, *dbName, *user, *password)) - defer db.Close() - if err != nil { - log.Printf("failed to open the database: %v", err) - useDB = false - } - csvReader := csv.NewReader(bufio.NewReader(file)) - client, err := jsonrpc.Dial("tcp", "localhost:2001") - useRPC := true - if err != nil { - log.Printf("Could not connect to rater server: %v!", err) - useRPC = false - } - for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() { - uuid := record[10] - t, _ := time.Parse("2012-05-21 17:48:20", record[5]) - fmt.Println(t) - if useDB { - cc, timespansText, err := readDbRecord(db, uuid) - if err != nil && useRPC { - // try getting the price from the rater - - tenant := record[0] - subject := record[1] - dest := record[2] - t1, _ := time.Parse("2012-05-21 17:48:20", record[5]) - t2, _ := time.Parse("2012-05-21 17:48:20", record[6]) - cd := timespans.CallDescriptor{ - Direction: "OUT", - Tenant: tenant, - TOR: "0", - Subject: subject, - Destination: dest, - TimeStart: t1, - TimeEnd: t2} - client.Call("Responder.GetCost", cd, cc) - } - _ = timespansText - //log.Print(cc, timespansText) - } - } -} diff --git a/cmd/cgr-loader/helpers.go b/cmd/cgr-loader/helpers.go index 5c2fd8e54..1f4bd87ed 100644 --- a/cmd/cgr-loader/helpers.go +++ b/cmd/cgr-loader/helpers.go @@ -27,11 +27,11 @@ import ( ) type Rate struct { - DestinationsTag string - ConnectFee, Price, BillingUnit float64 + DestinationsTag string + ConnectFee, Price, PricedUnits, RateIncrements float64 } -func NewRate(destinationsTag, connectFee, price, billingUnit string) (r *Rate, err error) { +func NewRate(destinationsTag, connectFee, price, pricedUnits, rateIncrements string) (r *Rate, err error) { cf, err := strconv.ParseFloat(connectFee, 64) if err != nil { log.Printf("Error parsing connect fee from: %v", connectFee) @@ -42,16 +42,22 @@ func NewRate(destinationsTag, connectFee, price, billingUnit string) (r *Rate, e log.Printf("Error parsing price from: %v", price) return } - bu, err := strconv.ParseFloat(billingUnit, 64) + pu, err := strconv.ParseFloat(pricedUnits, 64) if err != nil { - log.Printf("Error parsing billing unit from: %v", billingUnit) + log.Printf("Error parsing priced units from: %v", pricedUnits) + return + } + ri, err := strconv.ParseFloat(rateIncrements, 64) + if err != nil { + log.Printf("Error parsing rates increments from: %v", rateIncrements) return } r = &Rate{ DestinationsTag: destinationsTag, ConnectFee: cf, Price: p, - BillingUnit: bu, + PricedUnits: pu, + RateIncrements: ri, } return } @@ -99,14 +105,15 @@ func NewRateTiming(ratesTag string, timing *Timing, weight string) (rt *RateTimi func (rt *RateTiming) GetInterval(r *Rate) (i *timespans.Interval) { i = ×pans.Interval{ - Months: rt.timing.Months, - MonthDays: rt.timing.MonthDays, - WeekDays: rt.timing.WeekDays, - StartTime: rt.timing.StartTime, - Weight: rt.Weight, - ConnectFee: r.ConnectFee, - Price: r.Price, - BillingUnit: r.BillingUnit, + Months: rt.timing.Months, + MonthDays: rt.timing.MonthDays, + WeekDays: rt.timing.WeekDays, + StartTime: rt.timing.StartTime, + Weight: rt.Weight, + ConnectFee: r.ConnectFee, + Price: r.Price, + PricedUnits: r.PricedUnits, + RateIncrements: r.RateIncrements, } return } diff --git a/cmd/cgr-loader/rates.go b/cmd/cgr-loader/rates.go index e80395fdf..5cd9d269d 100644 --- a/cmd/cgr-loader/rates.go +++ b/cmd/cgr-loader/rates.go @@ -19,9 +19,9 @@ along with this program. If not, see package main import ( + "fmt" "github.com/cgrates/cgrates/timespans" "log" - "fmt" "time" ) @@ -78,7 +78,7 @@ func (csvr *CSVReader) loadRates(fn string) { // skip header line continue } - r, err := NewRate(record[1], record[2], record[3], record[4]) + r, err := NewRate(record[1], record[2], record[3], record[4], record[5]) if err != nil { continue } diff --git a/data/Rates.csv b/data/Rates.csv index e6946c608..043c42d1a 100644 --- a/data/Rates.csv +++ b/data/Rates.csv @@ -1,9 +1,9 @@ -Tag,DestinationsTag,ConnectFee,Price,BillingUnit -RT_STANDARD,GERMANY,0,0.2,1 -RT_STANDARD,GERMANY_O2,0,0.1,1 -RT_STANDARD,GERMANY_PREMIUM,0,0.1,1 -RT_DEFAULT,ALL,0,0.1,1 -RT_STD_WEEKEND,GERMANY,0,0.1,1 -RT_STD_WEEKEND,GERMANY_O2,0,0.05,1 -P1,NAT,0,1,1 -P2,NAT,0,0.5,1 \ No newline at end of file +Tag,DestinationsTag,ConnectFee,Price,PricedUnits,RateIncrements +RT_STANDARD,GERMANY,0,0.2,60,1 +RT_STANDARD,GERMANY_O2,0,0.1,60,1 +RT_STANDARD,GERMANY_PREMIUM,0,0.1,60,1 +RT_DEFAULT,ALL,0,0.1,60,1 +RT_STD_WEEKEND,GERMANY,0,0.1,60,1 +RT_STD_WEEKEND,GERMANY_O2,0,0.05,60,1 +P1,NAT,0,1,1,1 +P2,NAT,0,0.5,1,1 diff --git a/timespans/actions_test.go b/timespans/actions_test.go index 0d82d69b7..23ab5c9fb 100644 --- a/timespans/actions_test.go +++ b/timespans/actions_test.go @@ -31,15 +31,16 @@ func init() { func TestActionTimingStoreRestore(t *testing.T) { i := &Interval{ - Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, - MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, - WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, - StartTime: "18:00:00", - EndTime: "00:00:00", - Weight: 10.0, - ConnectFee: 0.0, - Price: 1.0, - BillingUnit: 1.0, + Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, + MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, + StartTime: "18:00:00", + EndTime: "00:00:00", + Weight: 10.0, + ConnectFee: 0.0, + Price: 1.0, + PricedUnits: 60, + RateIncrements: 1, } at := &ActionTiming{ Id: "some uuid", @@ -50,7 +51,7 @@ func TestActionTimingStoreRestore(t *testing.T) { ActionsId: "Commando", } r := at.store() - if string(r) != "some uuid|test|one,two,three|1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;1|10|Commando" { + if string(r) != "some uuid|test|one,two,three|1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1|10|Commando" { t.Errorf("Error serializing action timing: %v", string(r)) } o := &ActionTiming{} diff --git a/timespans/activationperiod_test.go b/timespans/activationperiod_test.go index 6fe1ac5df..20e15e068 100644 --- a/timespans/activationperiod_test.go +++ b/timespans/activationperiod_test.go @@ -19,9 +19,9 @@ along with this program. If not, see package timespans import ( + "encoding/json" "reflect" "testing" - "encoding/json" "time" //"log" ) @@ -42,7 +42,7 @@ func TestApStoreRestore(t *testing.T) { ap := &ActivationPeriod{ActivationTime: d} ap.AddInterval(i) result := ap.store() - expected := "1328106601000000000|2;1;3,4;14:30:00;15:00:00;0;0;0;0" + expected := "1328106601000000000|2;1;3,4;14:30:00;15:00:00;0;0;0;0;0" if result != expected { t.Errorf("Expected %q was %q", expected, result) } @@ -76,7 +76,7 @@ func TestApStoreRestoreJson(t *testing.T) { ap := &ActivationPeriod{ActivationTime: d} ap.AddInterval(i) result, _ := json.Marshal(ap) - expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Months\":[2],\"MonthDays\":[1],\"WeekDays\":[3,4],\"StartTime\":\"14:30:00\",\"EndTime\":\"15:00:00\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"BillingUnit\":0}]}" + expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Months\":[2],\"MonthDays\":[1],\"WeekDays\":[3,4],\"StartTime\":\"14:30:00\",\"EndTime\":\"15:00:00\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0}]}" if string(result) != expected { t.Errorf("Expected %q was %q", expected, result) } @@ -93,7 +93,7 @@ func TestApStoreRestoreBlank(t *testing.T) { ap := &ActivationPeriod{ActivationTime: d} ap.AddInterval(i) result, _ := json.Marshal(ap) - expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"BillingUnit\":0}]}" + expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0}]}" if string(result) != expected { t.Errorf("Expected %q was %q", expected, result) } diff --git a/timespans/interval.go b/timespans/interval.go index cc5326757..d0266e099 100644 --- a/timespans/interval.go +++ b/timespans/interval.go @@ -31,11 +31,11 @@ import ( Defines a time interval for which a certain set of prices will apply */ type Interval struct { - Months Months - MonthDays MonthDays - WeekDays WeekDays - StartTime, EndTime string // ##:##:## format - Weight, ConnectFee, Price, BillingUnit float64 + Months Months + MonthDays MonthDays + WeekDays WeekDays + StartTime, EndTime string // ##:##:## format + Weight, ConnectFee, Price, PricedUnits, RateIncrements float64 } /* @@ -139,7 +139,8 @@ func (i *Interval) store() (result string) { result += strconv.FormatFloat(i.Weight, 'f', -1, 64) + ";" result += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + ";" result += strconv.FormatFloat(i.Price, 'f', -1, 64) + ";" - result += strconv.FormatFloat(i.BillingUnit, 'f', -1, 64) + result += strconv.FormatFloat(i.PricedUnits, 'f', -1, 64) + ";" + result += strconv.FormatFloat(i.RateIncrements, 'f', -1, 64) return } @@ -156,5 +157,6 @@ func (i *Interval) restore(input string) { i.Weight, _ = strconv.ParseFloat(is[5], 64) i.ConnectFee, _ = strconv.ParseFloat(is[6], 64) i.Price, _ = strconv.ParseFloat(is[7], 64) - i.BillingUnit, _ = strconv.ParseFloat(is[8], 64) + i.PricedUnits, _ = strconv.ParseFloat(is[8], 64) + i.RateIncrements, _ = strconv.ParseFloat(is[9], 64) } diff --git a/timespans/interval_test.go b/timespans/interval_test.go index 9276db1f2..88c2d6916 100644 --- a/timespans/interval_test.go +++ b/timespans/interval_test.go @@ -26,18 +26,19 @@ import ( func TestIntervalStoreRestore(t *testing.T) { i := &Interval{ - Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, - MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, - WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, - StartTime: "18:00:00", - EndTime: "00:00:00", - Weight: 10.0, - ConnectFee: 0.0, - Price: 1.0, - BillingUnit: 1.0, + Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, + MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, + StartTime: "18:00:00", + EndTime: "00:00:00", + Weight: 10.0, + ConnectFee: 0.0, + Price: 1.0, + PricedUnits: 60, + RateIncrements: 1, } r := i.store() - if string(r) != "1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;1" { + if string(r) != "1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1" { t.Errorf("Error serializing interval: %v", string(r)) } o := &Interval{} diff --git a/timespans/timespans.go b/timespans/timespans.go index 16034a3d4..3f074310f 100644 --- a/timespans/timespans.go +++ b/timespans/timespans.go @@ -21,6 +21,7 @@ package timespans import ( "fmt" //"log" + "math" "time" ) @@ -59,10 +60,15 @@ func (ts *TimeSpan) getCost(cd *CallDescriptor) (cost float64) { if ts.Interval == nil { return 0 } - if ts.Interval.BillingUnit > 0 { - cost = (ts.GetDuration().Seconds() / ts.Interval.BillingUnit) * ts.Interval.Price + duration := ts.GetDuration().Seconds() + i := ts.Interval + if i.RateIncrements == 0 { + i.RateIncrements = 1 + } + if i.PricedUnits != 0 { + cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * (i.Price / i.PricedUnits) } else { - cost = ts.GetDuration().Seconds() * ts.Interval.Price + cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * i.Price } // if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { // userBalance.mux.RLock() diff --git a/timespans/timespans_test.go b/timespans/timespans_test.go index a65056a4f..40b40f005 100644 --- a/timespans/timespans_test.go +++ b/timespans/timespans_test.go @@ -196,8 +196,9 @@ func TestTimespanGetCost(t *testing.T) { if ts1.getCost(cd) != 600 { t.Error("Expected 10 got ", ts1.getCost(cd)) } - ts1.Interval.BillingUnit = .1 - if ts1.getCost(cd) != 6000 { + ts1.Interval.PricedUnits = 60 + ts1.Interval.RateIncrements = 1 + if ts1.getCost(cd) != 10 { t.Error("Expected 6000 got ", ts1.getCost(cd)) } }