diff --git a/cmd/cgr-loader/cgr-loader.go b/cmd/cgr-loader/cgr-loader.go index ac359032f..db41efc3d 100644 --- a/cmd/cgr-loader/cgr-loader.go +++ b/cmd/cgr-loader/cgr-loader.go @@ -36,7 +36,7 @@ import ( var ( //separator = flag.String("separator", ",", "Default field separator") cgrConfig, _ = config.NewDefaultCGRConfig() - migrateRC8 = flag.String("migrate_rc8", "", "Migrate Accounts, Actions, ActionTriggers and DerivedChargers to RC8 structures, possible values: *all,acc,atr,act,dcs") + migrateRC8 = flag.String("migrate_rc8", "", "Migrate Accounts, Actions, ActionTriggers and DerivedChargers to RC8 structures, possible values: *all,acc,atr,act,dcs,apl") tpdb_type = flag.String("tpdb_type", cgrConfig.TpDbType, "The type of the TariffPlan database ") tpdb_host = flag.String("tpdb_host", cgrConfig.TpDbHost, "The TariffPlan host to connect to.") tpdb_port = flag.String("tpdb_port", cgrConfig.TpDbPort, "The TariffPlan port to bind to.") @@ -142,6 +142,11 @@ func main() { log.Print(err.Error()) } } + if strings.Contains(*migrateRC8, "apl") || strings.Contains(*migrateRC8, "*all") { + if err := migratorRC8rat.migrateActionPlans(); err != nil { + log.Print(err.Error()) + } + } log.Print("Done!") return } diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index e237f1dbb..a1f11378b 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -425,3 +425,45 @@ func (mig MigratorRC8) migrateDerivedChargers() error { } return nil } + +func (mig MigratorRC8) migrateActionPlans() error { + keys, err := mig.db.Cmd("KEYS", utils.ACTION_PLAN_PREFIX+"*").List() + if err != nil { + return err + } + aplsMap := make(map[string]engine.ActionPlans, len(keys)) + for _, key := range keys { + log.Printf("Migrating action plans: %s...", key) + var apls engine.ActionPlans + var values []byte + if values, err = mig.db.Cmd("GET", key).Bytes(); err == nil { + if err := mig.ms.Unmarshal(values, &apls); err != nil { + return err + } + } + // change all AccountIds + for _, apl := range apls { + for idx, actionId := range apl.AccountIds { + // fix id + idElements := strings.Split(actionId, utils.CONCATENATED_KEY_SEP) + if len(idElements) != 3 { + log.Printf("Malformed account ID %s", actionId) + continue + } + apl.AccountIds[idx] = fmt.Sprintf("%s:%s", idElements[1], idElements[2]) + } + } + aplsMap[key] = apls + } + // write data back + for key, apl := range aplsMap { + result, err := mig.ms.Marshal(apl) + if err != nil { + return err + } + if err = mig.db.Cmd("SET", key, result).Err; err != nil { + return err + } + } + return nil +} diff --git a/console/actions.go b/console/actions.go new file mode 100644 index 000000000..25e9af0e5 --- /dev/null +++ b/console/actions.go @@ -0,0 +1,62 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2015 ITsysCOM GmbH + +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 console + +import "github.com/cgrates/cgrates/utils" + +func init() { + c := &CmdGetActions{ + name: "actions", + rpcMethod: "ApierV2.GetActions", + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdGetActions struct { + name string + rpcMethod string + rpcParams *StringWrapper + *CommandExecuter +} + +func (self *CmdGetActions) Name() string { + return self.name +} + +func (self *CmdGetActions) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdGetActions) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &StringWrapper{} + } + return self.rpcParams +} + +func (self *CmdGetActions) PostprocessRpcParams() error { + return nil +} + +func (self *CmdGetActions) RpcResult() interface{} { + a := make([]*utils.TPAction, 0) + return &a +} diff --git a/engine/account.go b/engine/account.go index 555545efc..dc7c5c5c4 100644 --- a/engine/account.go +++ b/engine/account.go @@ -365,7 +365,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo cc.Timespans = append(cc.Timespans, leftCC.Timespans...) } - //log.Printf("HERE: %+v %d", leftCC) + //log.Printf("HERE: %+v", leftCC) if leftCC.Cost > 0 && goNegative { initialLength := len(cc.Timespans) cc.Timespans = append(cc.Timespans, leftCC.Timespans...) @@ -379,6 +379,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo if len(leftCC.Timespans) > 0 && leftCC.Cost > 0 && !ub.AllowNegative && !dryRun { utils.Logger.Err(fmt.Sprintf(" Going negative on account %s with AllowNegative: false", cd.GetAccountKey())) } + leftCC.Timespans.Decompress() for _, ts := range leftCC.Timespans { if ts.Increments == nil { ts.createIncrementsSlice() diff --git a/engine/account_test.go b/engine/account_test.go index a272d0251..0cdcc7c10 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -143,6 +143,9 @@ func TestGetSpecialPricedSeconds(t *testing.T) { expected := 20 * time.Second if credit != 0 || seconds != expected || len(bucketList) != 2 || bucketList[0].Weight < bucketList[1].Weight { t.Log(seconds, credit, bucketList) + for _, b := range bucketList { + t.Logf("Balance: %+v", b) + } t.Errorf("Expected %v was %v", expected, seconds) } } diff --git a/engine/calldesc.go b/engine/calldesc.go index b98e3def8..96dacd589 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -503,6 +503,7 @@ func (cd *CallDescriptor) getCost() (*CallCost, error) { cost := 0.0 for i, ts := range timespans { + ts.createIncrementsSlice() // only add connect fee if this is the first/only call cost request //log.Printf("Interval: %+v", ts.RateInterval.Timing) if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil { diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 75926480f..2e8744fcd 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -574,7 +574,7 @@ func TestGetCostRoundingIssue(t *testing.T) { MaxCostSoFar: 0, } cc, err := cd.GetCost() - expected := 0.17 + expected := 0.39 if cc.Cost != expected || err != nil { t.Log(utils.ToIJSON(cc)) t.Errorf("Expected %v was %+v", expected, cc) diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 434fa6461..5d4beec49 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -193,7 +193,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 0.6417 { // 0.01 first minute, 0.04 25 seconds with RT_20CNT + } else if cc.Cost != 0.6425 { // 0.01 first minute, 0.04 25 seconds with RT_20CNT t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) } tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z") @@ -229,7 +229,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 1.3 { + } else if cc.Cost != 1.3002 { t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) } tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z") @@ -265,7 +265,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 1.3 { + } else if cc.Cost != 1.3002 { t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) } tStart = time.Date(2014, 8, 4, 13, 0, 0, 0, time.UTC)