Default RunId changed to *default for consistency, StoredCdr.Rated property so we can import already calculated CDRs, fixes for SkipErrors and SkipRated API filters for CDRs, created_time fix on MySQL, fix injected Cost when importing from external file and .Rated is false

This commit is contained in:
DanB
2015-02-05 17:01:45 +01:00
parent 98935b99b7
commit 211f980329
10 changed files with 177 additions and 34 deletions

View File

@@ -41,7 +41,7 @@ var cdrsRpc *rpc.Client
var cmdEngineCdrsMysql *exec.Cmd
func TestInitConfig(t *testing.T) {
func TestV2CdrsMysqlInitConfig(t *testing.T) {
if !*testLocal {
return
}
@@ -71,6 +71,29 @@ func TestV2CdrsMysqlInitCdrDb(t *testing.T) {
}
}
func TestV2CdrsMysqlInjectUnratedCdr(t *testing.T) {
if !*testLocal {
return
}
var mysqlDb *engine.MySQLStorage
if d, err := engine.NewMySQLStorage(cdrsCfg.StorDBHost, cdrsCfg.StorDBPort, cdrsCfg.StorDBName, cdrsCfg.StorDBUser, cdrsCfg.StorDBPass,
cdrsCfg.StorDBMaxOpenConns, cdrsCfg.StorDBMaxIdleConns); err != nil {
t.Error("Error on opening database connection: ", err)
return
} else {
mysqlDb = d.(*engine.MySQLStorage)
}
strCdr1 := &utils.StoredCdr{CgrId: utils.Sha1("bbb1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC),
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
if err := mysqlDb.SetCdr(strCdr1); err != nil {
t.Error(err.Error())
}
}
func TestV2CdrsMysqlStartEngine(t *testing.T) {
if !*testLocal {
return
@@ -103,17 +126,20 @@ func TestV2CdrsMysqlProcessCdr(t *testing.T) {
&utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", 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), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans", Rated: true,
},
&utils.StoredCdr{CgrId: utils.Sha1("abcdeftg", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", 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), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
&utils.StoredCdr{CgrId: utils.Sha1("aererfddf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", 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), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
}
for _, cdr := range cdrs {
@@ -130,15 +156,42 @@ func TestV2CdrsMysqlGetCdrs(t *testing.T) {
return
}
var reply []*utils.StoredCdr
req := utils.AttrGetCdrs{}
req := utils.RpcCdrsFilter{}
if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 4 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
// CDRs with errors
req = utils.RpcCdrsFilter{CostStart: utils.Float64Pointer(-1.0), CostEnd: utils.Float64Pointer(0.0)}
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)
}
// CDRs Rated
req = utils.RpcCdrsFilter{CostStart: utils.Float64Pointer(-1.0)}
if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 3 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
t.Error("Unexpected number of CDRs returned: ", reply)
}
// CDRs non rated OR SkipRated
req = utils.RpcCdrsFilter{CostEnd: 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)
}
// Skip Errors
req = utils.RpcCdrsFilter{CostStart: utils.Float64Pointer(0.0), CostEnd: utils.Float64Pointer(-1.0)}
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)
}
}
/*
func TestV2CdrsMysqlCountCdrs(t *testing.T) {
if !*testLocal {
return
@@ -147,11 +200,10 @@ func TestV2CdrsMysqlCountCdrs(t *testing.T) {
req := utils.AttrGetCdrs{}
if err := cdrsRpc.Call("ApierV2.CountCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != 3 {
} else if reply != 4 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
}
*/
func TestV2CdrsMysqlKillEngine(t *testing.T) {
if !*testLocal {

View File

@@ -65,6 +65,29 @@ func TestV2CdrsPsqlInitCdrDb(t *testing.T) {
}
}
func TestV2CdrsPsqlInjectUnratedCdr(t *testing.T) {
if !*testLocal {
return
}
var psqlDb *engine.PostgresStorage
if d, err := engine.NewPostgresStorage(cdrsPsqlCfg.StorDBHost, cdrsPsqlCfg.StorDBPort, cdrsPsqlCfg.StorDBName, cdrsPsqlCfg.StorDBUser, cdrsPsqlCfg.StorDBPass,
cdrsPsqlCfg.StorDBMaxOpenConns, cdrsPsqlCfg.StorDBMaxIdleConns); err != nil {
t.Error("Error on opening database connection: ", err)
return
} else {
psqlDb = d.(*engine.PostgresStorage)
}
strCdr1 := &utils.StoredCdr{CgrId: utils.Sha1("bbb1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC),
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
if err := psqlDb.SetCdr(strCdr1); err != nil {
t.Error(err.Error())
}
}
func TestV2CdrsPsqlStartEngine(t *testing.T) {
if !*testLocal {
return
@@ -97,17 +120,20 @@ func TestV2CdrsPsqlProcessCdr(t *testing.T) {
&utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", 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), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans", Rated: true,
},
&utils.StoredCdr{CgrId: utils.Sha1("abcdeftg", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", 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), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
&utils.StoredCdr{CgrId: utils.Sha1("aererfddf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", 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), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
}
for _, cdr := range cdrs {
@@ -124,15 +150,42 @@ func TestV2CdrsPsqlGetCdrs(t *testing.T) {
return
}
var reply []*utils.StoredCdr
req := utils.AttrGetCdrs{}
req := utils.RpcCdrsFilter{}
if err := cdrsPsqlRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 4 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
// CDRs with errors
req = utils.RpcCdrsFilter{CostStart: utils.Float64Pointer(-1.0), CostEnd: utils.Float64Pointer(0.0)}
if err := cdrsPsqlRpc.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)
}
// CDRs Rated
req = utils.RpcCdrsFilter{CostStart: utils.Float64Pointer(-1.0)}
if err := cdrsPsqlRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 3 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
t.Error("Unexpected number of CDRs returned: ", reply)
}
// CDRs non rated OR SkipRated
req = utils.RpcCdrsFilter{CostEnd: utils.Float64Pointer(-1.0)}
if err := cdrsPsqlRpc.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)
}
// Skip Errors
req = utils.RpcCdrsFilter{CostStart: utils.Float64Pointer(0.0), CostEnd: utils.Float64Pointer(-1.0)}
if err := cdrsPsqlRpc.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)
}
}
/*
func TestV2CdrsPsqlCountCdrs(t *testing.T) {
if !*testLocal {
return
@@ -141,11 +194,10 @@ func TestV2CdrsPsqlCountCdrs(t *testing.T) {
req := utils.AttrGetCdrs{}
if err := cdrsPsqlRpc.Call("ApierV2.CountCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != 3 {
} else if reply != 4 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
}
*/
func TestV2CdrsPsqlKillEngine(t *testing.T) {
if !*testLocal {

View File

@@ -48,7 +48,7 @@ func TestCsvCdrWriter(t *testing.T) {
if err := cdre.writeCsv(csvWriter); err != nil {
t.Error("Unexpected error: ", err)
}
expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,default,*voice,dsafdsaf,rated,*out,cgrates.org,call,1001,1001,1002,2013-11-07T08:42:25Z,2013-11-07T08:42:26Z,10,1.0100`
expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,*default,*voice,dsafdsaf,rated,*out,cgrates.org,call,1001,1001,1002,2013-11-07T08:42:25Z,2013-11-07T08:42:26Z,10,1.0100`
result := strings.TrimSpace(writer.String())
if result != expected {
t.Errorf("Expected: \n%s received: \n%s.", expected, result)
@@ -76,7 +76,7 @@ func TestAlternativeFieldSeparator(t *testing.T) {
if err := cdre.writeCsv(csvWriter); err != nil {
t.Error("Unexpected error: ", err)
}
expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6|default|*voice|dsafdsaf|rated|*out|cgrates.org|call|1001|1001|1002|2013-11-07T08:42:25Z|2013-11-07T08:42:26Z|10|1.0100`
expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6|*default|*voice|dsafdsaf|rated|*out|cgrates.org|call|1001|1001|1002|2013-11-07T08:42:25Z|2013-11-07T08:42:26Z|10|1.0100`
result := strings.TrimSpace(writer.String())
if result != expected {
t.Errorf("Expected: \n%s received: \n%s.", expected, result)

View File

@@ -19,7 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"fmt"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
"os/exec"
"path"
"time"
@@ -40,6 +42,8 @@ func InitDataDb(cfg *config.CGRConfig) error {
return err
}
}
ratingDb.CacheRating(nil, nil, nil, nil, nil)
accountDb.CacheAccounting(nil, nil, nil, nil)
return nil
}
@@ -77,3 +81,28 @@ func KillEngine(waitEngine int) error {
time.Sleep(time.Duration(waitEngine) * time.Millisecond)
return nil
}
func LoadTariffPlanFromFolder(tpPath string, ratingDb RatingStorage, accountingDb AccountingStorage) error {
loader := NewFileCSVReader(ratingDb, accountingDb, utils.CSV_SEP,
path.Join(tpPath, utils.DESTINATIONS_CSV),
path.Join(tpPath, utils.TIMINGS_CSV),
path.Join(tpPath, utils.RATES_CSV),
path.Join(tpPath, utils.DESTINATION_RATES_CSV),
path.Join(tpPath, utils.RATING_PLANS_CSV),
path.Join(tpPath, utils.RATING_PROFILES_CSV),
path.Join(tpPath, utils.SHARED_GROUPS_CSV),
path.Join(tpPath, utils.LCRS_CSV),
path.Join(tpPath, utils.ACTIONS_CSV),
path.Join(tpPath, utils.ACTION_PLANS_CSV),
path.Join(tpPath, utils.ACTION_TRIGGERS_CSV),
path.Join(tpPath, utils.ACCOUNT_ACTIONS_CSV),
path.Join(tpPath, utils.DERIVED_CHARGERS_CSV),
path.Join(tpPath, utils.CDR_STATS_CSV))
if err := loader.LoadAll(); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
}
if err := loader.WriteToDatabase(false, false); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
}
return nil
}

View File

@@ -165,8 +165,11 @@ func (self *Mediator) RateCdr(storedCdr *utils.StoredCdr, sendToStats bool) erro
}
for _, cdr := range cdrRuns {
extraInfo := ""
if err := self.rateCDR(cdr); err != nil {
extraInfo = err.Error()
if cdr.MediationRunId != utils.DEFAULT_RUNID || !cdr.Rated { // Do not rate calls which are out of default run and marked as rated already, eg premium SMSes
if err := self.rateCDR(cdr); err != nil {
cdr.Cost = -1.0 // If there was an error, mark the CDR as it is
extraInfo = err.Error()
}
}
if !self.cgrCfg.MediatorStoreDisable {
if err := self.cdrDb.SetRatedCdr(cdr, extraInfo); err != nil {

View File

@@ -104,7 +104,7 @@ func (self *MySQLStorage) LogCallCost(cgrid, source, runid string, cc *CallCost)
}
func (self *MySQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string) (err error) {
_, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid,runid,reqtype,direction,tenant,category,account,subject,destination,setup_time,answer_time,`usage`,cost,extra_info,created_at) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%v,%f,'%s',%d) ON DUPLICATE KEY UPDATE reqtype=values(reqtype),direction=values(direction),tenant=values(tenant),category=values(category),account=values(account),subject=values(subject),destination=values(destination),setup_time=values(setup_time),answer_time=values(answer_time),`usage`=values(`usage`),cost=values(cost),extra_info=values(extra_info), updated_at=%d",
_, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid,runid,reqtype,direction,tenant,category,account,subject,destination,setup_time,answer_time,`usage`,cost,extra_info,created_at) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%v,%f,'%s','%s') ON DUPLICATE KEY UPDATE reqtype=values(reqtype),direction=values(direction),tenant=values(tenant),category=values(category),account=values(account),subject=values(subject),destination=values(destination),setup_time=values(setup_time),answer_time=values(answer_time),`usage`=values(`usage`),cost=values(cost),extra_info=values(extra_info), updated_at='%s'",
utils.TBL_RATED_CDRS,
storedCdr.CgrId,
storedCdr.MediationRunId,
@@ -120,8 +120,8 @@ func (self *MySQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo stri
storedCdr.Usage.Seconds(),
storedCdr.Cost,
extraInfo,
time.Now().Unix(),
time.Now().Unix()))
time.Now().Format(time.RFC3339),
time.Now().Format(time.RFC3339)))
if err != nil {
Logger.Err(fmt.Sprintf("failed to execute cdr insert statement: %s", err.Error()))
}

View File

@@ -873,10 +873,10 @@ func (self *SQLStorage) GetStoredCdrs(qryFltr *utils.CdrsFilter) ([]*utils.Store
q = q.Where(utils.TBL_COST_DETAILS+".subject not in (?)", qryFltr.NotRatedSubjects)
}
if len(qryFltr.Costs) != 0 {
q = q.Where(utils.TBL_COST_DETAILS+".cost in (?)", qryFltr.Costs)
q = q.Where(utils.TBL_RATED_CDRS+".cost in (?)", qryFltr.Costs)
}
if len(qryFltr.NotCosts) != 0 {
q = q.Where(utils.TBL_COST_DETAILS+".cost not in (?)", qryFltr.NotCosts)
q = q.Where(utils.TBL_RATED_CDRS+".cost not in (?)", qryFltr.NotCosts)
}
if len(qryFltr.ExtraFields) != 0 { // Extra fields searches, implemented as contains in extra field
qIds := bytes.NewBufferString("(")
@@ -935,11 +935,15 @@ func (self *SQLStorage) GetStoredCdrs(qryFltr *utils.CdrsFilter) ([]*utils.Store
q = q.Where(utils.TBL_RATED_CDRS+".usage < ?", qryFltr.RatedUsageEnd)
}
if qryFltr.CostStart != nil {
q = q.Where(utils.TBL_RATED_CDRS+".cost >= ?", *qryFltr.CostStart)
if qryFltr.CostEnd != nil {
if qryFltr.CostEnd == nil {
q = q.Where(utils.TBL_RATED_CDRS+".cost >= ?", *qryFltr.CostStart)
} else if *qryFltr.CostStart == 0.0 && *qryFltr.CostEnd == -1.0 { // Special case when we want to skip errors
q = q.Where(fmt.Sprintf("( %s.cost IS NULL OR %s.cost >= 0.0 )", utils.TBL_RATED_CDRS, utils.TBL_RATED_CDRS))
} else {
q = q.Where(utils.TBL_RATED_CDRS+".cost >= ?", *qryFltr.CostStart)
q = q.Where(utils.TBL_RATED_CDRS+".cost < ?", *qryFltr.CostEnd)
}
} else if qryFltr.CostEnd != nil { // In case of both nil, we do not need to add any filter
} else if qryFltr.CostEnd != nil {
if *qryFltr.CostEnd == -1.0 { // Non-rated CDRs
q = q.Where(utils.TBL_RATED_CDRS + ".cost IS NULL") // Need to include it otherwise all CDRs will be returned
} else { // Above limited CDRs, since costStart is empty, make sure we query also NULL cost

View File

@@ -725,8 +725,9 @@ func (self *AttrExpFileCdrs) AsCdrsFilter() (*CdrsFilter, error) {
}
}
if self.SkipRated {
cdrFltr.CostEnd = Float64Pointer(0.0)
} else if self.SkipErrors {
cdrFltr.CostEnd = Float64Pointer(-1.0)
} else if self.SkipRated {
cdrFltr.CostStart = Float64Pointer(0.0)
cdrFltr.CostEnd = Float64Pointer(-1.0)
}
return cdrFltr, nil
@@ -799,8 +800,9 @@ func (self *AttrGetCdrs) AsCdrsFilter() (*CdrsFilter, error) {
}
}
if self.SkipRated {
cdrFltr.CostEnd = Float64Pointer(0.0)
} else if self.SkipErrors {
cdrFltr.CostEnd = Float64Pointer(-1.0)
} else if self.SkipRated {
cdrFltr.CostStart = Float64Pointer(0.0)
cdrFltr.CostEnd = Float64Pointer(-1.0)
}
return cdrFltr, nil

View File

@@ -101,7 +101,7 @@ const (
RATED_ACCOUNT = "rated_account"
RATED_SUBJECT = "rated_subject"
COST = "cost"
DEFAULT_RUNID = "default"
DEFAULT_RUNID = "*default"
META_DEFAULT = "*default"
STATIC_VALUE_PREFIX = "^"
CSV = "csv"

View File

@@ -52,6 +52,7 @@ type StoredCdr struct {
RatedAccount string // Populated out of rating data
RatedSubject string
Cost float64
Rated bool // Mark the CDR as rated so we do not process it during mediation
}
// Used to multiply usage on export