From aa6e18b27bcac12e9d2e06168496e37f2bad2347 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 24 Aug 2018 20:25:04 +0200 Subject: [PATCH] Integration tests for CdrsV2.ProcesCDR with ChargerS --- apier/v2/cdrs_it_test.go | 257 ++++---------- apier/v2/cdrsold_it_test.go | 317 ++++++++++++++++++ cdrc/csv.go | 1 - config/config_test.go | 31 ++ .../conf/samples/cdrsv2mysql/cdrsv2mysql.json | 24 +- data/conf/samples/tutmysql/cgrates.json | 5 +- data/tariffplans/testit/Attributes.csv | 2 + data/tariffplans/testit/Chargers.csv | 3 + data/tariffplans/testit/DestinationRates.csv | 1 + data/tariffplans/testit/Rates.csv | 1 + data/tariffplans/testit/RatingPlans.csv | 4 +- data/tariffplans/testit/RatingProfiles.csv | 3 + engine/cdr.go | 6 + engine/cdrs.go | 10 +- utils/consts.go | 5 + 15 files changed, 466 insertions(+), 204 deletions(-) create mode 100644 apier/v2/cdrsold_it_test.go create mode 100644 data/tariffplans/testit/Chargers.csv diff --git a/apier/v2/cdrs_it_test.go b/apier/v2/cdrs_it_test.go index 59e423669..745a0f9f6 100644 --- a/apier/v2/cdrs_it_test.go +++ b/apier/v2/cdrs_it_test.go @@ -41,18 +41,12 @@ var sTestsCDRsIT = []func(t *testing.T){ testV2CDRsInitConfig, testV2CDRsInitDataDb, testV2CDRsInitCdrDb, - testV2CDRsInjectUnratedCdr, testV2CDRsStartEngine, testV2CDRsRpcConn, - testV2CDRsProcessCdrRated, - testV2CDRsProcessCdrRaw, - testV2CDRsGetCdrs, - testV2CDRsCountCdrs, - testV2CDRsProcessPrepaidCdr, - testV2CDRsRateWithoutTP, testV2CDRsLoadTariffPlanFromFolder, - testV2CDRsRateWithTP, - // ToDo: test engine shutdown + testV2CDRsProcessCDR, + testV2CDRsGetCdrs, + testV2CDRsKillEngine, } // Tests starting here @@ -98,34 +92,6 @@ func testV2CDRsInitCdrDb(t *testing.T) { } } -func testV2CDRsInjectUnratedCdr(t *testing.T) { - var db engine.CdrStorage - switch cdrsConfDIR { - case "cdrsv2mysql": - db, err = engine.NewMySQLStorage(cdrsCfg.StorDBHost, cdrsCfg.StorDBPort, cdrsCfg.StorDBName, cdrsCfg.StorDBUser, cdrsCfg.StorDBPass, - cdrsCfg.StorDBMaxOpenConns, cdrsCfg.StorDBMaxIdleConns, cdrsCfg.StorDBConnMaxLifetime) - case "cdrsv2psql": - db, err = engine.NewPostgresStorage(cdrsCfg.StorDBHost, cdrsCfg.StorDBPort, cdrsCfg.StorDBName, cdrsCfg.StorDBUser, cdrsCfg.StorDBPass, - cdrsCfg.StorDBMaxOpenConns, cdrsCfg.StorDBMaxIdleConns, cdrsCfg.StorDBConnMaxLifetime) - case "cdrsv2mongo": - db, err = engine.NewMongoStorage(cdrsCfg.StorDBHost, cdrsCfg.StorDBPort, cdrsCfg.StorDBName, - cdrsCfg.StorDBUser, cdrsCfg.StorDBPass, utils.StorDB, cdrsCfg.StorDBCDRSIndexes, nil, 10) - } - if err != nil { - t.Error("Error on opening database connection: ", err) - return - } - strCdr1 := &engine.CDR{CGRID: utils.Sha1("bbb1", time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC).String()), RunID: utils.MetaRaw, - ToR: utils.VOICE, OriginID: "bbb1", OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoInjectUnratedCdr", RequestType: utils.META_RATED, - Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC), AnswerTime: time.Date(2015, 11, 21, 10, 47, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - Cost: -1} - if err := db.SetCDR(strCdr1, false); err != nil { - t.Error(err.Error()) - } -} - func testV2CDRsStartEngine(t *testing.T) { if _, err := engine.StopStartEngine(cdrsCfgPath, *waitRater); err != nil { t.Fatal(err) @@ -140,178 +106,85 @@ func testV2CDRsRpcConn(t *testing.T) { } } -func testV2CDRsProcessCdrRated(t *testing.T) { - cdr := &engine.CDR{ - CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC).String()), RunID: utils.DEFAULT_RUNID, - OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessCdrRated", RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", - Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC), AnswerTime: time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - Cost: 1.01, CostSource: "TestV2CdrsMongoProcessCdrRated", PreRated: true, - } - var reply string - if err := cdrsRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if reply != utils.OK { - t.Error("Unexpected reply received: ", reply) - } -} - -func testV2CDRsProcessCdrRaw(t *testing.T) { - cdr := &engine.CDR{ - CGRID: utils.Sha1("abcdeftg", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, RunID: utils.MetaRaw, - ToR: utils.VOICE, OriginID: "abcdeftg", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessCdrRaw", RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", - Account: "1002", Subject: "1002", Destination: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - } - var reply string - if err := cdrsRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if reply != utils.OK { - t.Error("Unexpected reply received: ", reply) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) -} - -func testV2CDRsGetCdrs(t *testing.T) { - var reply []*engine.ExternalCDR - req := utils.RPCCDRsFilter{} - if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if len(reply) != 4 { // 1 injected, 1 rated, 1 *raw and it's pair in *default run - t.Error("Unexpected number of CDRs returned: ", len(reply)) - } - // CDRs with rating errors - req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, MinCost: utils.Float64Pointer(-1.0), MaxCost: utils.Float64Pointer(0.0)} - if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if len(reply) != 1 { - t.Error("Unexpected number of CDRs returned: ", reply) - } - // CDRs Rated - req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}} - if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if len(reply) != 2 { - t.Error("Unexpected number of CDRs returned: ", reply) - } - // Raw CDRs - req = utils.RPCCDRsFilter{RunIDs: []string{utils.MetaRaw}} - if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if len(reply) != 2 { - t.Error("Unexpected number of CDRs returned: ", reply) - } - // Skip Errors - req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, MinCost: utils.Float64Pointer(0.0), MaxCost: utils.Float64Pointer(-1.0)} - if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if len(reply) != 1 { - t.Error("Unexpected number of CDRs returned: ", reply) - } -} - -func testV2CDRsCountCdrs(t *testing.T) { - var reply int64 - req := utils.AttrGetCdrs{} - if err := cdrsRpc.Call("ApierV2.CountCdrs", req, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if reply != 4 { - t.Error("Unexpected number of CDRs returned: ", reply) - } -} - -// Make sure *prepaid does not block until finding previous costs -func testV2CDRsProcessPrepaidCdr(t *testing.T) { - var reply string - cdrs := []*engine.CDR{ - &engine.CDR{CGRID: utils.Sha1("dsafdsaf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr1", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", - Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, PreRated: true, - }, - &engine.CDR{CGRID: utils.Sha1("abcdeftg2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr2", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", - Category: "call", Account: "1002", Subject: "1002", Destination: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, - }, - &engine.CDR{CGRID: utils.Sha1("aererfddf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr3", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", - Category: "call", Account: "1003", Subject: "1003", Destination: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, - }, - } - tStart := time.Now() - for _, cdr := range cdrs { - if err := cdrsRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if reply != utils.OK { - t.Error("Unexpected reply received: ", reply) - } - } - if processDur := time.Now().Sub(tStart); processDur > 1*time.Second { - t.Error("Unexpected processing time", processDur) - } -} - -func testV2CDRsRateWithoutTP(t *testing.T) { - rawCdrCGRID := utils.Sha1("bbb1", time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC).String()) - // Rate the injected CDR, should not rate it since we have no TP loaded - attrs := utils.AttrRateCdrs{CgrIds: []string{rawCdrCGRID}} - var reply string - if err := cdrsRpc.Call("CdrsV2.RateCdrs", attrs, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if reply != utils.OK { - t.Error("Unexpected reply received: ", reply) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{CGRIDs: []string{rawCdrCGRID}, RunIDs: []string{utils.META_DEFAULT}} - if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if len(cdrs) != 1 { // Injected CDR did not have a charging run - t.Error("Unexpected number of CDRs returned: ", len(cdrs)) - } else { - if cdrs[0].Cost != -1 { - t.Errorf("Unexpected CDR returned: %+v", cdrs[0]) - } - } -} - func testV2CDRsLoadTariffPlanFromFolder(t *testing.T) { var loadInst utils.LoadInstance - attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "oldtutorial")} - if err := cdrsRpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { + if err := cdrsRpc.Call("ApierV2.LoadTariffPlanFromFolder", + &utils.AttrLoadTpFromFolder{FolderPath: path.Join( + *dataDir, "tariffplans", "testit")}, &loadInst); err != nil { t.Error(err) } time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups } -func testV2CDRsRateWithTP(t *testing.T) { - rawCdrCGRID := utils.Sha1("bbb1", time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC).String()) - attrs := utils.AttrRateCdrs{CgrIds: []string{rawCdrCGRID}} +func testV2CDRsProcessCDR(t *testing.T) { + cgrEv := &utils.CGREvent{ + Tenant: "cgrates.org", + Event: map[string]interface{}{ + utils.OriginID: "testV2CDRsProcessCDR1", + utils.OriginHost: "192.168.1.1", + utils.Source: "testV2CDRsProcessCDR", + utils.RequestType: utils.META_RATED, + utils.Category: "customers", + utils.Account: "testV2CDRsProcessCDR", + utils.Destination: "+4986517174963", + utils.AnswerTime: time.Date(2018, 8, 24, 16, 00, 26, 0, time.UTC), + utils.Usage: time.Duration(1) * time.Minute, + "field_extr1": "val_extr1", + "fieldextr2": "valextr2", + }, + } var reply string - if err := cdrsRpc.Call("CdrsV2.RateCdrs", attrs, &reply); err != nil { + if err := cdrsRpc.Call(utils.CdrsV2ProcessCDR, cgrEv, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if reply != utils.OK { t.Error("Unexpected reply received: ", reply) } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) + time.Sleep(time.Duration(2000) * time.Millisecond) // Give time for CDR to be rated +} + +func testV2CDRsGetCdrs(t *testing.T) { + var cdrCnt int64 + req := utils.AttrGetCdrs{} + if err := cdrsRpc.Call("ApierV2.CountCdrs", req, &cdrCnt); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if cdrCnt != 3 { + t.Error("Unexpected number of CDRs returned: ", cdrCnt) + } var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{CGRIDs: []string{rawCdrCGRID}, RunIDs: []string{utils.META_DEFAULT}} - if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { + args := utils.RPCCDRsFilter{RunIDs: []string{utils.MetaRaw}} + if err := cdrsRpc.Call("ApierV2.GetCdrs", args, &cdrs); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(cdrs) != 1 { t.Error("Unexpected number of CDRs returned: ", len(cdrs)) } else { - if cdrs[0].Cost != 0.3 { - t.Errorf("Unexpected CDR returned: %+v", cdrs[0]) + if cdrs[0].Cost != -1.0 { + t.Errorf("Unexpected cost for CDR: %f", cdrs[0].Cost) + } + } + args = utils.RPCCDRsFilter{RunIDs: []string{"CustomerCharges"}} + if err := cdrsRpc.Call("ApierV2.GetCdrs", args, &cdrs); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(cdrs) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(cdrs)) + } else { + if cdrs[0].Cost != 0.0198 { + t.Errorf("Unexpected cost for CDR: %f", cdrs[0].Cost) + } + } + args = utils.RPCCDRsFilter{RunIDs: []string{"SupplierCharges"}} + if err := cdrsRpc.Call("ApierV2.GetCdrs", args, &cdrs); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(cdrs) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(cdrs)) + } else { + if cdrs[0].Cost != 0.0102 { + t.Errorf("Unexpected cost for CDR: %f", cdrs[0].Cost) } } } + +func testV2CDRsKillEngine(t *testing.T) { + if err := engine.KillEngine(100); err != nil { + t.Error(err) + } +} diff --git a/apier/v2/cdrsold_it_test.go b/apier/v2/cdrsold_it_test.go new file mode 100644 index 000000000..851a006cd --- /dev/null +++ b/apier/v2/cdrsold_it_test.go @@ -0,0 +1,317 @@ +// +build integration + +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) 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 v2 + +import ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var cdrsOldCfgPath string +var cdrsOldCfg *config.CGRConfig +var cdrsOldRpc *rpc.Client +var cdrsOldConfDIR string // run the tests for specific configuration + +// subtests to be executed for each confDIR +var sOldTestsCDRsIT = []func(t *testing.T){ + testV2CDRsOldInitConfig, + testV2CDRsOldInitDataDb, + testV2CDRsOldInitCdrDb, + testV2CDRsOldInjectUnratedCdr, + testV2CDRsOldStartEngine, + testV2CDRsOldOldRpcConn, + testV2CDRsOldProcessCdrRated, + testV2CDRsOldProcessCdrRaw, + testV2CDRsOldGetCdrs, + testV2CDRsOldCountCdrs, + testV2CDRsOldProcessPrepaidCdr, + testV2CDRsOldRateWithoutTP, + testV2CDRsOldLoadTariffPlanFromFolder, + testV2CDRsOldRateWithTP, + // ToDo: test engine shutdown +} + +// Tests starting here +func TestCDRsOldITMySQL(t *testing.T) { + cdrsOldConfDIR = "cdrsv2mysql" + for _, stest := range sOldTestsCDRsIT { + t.Run(cdrsOldConfDIR, stest) + } +} + +func TestCDRsOldITpg(t *testing.T) { + cdrsOldConfDIR = "cdrsv2psql" + for _, stest := range sOldTestsCDRsIT { + t.Run(cdrsOldConfDIR, stest) + } +} + +func TestCDRsOldITMongo(t *testing.T) { + cdrsOldConfDIR = "cdrsv2mongo" + for _, stest := range sOldTestsCDRsIT { + t.Run(cdrsOldConfDIR, stest) + } +} + +func testV2CDRsOldInitConfig(t *testing.T) { + var err error + cdrsOldCfgPath = path.Join(*dataDir, "conf", "samples", cdrsOldConfDIR) + if cdrsOldCfg, err = config.NewCGRConfigFromFolder(cdrsOldCfgPath); err != nil { + t.Fatal("Got config error: ", err.Error()) + } +} + +func testV2CDRsOldInitDataDb(t *testing.T) { + if err := engine.InitDataDb(cdrsOldCfg); err != nil { + t.Fatal(err) + } +} + +// InitDb so we can rely on count +func testV2CDRsOldInitCdrDb(t *testing.T) { + if err := engine.InitStorDb(cdrsOldCfg); err != nil { + t.Fatal(err) + } +} + +func testV2CDRsOldInjectUnratedCdr(t *testing.T) { + var db engine.CdrStorage + switch cdrsOldConfDIR { + case "cdrsv2mysql": + db, err = engine.NewMySQLStorage(cdrsOldCfg.StorDBHost, cdrsOldCfg.StorDBPort, cdrsOldCfg.StorDBName, cdrsOldCfg.StorDBUser, cdrsOldCfg.StorDBPass, + cdrsOldCfg.StorDBMaxOpenConns, cdrsOldCfg.StorDBMaxIdleConns, cdrsOldCfg.StorDBConnMaxLifetime) + case "cdrsv2psql": + db, err = engine.NewPostgresStorage(cdrsOldCfg.StorDBHost, cdrsOldCfg.StorDBPort, cdrsOldCfg.StorDBName, cdrsOldCfg.StorDBUser, cdrsOldCfg.StorDBPass, + cdrsOldCfg.StorDBMaxOpenConns, cdrsOldCfg.StorDBMaxIdleConns, cdrsOldCfg.StorDBConnMaxLifetime) + case "cdrsv2mongo": + db, err = engine.NewMongoStorage(cdrsOldCfg.StorDBHost, cdrsOldCfg.StorDBPort, cdrsOldCfg.StorDBName, + cdrsOldCfg.StorDBUser, cdrsOldCfg.StorDBPass, utils.StorDB, cdrsOldCfg.StorDBCDRSIndexes, nil, 10) + } + if err != nil { + t.Error("Error on opening database connection: ", err) + return + } + strCdr1 := &engine.CDR{CGRID: utils.Sha1("bbb1", time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC).String()), RunID: utils.MetaRaw, + ToR: utils.VOICE, OriginID: "bbb1", OriginHost: "192.168.1.1", Source: "testV2CDRsOldMongoInjectUnratedCdr", RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC), AnswerTime: time.Date(2015, 11, 21, 10, 47, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Cost: -1} + if err := db.SetCDR(strCdr1, false); err != nil { + t.Error(err.Error()) + } +} + +func testV2CDRsOldStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(cdrsOldCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testV2CDRsOldOldRpcConn(t *testing.T) { + cdrsOldRpc, err = jsonrpc.Dial("tcp", cdrsOldCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal("Could not connect to rater: ", err.Error()) + } +} + +func testV2CDRsOldProcessCdrRated(t *testing.T) { + cdr := &engine.CDR{ + CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC).String()), RunID: utils.DEFAULT_RUNID, + OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: "testV2CDRsOldMongoProcessCdrRated", RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", + Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC), AnswerTime: time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Cost: 1.01, CostSource: "testV2CDRsOldMongoProcessCdrRated", PreRated: true, + } + var reply string + if err := cdrsOldRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if reply != utils.OK { + t.Error("Unexpected reply received: ", reply) + } +} + +func testV2CDRsOldProcessCdrRaw(t *testing.T) { + cdr := &engine.CDR{ + CGRID: utils.Sha1("abcdeftg", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, RunID: utils.MetaRaw, + ToR: utils.VOICE, OriginID: "abcdeftg", + OriginHost: "192.168.1.1", Source: "testV2CDRsOldMongoProcessCdrRaw", RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", + Account: "1002", Subject: "1002", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + } + var reply string + if err := cdrsOldRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if reply != utils.OK { + t.Error("Unexpected reply received: ", reply) + } + time.Sleep(time.Duration(*waitRater) * time.Millisecond) +} + +func testV2CDRsOldGetCdrs(t *testing.T) { + var reply []*engine.ExternalCDR + req := utils.RPCCDRsFilter{} + if err := cdrsOldRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 4 { // 1 injected, 1 rated, 1 *raw and it's pair in *default run + t.Error("Unexpected number of CDRs returned: ", len(reply)) + } + // CDRs with rating errors + req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, MinCost: utils.Float64Pointer(-1.0), MaxCost: utils.Float64Pointer(0.0)} + if err := cdrsOldRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 1 { + t.Error("Unexpected number of CDRs returned: ", reply) + } + // CDRs Rated + req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}} + if err := cdrsOldRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 2 { + t.Error("Unexpected number of CDRs returned: ", reply) + } + // Raw CDRs + req = utils.RPCCDRsFilter{RunIDs: []string{utils.MetaRaw}} + if err := cdrsOldRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 2 { + t.Error("Unexpected number of CDRs returned: ", reply) + } + // Skip Errors + req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, MinCost: utils.Float64Pointer(0.0), MaxCost: utils.Float64Pointer(-1.0)} + if err := cdrsOldRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 1 { + t.Error("Unexpected number of CDRs returned: ", reply) + } +} + +func testV2CDRsOldCountCdrs(t *testing.T) { + var reply int64 + req := utils.AttrGetCdrs{} + if err := cdrsOldRpc.Call("ApierV2.CountCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if reply != 4 { + t.Error("Unexpected number of CDRs returned: ", reply) + } +} + +// Make sure *prepaid does not block until finding previous costs +func testV2CDRsOldProcessPrepaidCdr(t *testing.T) { + var reply string + cdrs := []*engine.CDR{ + &engine.CDR{CGRID: utils.Sha1("dsafdsaf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: "testV2CDRsOldMongoProcessPrepaidCdr1", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", + Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, PreRated: true, + }, + &engine.CDR{CGRID: utils.Sha1("abcdeftg2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: "testV2CDRsOldMongoProcessPrepaidCdr2", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", + Category: "call", Account: "1002", Subject: "1002", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, + }, + &engine.CDR{CGRID: utils.Sha1("aererfddf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: "testV2CDRsOldMongoProcessPrepaidCdr3", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", + Category: "call", Account: "1003", Subject: "1003", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, + }, + } + tStart := time.Now() + for _, cdr := range cdrs { + if err := cdrsOldRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if reply != utils.OK { + t.Error("Unexpected reply received: ", reply) + } + } + if processDur := time.Now().Sub(tStart); processDur > 1*time.Second { + t.Error("Unexpected processing time", processDur) + } +} + +func testV2CDRsOldRateWithoutTP(t *testing.T) { + rawCdrCGRID := utils.Sha1("bbb1", time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC).String()) + // Rate the injected CDR, should not rate it since we have no TP loaded + attrs := utils.AttrRateCdrs{CgrIds: []string{rawCdrCGRID}} + var reply string + if err := cdrsOldRpc.Call("CdrsV2.RateCdrs", attrs, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if reply != utils.OK { + t.Error("Unexpected reply received: ", reply) + } + time.Sleep(time.Duration(*waitRater) * time.Millisecond) + var cdrs []*engine.ExternalCDR + req := utils.RPCCDRsFilter{CGRIDs: []string{rawCdrCGRID}, RunIDs: []string{utils.META_DEFAULT}} + if err := cdrsOldRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(cdrs) != 1 { // Injected CDR did not have a charging run + t.Error("Unexpected number of CDRs returned: ", len(cdrs)) + } else { + if cdrs[0].Cost != -1 { + t.Errorf("Unexpected CDR returned: %+v", cdrs[0]) + } + } +} + +func testV2CDRsOldLoadTariffPlanFromFolder(t *testing.T) { + var loadInst utils.LoadInstance + attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "oldtutorial")} + if err := cdrsOldRpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { + t.Error(err) + } + time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups +} + +func testV2CDRsOldRateWithTP(t *testing.T) { + rawCdrCGRID := utils.Sha1("bbb1", time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC).String()) + attrs := utils.AttrRateCdrs{CgrIds: []string{rawCdrCGRID}} + var reply string + if err := cdrsOldRpc.Call("CdrsV2.RateCdrs", attrs, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if reply != utils.OK { + t.Error("Unexpected reply received: ", reply) + } + time.Sleep(time.Duration(*waitRater) * time.Millisecond) + var cdrs []*engine.ExternalCDR + req := utils.RPCCDRsFilter{CGRIDs: []string{rawCdrCGRID}, RunIDs: []string{utils.META_DEFAULT}} + if err := cdrsOldRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(cdrs) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(cdrs)) + } else { + if cdrs[0].Cost != 0.3 { + t.Errorf("Unexpected CDR returned: %+v", cdrs[0]) + } + } +} diff --git a/cdrc/csv.go b/cdrc/csv.go index 748f4a7de..8cb4f55cc 100644 --- a/cdrc/csv.go +++ b/cdrc/csv.go @@ -102,7 +102,6 @@ func (self *CsvRecordsProcessor) processFlatstoreRecord(record []string) ([]stri // Takes the record from a slice and turns it into StoredCdrs, posting them to the cdrServer func (self *CsvRecordsProcessor) processRecord(record []string) ([]*engine.CDR, error) { - utils.Logger.Debug(fmt.Sprintf("Record from CSV : %+v \n", record)) recordCdrs := make([]*engine.CDR, 0) // More CDRs based on the number of filters and field templates for _, cdrcCfg := range self.cdrcCfgs { // cdrFields coming from more templates will produce individual storCdr records // Make sure filters are matching diff --git a/config/config_test.go b/config/config_test.go index 76cd395cd..c0f4c985d 100755 --- a/config/config_test.go +++ b/config/config_test.go @@ -534,6 +534,37 @@ func TestCgrCfgJSONDefaultsCDRS(t *testing.T) { } } +func TestCgrCfgJSONLoadCDRS(t *testing.T) { + JSN_RAW_CFG := ` +{ +"cdrs": { + "enabled": true, + "chargers_conns": [ + {"address": "*internal"} + ], + "rals_conns": [ + {"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234> + ], +}, +} + ` + cgrCfg, err := NewCGRConfigFromJsonStringWithDefaults(JSN_RAW_CFG) + if err != nil { + t.Error(err) + } + if !cgrCfg.CDRSEnabled { + t.Error(cgrCfg.CDRSEnabled) + } + if !reflect.DeepEqual(cgrCfg.CDRSChargerSConns, + []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}}) { + t.Error(cgrCfg.CDRSChargerSConns) + } + if !reflect.DeepEqual(cgrCfg.CDRSRaterConns, + []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}}) { + t.Error(cgrCfg.CDRSRaterConns) + } +} + func TestCgrCfgJSONDefaultsCDRStats(t *testing.T) { if cgrCfg.CDRStatsEnabled != false { t.Error(cgrCfg.CDRStatsEnabled) diff --git a/data/conf/samples/cdrsv2mysql/cdrsv2mysql.json b/data/conf/samples/cdrsv2mysql/cdrsv2mysql.json index 55c24bcbd..48491fdd9 100644 --- a/data/conf/samples/cdrsv2mysql/cdrsv2mysql.json +++ b/data/conf/samples/cdrsv2mysql/cdrsv2mysql.json @@ -9,19 +9,33 @@ }, -"stor_db": { // database used to store offline tariff plans and CDRs - "db_password": "CGRateS.org", // password to use when connecting to stordb +"stor_db": { + "db_password": "CGRateS.org", }, "rals": { - "enabled": true, // enable Rater service: + "enabled": true, }, "cdrs": { - "enabled": true, // start the CDR Server service: + "enabled": true, + "chargers_conns":[ + {"address": "127.0.0.1:2012", "transport":"*json"}, + ], "rals_conns": [ - {"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234> + {"address": "*internal"} + ], +}, + +"attributes": { + "enabled": true, +}, + +"chargers": { + "enabled": true, + "attributes_conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"}, ], }, diff --git a/data/conf/samples/tutmysql/cgrates.json b/data/conf/samples/tutmysql/cgrates.json index 8273f91a3..96ba17479 100644 --- a/data/conf/samples/tutmysql/cgrates.json +++ b/data/conf/samples/tutmysql/cgrates.json @@ -74,6 +74,9 @@ "cdrs": { "enabled": true, + "chargers_conns":[ + {"address": "127.0.0.1:2012", "transport":"*json"}, + ], }, @@ -226,7 +229,7 @@ "chargers": { "enabled": true, "attributes_conns": [ - {"address": "*internal"} + {"address": "127.0.0.1:2012", "transport":"*json"}, ], }, diff --git a/data/tariffplans/testit/Attributes.csv b/data/tariffplans/testit/Attributes.csv index 4070be620..21db47b7a 100644 --- a/data/tariffplans/testit/Attributes.csv +++ b/data/tariffplans/testit/Attributes.csv @@ -1,2 +1,4 @@ #Tenant,ID,Context,FilterIDs,ActivationInterval,FieldName,Initial,Substitute,Append,Blocker,Weight cgrates.org,ATTR_ACNT_1001,*sessions,FLTR_ACCOUNT_1001,,OfficeGroup,*any,Marketing,true,false,10 +cgrates.org,ATTR_SUPPLIER1,*chargers,*string:Category:customers,,Subject,*any,supplier1,false,false,10 +cgrates.org,ATTR_SUPPLIER1,,,,Category,*any,call,false,false,10 diff --git a/data/tariffplans/testit/Chargers.csv b/data/tariffplans/testit/Chargers.csv new file mode 100644 index 000000000..c8fb8e16c --- /dev/null +++ b/data/tariffplans/testit/Chargers.csv @@ -0,0 +1,3 @@ +#Tenant,ID,FilterIDs,ActivationInterval,RunID,AttributeIDs,Weight +cgrates.org,CustomerCharges,,,CustomerCharges,,20 +cgrates.org,SupplierCharges,,,SupplierCharges,ATTR_SUPPLIER1,10 \ No newline at end of file diff --git a/data/tariffplans/testit/DestinationRates.csv b/data/tariffplans/testit/DestinationRates.csv index 397bbdc3a..aa5d84c08 100644 --- a/data/tariffplans/testit/DestinationRates.csv +++ b/data/tariffplans/testit/DestinationRates.csv @@ -1,4 +1,5 @@ #Id,DestinationId,RatesTag,RoundingMethod,RoundingDecimals,MaxCost,MaxCostStrategy DR_ANY_1CNT,*any,RT_1CNT,*up,5,0, +DR_ANY_2CNT,*any,RT_2CNT,*up,5,0, DR_SPECIAL_1002,DST_1002,RT_1CNT,*up,4,0, DR_FS_40CNT,DST_FS,RT_40CNT,*up,4,0, \ No newline at end of file diff --git a/data/tariffplans/testit/Rates.csv b/data/tariffplans/testit/Rates.csv index 318fe8358..e55963098 100644 --- a/data/tariffplans/testit/Rates.csv +++ b/data/tariffplans/testit/Rates.csv @@ -1,4 +1,5 @@ #Id,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart RT_1CNT,0,0.01,60s,1s,0s +RT_2CNT,0,0.02,60s,1s,0s RT_40CNT,0.8,0.4,60s,30s,0s RT_40CNT,0,0.2,60s,10s,60s diff --git a/data/tariffplans/testit/RatingPlans.csv b/data/tariffplans/testit/RatingPlans.csv index 46ef7883d..cf5c4be97 100644 --- a/data/tariffplans/testit/RatingPlans.csv +++ b/data/tariffplans/testit/RatingPlans.csv @@ -1,4 +1,6 @@ #Id,DestinationRatesId,TimingTag,Weight RP_TESTIT1,DR_ANY_1CNT,*any,10 RP_SPECIAL_1002,DR_SPECIAL_1002,*any,10 -RP_RETAIL1,DR_FS_40CNT,*any,10 \ No newline at end of file +RP_RETAIL1,DR_FS_40CNT,*any,10 +RP_ANY2CNT,DR_ANY_2CNT,*any,10 +RP_ANY1CNT,DR_ANY_1CNT,*any,10 \ No newline at end of file diff --git a/data/tariffplans/testit/RatingProfiles.csv b/data/tariffplans/testit/RatingProfiles.csv index 55e587416..102f97f27 100644 --- a/data/tariffplans/testit/RatingProfiles.csv +++ b/data/tariffplans/testit/RatingProfiles.csv @@ -1,3 +1,6 @@ #Direction,Tenant,Category,Subject,ActivationTime,RatingPlanId,RatesFallbackSubject,CdrStatQueueIds *out,cgrates.org,call,*any,2018-01-01T00:00:00Z,RP_TESTIT1,, *out,cgrates.org,call,SPECIAL_1002,2014-01-14T00:00:00Z,RP_SPECIAL_1002,, +*out,cgrates.org,customers,*any,2018-01-01T00:00:00Z,RP_ANY2CNT,, +*out,cgrates.org,call,supplier1,2018-01-01T00:00:00Z,RP_ANY1CNT,, + diff --git a/engine/cdr.go b/engine/cdr.go index 85ec3dd60..e1bdbcc27 100644 --- a/engine/cdr.go +++ b/engine/cdr.go @@ -105,6 +105,9 @@ func (cdr *CDR) AddDefaults(cfg *config.CGRConfig) { if cdr.CGRID == "" { cdr.ComputeCGRID() } + if cdr.RunID == "" { + cdr.RunID = utils.MetaRaw + } if cdr.ToR == "" { cdr.ToR = utils.VOICE } @@ -117,6 +120,9 @@ func (cdr *CDR) AddDefaults(cfg *config.CGRConfig) { if cdr.Category == "" { cdr.Category = cfg.DefaultCategory } + if cdr.Subject == "" { + cdr.Subject = cdr.Account + } } func (cdr *CDR) CostDetailsJson() string { diff --git a/engine/cdrs.go b/engine/cdrs.go index 9c1b2ad68..816f7a54d 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -468,7 +468,8 @@ func (self *CdrServer) getCostFromRater(cdr *CDR) (*CallCost, error) { DurationIndex: cdr.Usage, PerformRounding: true, } - if utils.IsSliceMember([]string{utils.META_PSEUDOPREPAID, utils.META_POSTPAID, utils.META_PREPAID, utils.PSEUDOPREPAID, utils.POSTPAID, utils.PREPAID}, cdr.RequestType) { // Prepaid - Cost can be recalculated in case of missing records from SM + if utils.IsSliceMember([]string{utils.META_PSEUDOPREPAID, utils.META_POSTPAID, utils.META_PREPAID, + utils.PSEUDOPREPAID, utils.POSTPAID, utils.PREPAID}, cdr.RequestType) { // Prepaid - Cost can be recalculated in case of missing records from SM err = self.rals.Call("Responder.Debit", cd, cc) } else { err = self.rals.Call("Responder.GetCost", cd, cc) @@ -694,7 +695,7 @@ func (cdrS *CdrServer) chrgrSProcessEvent(cgrEv *utils.CGREvent) { return } var chrgrs []*ChrgSProcessEventReply - if err := cdrS.chargerS.Call(utils.ChargerSv1ProcessEvent, cgrEv, &chrgrs); err == nil || + if err := cdrS.chargerS.Call(utils.ChargerSv1ProcessEvent, cgrEv, &chrgrs); err != nil && err.Error() != utils.ErrNotFound.Error() { utils.Logger.Warning( fmt.Sprintf("<%s> error: %s processing CGR event %+v with %s.", @@ -723,13 +724,13 @@ func (cdrS *CdrServer) chrgrSProcessEvent(cgrEv *utils.CGREvent) { } for _, cdr := range processedCDRs { if cdrS.cgrCfg.CDRSStoreCdrs { // Store CDR - go func() { + go func(cdr *CDR) { if err := cdrS.cdrDb.SetCDR(cdr, true); err != nil { utils.Logger.Warning( fmt.Sprintf("<%s> error: %s storing CDR %+v.", utils.CDRs, err.Error(), cdr)) } - }() + }(cdr) } go cdrS.replicateCDRs([]*CDR{cdr}) // Replicate CDR cgrEv := cdr.AsCGREvent() @@ -753,6 +754,7 @@ func (cdrS *CdrServer) V2ProcessCDR(cgrEv *utils.CGREvent, reply *string) (err e go cdrS.thdSProcessEvent(cgrEv) go cdrS.statSProcessEvent(cgrEv) + go cdrS.chrgrSProcessEvent(cgrEv) *reply = utils.OK diff --git a/utils/consts.go b/utils/consts.go index 9ae76681a..378898c3e 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -764,6 +764,11 @@ const ( CacheSv1Clear = "CacheSv1.Clear" ) +// CdrsV2 APIs +const ( + CdrsV2ProcessCDR = "CdrsV2.ProcessCDR" +) + // Scheduler const ( SchedulerPing = "Scheduler.Ping"