Merge branch 'data'

Conflicts:
	engine/calldesc.go
	mediator/mediator.go
	sessionmanager/fssessionmanager.go
	utils/storedcdr.go
This commit is contained in:
Radu Ioan Fericean
2014-04-29 14:13:48 +03:00
57 changed files with 1063 additions and 692 deletions

View File

@@ -211,7 +211,7 @@ func (self *ApierV1) LoadRatingProfile(attrs utils.TPRatingProfile, reply *strin
type AttrSetRatingProfile struct {
Tenant string // Tenant's Id
TOR string // TypeOfRecord
Category string // TypeOfRecord
Direction string // Traffic direction, OUT is the only one supported for now
Subject string // Rating subject, usually the same as account
Overwrite bool // Overwrite if exists
@@ -228,7 +228,7 @@ func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string)
return fmt.Errorf("%s:RatingPlanActivation:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
}
}
tpRpf := utils.TPRatingProfile{Tenant: attrs.Tenant, TOR: attrs.TOR, Direction: attrs.Direction, Subject: attrs.Subject}
tpRpf := utils.TPRatingProfile{Tenant: attrs.Tenant, Category: attrs.Category, Direction: attrs.Direction, Subject: attrs.Subject}
keyId := tpRpf.KeyId()
if !attrs.Overwrite {
if exists, err := self.RatingDb.HasData(engine.RATING_PROFILE_PREFIX, keyId); err != nil {
@@ -249,7 +249,7 @@ func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string)
return fmt.Errorf(fmt.Sprintf("%s:RatingPlanId:%s", utils.ERR_NOT_FOUND, ra.RatingPlanId))
}
rpfl.RatingPlanActivations[idx] = &engine.RatingPlanActivation{ActivationTime: at, RatingPlanId: ra.RatingPlanId,
FallbackKeys: utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.TOR, ra.FallbackSubjects)}
FallbackKeys: utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, ra.FallbackSubjects)}
}
if err := self.RatingDb.SetRatingProfile(rpfl); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())

View File

@@ -450,7 +450,7 @@ func TestApierTPRatingProfile(t *testing.T) {
return
}
reply := ""
rpf := &utils.TPRatingProfile{TPid: engine.TEST_SQL, LoadId: engine.TEST_SQL, Tenant: "cgrates.org", TOR: "call", Direction: "*out", Subject: "*any",
rpf := &utils.TPRatingProfile{TPid: engine.TEST_SQL, LoadId: engine.TEST_SQL, Tenant: "cgrates.org", Category: "call", Direction: "*out", Subject: "*any",
RatingPlanActivations: []*utils.TPRatingActivation{
&utils.TPRatingActivation{ActivationTime: "2012-01-01T00:00:00Z", RatingPlanId: "RETAIL1", FallbackSubjects: ""},
}}
@@ -743,7 +743,7 @@ func TestApierSetRatingProfile(t *testing.T) {
}
reply := ""
rpa := &utils.TPRatingActivation{ActivationTime: "2012-01-01T00:00:00Z", RatingPlanId: "RETAIL1", FallbackSubjects: "dan2"}
rpf := &AttrSetRatingProfile{Tenant: "cgrates.org", TOR: "call", Direction: "*out", Subject: "dan", RatingPlanActivations: []*utils.TPRatingActivation{rpa}}
rpf := &AttrSetRatingProfile{Tenant: "cgrates.org", Category: "call", Direction: "*out", Subject: "dan", RatingPlanActivations: []*utils.TPRatingActivation{rpa}}
if err := rater.Call("ApierV1.SetRatingProfile", rpf, &reply); err != nil {
t.Error("Got error on ApierV1.SetRatingProfile: ", err.Error())
} else if reply != "OK" {
@@ -759,15 +759,15 @@ func TestApierSetRatingProfile(t *testing.T) {
tStart, _ := utils.ParseDate("2013-08-07T17:30:00Z")
tEnd, _ := utils.ParseDate("2013-08-07T17:31:30Z")
cd := engine.CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "dan",
Account: "dan",
Destination: "+4917621621391",
CallDuration: 90,
TimeStart: tStart,
TimeEnd: tEnd,
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "dan",
Account: "dan",
Destination: "+4917621621391",
DurationIndex: 90,
TimeStart: tStart,
TimeEnd: tEnd,
}
var cc engine.CallCost
// Simple test that command is executed without errors
@@ -784,7 +784,7 @@ func TestApierLoadRatingProfile(t *testing.T) {
return
}
reply := ""
rpf := &utils.TPRatingProfile{TPid: engine.TEST_SQL, LoadId: engine.TEST_SQL, Tenant: "cgrates.org", TOR: "call", Direction: "*out", Subject: "*any"}
rpf := &utils.TPRatingProfile{TPid: engine.TEST_SQL, LoadId: engine.TEST_SQL, Tenant: "cgrates.org", Category: "call", Direction: "*out", Subject: "*any"}
if err := rater.Call("ApierV1.LoadRatingProfile", rpf, &reply); err != nil {
t.Error("Got error on ApierV1.LoadRatingProfile: ", err.Error())
} else if reply != "OK" {
@@ -1300,15 +1300,15 @@ func TestResponderGetCost(t *testing.T) {
tStart, _ := utils.ParseDate("2013-08-07T17:30:00Z")
tEnd, _ := utils.ParseDate("2013-08-07T17:31:30Z")
cd := engine.CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "1001",
Account: "1001",
Destination: "+4917621621391",
CallDuration: 90,
TimeStart: tStart,
TimeEnd: tEnd,
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "1001",
Account: "1001",
Destination: "+4917621621391",
DurationIndex: 90,
TimeStart: tStart,
TimeEnd: tEnd,
}
var cc engine.CallCost
// Simple test that command is executed without errors
@@ -1345,7 +1345,7 @@ func TestMaxDebitInexistentAcnt(t *testing.T) {
cd := engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
TOR: "call",
Category: "call",
Subject: "INVALID",
Account: "INVALID",
Destination: "1002",

View File

@@ -78,7 +78,7 @@ func (self *ApierV1) GetTPRatingProfileLoadIds(attrs utils.AttrTPRatingProfileId
}
if ids, err := self.StorDb.GetTPTableIds(attrs.TPid, utils.TBL_TP_RATE_PROFILES, "loadid", map[string]string{
"tenant": attrs.Tenant,
"tor": attrs.TOR,
"tor": attrs.Category,
"direction": attrs.Direction,
"subject": attrs.Subject,
}); err != nil {
@@ -96,7 +96,7 @@ func (self *ApierV1) RemTPRatingProfile(attrs utils.TPRatingProfile, reply *stri
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LoadId", "Tenant", "TOR", "Direction", "Subject"}); len(missing) != 0 { //Params missing
return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
}
if err := self.StorDb.RemTPData(utils.TBL_TP_RATE_PROFILES, attrs.TPid, attrs.LoadId, attrs.Tenant, attrs.TOR, attrs.Direction, attrs.Subject); err != nil {
if err := self.StorDb.RemTPData(utils.TBL_TP_RATE_PROFILES, attrs.TPid, attrs.LoadId, attrs.Tenant, attrs.Category, attrs.Direction, attrs.Subject); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
} else {
*reply = "OK"

View File

@@ -197,15 +197,15 @@ func TestFsCsvCall1(t *testing.T) {
tStart := time.Date(2014, 01, 15, 6, 0, 0, 0, time.UTC)
tEnd := time.Date(2014, 01, 15, 6, 0, 35, 0, time.UTC)
cd := engine.CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "1001",
Account: "1001",
Destination: "1002",
TimeStart: tStart,
TimeEnd: tEnd,
CallDuration: 35,
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "1001",
Account: "1001",
Destination: "1002",
TimeStart: tStart,
TimeEnd: tEnd,
DurationIndex: 35,
}
var cc engine.CallCost
// Make sure the cost is what we expect it is
@@ -232,16 +232,16 @@ func TestFsCsvCall1(t *testing.T) {
t.Errorf("Received unexpected UnitCounters: %v", reply.UnitCounters)
}
cd = engine.CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "1001",
Account: "1001",
Destination: "1002",
TimeStart: tStart,
TimeEnd: tEnd,
CallDuration: 35,
LoopIndex: 1, // Should not charge ConnectFee
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "1001",
Account: "1001",
Destination: "1002",
TimeStart: tStart,
TimeEnd: tEnd,
DurationIndex: 35,
LoopIndex: 1, // Should not charge ConnectFee
}
// Make sure debit charges what cost returned
if err := rater.Call("Responder.MaxDebit", cd, &cc); err != nil {

View File

@@ -311,7 +311,7 @@ func TestMaxCallDuration(t *testing.T) {
cd := engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
TOR: "call",
Category: "call",
Subject: "1001",
Account: "1001",
Destination: "1002",
@@ -330,7 +330,7 @@ func TestMaxCallDuration(t *testing.T) {
cd = engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
TOR: "call",
Category: "call",
Subject: "1002",
Account: "1002",
Destination: "1001",
@@ -348,7 +348,7 @@ func TestMaxCallDuration(t *testing.T) {
cd = engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
TOR: "call",
Category: "call",
Subject: "1006",
Account: "1006",
Destination: "1001",
@@ -367,7 +367,7 @@ func TestMaxCallDuration(t *testing.T) {
cd = engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
TOR: "call",
Category: "call",
Subject: "1007",
Account: "1007",
Destination: "1001",
@@ -393,7 +393,7 @@ func TestMaxDebit1001(t *testing.T) {
cd := engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
TOR: "call",
Category: "call",
Subject: "1001",
Account: "1001",
Destination: "1002",
@@ -432,7 +432,7 @@ func TestMaxDebit1007(t *testing.T) {
cd := engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
TOR: "call",
Category: "call",
Subject: "1007",
Account: "1007",
Destination: "1002",

View File

@@ -86,7 +86,7 @@ func (self *Cdrc) parseFieldsConfig() error {
utils.REQTYPE: self.cgrCfg.CdrcReqTypeField,
utils.DIRECTION: self.cgrCfg.CdrcDirectionField,
utils.TENANT: self.cgrCfg.CdrcTenantField,
utils.TOR: self.cgrCfg.CdrcTorField,
utils.Category: self.cgrCfg.CdrcTorField,
utils.ACCOUNT: self.cgrCfg.CdrcAccountField,
utils.SUBJECT: self.cgrCfg.CdrcSubjectField,
utils.DESTINATION: self.cgrCfg.CdrcDestinationField,
@@ -145,8 +145,8 @@ func (self *Cdrc) recordForkCdr(record []string) (*utils.StoredCdr, error) {
ratedCdr.Direction = fieldVal
case utils.TENANT:
ratedCdr.Tenant = fieldVal
case utils.TOR:
ratedCdr.TOR = fieldVal
case utils.Category:
ratedCdr.Category = fieldVal
case utils.ACCOUNT:
ratedCdr.Account = fieldVal
case utils.SUBJECT:

View File

@@ -81,7 +81,7 @@ func TestRecordForkCdr(t *testing.T) {
ReqType: cdrRow[1],
Direction: cdrRow[2],
Tenant: cdrRow[3],
TOR: cdrRow[4],
Category: cdrRow[4],
Account: cdrRow[5],
Subject: cdrRow[6],
Destination: cdrRow[7],

View File

@@ -34,7 +34,7 @@ func TestCsvCdrWriter(t *testing.T) {
csvCdrWriter := NewCsvCdrWriter(writer, 0, 4, "", -1, exportedFields)
ratedCdr := &utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID,
ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01,
}

View File

@@ -82,7 +82,7 @@ func TestWriteCdr(t *testing.T) {
}
fwWriter := FixedWidthCdrWriter{writer: wrBuf, exportTemplate: exportTpl, roundDecimals: 4, header: &bytes.Buffer{}, content: &bytes.Buffer{}, trailer: &bytes.Buffer{}}
cdr := &utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 1, AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Duration: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.34567,
@@ -143,14 +143,14 @@ func TestWriteCdrs(t *testing.T) {
}
fwWriter := FixedWidthCdrWriter{writer: wrBuf, exportTemplate: exportTpl, roundDecimals: 4, header: &bytes.Buffer{}, content: &bytes.Buffer{}, trailer: &bytes.Buffer{}}
cdr1 := &utils.StoredCdr{CgrId: utils.Sha1("aaa1", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 2, AccId: "aaa1", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1010",
Category: "call", Account: "1001", Subject: "1001", Destination: "1010",
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Duration: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.25,
ExtraFields: map[string]string{"productnumber": "12341", "fieldextr2": "valextr2"},
}
cdr2 := &utils.StoredCdr{CgrId: utils.Sha1("aaa2", time.Date(2013, 11, 7, 7, 42, 20, 0, time.UTC).String()), OrderId: 4, AccId: "aaa2", CdrHost: "192.168.1.2", ReqType: "prepaid", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1002", Subject: "1002", Destination: "1011",
Category: "call", Account: "1002", Subject: "1002", Destination: "1011",
SetupTime: time.Date(2013, 11, 7, 7, 42, 20, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 7, 42, 26, 0, time.UTC),
Duration: time.Duration(5) * time.Minute, MediationRunId: utils.DEFAULT_RUNID, Cost: 1.40001,
@@ -158,7 +158,7 @@ func TestWriteCdrs(t *testing.T) {
}
cdr3 := &utils.StoredCdr{}
cdr4 := &utils.StoredCdr{CgrId: utils.Sha1("aaa3", time.Date(2013, 11, 7, 9, 42, 18, 0, time.UTC).String()), OrderId: 3, AccId: "aaa4", CdrHost: "192.168.1.4", ReqType: "postpaid", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1004", Subject: "1004", Destination: "1013",
Category: "call", Account: "1004", Subject: "1004", Destination: "1013",
SetupTime: time.Date(2013, 11, 7, 9, 42, 18, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 9, 42, 26, 0, time.UTC),
Duration: time.Duration(20) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.34567,

View File

@@ -39,7 +39,7 @@ const (
FS_ACCOUNT = "cgr_account"
FS_DESTINATION = "cgr_destination"
FS_REQTYPE = "cgr_reqtype" //prepaid or postpaid
FS_TOR = "cgr_tor"
FS_CATEGORY = "cgr_category"
FS_UUID = "uuid" // -Unique ID for this call leg
FS_CSTMID = "cgr_tenant"
FS_CALL_DEST_NR = "dialed_extension"
@@ -104,8 +104,8 @@ func (fsCdr FSCdr) GetDestination() string {
return utils.FirstNonEmpty(fsCdr.vars[FS_DESTINATION], fsCdr.vars[FS_CALL_DEST_NR], fsCdr.vars[FS_SIP_REQUSER])
}
func (fsCdr FSCdr) GetTOR() string {
return utils.FirstNonEmpty(fsCdr.vars[FS_TOR], cfg.DefaultTOR)
func (fsCdr FSCdr) GetCategory() string {
return utils.FirstNonEmpty(fsCdr.vars[FS_CATEGORY], cfg.DefaultCategory)
}
func (fsCdr FSCdr) GetTenant() string {
@@ -177,7 +177,7 @@ func (fsCdr FSCdr) Store() (result string, err error) {
result += fsCdr.GetSubject() + "|"
result += fsCdr.GetAccount() + "|"
result += fsCdr.GetDestination() + "|"
result += fsCdr.GetTOR() + "|"
result += fsCdr.GetCategory() + "|"
result += fsCdr.GetAccId() + "|"
result += fsCdr.GetTenant() + "|"
result += fsCdr.GetReqType() + "|"
@@ -240,8 +240,8 @@ func (fsCdr FSCdr) ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, a
return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, tenantFld))
}
if strings.HasPrefix(torFld, utils.STATIC_VALUE_PREFIX) {
rtCdr.TOR = torFld[1:]
} else if rtCdr.TOR, hasKey = fsCdr.vars[torFld]; !hasKey && fieldsMandatory {
rtCdr.Category = torFld[1:]
} else if rtCdr.Category, hasKey = fsCdr.vars[torFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, torFld))
}
if strings.HasPrefix(accountFld, utils.STATIC_VALUE_PREFIX) {

View File

@@ -69,7 +69,7 @@ func TestCDRFields(t *testing.T) {
if fsCdr.GetDestination() != "+4986517174963" {
t.Error("Error parsing cdr: ", fsCdr)
}
if fsCdr.GetTOR() != "call" {
if fsCdr.GetCategory() != "call" {
t.Error("Error parsing cdr: ", fsCdr)
}
if fsCdr.GetTenant() != "ipbx.itsyscom.com" {
@@ -115,7 +115,7 @@ func TestFsCdrForkCdr(t *testing.T) {
}
expctRatedCdr := &utils.StoredCdr{CgrId: utils.Sha1("01df56f4-d99a-4ef6-b7fe-b924b2415b7f", time.Date(2013, 8, 4, 9, 50, 54, 0, time.UTC).Local().String()), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f",
CdrHost: "127.0.0.1", CdrSource: FS_CDR_SOURCE, ReqType: utils.RATED,
Direction: "*out", Tenant: "ipbx.itsyscom.com", TOR: "call", Account: "dan", Subject: "dan", Destination: "+4986517174963",
Direction: "*out", Tenant: "ipbx.itsyscom.com", Category: "call", Account: "dan", Subject: "dan", Destination: "+4986517174963",
SetupTime: time.Date(2013, 8, 4, 9, 50, 54, 0, time.UTC).Local(),
AnswerTime: time.Date(2013, 8, 4, 9, 50, 56, 0, time.UTC).Local(), Duration: time.Duration(4) * time.Second,
ExtraFields: map[string]string{"effective_caller_id_number": "+4986517174960"}, MediationRunId: "wholesale_run", Cost: -1}
@@ -129,7 +129,7 @@ func TestFsCdrForkCdr(t *testing.T) {
}
expctRatedCdr2 := &utils.StoredCdr{CgrId: utils.Sha1("01df56f4-d99a-4ef6-b7fe-b924b2415b7f", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f", CdrHost: "127.0.0.1",
CdrSource: FS_CDR_SOURCE, ReqType: "postpaid",
Direction: "*in", Tenant: "cgrates.com", TOR: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "+4986517174963",
Direction: "*in", Tenant: "cgrates.com", Category: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "+4986517174963",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC),
AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(12) * time.Second,
ExtraFields: map[string]string{"effective_caller_id_number": "+4986517174960"}, MediationRunId: "wholesale_run", Cost: -1}

View File

@@ -77,7 +77,7 @@ type CGRConfig struct {
RPCGOBListen string // RPC GOB listening address
HTTPListen string // HTTP listening address
DefaultReqType string // Use this request type if not defined on top
DefaultTOR string // set default type of record
DefaultCategory string // set default type of record
DefaultTenant string // set default tenant
DefaultSubject string // set default rating subject, useful in case of fallback
RoundingMethod string // Rounding method for the end price: <*up|*middle|*down>
@@ -166,7 +166,7 @@ func (self *CGRConfig) setDefaults() error {
self.RPCGOBListen = "127.0.0.1:2013"
self.HTTPListen = "127.0.0.1:2080"
self.DefaultReqType = utils.RATED
self.DefaultTOR = "call"
self.DefaultCategory = "call"
self.DefaultTenant = "cgrates.org"
self.DefaultSubject = "cgrates"
self.RoundingMethod = utils.ROUNDING_MIDDLE
@@ -235,7 +235,7 @@ func (self *CGRConfig) setDefaults() error {
&utils.RSRField{Id: utils.REQTYPE},
&utils.RSRField{Id: utils.DIRECTION},
&utils.RSRField{Id: utils.TENANT},
&utils.RSRField{Id: utils.TOR},
&utils.RSRField{Id: utils.Category},
&utils.RSRField{Id: utils.ACCOUNT},
&utils.RSRField{Id: utils.SUBJECT},
&utils.RSRField{Id: utils.DESTINATION},
@@ -371,8 +371,8 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
if hasOpt = c.HasOption("global", "default_reqtype"); hasOpt {
cfg.DefaultReqType, _ = c.GetString("global", "default_reqtype")
}
if hasOpt = c.HasOption("global", "default_tor"); hasOpt {
cfg.DefaultTOR, _ = c.GetString("global", "default_tor")
if hasOpt = c.HasOption("global", "default_category"); hasOpt {
cfg.DefaultCategory, _ = c.GetString("global", "default_category")
}
if hasOpt = c.HasOption("global", "default_tenant"); hasOpt {
cfg.DefaultTenant, _ = c.GetString("global", "default_tenant")

View File

@@ -69,7 +69,7 @@ func TestDefaults(t *testing.T) {
eCfg.RPCGOBListen = "127.0.0.1:2013"
eCfg.HTTPListen = "127.0.0.1:2080"
eCfg.DefaultReqType = utils.RATED
eCfg.DefaultTOR = "call"
eCfg.DefaultCategory = "call"
eCfg.DefaultTenant = "cgrates.org"
eCfg.DefaultSubject = "cgrates"
eCfg.RoundingMethod = utils.ROUNDING_MIDDLE
@@ -138,7 +138,7 @@ func TestDefaults(t *testing.T) {
&utils.RSRField{Id: utils.REQTYPE},
&utils.RSRField{Id: utils.DIRECTION},
&utils.RSRField{Id: utils.TENANT},
&utils.RSRField{Id: utils.TOR},
&utils.RSRField{Id: utils.Category},
&utils.RSRField{Id: utils.ACCOUNT},
&utils.RSRField{Id: utils.SUBJECT},
&utils.RSRField{Id: utils.DESTINATION},
@@ -203,7 +203,7 @@ func TestConfigFromFile(t *testing.T) {
eCfg.RPCGOBListen = "test"
eCfg.HTTPListen = "test"
eCfg.DefaultReqType = "test"
eCfg.DefaultTOR = "test"
eCfg.DefaultCategory = "test"
eCfg.DefaultTenant = "test"
eCfg.DefaultSubject = "test"
eCfg.RoundingMethod = "test"

View File

@@ -25,7 +25,7 @@ rpc_json_listen = test # RPC JSON listening address
rpc_gob_listen = test # RPC GOB listening address
http_listen = test # HTTP listening address
default_reqtype = test # Default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>.
default_tor = test # Default Type of Record to consider when missing from requests.
default_category = test # Default Type of Record to consider when missing from requests.
default_tenant = test # Default Tenant to consider when missing from requests.
default_subject = test # Default rating Subject to consider when missing from requests.
rounding_method = test # Rounding method for floats/costs: <up|middle|down>

View File

@@ -50,7 +50,7 @@ func (self *CmdGetCallCost) RpcMethod() string {
}
func (self *CmdGetCallCost) RpcParams() interface{} {
if self.rpcParams == nil {
if self.rpcParams == nil {
self.rpcParams = &apier.AttrGetCallCost{RunId: utils.DEFAULT_RUNID}
}
return self.rpcParams

View File

@@ -183,6 +183,7 @@ CREATE TABLE `tp_action_triggers` (
`direction` varchar(8) NOT NULL,
`threshold_type` char(12) NOT NULL,
`threshold_value` double(20,4) NOT NULL,
`recurrent` bool NOT NULL,
`destination_id` varchar(64) NOT NULL,
`actions_id` varchar(64) NOT NULL,
`weight` double(8,2) NOT NULL,
@@ -219,9 +220,9 @@ CREATE TABLE tp_derived_chargers (
tbid int(11) NOT NULL AUTO_INCREMENT,
tpid varchar(64) NOT NULL,
loadid varchar(64) NOT NULL,
direction varchar(8) NOT NULL,
tenant varchar(64) NOT NULL,
tor varchar(16) NOT NULL,
direction varchar(8) NOT NULL,
account varchar(24) NOT NULL,
subject varchar(64) NOT NULL,
runid_field varchar(24) NOT NULL,
@@ -237,4 +238,4 @@ CREATE TABLE tp_derived_chargers (
duration_field varchar(24) NOT NULL,
PRIMARY KEY (`tbid`),
KEY `tpid` (`tpid`)
);
);

View File

@@ -1,4 +1,4 @@
#Tag,BalanceType,Direction,ThresholdType,ThresholdValue,DestinationTag,ActionsTag,Weight
STANDARD_TRIGGERS,*monetary,*out,*min_balance,2,,LOG_BALANCE,10
STANDARD_TRIGGERS,*monetary,*out,*max_balance,20,,LOG_BALANCE,10
STANDARD_TRIGGERS,*monetary,*out,*max_counter,15,FS_USERS,LOG_BALANCE,10
STANDARD_TRIGGERS,*monetary,*out,*min_balance,2,false,,LOG_BALANCE,10
STANDARD_TRIGGERS,*monetary,*out,*max_balance,20,false,,LOG_BALANCE,10
STANDARD_TRIGGERS,*monetary,*out,*max_counter,15,false,FS_USERS,LOG_BALANCE,10
1 #Tag #Tag,BalanceType,Direction,ThresholdType,ThresholdValue,DestinationTag,ActionsTag,Weight BalanceType Direction ThresholdType ThresholdValue DestinationTag ActionsTag Weight
2 STANDARD_TRIGGERS STANDARD_TRIGGERS,*monetary,*out,*min_balance,2,false,,LOG_BALANCE,10 *monetary *out *min_balance 2 LOG_BALANCE 10
3 STANDARD_TRIGGERS STANDARD_TRIGGERS,*monetary,*out,*max_balance,20,false,,LOG_BALANCE,10 *monetary *out *max_balance 20 LOG_BALANCE 10
4 STANDARD_TRIGGERS STANDARD_TRIGGERS,*monetary,*out,*max_counter,15,false,FS_USERS,LOG_BALANCE,10 *monetary *out *max_counter 15 FS_USERS LOG_BALANCE 10

View File

@@ -34,14 +34,10 @@ const (
INBOUND = "*in"
OUTBOUND = "*out"
// Balance types
CREDIT = "*monetary"
SMS = "*sms"
DATA = "*data"
DATA_TIME = "*data_time"
MINUTES = "*minutes"
// action price type
PRICE_PERCENT = "*percent"
PRICE_ABSOLUTE = "*absolute"
CREDIT = "*monetary"
SMS = "*sms"
DATA = "*data"
MINUTES = "*call_duration"
// action trigger threshold types
TRIGGER_MIN_COUNTER = "*min_counter"
TRIGGER_MAX_COUNTER = "*max_counter"
@@ -49,10 +45,6 @@ const (
TRIGGER_MAX_BALANCE = "*max_balance"
)
var (
AMOUNT_TOO_BIG = errors.New("Amount excedes balance!")
)
/*
Structure containing information about user's credit (minutes, cents, sms...).'
This can represent a user or a shared group.
@@ -70,7 +62,7 @@ type Account struct {
func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances BalanceChain) {
creditBalances := ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[CREDIT+cd.Direction], "")
minuteBalances := ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[MINUTES+cd.Direction], "")
unitBalances := ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[cd.Tor+cd.Direction], "")
// gather all balances from shared groups
var extendedCreditBalances BalanceChain
for _, cb := range creditBalances {
@@ -85,10 +77,10 @@ func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duratio
}
}
var extendedMinuteBalances BalanceChain
for _, mb := range minuteBalances {
for _, mb := range unitBalances {
if mb.SharedGroup != "" {
if sharedGroup, _ := accountingStorage.GetSharedGroup(mb.SharedGroup, false); sharedGroup != nil {
sgb := sharedGroup.GetBalances(cd.Destination, MINUTES+cd.Direction, ub)
sgb := sharedGroup.GetBalances(cd.Destination, cd.Tor+cd.Direction, ub)
sgb = sharedGroup.SortBalancesByStrategy(mb, sgb)
extendedMinuteBalances = append(extendedMinuteBalances, sgb...)
}
@@ -211,11 +203,11 @@ func (account *Account) getAlldBalancesForPrefix(destination, balanceType string
}
func (ub *Account) debitCreditBalance(cc *CallCost, count bool) (err error) {
usefulMinuteBalances := ub.getAlldBalancesForPrefix(cc.Destination, MINUTES+cc.Direction)
usefulUnitBalances := ub.getAlldBalancesForPrefix(cc.Destination, cc.Tor+cc.Direction)
usefulMoneyBalances := ub.getAlldBalancesForPrefix(cc.Destination, CREDIT+cc.Direction)
// debit minutes
for _, balance := range usefulMinuteBalances {
balance.DebitMinutes(cc, count, balance.account, usefulMoneyBalances)
for _, balance := range usefulUnitBalances {
balance.DebitUnits(cc, count, balance.account, usefulMoneyBalances)
if cc.IsPaid() {
goto CONNECT_FEE
}
@@ -292,7 +284,7 @@ CONNECT_FEE:
}
// save darty shared balances
usefulMoneyBalances.SaveDirtyBalances(ub)
usefulMinuteBalances.SaveDirtyBalances(ub)
usefulUnitBalances.SaveDirtyBalances(ub)
return
}
@@ -308,15 +300,15 @@ func (ub *Account) GetDefaultMoneyBalance(direction string) *Balance {
return defaultBalance
}
func (ub *Account) refundIncrement(increment *Increment, direction string, count bool) {
func (ub *Account) refundIncrement(increment *Increment, direction, unitType string, count bool) {
var balance *Balance
if increment.BalanceInfo.MinuteBalanceUuid != "" {
if balance = ub.BalanceMap[MINUTES+direction].GetBalance(increment.BalanceInfo.MinuteBalanceUuid); balance == nil {
if increment.BalanceInfo.UnitBalanceUuid != "" {
if balance = ub.BalanceMap[unitType+direction].GetBalance(increment.BalanceInfo.UnitBalanceUuid); balance == nil {
return
}
balance.Value += increment.Duration.Seconds()
if count {
ub.countUnits(&Action{BalanceType: MINUTES, Direction: direction, Balance: &Balance{Value: -increment.Duration.Seconds()}})
ub.countUnits(&Action{BalanceType: unitType, Direction: direction, Balance: &Balance{Value: -increment.Duration.Seconds()}})
}
}
// check money too
@@ -391,6 +383,16 @@ func (ub *Account) ResetActionTriggers(a *Action) {
ub.executeActionTriggers(a)
}
// Sets/Unsets recurrent flag for action triggers
func (ub *Account) SetRecurrent(a *Action, recurrent bool) {
for _, at := range ub.ActionTriggers {
if !at.Match(a) {
continue
}
at.Recurrent = recurrent
}
}
// Returns the unit counter that matches the specified action type
func (ub *Account) getUnitCounter(a *Action) *UnitsCounter {
for _, uc := range ub.UnitCounters {
@@ -477,9 +479,9 @@ func (ub *Account) GetSharedGroups() (groups []string) {
return
}
func (account *Account) GetUniqueSharedGroupMembers(destination, direction string) ([]string, error) {
func (account *Account) GetUniqueSharedGroupMembers(destination, direction, unitType string) ([]string, error) {
creditBalances := account.getBalancesForPrefix(destination, account.BalanceMap[CREDIT+direction], "")
minuteBalances := account.getBalancesForPrefix(destination, account.BalanceMap[MINUTES+direction], "")
unitBalances := account.getBalancesForPrefix(destination, account.BalanceMap[unitType+direction], "")
// gather all shared group ids
var sharedGroupIds []string
for _, cb := range creditBalances {
@@ -487,7 +489,7 @@ func (account *Account) GetUniqueSharedGroupMembers(destination, direction strin
sharedGroupIds = append(sharedGroupIds, cb.SharedGroup)
}
}
for _, mb := range minuteBalances {
for _, mb := range unitBalances {
if mb.SharedGroup != "" {
sharedGroupIds = append(sharedGroupIds, mb.SharedGroup)
}

View File

@@ -97,14 +97,15 @@ func TestGetSecondsForPrefix(t *testing.T) {
b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"}
ub1 := &Account{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 200}}}}
cd := &CallDescriptor{
TOR: "0",
Tenant: "vdf",
TimeStart: time.Date(2013, 10, 4, 15, 46, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 4, 15, 46, 10, 0, time.UTC),
LoopIndex: 0,
CallDuration: 10 * time.Second,
Direction: OUTBOUND,
Destination: "0723",
Category: "0",
Tenant: "vdf",
TimeStart: time.Date(2013, 10, 4, 15, 46, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 4, 15, 46, 10, 0, time.UTC),
LoopIndex: 0,
DurationIndex: 10 * time.Second,
Direction: OUTBOUND,
Destination: "0723",
Tor: MINUTES,
}
seconds, credit, bucketList := ub1.getCreditForPrefix(cd)
expected := 110 * time.Second
@@ -126,13 +127,14 @@ func TestGetSpecialPricedSeconds(t *testing.T) {
},
}
cd := &CallDescriptor{
TOR: "0",
Category: "0",
Tenant: "vdf",
TimeStart: time.Date(2013, 10, 4, 15, 46, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 4, 15, 46, 60, 0, time.UTC),
LoopIndex: 0,
Direction: OUTBOUND,
Destination: "0723",
Tor: MINUTES,
}
seconds, credit, bucketList := ub1.getCreditForPrefix(cd)
expected := 20 * time.Second
@@ -164,19 +166,20 @@ func TestDebitCreditZeroSecond(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" {
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" {
t.Logf("%+v", cc.Timespans[0])
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -193,12 +196,13 @@ func TestDebitCreditZeroMinute(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
@@ -209,7 +213,7 @@ func TestDebitCreditZeroMinute(t *testing.T) {
t.Error("Error debiting balance: ", err)
}
t.Logf("%+v", cc.Timespans)
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -227,13 +231,14 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 20, 0, time.UTC),
ratingInfo: &RatingInfo{},
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 20, 0, time.UTC),
ratingInfo: &RatingInfo{},
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1, b2},
@@ -243,8 +248,8 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "tests" ||
cc.Timespans[1].Increments[0].BalanceInfo.MinuteBalanceUuid != "testm" {
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "tests" ||
cc.Timespans[1].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0], cc.Timespans[1].Increments[0])
}
if rifsBalance.BalanceMap[MINUTES+OUTBOUND][1].Value != 0 ||
@@ -261,18 +266,19 @@ func TestDebitCreditNoCredit(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
CallDuration: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
DurationIndex: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
@@ -281,7 +287,7 @@ func TestDebitCreditNoCredit(t *testing.T) {
if err == nil {
t.Error("Showing no enough credit error ")
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -301,18 +307,19 @@ func TestDebitCreditHasCredit(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
CallDuration: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
DurationIndex: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
@@ -322,7 +329,7 @@ func TestDebitCreditHasCredit(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -343,13 +350,14 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 20, 0, time.UTC),
CallDuration: 0,
ratingInfo: &RatingInfo{},
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 20, 0, time.UTC),
DurationIndex: 0,
ratingInfo: &RatingInfo{},
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
@@ -359,7 +367,7 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != 10*time.Second {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -380,18 +388,19 @@ func TestDebitCreditMoreTimespans(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
CallDuration: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
DurationIndex: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
@@ -400,7 +409,7 @@ func TestDebitCreditMoreTimespans(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -418,18 +427,19 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
CallDuration: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
DurationIndex: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1, b2},
@@ -438,7 +448,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -456,18 +466,19 @@ func TestDebitCreditNoConectFeeCredit(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{ConnectFee: 10.0, Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{ConnectFee: 10.0, Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
CallDuration: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
DurationIndex: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
@@ -488,19 +499,20 @@ func TestDebitCreditMoneyOnly(t *testing.T) {
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
CallDuration: 10 * time.Second,
ratingInfo: &RatingInfo{},
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
DurationIndex: 10 * time.Second,
ratingInfo: &RatingInfo{},
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "money", Value: 50}},
@@ -530,17 +542,18 @@ func TestDebitCreditSubjectMinutes(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 250, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
deductConnectFee: true,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
@@ -551,7 +564,7 @@ func TestDebitCreditSubjectMinutes(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].BalanceInfo.MoneyBalanceUuid != "moneya" ||
cc.Timespans[0].Increments[0].Duration != time.Second {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
@@ -572,17 +585,18 @@ func TestDebitCreditSubjectMinutes(t *testing.T) {
func TestDebitCreditSubjectMoney(t *testing.T) {
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
deductConnectFee: true,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
@@ -609,17 +623,18 @@ func TestDebitCreditSubjectMixed(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 40, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 55, 0, time.UTC),
CallDuration: 55 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 55, 0, time.UTC),
DurationIndex: 55 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
deductConnectFee: true,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
@@ -630,7 +645,7 @@ func TestDebitCreditSubjectMixed(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].BalanceInfo.MoneyBalanceUuid != "moneya" ||
cc.Timespans[0].Increments[0].Duration != time.Second {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
@@ -652,23 +667,24 @@ func TestDebitCreditSubjectMixedMoreTS(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
CallDuration: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
DurationIndex: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
deductConnectFee: true,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
@@ -680,7 +696,7 @@ func TestDebitCreditSubjectMixedMoreTS(t *testing.T) {
t.Error("Error showing debiting balance error: ", err)
}
//t.Logf("%+v %+v", cc.Timespans[0], cc.Timespans[1])
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Second {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -703,23 +719,24 @@ func TestDebitCreditSubjectMixedPartPay(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
CallDuration: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
CallDuration: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
DurationIndex: 10 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: MINUTES,
deductConnectFee: true,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
@@ -731,7 +748,7 @@ func TestDebitCreditSubjectMixedPartPay(t *testing.T) {
t.Error("Error showing debiting balance error: ", err)
}
//t.Logf("%+v %+v", cc.Timespans[0], cc.Timespans[1])
if cc.Timespans[0].Increments[0].BalanceInfo.MinuteBalanceUuid != "testb" ||
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Second {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -940,12 +957,12 @@ func TestAccountRefund(t *testing.T) {
},
}
increments := Increments{
&Increment{Cost: 2, BalanceInfo: &BalanceInfo{MinuteBalanceUuid: "", MoneyBalanceUuid: "moneya"}},
&Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{MinuteBalanceUuid: "minutea", MoneyBalanceUuid: "moneya"}},
&Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{MinuteBalanceUuid: "minuteb", MoneyBalanceUuid: ""}},
&Increment{Cost: 2, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "", MoneyBalanceUuid: "moneya"}},
&Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minutea", MoneyBalanceUuid: "moneya"}},
&Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: ""}},
}
for _, increment := range increments {
ub.refundIncrement(increment, OUTBOUND, false)
ub.refundIncrement(increment, OUTBOUND, MINUTES, false)
}
if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 104 ||
ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 13 ||
@@ -957,15 +974,15 @@ func TestAccountRefund(t *testing.T) {
func TestDebitShared(t *testing.T) {
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 0, 0, time.UTC),
CallDuration: 55 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 2, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 0, 0, time.UTC),
DurationIndex: 55 * time.Second,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 2, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
},
},
deductConnectFee: true,
@@ -1013,6 +1030,114 @@ func TestDebitShared(t *testing.T) {
}
}
func TestDebitSMS(t *testing.T) {
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 48, 1, 0, time.UTC),
ratingInfo: &RatingInfo{},
DurationIndex: 0,
RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1 * time.Second, RateUnit: time.Second}}}},
},
},
Tor: SMS,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
SMS + OUTBOUND: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationId: "NAT"}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != 99 ||
rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 21 {
t.Log(cc.Timespans[0].Increments)
t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
}
}
func TestDebitDataUnits(t *testing.T) {
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
ratingInfo: &RatingInfo{},
DurationIndex: 0,
RateInterval: &RateInterval{
Rating: &RIRate{
Rates: RateGroups{
&Rate{GroupIntervalStart: 0, Value: 2, RateIncrement: 1 * time.Second, RateUnit: time.Minute},
&Rate{GroupIntervalStart: 60, Value: 1, RateIncrement: 1 * time.Second, RateUnit: time.Second},
},
},
},
},
},
Tor: DATA,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
DATA + OUTBOUND: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationId: "NAT"}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
if rifsBalance.BalanceMap[DATA+OUTBOUND][0].Value != 20 ||
rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 21 {
t.Log(cc.Timespans[0].Increments)
t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[DATA+OUTBOUND][0].Value, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
}
}
func TestDebitDataMoney(t *testing.T) {
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
Timespans: []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC),
ratingInfo: &RatingInfo{},
DurationIndex: 0,
RateInterval: &RateInterval{
Rating: &RIRate{
Rates: RateGroups{
&Rate{GroupIntervalStart: 0, Value: 2, RateIncrement: time.Minute, RateUnit: time.Second},
},
},
},
},
},
Tor: DATA,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
DATA + OUTBOUND: BalanceChain{&Balance{Uuid: "testm", Value: 0, Weight: 5, DestinationId: "NAT"}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 160}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
t.Error("Error debiting balance: ", err)
}
if rifsBalance.BalanceMap[DATA+OUTBOUND][0].Value != 0 ||
rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 0 {
t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[DATA+OUTBOUND][0].Value, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
}
}
/*********************************** Benchmarks *******************************/
func BenchmarkGetSecondForPrefix(b *testing.B) {

View File

@@ -50,6 +50,8 @@ type Action struct {
const (
LOG = "*log"
RESET_TRIGGERS = "*reset_triggers"
SET_RECURRENT = "*set_recurrent"
UNSET_RECURRENT = "*unset_recurrent"
ALLOW_NEGATIVE = "*allow_negative"
DENY_NEGATIVE = "*deny_negative"
RESET_ACCOUNT = "*reset_account"
@@ -74,6 +76,10 @@ func getActionFunc(typ string) (actionTypeFunc, bool) {
return logAction, true
case RESET_TRIGGERS:
return resetTriggersAction, true
case SET_RECURRENT:
return setRecurrentAction, true
case UNSET_RECURRENT:
return unsetRecurrentAction, true
case ALLOW_NEGATIVE:
return allowNegativeAction, true
case DENY_NEGATIVE:
@@ -115,6 +121,16 @@ func resetTriggersAction(ub *Account, a *Action) (err error) {
return
}
func setRecurrentAction(ub *Account, a *Action) (err error) {
ub.SetRecurrent(a, true)
return
}
func unsetRecurrentAction(ub *Account, a *Action) (err error) {
ub.SetRecurrent(a, false)
return
}
func allowNegativeAction(ub *Account, a *Action) (err error) {
ub.AllowNegative = true
return

View File

@@ -32,6 +32,7 @@ type ActionTrigger struct {
Direction string
ThresholdType string //*min_counter, *max_counter, *min_balance, *max_balance
ThresholdValue float64
Recurrent bool // reset eexcuted flag each run
DestinationId string
Weight float64
ActionsId string
@@ -68,7 +69,7 @@ func (at *ActionTrigger) Execute(ub *Account) (err error) {
atLeastOneActionExecuted = true
}
}
if !atLeastOneActionExecuted {
if !atLeastOneActionExecuted || at.Recurrent {
at.Executed = false
}
storageLogger.LogActionTrigger(ub.Id, RATER_SOURCE, at, aac)

View File

@@ -142,7 +142,7 @@ func (b *Balance) SubstractAmount(amount float64) {
b.dirty = true
}
func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalances BalanceChain) error {
func (b *Balance) DebitUnits(cc *CallCost, count bool, ub *Account, moneyBalances BalanceChain) error {
for tsIndex := 0; tsIndex < len(cc.Timespans); tsIndex++ {
if b.Value <= 0 {
return nil
@@ -202,13 +202,13 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalan
inc = newTs.Increments[0]
}
b.SubstractAmount(amount)
inc.BalanceInfo.MinuteBalanceUuid = b.Uuid
inc.BalanceInfo.UnitBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.Id
inc.MinuteInfo = &MinuteInfo{cc.Destination, amount}
inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.Tor}
inc.Cost = 0
inc.paid = true
if count {
ub.countUnits(&Action{BalanceType: MINUTES, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}})
ub.countUnits(&Action{BalanceType: cc.Tor, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}})
}
}
continue
@@ -218,7 +218,7 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalan
cd.Subject = b.RatingSubject
cd.TimeStart = ts.GetTimeStartForIncrement(incrementIndex)
cd.TimeEnd = cc.Timespans[len(cc.Timespans)-1].TimeEnd
cd.CallDuration = cc.Timespans[len(cc.Timespans)-1].CallDuration
cd.DurationIndex = cc.Timespans[len(cc.Timespans)-1].DurationIndex
newCC, err := b.GetCost(cd)
if err != nil {
Logger.Err(fmt.Sprintf("Error getting new cost for balance subject: %v", err))
@@ -242,9 +242,9 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalan
}
if (cost == 0 || moneyBal != nil) && b.Value >= seconds {
b.SubstractAmount(seconds)
nInc.BalanceInfo.MinuteBalanceUuid = b.Uuid
nInc.BalanceInfo.UnitBalanceUuid = b.Uuid
nInc.BalanceInfo.AccountId = ub.Id
nInc.MinuteInfo = &MinuteInfo{newCC.Destination, seconds}
nInc.UnitInfo = &UnitInfo{newCC.Destination, seconds, cc.Tor}
if cost != 0 {
nInc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid
moneyBal.Value -= cost
@@ -252,7 +252,7 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalan
}
nInc.paid = true
if count {
ub.countUnits(&Action{BalanceType: MINUTES, Direction: newCC.Direction, Balance: &Balance{Value: seconds, DestinationId: newCC.Destination}})
ub.countUnits(&Action{BalanceType: newCC.Tor, Direction: newCC.Direction, Balance: &Balance{Value: seconds, DestinationId: newCC.Destination}})
if cost != 0 {
ub.countUnits(&Action{BalanceType: CREDIT, Direction: newCC.Direction, Balance: &Balance{Value: cost, DestinationId: newCC.Destination}})
}
@@ -327,7 +327,7 @@ func (b *Balance) DebitMoney(cc *CallCost, count bool, ub *Account) error {
cd.Subject = b.RatingSubject
cd.TimeStart = ts.GetTimeStartForIncrement(incrementIndex)
cd.TimeEnd = cc.Timespans[len(cc.Timespans)-1].TimeEnd
cd.CallDuration = cc.Timespans[len(cc.Timespans)-1].CallDuration
cd.DurationIndex = cc.Timespans[len(cc.Timespans)-1].DurationIndex
newCC, err := b.GetCost(cd)
if err != nil {
Logger.Err(fmt.Sprintf("Error getting new cost for balance subject: %v", err))

View File

@@ -18,16 +18,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"errors"
"reflect"
"time"
)
// The output structure that will be returned with the call cost information.
type CallCost struct {
Direction, TOR, Tenant, Subject, Account, Destination string
Cost float64
Timespans TimeSpans
deductConnectFee bool
Direction, Category, Tenant, Subject, Account, Destination, Tor string
Cost float64
Timespans TimeSpans
deductConnectFee bool
}
// Pretty printing for call cost
@@ -98,7 +99,7 @@ func (cc *CallCost) GetConnectFee() float64 {
func (cc *CallCost) CreateCallDescriptor() *CallDescriptor {
return &CallDescriptor{
Direction: cc.Direction,
TOR: cc.TOR,
Category: cc.Category,
Tenant: cc.Tenant,
Subject: cc.Subject,
Account: cc.Account,
@@ -114,3 +115,49 @@ func (cc *CallCost) IsPaid() bool {
}
return true
}
func (cc *CallCost) ToDataCost() (*DataCost, error) {
if cc.Tor == MINUTES {
return nil, errors.New("Not a data call!")
}
dc := &DataCost{
Direction: cc.Direction,
Category: cc.Category,
Tenant: cc.Tenant,
Subject: cc.Subject,
Account: cc.Account,
Destination: cc.Destination,
Tor: cc.Tor,
Cost: cc.Cost,
deductConnectFee: cc.deductConnectFee,
}
dc.DataSpans = make([]*DataSpan, len(cc.Timespans))
for i, ts := range cc.Timespans {
length := ts.TimeEnd.Sub(ts.TimeStart).Seconds()
callDuration := ts.DurationIndex.Seconds()
dc.DataSpans[i] = &DataSpan{
DataStart: callDuration - length,
DataEnd: callDuration,
Cost: ts.Cost,
ratingInfo: ts.ratingInfo,
RateInterval: ts.RateInterval,
DataIndex: callDuration,
MatchedSubject: ts.MatchedSubject,
MatchedPrefix: ts.MatchedPrefix,
MatchedDestId: ts.MatchedDestId,
}
dc.DataSpans[i].Increments = make([]*DataIncrement, len(ts.Increments))
for j, incr := range ts.Increments {
dc.DataSpans[i].Increments[j] = &DataIncrement{
Amount: incr.Duration.Seconds(),
Cost: incr.Cost,
BalanceInfo: incr.BalanceInfo,
BalanceRateInterval: incr.BalanceRateInterval,
UnitInfo: incr.UnitInfo,
CompressFactor: incr.CompressFactor,
paid: incr.paid,
}
}
}
return dc, nil
}

View File

@@ -19,14 +19,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"encoding/json"
"testing"
"time"
"github.com/cgrates/cgrates/utils"
)
func TestSingleResultMerge(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 0, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 17, 1, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
if cc1.Cost != 61 {
t.Errorf("expected 61 was %v", cc1.Cost)
@@ -50,7 +53,7 @@ func TestSingleResultMerge(t *testing.T) {
func TestMultipleResultMerge(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 0, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
if cc1.Cost != 61 {
t.Errorf("expected 61 was %v", cc1.Cost)
@@ -60,7 +63,7 @@ func TestMultipleResultMerge(t *testing.T) {
}
t1 = time.Date(2012, time.February, 2, 18, 00, 0, 0, time.UTC)
t2 = time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
cd = &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd = &CallDescriptor{Direction: OUTBOUND, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc2, _ := cd.GetCost()
if cc2.Cost != 30 {
t.Errorf("expected 30 was %v", cc2.Cost)
@@ -80,7 +83,7 @@ func TestMultipleResultMerge(t *testing.T) {
func TestMultipleInputLeftMerge(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
if cc1.Cost != 91 {
t.Errorf("expected 91 was %v", cc1.Cost)
@@ -104,14 +107,14 @@ func TestMultipleInputLeftMerge(t *testing.T) {
func TestMultipleInputRightMerge(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 58, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
if cc1.Cost != 61 {
t.Errorf("expected 61 was %v", cc1.Cost)
}
t1 = time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
t2 = time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
cd = &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd = &CallDescriptor{Direction: OUTBOUND, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc2, _ := cd.GetCost()
if cc2.Cost != 91 {
t.Errorf("expected 91 was %v", cc2.Cost)
@@ -128,7 +131,7 @@ func TestMultipleInputRightMerge(t *testing.T) {
func TestCallCostMergeEmpty(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 58, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
cc2 := &CallCost{}
cc1.Merge(cc2)
@@ -161,3 +164,44 @@ func TestCallCostGetDuration(t *testing.T) {
t.Error("Wrong call cost duration: ", cc.GetDuration())
}
}
func TestCallCostToDataCostError(t *testing.T) {
cd := &CallDescriptor{
Direction: "*out",
Category: "data",
Tenant: "cgrates.org",
Subject: "rif",
Destination: utils.ANY,
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 1, 5, 0, time.UTC),
Tor: MINUTES,
}
cc, _ := cd.GetCost()
_, err := cc.ToDataCost()
if err == nil {
t.Error("Failed to throw error on call to datacost!")
}
}
func TestCallCostToDataCost(t *testing.T) {
cd := &CallDescriptor{
Direction: "*out",
Category: "data",
Tenant: "cgrates.org",
Subject: "rif",
Destination: utils.ANY,
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 1, 5, 0, time.UTC),
Tor: DATA,
}
cc, _ := cd.GetCost()
dc, err := cc.ToDataCost()
if err != nil {
t.Error("Error convertiong to data cost: ", err)
}
js, _ := json.Marshal(dc)
expected := `{"Direction":"*out","Category":"data","Tenant":"cgrates.org","Subject":"rif","Account":"","Destination":"*any","Tor":"*data","Cost":65,"DataSpans":[{"DataStart":0,"DataEnd":60,"Cost":60,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":1000000000},{"GroupIntervalStart":60000000000,"Value":1,"RateIncrement":1000000000,"RateUnit":1000000000}],"RoundingMethod":"*up","RoundingDecimals":4},"Weight":10},"DataIndex":60,"Increments":[],"MatchedSubject":"","MatchedPrefix":"","MatchedDestId":""},{"DataStart":60,"DataEnd":65,"Cost":5,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":1000000000},{"GroupIntervalStart":60000000000,"Value":1,"RateIncrement":1000000000,"RateUnit":1000000000}],"RoundingMethod":"*up","RoundingDecimals":4},"Weight":10},"DataIndex":65,"Increments":[],"MatchedSubject":"*out:cgrates.org:data:rif","MatchedPrefix":"*any","MatchedDestId":"*any"}]}`
if string(js) != expected {
t.Error("Error coverting to data cost: ", string(js))
}
}

View File

@@ -107,11 +107,11 @@ The input stucture that contains call information.
*/
type CallDescriptor struct {
Direction string
TOR string
Category string
Tenant, Subject, Account, Destination string
TimeStart, TimeEnd time.Time
LoopIndex float64 // indicates the position of this segment in a cost request loop
CallDuration time.Duration // the call duration so far (till TimeEnd)
DurationIndex time.Duration // the call duration so far (till TimeEnd)
//Amount float64
FallbackSubject string // the subject to check for destination if not found on primary subject
RatingInfos RatingInfos
@@ -120,14 +120,15 @@ type CallDescriptor struct {
MaxRate float64
MaxRateUnit time.Duration
account *Account
Tor string
}
func (cd *CallDescriptor) ValidateCallData() error {
if cd.TimeStart.After(cd.TimeEnd) || cd.TimeStart.Equal(cd.TimeEnd) {
return errors.New("TimeStart must be strctly before TimeEnd")
}
if cd.TimeEnd.Sub(cd.TimeStart) < cd.CallDuration {
return errors.New("CallDuration must be equal or grater than TimeEnd - TimeStart")
if cd.TimeEnd.Sub(cd.TimeStart) < cd.DurationIndex {
return errors.New("DurationIndex must be equal or grater than TimeEnd - TimeStart")
}
return nil
}
@@ -200,7 +201,7 @@ func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int
}
if len(ri.FallbackKeys) > 0 {
tempCD := &CallDescriptor{
TOR: cd.TOR,
Category: cd.Category,
Direction: cd.Direction,
Tenant: cd.Tenant,
Destination: cd.Destination,
@@ -295,41 +296,43 @@ func (cd *CallDescriptor) GetKey(subject string) string {
subject = realSubject
cd.Subject = realSubject
}
return fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, subject)
return fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.Category, subject)
}
// Splits the received timespan into sub time spans according to the activation periods intervals.
func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*TimeSpan) {
if firstSpan == nil {
firstSpan = &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, CallDuration: cd.CallDuration}
}
func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) {
firstSpan := &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, DurationIndex: cd.DurationIndex}
timespans = append(timespans, firstSpan)
if len(cd.RatingInfos) == 0 {
return
}
firstSpan.ratingInfo = cd.RatingInfos[0]
// split on rating plans
afterStart, afterEnd := false, false //optimization for multiple activation periods
for _, rp := range cd.RatingInfos {
if !afterStart && !afterEnd && rp.ActivationTime.Before(cd.TimeStart) {
firstSpan.ratingInfo = rp
firstSpan.MatchedSubject = rp.MatchedSubject
firstSpan.MatchedPrefix = rp.MatchedPrefix
firstSpan.MatchedDestId = rp.MatchedDestId
} else {
afterStart = true
for i := 0; i < len(timespans); i++ {
newTs := timespans[i].SplitByRatingPlan(rp)
if newTs != nil {
timespans = append(timespans, newTs)
} else {
afterEnd = true
break
if cd.Tor == MINUTES {
// split on rating plans
afterStart, afterEnd := false, false //optimization for multiple activation periods
for _, rp := range cd.RatingInfos {
if !afterStart && !afterEnd && rp.ActivationTime.Before(cd.TimeStart) {
firstSpan.ratingInfo = rp
firstSpan.MatchedSubject = rp.MatchedSubject
firstSpan.MatchedPrefix = rp.MatchedPrefix
firstSpan.MatchedDestId = rp.MatchedDestId
} else {
afterStart = true
for i := 0; i < len(timespans); i++ {
newTs := timespans[i].SplitByRatingPlan(rp)
if newTs != nil {
timespans = append(timespans, newTs)
} else {
afterEnd = true
break
}
}
}
}
}
// Logger.Debug(fmt.Sprintf("After SplitByRatingPlan: %+v", timespans))
// split on price intervals
for i := 0; i < len(timespans); i++ {
@@ -379,7 +382,7 @@ func (cd *CallDescriptor) roundTimeSpansToIncrement(timespans TimeSpans) []*Time
if rateIncrement > ts.GetDuration() {
initialDuration := ts.GetDuration()
ts.TimeEnd = ts.TimeStart.Add(rateIncrement)
ts.CallDuration = ts.CallDuration + (rateIncrement - initialDuration)
ts.DurationIndex = ts.DurationIndex + (rateIncrement - initialDuration)
timespans.RemoveOverlapedFromIndex(i)
}
}
@@ -397,15 +400,19 @@ func (cd *CallDescriptor) GetDuration() time.Duration {
Creates a CallCost structure with the cost information calculated for the received CallDescriptor.
*/
func (cd *CallDescriptor) GetCost() (*CallCost, error) {
if cd.CallDuration < cd.TimeEnd.Sub(cd.TimeStart) {
cd.CallDuration = cd.TimeEnd.Sub(cd.TimeStart)
if cd.DurationIndex < cd.TimeEnd.Sub(cd.TimeStart) {
cd.DurationIndex = cd.TimeEnd.Sub(cd.TimeStart)
}
if cd.Tor == "" {
cd.Tor = MINUTES
}
err := cd.LoadRatingPlans()
if err != nil {
Logger.Err(fmt.Sprintf("error getting cost for key %s: %v", cd.GetKey(cd.Subject), err))
return &CallCost{Cost: -1}, err
}
timespans := cd.splitInTimeSpans(nil)
timespans := cd.splitInTimeSpans()
cost := 0.0
for i, ts := range timespans {
@@ -420,7 +427,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
//startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.TOR))
cc := &CallCost{
Direction: cd.Direction,
TOR: cd.TOR,
Category: cd.Category,
Tenant: cd.Tenant,
Account: cd.Account,
Destination: cd.Destination,
@@ -428,6 +435,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
Cost: cost,
Timespans: timespans,
deductConnectFee: cd.LoopIndex == 0,
Tor: cd.Tor,
}
//Logger.Info(fmt.Sprintf("<Rater> Get Cost: %s => %v", cd.GetKey(), cc))
cc.Timespans.Compress()
@@ -440,8 +448,11 @@ If the user has no credit then it will return 0.
If the user has postpayed plan it returns -1.
*/
func (origCD *CallDescriptor) getMaxSessionDuration(account *Account) (time.Duration, error) {
if origCD.CallDuration < origCD.TimeEnd.Sub(origCD.TimeStart) {
origCD.CallDuration = origCD.TimeEnd.Sub(origCD.TimeStart)
if origCD.DurationIndex < origCD.TimeEnd.Sub(origCD.TimeStart) {
origCD.DurationIndex = origCD.TimeEnd.Sub(origCD.TimeStart)
}
if origCD.Tor == "" {
origCD.Tor = MINUTES
}
cd := origCD.Clone()
//Logger.Debug(fmt.Sprintf("MAX SESSION cd: %+v", cd))
@@ -464,10 +475,6 @@ func (origCD *CallDescriptor) getMaxSessionDuration(account *Account) (time.Dura
// there are enough minutes for requested interval
return initialDuration, nil
}
// check for zero balance
if availableCredit == 0 {
return utils.MinDuration(initialDuration, availableDuration), nil
}
//Logger.Debug(fmt.Sprintf("initial Duration: %v", initialDuration))
// we must move the timestart for the interval with the available duration because
// that was already checked
@@ -478,6 +485,10 @@ func (origCD *CallDescriptor) getMaxSessionDuration(account *Account) (time.Dura
if availableDuration == 0 && cc.deductConnectFee { // only if we did not already used minutes
availableCredit -= cc.GetConnectFee()
}
// check for zero balance
if (availableCredit < 0) || (availableCredit == 0 && cc.Cost > 0) {
return utils.MinDuration(initialDuration, availableDuration), nil
}
if err != nil {
Logger.Err(fmt.Sprintf("Could not get cost for %s: %s.", cd.GetKey(cd.Subject), err.Error()))
return 0, err
@@ -513,7 +524,7 @@ func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err e
Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetAccountKey(), err.Error()))
return 0, err
} else {
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction); err == nil {
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Tor); err == nil {
AccLock.GuardMany(memberIds, func() (float64, error) {
duration, err = cd.getMaxSessionDuration(account)
return 0, err
@@ -563,7 +574,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetAccountKey(), err.Error()))
return nil, err
} else {
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction); err == nil {
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Tor); err == nil {
AccLock.GuardMany(memberIds, func() (float64, error) {
cc, err = cd.debit(account)
return 0, err
@@ -584,7 +595,7 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetAccountKey(), err.Error()))
return nil, err
} else {
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction); err == nil {
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Tor); err == nil {
AccLock.GuardMany(memberIds, func() (float64, error) {
remainingDuration, err := cd.getMaxSessionDuration(account)
if err != nil || remainingDuration == 0 {
@@ -617,7 +628,7 @@ func (cd *CallDescriptor) RefundIncrements() (left float64, err error) {
defer accountingStorage.SetAccount(account)
}
}
account.refundIncrement(increment, cd.Direction, true)
account.refundIncrement(increment, cd.Direction, cd.Tor, true)
}
return 0.0, err
}
@@ -634,31 +645,33 @@ func (cd *CallDescriptor) FlushCache() (err error) {
func (cd *CallDescriptor) CreateCallCost() *CallCost {
return &CallCost{
Direction: cd.Direction,
TOR: cd.TOR,
Category: cd.Category,
Tenant: cd.Tenant,
Subject: cd.Subject,
Account: cd.Account,
Destination: cd.Destination,
Tor: cd.Tor,
}
}
func (cd *CallDescriptor) Clone() *CallDescriptor {
return &CallDescriptor{
Direction: cd.Direction,
TOR: cd.TOR,
Tenant: cd.Tenant,
Subject: cd.Subject,
Account: cd.Account,
Destination: cd.Destination,
TimeStart: cd.TimeStart,
TimeEnd: cd.TimeEnd,
LoopIndex: cd.LoopIndex,
CallDuration: cd.CallDuration,
MaxRate: cd.MaxRate,
MaxRateUnit: cd.MaxRateUnit,
Direction: cd.Direction,
Category: cd.Category,
Tenant: cd.Tenant,
Subject: cd.Subject,
Account: cd.Account,
Destination: cd.Destination,
TimeStart: cd.TimeStart,
TimeEnd: cd.TimeEnd,
LoopIndex: cd.LoopIndex,
DurationIndex: cd.DurationIndex,
MaxRate: cd.MaxRate,
MaxRateUnit: cd.MaxRateUnit,
// Amount: cd.Amount,
FallbackSubject: cd.FallbackSubject,
//RatingInfos: cd.RatingInfos,
//Increments: cd.Increments,
Tor: cd.Tor,
}
}

View File

@@ -63,6 +63,13 @@ func populateDB() {
&Balance{Value: 100, DestinationId: "RET", Weight: 20},
}},
}
luna := &Account{
Id: "*out:vdf:luna",
BalanceMap: map[string]BalanceChain{
CREDIT + OUTBOUND: BalanceChain{
&Balance{Value: 0, Weight: 20},
}},
}
// this is added to test if csv load tests account will not overwrite balances
minitsboy := &Account{
Id: "*out:vdf:minitsboy",
@@ -82,6 +89,7 @@ func populateDB() {
accountingStorage.SetAccount(broker)
accountingStorage.SetAccount(minu)
accountingStorage.SetAccount(minitsboy)
accountingStorage.SetAccount(luna)
} else {
log.Fatal("Could not connect to db!")
}
@@ -90,10 +98,10 @@ func populateDB() {
func TestSplitSpans(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd.LoadRatingPlans()
timespans := cd.splitInTimeSpans(nil)
timespans := cd.splitInTimeSpans()
if len(timespans) != 2 {
t.Log(cd.RatingInfos)
t.Error("Wrong number of timespans: ", len(timespans))
@@ -103,10 +111,10 @@ func TestSplitSpans(t *testing.T) {
func TestSplitSpansRoundToIncrements(t *testing.T) {
t1 := time.Date(2013, time.October, 7, 14, 50, 0, 0, time.UTC)
t2 := time.Date(2013, time.October, 7, 14, 52, 12, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "test", Subject: "trp", Destination: "0256", TimeStart: t1, TimeEnd: t2, CallDuration: 132 * time.Second}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "test", Subject: "trp", Destination: "0256", TimeStart: t1, TimeEnd: t2, DurationIndex: 132 * time.Second}
cd.LoadRatingPlans()
timespans := cd.splitInTimeSpans(nil)
timespans := cd.splitInTimeSpans()
if len(timespans) != 2 {
t.Logf("%+v", cd)
t.Log(cd.RatingInfos)
@@ -124,7 +132,7 @@ func TestSplitSpansRoundToIncrements(t *testing.T) {
func TestGetCost(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 0}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 0}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2701}
if result.Cost != expected.Cost || result.GetConnectFee() != 1 {
@@ -135,7 +143,7 @@ func TestGetCost(t *testing.T) {
func TestGetCostTimespans(t *testing.T) {
t1 := time.Date(2013, time.October, 8, 9, 23, 2, 0, time.UTC)
t2 := time.Date(2013, time.October, 8, 9, 24, 27, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "test", Subject: "trp", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, CallDuration: 85 * time.Second}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "test", Subject: "trp", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, DurationIndex: 85 * time.Second}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "test", Subject: "trp", Destination: "0256", Cost: 85}
if result.Cost != expected.Cost || result.GetConnectFee() != 0 || len(result.Timespans) != 2 {
@@ -147,7 +155,7 @@ func TestGetCostTimespans(t *testing.T) {
func TestGetCostRatingPlansAndRatingIntervals(t *testing.T) {
t1 := time.Date(2012, time.February, 27, 23, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 28, 18, 10, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "CUSTOMER_1", Subject: "rif:from:tm", Destination: "49178", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, CallDuration: t2.Sub(t1)}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "CUSTOMER_1", Subject: "rif:from:tm", Destination: "49178", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, DurationIndex: t2.Sub(t1)}
result, _ := cd.GetCost()
if len(result.Timespans) != 3 ||
!result.Timespans[0].TimeEnd.Equal(result.Timespans[1].TimeStart) ||
@@ -162,7 +170,7 @@ func TestGetCostRatingPlansAndRatingIntervals(t *testing.T) {
func TestGetCostRatingPlansAndRatingIntervalsMore(t *testing.T) {
t1 := time.Date(2012, time.February, 27, 9, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 28, 18, 10, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "CUSTOMER_1", Subject: "rif:from:tm", Destination: "49178", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, CallDuration: t2.Sub(t1)}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "CUSTOMER_1", Subject: "rif:from:tm", Destination: "49178", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, DurationIndex: t2.Sub(t1)}
result, _ := cd.GetCost()
if len(result.Timespans) != 4 ||
!result.Timespans[0].TimeEnd.Equal(result.Timespans[1].TimeStart) ||
@@ -178,7 +186,7 @@ func TestGetCostRatingPlansAndRatingIntervalsMore(t *testing.T) {
func TestGetCostRateGroups(t *testing.T) {
t1 := time.Date(2013, time.October, 7, 14, 50, 0, 0, time.UTC)
t2 := time.Date(2013, time.October, 7, 14, 52, 12, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "test", Subject: "trp", Destination: "0256", TimeStart: t1, TimeEnd: t2, CallDuration: 132 * time.Second}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "test", Subject: "trp", Destination: "0256", TimeStart: t1, TimeEnd: t2, DurationIndex: 132 * time.Second}
result, err := cd.GetCost()
if err != nil {
@@ -192,7 +200,7 @@ func TestGetCostRateGroups(t *testing.T) {
func TestGetCostNoConnectFee(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 1}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 1}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2700}
// connect fee is not added because LoopIndex is 1
@@ -204,7 +212,7 @@ func TestGetCostNoConnectFee(t *testing.T) {
func TestGetCostAccount(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Account: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Account: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2701}
if result.Cost != expected.Cost || result.GetConnectFee() != 1 {
@@ -215,7 +223,7 @@ func TestGetCostAccount(t *testing.T) {
func TestFullDestNotFound(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256308200", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2701}
if result.Cost != expected.Cost || result.GetConnectFee() != 1 {
@@ -227,7 +235,7 @@ func TestFullDestNotFound(t *testing.T) {
func TestSubjectNotFound(t *testing.T) {
t1 := time.Date(2013, time.February, 1, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2013, time.February, 1, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "not_exiting", Destination: "025740532", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "not_exiting", Destination: "025740532", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 2701}
if result.Cost != expected.Cost || result.GetConnectFee() != 1 {
@@ -239,7 +247,7 @@ func TestSubjectNotFound(t *testing.T) {
func TestMultipleRatingPlans(t *testing.T) {
t1 := time.Date(2012, time.February, 8, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 2701}
if result.Cost != expected.Cost || result.GetConnectFee() != 1 {
@@ -251,7 +259,7 @@ func TestMultipleRatingPlans(t *testing.T) {
func TestSpansMultipleRatingPlans(t *testing.T) {
t1 := time.Date(2012, time.February, 7, 23, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 0, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
if result.Cost != 1200 || result.GetConnectFee() != 0 {
t.Errorf("Expected %v was %v", 1200, result)
@@ -261,7 +269,7 @@ func TestSpansMultipleRatingPlans(t *testing.T) {
func TestLessThanAMinute(t *testing.T) {
t1 := time.Date(2012, time.February, 8, 23, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 23, 50, 30, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 15}
if result.Cost != expected.Cost || result.GetConnectFee() != 0 {
@@ -272,7 +280,7 @@ func TestLessThanAMinute(t *testing.T) {
func TestUniquePrice(t *testing.T) {
t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 23, 50, 21, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723045326", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0723045326", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0723", Cost: 1810.5}
if result.Cost != expected.Cost || result.GetConnectFee() != 0 {
@@ -283,7 +291,7 @@ func TestUniquePrice(t *testing.T) {
func TestMinutesCost(t *testing.T) {
t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 22, 51, 50, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0723", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "minutosu", Destination: "0723", Cost: 55}
if result.Cost != expected.Cost || result.GetConnectFee() != 0 {
@@ -296,7 +304,7 @@ func TestMaxSessionTimeNoAccount(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "ttttttt",
Destination: "0723"}
@@ -311,7 +319,7 @@ func TestMaxSessionTimeWithAccount(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "minu",
Destination: "0723",
@@ -330,7 +338,7 @@ func TestMaxSessionTimeWithMaxRate(t *testing.T) {
}
cd := &CallDescriptor{
Direction: "*out",
TOR: "call",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
@@ -352,7 +360,7 @@ func TestMaxSessionTimeWithAccountAlias(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "a1",
Account: "a1",
@@ -379,7 +387,7 @@ func TestMaxSessionTimeWithAccountShared(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "rif",
Account: "empty0",
@@ -390,7 +398,7 @@ func TestMaxSessionTimeWithAccountShared(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "rif",
Account: "empty10",
@@ -418,7 +426,7 @@ func TestMaxDebitWithAccountShared(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 34, 5, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "minu",
Account: "empty0",
@@ -445,7 +453,7 @@ func TestMaxSessionTimeWithAccountAccount(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "minu_from_tm",
Account: "minu",
@@ -463,10 +471,11 @@ func TestMaxSessionTimeNoCredit(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "broker",
Destination: "0723",
Tor: MINUTES,
}
result, err := cd.GetMaxSessionDuration()
if result != time.Minute || err != nil {
@@ -478,15 +487,16 @@ func TestMaxSessionModifiesCallDesc(t *testing.T) {
t1 := time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC)
t2 := time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC)
cd := &CallDescriptor{
TimeStart: t1,
TimeEnd: t2,
Direction: "*out",
TOR: "0",
Tenant: "vdf",
Subject: "minu_from_tm",
Account: "minu",
Destination: "0723",
CallDuration: t2.Sub(t1),
TimeStart: t1,
TimeEnd: t2,
Direction: "*out",
Category: "0",
Tenant: "vdf",
Subject: "minu_from_tm",
Account: "minu",
Destination: "0723",
DurationIndex: t2.Sub(t1),
Tor: MINUTES,
}
initial := cd.Clone()
cd.GetMaxSessionDuration()
@@ -501,7 +511,7 @@ func TestMaxDebitDurationNoGreatherThanInitialDuration(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "minu_from_tm",
Account: "minu",
@@ -519,7 +529,7 @@ func TestDebitAndMaxDebit(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "minu_from_tm",
Account: "minu",
@@ -536,6 +546,42 @@ func TestDebitAndMaxDebit(t *testing.T) {
}
}
func TestMaxSesionTimeEmptyBalance(t *testing.T) {
cd := &CallDescriptor{
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
Category: "0",
Tenant: "vdf",
Subject: "minu_from_tm",
Account: "luna",
Destination: "0723",
}
acc, _ := accountingStorage.GetAccount("*out:vdf:luna")
allowedTime, err := cd.getMaxSessionDuration(acc)
if err != nil || allowedTime != 0 {
t.Error("Error get max session for 0 acount")
}
}
func TestMaxSesionTimeEmptyBalanceAndNoCost(t *testing.T) {
cd := &CallDescriptor{
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: "*out",
Category: "0",
Tenant: "vdf",
Subject: "one",
Account: "luna",
Destination: "112",
}
acc, _ := accountingStorage.GetAccount("*out:vdf:luna")
allowedTime, err := cd.getMaxSessionDuration(acc)
if err != nil || allowedTime == 0 {
t.Error("Error get max session for 0 acount")
}
}
func TestDebitFromShareAndNormal(t *testing.T) {
ap, _ := accountingStorage.GetActionTimings("TOPUP_SHARED10_AT")
for _, at := range ap {
@@ -546,7 +592,7 @@ func TestDebitFromShareAndNormal(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 34, 5, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "rif",
Account: "empty10",
@@ -574,7 +620,7 @@ func TestDebitFromEmptyShare(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 34, 5, 0, time.UTC),
Direction: "*out",
TOR: "0",
Category: "0",
Tenant: "vdf",
Subject: "rif",
Account: "emptyX",
@@ -598,16 +644,16 @@ func TestMaxDebitZeroDefinedRate(t *testing.T) {
at.Execute()
}
cd1 := &CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 1, 0, 0, time.UTC),
LoopIndex: 0,
CallDuration: 0}
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 1, 0, 0, time.UTC),
LoopIndex: 0,
DurationIndex: 0}
cc, err := cd1.MaxDebit()
if err != nil {
t.Error("Error maxdebiting: ", err)
@@ -626,16 +672,16 @@ func TestMaxDebitZeroDefinedRateOnlyMinutes(t *testing.T) {
at.Execute()
}
cd1 := &CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 40, 0, time.UTC),
LoopIndex: 0,
CallDuration: 0}
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 40, 0, time.UTC),
LoopIndex: 0,
DurationIndex: 0}
cc, err := cd1.MaxDebit()
if err != nil {
t.Fatal("Error maxdebiting: ", err)
@@ -654,16 +700,16 @@ func TestMaxDebitConsumesMinutes(t *testing.T) {
at.Execute()
}
cd1 := &CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 5, 0, time.UTC),
LoopIndex: 0,
CallDuration: 0}
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 5, 0, time.UTC),
LoopIndex: 0,
DurationIndex: 0}
cd1.MaxDebit()
if cd1.account.BalanceMap[MINUTES+OUTBOUND][0].Value != 20 {
t.Error("Error using minutes: ", cd1.account.BalanceMap[MINUTES+OUTBOUND][0].Value)
@@ -672,17 +718,54 @@ func TestMaxDebitConsumesMinutes(t *testing.T) {
func TestCDGetCostANY(t *testing.T) {
cd1 := &CallDescriptor{
Direction: "*out",
TOR: "0",
Tenant: "vdf",
Subject: "rif",
Destination: utils.ANY,
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 5, 0, time.UTC),
LoopIndex: 0,
CallDuration: 0}
Direction: "*out",
Category: "data",
Tenant: "cgrates.org",
Subject: "rif",
Destination: utils.ANY,
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 1, 0, time.UTC),
Tor: DATA,
}
cc, err := cd1.GetCost()
if err != nil || cc.Cost != 6 {
if err != nil || cc.Cost != 60 {
t.Errorf("Error getting *any dest: %+v %v", cc, err)
}
}
func TestCDSplitInDataSlots(t *testing.T) {
cd := &CallDescriptor{
Direction: "*out",
Category: "data",
Tenant: "cgrates.org",
Subject: "rif",
Destination: utils.ANY,
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 1, 5, 0, time.UTC),
Tor: DATA,
DurationIndex: 65 * time.Second,
}
cd.LoadRatingPlans()
timespans := cd.splitInTimeSpans()
if len(timespans) != 2 {
t.Log(cd.RatingInfos[0])
t.Error("Wrong number of timespans: ", len(timespans))
}
}
func TestCDDataGetCost(t *testing.T) {
cd := &CallDescriptor{
Direction: "*out",
Category: "data",
Tenant: "cgrates.org",
Subject: "rif",
Destination: utils.ANY,
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 1, 5, 0, time.UTC),
Tor: DATA,
}
cc, err := cd.GetCost()
if err != nil || cc.Cost != 65 {
t.Errorf("Error getting *any dest: %+v %v", cc, err)
}
}
@@ -692,7 +775,7 @@ func BenchmarkStorageGetting(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
dataStorage.GetRatingProfile(cd.GetKey(cd.Subject), false)
@@ -703,7 +786,7 @@ func BenchmarkStorageRestoring(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.LoadRatingPlans()
@@ -714,7 +797,7 @@ func BenchmarkStorageGetCost(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetCost()
@@ -725,11 +808,11 @@ func BenchmarkSplitting(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd.LoadRatingPlans()
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.splitInTimeSpans(nil)
cd.splitInTimeSpans()
}
}
@@ -744,7 +827,7 @@ func BenchmarkStorageSingleGetSessionTime(b *testing.B) {
func BenchmarkStorageMultipleGetSessionTime(b *testing.B) {
b.StopTimer()
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "minutosu", Destination: "0723"}
cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "minutosu", Destination: "0723"}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetMaxSessionDuration()

45
engine/datacost.go Normal file
View File

@@ -0,0 +1,45 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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 <http://www.gnu.org/licenses/>
*/
package engine
// type used for showing sane data cost
type DataCost struct {
Direction, Category, Tenant, Subject, Account, Destination, Tor string
Cost float64
DataSpans []*DataSpan
deductConnectFee bool
}
type DataSpan struct {
DataStart, DataEnd float64
Cost float64
ratingInfo *RatingInfo
RateInterval *RateInterval
DataIndex float64 // the data transfer so far till DataEnd
Increments []*DataIncrement
MatchedSubject, MatchedPrefix, MatchedDestId string
}
type DataIncrement struct {
Amount float64
Cost float64
BalanceInfo *BalanceInfo // need more than one for units with cost
BalanceRateInterval *RateInterval
UnitInfo *UnitInfo
CompressFactor int
paid bool
}

View File

@@ -47,6 +47,7 @@ func TestHistoryDestinations(t *testing.T) {
{"Id":"PSTN_71","Prefixes":["+4971"]},
{"Id":"PSTN_72","Prefixes":["+4972"]},
{"Id":"RET","Prefixes":["0723","0724"]},
{"Id":"URG","Prefixes":["112"]},
{"Id":"nat","Prefixes":["0257","0256","0723"]}]`
if buf.String() != expected {
t.Error("Error in destination history content:", buf.String())

View File

@@ -482,7 +482,7 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) {
defer fp.Close()
}
for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
tenant, tor, direction, subject, fallbacksubject := record[0], record[1], record[2], record[3], record[6]
direction, tenant, tor, subject, fallbacksubject := record[0], record[1], record[2], record[3], record[6]
at, err := utils.ParseDate(record[4])
if err != nil {
return fmt.Errorf("Cannot parse activation time from %v", record[4])
@@ -675,7 +675,11 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) {
if err != nil {
return fmt.Errorf("Could not parse action trigger value: %v", err)
}
weight, err := strconv.ParseFloat(record[7], 64)
recurrent, err := strconv.ParseBool(record[5])
if err != nil {
return fmt.Errorf("Could not parse action trigger recurrent flag: %v", err)
}
weight, err := strconv.ParseFloat(record[8], 64)
if err != nil {
return fmt.Errorf("Could not parse action trigger weight: %v", err)
}
@@ -685,8 +689,9 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) {
Direction: record[2],
ThresholdType: record[3],
ThresholdValue: value,
DestinationId: record[5],
ActionsId: record[6],
Recurrent: recurrent,
DestinationId: record[6],
ActionsId: record[7],
Weight: weight,
}
csvr.actionsTriggers[tag] = append(csvr.actionsTriggers[tag], at)

View File

@@ -44,6 +44,7 @@ PSTN_71,+4971
PSTN_72,+4972
PSTN_70,+4970
DST_UK_Mobile_BIG5,447956
URG,112
*any,
`
timings = `
@@ -60,13 +61,14 @@ R2,0,0.1,60s,1s,0,*middle,2
R3,0,0.05,60s,1s,0,*middle,2
R4,1,1,1s,1s,0,*up,2
R5,0,0.5,1s,1s,0,*down,2
LANDLINE_OFFPEAK,0,1,1s,60s,0s,*up,4
LANDLINE_OFFPEAK,0,1,1s,1s,60s,*up,4
LANDLINE_OFFPEAK,0,1,1,60,0,*up,4
LANDLINE_OFFPEAK,0,1,1,1,60,*up,4
GBP_71,0.000000,5.55555,1s,1s,0s,*up,4
GBP_72,0.000000,7.77777,1s,1s,0s,*up,4
GBP_70,0.000000,1,1,1,0,*up,4
RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s,*up,8
RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8
R_URG,0,0,1,1,0,*down,2
`
destinationRates = `
RT_STANDARD,GERMANY,R1
@@ -83,12 +85,14 @@ T2,GERMANY_O2,GBP_70
T2,GERMANY_PREMIUM,GBP_71
DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG
DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5
DATA_RATE,*any,R4
DATA_RATE,*any,LANDLINE_OFFPEAK
RT_URG,URG,R_URG
`
ratingPlans = `
STANDARD,RT_STANDARD,WORKDAYS_00,10
STANDARD,RT_STD_WEEKEND,WORKDAYS_18,10
STANDARD,RT_STD_WEEKEND,WEEKENDS,10
STANDARD,RT_URG,ALWAYS,20
PREMIUM,RT_STANDARD,WORKDAYS_00,10
PREMIUM,RT_STD_WEEKEND,WORKDAYS_18,10
PREMIUM,RT_STD_WEEKEND,WEEKENDS,10
@@ -96,33 +100,34 @@ DEFAULT,RT_DEFAULT,WORKDAYS_00,10
EVENING,P1,WORKDAYS_00,10
EVENING,P2,WORKDAYS_18,10
EVENING,P2,WEEKENDS,10
EVENING,DATA_RATE,ALWAYS,10
TDRT,T1,WORKDAYS_00,10
TDRT,T2,WORKDAYS_00,10
G,RT_STANDARD,WORKDAYS_00,10
R,P1,WORKDAYS_00,10
RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10
RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10
RP_DATA,DATA_RATE,ALWAYS,10
`
ratingProfiles = `
CUSTOMER_1,0,*out,rif:from:tm,2012-01-01T00:00:00Z,PREMIUM,danb
CUSTOMER_1,0,*out,rif:from:tm,2012-02-28T00:00:00Z,STANDARD,danb
CUSTOMER_2,0,*out,danb:87.139.12.167,2012-01-01T00:00:00Z,STANDARD,danb
CUSTOMER_1,0,*out,danb,2012-01-01T00:00:00Z,PREMIUM,
vdf,0,*out,rif,2012-01-01T00:00:00Z,EVENING,
vdf,0,*out,rif,2012-02-28T00:00:00Z,EVENING,
vdf,0,*out,minu;a1;a2;a3,2012-01-01T00:00:00Z,EVENING,
vdf,0,*out,*any,2012-02-28T00:00:00Z,EVENING,
vdf,0,*out,one,2012-02-28T00:00:00Z,STANDARD,
vdf,0,*out,inf,2012-02-28T00:00:00Z,STANDARD,inf
vdf,0,*out,fall,2012-02-28T00:00:00Z,PREMIUM,rif
test,0,*out,trp,2013-10-01T00:00:00Z,TDRT,rif;danb
vdf,0,*out,fallback1,2013-11-18T13:45:00Z,G,fallback2
vdf,0,*out,fallback1,2013-11-18T13:46:00Z,G,fallback2
vdf,0,*out,fallback1,2013-11-18T13:47:00Z,G,fallback2
vdf,0,*out,fallback2,2013-11-18T13:45:00Z,R,rif
cgrates.org,call,*out,*any,2013-01-06T00:00:00Z,RP_UK,
cgrates.org,call,*out,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,
*out,CUSTOMER_1,0,rif:from:tm,2012-01-01T00:00:00Z,PREMIUM,danb
*out,CUSTOMER_1,0,rif:from:tm,2012-02-28T00:00:00Z,STANDARD,danb
*out,CUSTOMER_2,0,danb:87.139.12.167,2012-01-01T00:00:00Z,STANDARD,danb
*out,CUSTOMER_1,0,danb,2012-01-01T00:00:00Z,PREMIUM,
*out,vdf,0,rif,2012-01-01T00:00:00Z,EVENING,
*out,vdf,0,rif,2012-02-28T00:00:00Z,EVENING,
*out,vdf,0,minu;a1;a2;a3,2012-01-01T00:00:00Z,EVENING,
*out,vdf,0,*any,2012-02-28T00:00:00Z,EVENING,
*out,vdf,0,one,2012-02-28T00:00:00Z,STANDARD,
*out,vdf,0,inf,2012-02-28T00:00:00Z,STANDARD,inf
*out,vdf,0,fall,2012-02-28T00:00:00Z,PREMIUM,rif
*out,test,0,trp,2013-10-01T00:00:00Z,TDRT,rif;danb
*out,vdf,0,fallback1,2013-11-18T13:45:00Z,G,fallback2
*out,vdf,0,fallback1,2013-11-18T13:46:00Z,G,fallback2
*out,vdf,0,fallback1,2013-11-18T13:47:00Z,G,fallback2
*out,vdf,0,fallback2,2013-11-18T13:45:00Z,R,rif
*out,cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_UK,
*out,cgrates.org,call,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,
*out,cgrates.org,data,rif,2013-01-06T00:00:00Z,RP_DATA,
`
sharedGroups = `
SG1,*any,*lowest,
@@ -132,10 +137,10 @@ SG3,*any,*lowest,
actions = `
MINI,*topup_reset,*monetary,*out,10,*unlimited,,,10,,,10
MINI,*topup,*minutes,*out,100,*unlimited,NAT,test,10,,,10
MINI,*topup,*call_duration,*out,100,*unlimited,NAT,test,10,,,10
SHARED,*topup,*monetary,*out,100,*unlimited,,,10,SG1,,10
TOPUP10_AC,*topup_reset,*monetary,*out,1,*unlimited,*any,,10,,,10
TOPUP10_AC1,*topup_reset,*minutes,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10
TOPUP10_AC1,*topup_reset,*call_duration,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10
SE0,*topup_reset,*monetary,*out,0,*unlimited,,,10,SG2,,10
SE10,*topup_reset,*monetary,*out,10,*unlimited,,,5,SG2,,10
SE10,*topup,*monetary,*out,10,*unlimited,,,10,,,10
@@ -152,11 +157,11 @@ TOPUP_SHARED10_AT,SE10,ASAP,10
TOPUP_EMPTY_AT,EE0,ASAP,10
`
actionTriggers = `
STANDARD_TRIGGER,*minutes,*out,*min_counter,10,GERMANY_O2,SOME_1,10
STANDARD_TRIGGER,*minutes,*out,*max_balance,200,GERMANY,SOME_2,10
STANDARD_TRIGGERS,*monetary,*out,*min_balance,2,,LOG_WARNING,10
STANDARD_TRIGGERS,*monetary,*out,*max_balance,20,,LOG_WARNING,10
STANDARD_TRIGGERS,*monetary,*out,*max_counter,5,FS_USERS,LOG_WARNING,10
STANDARD_TRIGGER,*call_duration,*out,*min_counter,10,false,GERMANY_O2,SOME_1,10
STANDARD_TRIGGER,*call_duration,*out,*max_balance,200,false,GERMANY,SOME_2,10
STANDARD_TRIGGERS,*monetary,*out,*min_balance,2,false,,LOG_WARNING,10
STANDARD_TRIGGERS,*monetary,*out,*max_balance,20,false,,LOG_WARNING,10
STANDARD_TRIGGERS,*monetary,*out,*max_counter,5,false,FS_USERS,LOG_WARNING,10
`
accountActions = `
vdf,minitsboy;a1;a2,*out,MORE_MINUTES,STANDARD_TRIGGER
@@ -198,7 +203,7 @@ func init() {
}
func TestLoadDestinations(t *testing.T) {
if len(csvr.destinations) != 11 {
if len(csvr.destinations) != 12 {
t.Error("Failed to load destinations: ", len(csvr.destinations))
}
for _, d := range csvr.destinations {
@@ -294,7 +299,7 @@ func TestLoadTimimgs(t *testing.T) {
}
func TestLoadRates(t *testing.T) {
if len(csvr.rates) != 11 {
if len(csvr.rates) != 12 {
t.Error("Failed to load rates: ", csvr.rates)
}
rate := csvr.rates["R1"].RateSlots[0]
@@ -344,7 +349,7 @@ func TestLoadRates(t *testing.T) {
t.Error("Error loading rate: ", rate)
}
rate = csvr.rates["LANDLINE_OFFPEAK"].RateSlots[0]
if expctRs, err = utils.NewRateSlot(0, 1, "1s", "60s", "0s", utils.ROUNDING_UP, 4); err != nil {
if expctRs, err = utils.NewRateSlot(0, 1, "1", "60", "0", utils.ROUNDING_UP, 4); err != nil {
t.Error("Error loading rate: ", rate, err.Error())
} else if !reflect.DeepEqual(rate, expctRs) ||
rate.RateUnitDuration() != expctRs.RateUnitDuration() ||
@@ -353,7 +358,7 @@ func TestLoadRates(t *testing.T) {
t.Error("Error loading rate: ", rate)
}
rate = csvr.rates["LANDLINE_OFFPEAK"].RateSlots[1]
if expctRs, err = utils.NewRateSlot(0, 1, "1s", "1s", "60s", utils.ROUNDING_UP, 4); err != nil {
if expctRs, err = utils.NewRateSlot(0, 1, "1", "1", "60", utils.ROUNDING_UP, 4); err != nil {
t.Error("Error loading rate: ", rate, err.Error())
} else if !reflect.DeepEqual(rate, expctRs) ||
rate.RateUnitDuration() != expctRs.RateUnitDuration() ||
@@ -364,7 +369,7 @@ func TestLoadRates(t *testing.T) {
}
func TestLoadDestinationRates(t *testing.T) {
if len(csvr.destinationRates) != 10 {
if len(csvr.destinationRates) != 11 {
t.Error("Failed to load destinationrates: ", csvr.destinationRates)
}
drs := csvr.destinationRates["RT_STANDARD"]
@@ -476,7 +481,7 @@ func TestLoadDestinationRates(t *testing.T) {
}
func TestLoadRatingPlans(t *testing.T) {
if len(csvr.ratingPlans) != 9 {
if len(csvr.ratingPlans) != 10 {
t.Error("Failed to load rating plans: ", len(csvr.ratingPlans))
}
rplan := csvr.ratingPlans["STANDARD"]
@@ -504,6 +509,13 @@ func TestLoadRatingPlans(t *testing.T) {
WeekDays: utils.WeekDays{time.Saturday, time.Sunday},
StartTime: "00:00:00",
},
"96c78ff5": &RITiming{
Years: utils.Years{},
Months: utils.Months{},
MonthDays: utils.MonthDays{},
WeekDays: utils.WeekDays{},
StartTime: "00:00:00",
},
},
Ratings: map[string]*RIRate{
"d54545c1": &RIRate{
@@ -545,6 +557,19 @@ func TestLoadRatingPlans(t *testing.T) {
RoundingMethod: utils.ROUNDING_MIDDLE,
RoundingDecimals: 2,
},
"2efe78aa": &RIRate{
ConnectFee: 0,
Rates: []*Rate{
&Rate{
GroupIntervalStart: 0,
Value: 0,
RateIncrement: time.Second,
RateUnit: time.Second,
},
},
RoundingMethod: utils.ROUNDING_DOWN,
RoundingDecimals: 2,
},
},
DestinationRates: map[string]RPRateList{
"GERMANY": []*RPRate{
@@ -588,15 +613,22 @@ func TestLoadRatingPlans(t *testing.T) {
Weight: 10,
},
},
"URG": []*RPRate{
&RPRate{
Timing: "96c78ff5",
Rating: "2efe78aa",
Weight: 20,
},
},
},
}
if !reflect.DeepEqual(rplan, expected) {
t.Errorf("Error loading destination rate timing: %+v", rplan.Ratings["e06c337f"])
t.Errorf("Error loading destination rate timing: %+v", rplan.DestinationRates["URG"][0])
}
}
func TestLoadRatingProfiles(t *testing.T) {
if len(csvr.ratingProfiles) != 14 {
if len(csvr.ratingProfiles) != 15 {
t.Error("Failed to load rating profiles: ", len(csvr.ratingProfiles), csvr.ratingProfiles)
}
rp := csvr.ratingProfiles["*out:test:0:trp"]

View File

@@ -341,7 +341,7 @@ func (dbr *DbReader) LoadRatingProfiles() error {
&RatingPlanActivation{
ActivationTime: at,
RatingPlanId: tpRa.RatingPlanId,
FallbackKeys: utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.TOR, tpRa.FallbackSubjects),
FallbackKeys: utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, tpRa.FallbackSubjects),
})
}
dbr.ratingProfiles[tpRpf.KeyId()] = rpf
@@ -430,7 +430,7 @@ func (dbr *DbReader) LoadRatingProfileFiltered(qriedRpf *utils.TPRatingProfile)
}
resultRatingProfile.RatingPlanActivations = append(resultRatingProfile.RatingPlanActivations,
&RatingPlanActivation{at, tpRa.RatingPlanId,
utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.TOR, tpRa.FallbackSubjects)})
utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, tpRa.FallbackSubjects)})
}
if err := dbr.dataDb.SetRatingProfile(resultRatingProfile); err != nil {
return err
@@ -528,6 +528,7 @@ func (dbr *DbReader) LoadActionTriggers() (err error) {
DestinationId: apiAtr.DestinationId,
Weight: apiAtr.Weight,
ActionsId: apiAtr.ActionsId,
Recurrent: apiAtr.Recurrent,
}
}
dbr.actionsTriggers[key] = atrs

View File

@@ -33,7 +33,7 @@ func TestApRestoreFromStorage(t *testing.T) {
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
Direction: OUTBOUND,
TOR: "0",
Category: "0",
Tenant: "CUSTOMER_1",
Subject: "rif:from:tm",
Destination: "49"}
@@ -76,7 +76,7 @@ func TestFallbackDirect(t *testing.T) {
cd := &CallDescriptor{
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Tenant: "CUSTOMER_2",
Subject: "danb:87.139.12.167",
@@ -91,7 +91,7 @@ func TestFallbackMultiple(t *testing.T) {
cd := &CallDescriptor{
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Tenant: "vdf",
Subject: "fall",
@@ -106,7 +106,7 @@ func TestFallbackWithBackTrace(t *testing.T) {
cd := &CallDescriptor{
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Tenant: "CUSTOMER_2",
Subject: "danb:87.139.12.167",
@@ -121,7 +121,7 @@ func TestFallbackDefault(t *testing.T) {
cd := &CallDescriptor{
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Tenant: "vdf",
Subject: "one",
@@ -133,7 +133,7 @@ func TestFallbackDefault(t *testing.T) {
}
func TestFallbackNoInfiniteLoop(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "rif", Destination: "0721"}
cd := &CallDescriptor{Category: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "rif", Destination: "0721"}
cd.LoadRatingPlans()
if len(cd.RatingInfos) != 0 {
t.Error("Error restoring activation periods: ", len(cd.RatingInfos))
@@ -141,7 +141,7 @@ func TestFallbackNoInfiniteLoop(t *testing.T) {
}
func TestFallbackNoInfiniteLoopSelf(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "inf", Destination: "0721"}
cd := &CallDescriptor{Category: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "inf", Destination: "0721"}
cd.LoadRatingPlans()
if len(cd.RatingInfos) != 0 {
t.Error("Error restoring activation periods: ", len(cd.RatingInfos))

View File

@@ -28,7 +28,7 @@ func TestGetRatingProfileForPrefix(t *testing.T) {
TimeStart: time.Date(2013, 11, 18, 13, 45, 1, 0, time.UTC),
TimeEnd: time.Date(2013, 11, 18, 13, 47, 30, 0, time.UTC),
Tenant: "vdf",
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Subject: "fallback1",
Destination: "0256098",
@@ -47,7 +47,7 @@ func TestGetRatingProfileForPrefixFirstEmpty(t *testing.T) {
TimeStart: time.Date(2013, 11, 18, 13, 44, 1, 0, time.UTC),
TimeEnd: time.Date(2013, 11, 18, 13, 47, 30, 0, time.UTC),
Tenant: "vdf",
TOR: "0",
Category: "0",
Direction: OUTBOUND,
Subject: "fallback1",
Destination: "0256098",

View File

@@ -25,7 +25,7 @@ import (
var balance1 string = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","BalanceMap":{"*monetary*out":[{"Uuid":"7fe5d6e740b6edd180b96274b8bd4123","Value":10,"ExpirationDate":"0001-01-01T00:00:00Z","Weight":10,"GroupIds":null,"DestinationId":"*any","RateSubject":""}]},"UnitCounters":null,"ActionTriggers":[{"Id":"120ea04d40af91c580adb0da11554c88","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*min_balance","ThresholdValue":2,"DestinationId":"","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"fa217a904059cfd3806239f5ad229f4a","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*max_balance","ThresholdValue":20,"DestinationId":"","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"f05174b740ab987c802a0a29aa5a2764","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*max_counter","ThresholdValue":15,"DestinationId":"FS_USERS","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"4d6ebf454048371280100094246163a7","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*min_balance","ThresholdValue":0.1,"DestinationId":"","Weight":10,"ActionsId":"WARN_HTTP","Executed":false}],"Groups":null,"UserIds":null}`
var callCost1 string = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":0.6,"ConnectFee":0,"Timespans":[{"TimeStart":"2013-12-03T14:36:48+01:00","TimeEnd":"2013-12-03T14:37:48+01:00","Cost":0.6,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":0.6,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}`
var callCost1 string = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":0.6,"ConnectFee":0,"Timespans":[{"TimeStart":"2013-12-03T14:36:48+01:00","TimeEnd":"2013-12-03T14:37:48+01:00","Cost":0.6,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":0.6,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"DurationIndex":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}`
func TestDebitBalanceForCall1(t *testing.T) {
b1 := new(Account)
@@ -47,7 +47,7 @@ func TestDebitBalanceForCall1(t *testing.T) {
var balanceInsufficient = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","BalanceMap":{"*monetary*out":[{"Uuid":"de49cb1a40b2740a8087a1d28112e11c","Value":1,"ExpirationDate":"0001-01-01T00:00:00Z","Weight":10,"GroupIds":null,"DestinationId":"*any","RateSubject":""}]},"UnitCounters":[{"Direction":"*out","BalanceId":"*monetary","Balances":[{"Uuid":"","Value":4,"ExpirationDate":"0001-01-01T00:00:00Z","Weight":0,"GroupIds":null,"DestinationId":"","RateSubject":""}]}],"ActionTriggers":[{"Id":"a33f036e402c2b47801f1380c5c82564","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*min_balance","ThresholdValue":2,"DestinationId":"","Weight":10,"ActionsId":"LOG_BALANCE","Executed":true},{"Id":"91776f2d40496c03806cdbc73ae6b5f9","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*max_balance","ThresholdValue":20,"DestinationId":"","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"e85e933a4045f77c80ef647295ae209b","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*max_counter","ThresholdValue":15,"DestinationId":"FS_USERS","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"89c178b440a640b6802334b17b63cd4e","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*min_balance","ThresholdValue":2.1,"DestinationId":"","Weight":10,"ActionsId":"WARN_HTTP","Executed":true}],"Groups":null,"UserIds":null}`
var costInsufficient = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":1,"ConnectFee":3,"Timespans":[{"TimeStart":"2013-12-05T09:52:17+01:00","TimeEnd":"2013-12-05T09:53:17+01:00","Cost":1,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":3,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}`
var costInsufficient = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":1,"ConnectFee":3,"Timespans":[{"TimeStart":"2013-12-05T09:52:17+01:00","TimeEnd":"2013-12-05T09:53:17+01:00","Cost":1,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":3,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"DurationIndex":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}`
func TestDebitInsufficientBalance(t *testing.T) {
b1 := new(Account)

View File

@@ -165,53 +165,6 @@ func (rs *Responder) Shutdown(arg string, reply *string) (err error) {
return
}
func (rs *Responder) GetMonetary(arg CallDescriptor, reply *CallCost) (err error) {
err = rs.getBalance(&arg, CREDIT, reply)
return err
}
func (rs *Responder) GetSMS(arg CallDescriptor, reply *CallCost) (err error) {
err = rs.getBalance(&arg, SMS, reply)
return err
}
func (rs *Responder) GetInternet(arg CallDescriptor, reply *CallCost) (err error) {
err = rs.getBalance(&arg, DATA, reply)
return err
}
func (rs *Responder) GetInternetTime(arg CallDescriptor, reply *CallCost) (err error) {
err = rs.getBalance(&arg, DATA_TIME, reply)
return err
}
func (rs *Responder) GetMinutes(arg CallDescriptor, reply *CallCost) (err error) {
err = rs.getBalance(&arg, MINUTES, reply)
return err
}
// Get balance
func (rs *Responder) getBalance(arg *CallDescriptor, balanceId string, reply *CallCost) (err error) {
if rs.Bal != nil {
return errors.New("No balancer supported for this command right now")
}
ubKey := arg.Direction + ":" + arg.Tenant + ":" + arg.Account
userBalance, err := accountingStorage.GetAccount(ubKey)
if err != nil {
return err
}
if balance, balExists := userBalance.BalanceMap[balanceId+arg.Direction]; !balExists {
// No match, balanceId not found
return errors.New("-BALANCE_NOT_FOUND")
} else {
reply.Tenant = arg.Tenant
reply.Account = arg.Account
reply.Direction = arg.Direction
reply.Cost = balance.GetTotalValue()
}
return nil
}
/*
The function that gets the information from the raters using balancer.
*/

View File

@@ -269,7 +269,7 @@ func (self *SQLStorage) SetTPRatingProfiles(tpid string, rps map[string]*utils.T
if i != 0 { //Consecutive values after the first will be prefixed with "," as separator
buffer.WriteRune(',')
}
buffer.WriteString(fmt.Sprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s')", tpid, rp.LoadId, rp.Tenant, rp.TOR, rp.Direction,
buffer.WriteString(fmt.Sprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s')", tpid, rp.LoadId, rp.Tenant, rp.Category, rp.Direction,
rp.Subject, rpa.ActivationTime, rpa.RatingPlanId, rpa.FallbackSubjects))
i++
}
@@ -476,7 +476,7 @@ func (self *SQLStorage) LogCallCost(cgrid, source, runid string, cc *CallCost) (
cgrid,
cc.Direction,
cc.Tenant,
cc.TOR,
cc.Category,
cc.Account,
cc.Subject,
cc.Destination,
@@ -500,7 +500,7 @@ func (self *SQLStorage) GetCallCostLog(cgrid, source, runid string) (cc *CallCos
var src string
var timespansJson string
cc = &CallCost{Cost: -1}
err = row.Scan(&cgrid, &cc.Direction, &cc.Tenant, &cc.TOR, &cc.Account, &cc.Subject,
err = row.Scan(&cgrid, &cc.Direction, &cc.Tenant, &cc.Category, &cc.Account, &cc.Subject,
&cc.Destination, &cc.Cost, &timespansJson, &src)
if len(timespansJson) == 0 { // No costs returned
return nil, nil
@@ -533,7 +533,7 @@ func (self *SQLStorage) SetCdr(cdr utils.RawCDR) (err error) {
cdr.GetReqType(),
cdr.GetDirection(),
cdr.GetTenant(),
cdr.GetTOR(),
cdr.GetCategory(),
cdr.GetAccount(),
cdr.GetSubject(),
cdr.GetDestination(),
@@ -796,11 +796,11 @@ func (self *SQLStorage) GetStoredCdrs(cgrIds, runIds, cdrHosts, cdrSources, reqT
return nil, err
}
if err := json.Unmarshal(extraFields, &extraFieldsMp); err != nil {
return nil, fmt.Errorf("JSON unmarshal error for cgrid: %s, runid: %s, error: %s", cgrid, runid, err.Error())
return nil, fmt.Errorf("JSON unmarshal error for cgrid: %s, runid: %v, error: %s", cgrid, runid, err.Error())
}
storCdr := &utils.StoredCdr{
CgrId: cgrid, OrderId: orderid, AccId: accid, CdrHost: cdrhost, CdrSource: cdrsrc, ReqType: reqtype, Direction: direction, Tenant: tenant,
TOR: tor, Account: account, Subject: subject, Destination: destination, SetupTime: setupTime, AnswerTime: answerTime, Duration: time.Duration(duration),
Category: tor, Account: account, Subject: subject, Destination: destination, SetupTime: setupTime, AnswerTime: answerTime, Duration: time.Duration(duration),
ExtraFields: extraFieldsMp, MediationRunId: runid.String, Cost: cost.Float64,
}
cdrs = append(cdrs, storCdr)
@@ -1005,7 +1005,7 @@ func (self *SQLStorage) GetTpRatingPlans(tpid, tag string) (map[string][]*utils.
}
func (self *SQLStorage) GetTpRatingProfiles(qryRpf *utils.TPRatingProfile) (map[string]*utils.TPRatingProfile, error) {
q := fmt.Sprintf("SELECT loadid,tenant,tor,direction,subject,activation_time,rating_plan_id,fallback_subjects FROM %s WHERE tpid='%s'",
q := fmt.Sprintf("SELECT loadid,direction,tenant,tor,subject,activation_time,rating_plan_id,fallback_subjects FROM %s WHERE tpid='%s'",
utils.TBL_TP_RATE_PROFILES, qryRpf.TPid)
if len(qryRpf.LoadId) != 0 {
q += fmt.Sprintf(" AND loadid='%s'", qryRpf.LoadId)
@@ -1013,8 +1013,8 @@ func (self *SQLStorage) GetTpRatingProfiles(qryRpf *utils.TPRatingProfile) (map[
if len(qryRpf.Tenant) != 0 {
q += fmt.Sprintf(" AND tenant='%s'", qryRpf.Tenant)
}
if len(qryRpf.TOR) != 0 {
q += fmt.Sprintf(" AND tor='%s'", qryRpf.TOR)
if len(qryRpf.Category) != 0 {
q += fmt.Sprintf(" AND tor='%s'", qryRpf.Category)
}
if len(qryRpf.Direction) != 0 {
q += fmt.Sprintf(" AND direction='%s'", qryRpf.Direction)
@@ -1033,7 +1033,7 @@ func (self *SQLStorage) GetTpRatingProfiles(qryRpf *utils.TPRatingProfile) (map[
if err := rows.Scan(&rcvLoadId, &tenant, &tor, &direction, &subject, &activation_time, &rating_plan_tag, &fallback_subjects); err != nil {
return nil, err
}
rp := &utils.TPRatingProfile{TPid: qryRpf.TPid, LoadId: rcvLoadId, Tenant: tenant, TOR: tor, Direction: direction, Subject: subject}
rp := &utils.TPRatingProfile{TPid: qryRpf.TPid, LoadId: rcvLoadId, Tenant: tenant, Category: tor, Direction: direction, Subject: subject}
if existingRp, has := rpfs[rp.KeyId()]; !has {
rp.RatingPlanActivations = []*utils.TPRatingActivation{
&utils.TPRatingActivation{ActivationTime: activation_time, RatingPlanId: rating_plan_tag, FallbackSubjects: fallback_subjects}}
@@ -1138,7 +1138,8 @@ func (self *SQLStorage) GetTpActionTriggers(tpid, tag string) (map[string][]*uti
for rows.Next() {
var threshold, weight float64
var tpid, tag, balances_type, direction, destinations_tag, actions_tag, thresholdType string
if err := rows.Scan(&tpid, &tag, &balances_type, &direction, &thresholdType, &threshold, &destinations_tag, &actions_tag, &weight); err != nil {
var recurrent bool
if err := rows.Scan(&tpid, &tag, &balances_type, &direction, &thresholdType, &threshold, &recurrent, &destinations_tag, &actions_tag, &weight); err != nil {
return nil, err
}

View File

@@ -92,7 +92,7 @@ func TestRemoveData(t *testing.T) {
}
// Create RatingProfile
ras := []*utils.TPRatingActivation{&utils.TPRatingActivation{ActivationTime: "2012-01-01T00:00:00Z", RatingPlanId: "RETAIL1"}}
rp := &utils.TPRatingProfile{TPid: TEST_SQL, LoadId: TEST_SQL, Tenant: "cgrates.org", TOR: "call", Direction: "*out", Subject: "*any", RatingPlanActivations: ras}
rp := &utils.TPRatingProfile{TPid: TEST_SQL, LoadId: TEST_SQL, Tenant: "cgrates.org", Category: "call", Direction: "*out", Subject: "*any", RatingPlanActivations: ras}
if err := mysql.SetTPRatingProfiles(TEST_SQL, map[string]*utils.TPRatingProfile{rp.KeyId(): rp}); err != nil {
t.Error(err.Error())
}
@@ -102,7 +102,7 @@ func TestRemoveData(t *testing.T) {
t.Error("Could not store TPRatingProfile")
}
// Remove RatingProfile
if err := mysql.RemTPData(utils.TBL_TP_RATE_PROFILES, rp.TPid, rp.LoadId, rp.Tenant, rp.TOR, rp.Direction, rp.Subject); err != nil {
if err := mysql.RemTPData(utils.TBL_TP_RATE_PROFILES, rp.TPid, rp.LoadId, rp.Tenant, rp.Category, rp.Direction, rp.Subject); err != nil {
t.Error(err.Error())
}
if rps, err := mysql.GetTpRatingProfiles(rp); err != nil {
@@ -158,19 +158,19 @@ func TestSetCdr(t *testing.T) {
}
}
strCdr1 := &utils.StoredCdr{AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
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),
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
strCdr1.CgrId = utils.Sha1(strCdr1.AccId, strCdr1.SetupTime.String())
strCdr2 := &utils.StoredCdr{AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN2", ReqType: "prepaid",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
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),
Duration: time.Duration(12) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 0.201}
strCdr2.CgrId = utils.Sha1(strCdr2.AccId, strCdr2.SetupTime.String())
strCdr3 := &utils.StoredCdr{AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: "rated",
Direction: "*out", Tenant: "itsyscom.com", TOR: "call", Account: "1002", Subject: "1000", Destination: "+4986517174963",
Direction: "*out", Tenant: "itsyscom.com", Category: "call", Account: "1002", Subject: "1000", Destination: "+4986517174963",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC),
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
@@ -188,19 +188,19 @@ func TestSetRatedCdr(t *testing.T) {
return
}
strCdr1 := &utils.StoredCdr{AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
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),
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
strCdr1.CgrId = utils.Sha1(strCdr1.AccId, strCdr1.SetupTime.String())
strCdr2 := &utils.StoredCdr{AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN", ReqType: "prepaid",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
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),
Duration: time.Duration(12) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 0.201}
strCdr2.CgrId = utils.Sha1(strCdr2.AccId, strCdr2.SetupTime.String())
strCdr3 := &utils.StoredCdr{AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: "rated",
Direction: "*out", Tenant: "itsyscom.com", TOR: "call", Account: "1002", Subject: "1002", Destination: "+4986517174964",
Direction: "*out", Tenant: "itsyscom.com", Category: "call", Account: "1002", Subject: "1002", Destination: "+4986517174964",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC),
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: "wholesale_run", Cost: 1.201}

View File

@@ -35,7 +35,7 @@ type TimeSpan struct {
Cost float64
ratingInfo *RatingInfo
RateInterval *RateInterval
CallDuration time.Duration // the call duration so far till TimeEnd
DurationIndex time.Duration // the call duration so far till TimeEnd
Increments Increments
MatchedSubject, MatchedPrefix, MatchedDestId string
}
@@ -43,34 +43,35 @@ type TimeSpan struct {
type Increment struct {
Duration time.Duration
Cost float64
BalanceInfo *BalanceInfo // need more than one for minutes with cost
BalanceInfo *BalanceInfo // need more than one for units with cost
BalanceRateInterval *RateInterval
MinuteInfo *MinuteInfo
UnitInfo *UnitInfo
CompressFactor int
paid bool
}
// Holds the minute information related to a specified timespan
type MinuteInfo struct {
type UnitInfo struct {
DestinationId string
Quantity float64
Tor string
//Price float64
}
func (mi *MinuteInfo) Equal(other *MinuteInfo) bool {
func (mi *UnitInfo) Equal(other *UnitInfo) bool {
return mi.DestinationId == other.DestinationId &&
mi.Quantity == other.Quantity
}
// Holds information about the balance that made a specific payment
type BalanceInfo struct {
MinuteBalanceUuid string
MoneyBalanceUuid string
AccountId string // used when debited from shared balance
UnitBalanceUuid string
MoneyBalanceUuid string
AccountId string // used when debited from shared balance
}
func (bi *BalanceInfo) Equal(other *BalanceInfo) bool {
return bi.MinuteBalanceUuid == other.MinuteBalanceUuid &&
return bi.UnitBalanceUuid == other.UnitBalanceUuid &&
bi.MoneyBalanceUuid == other.MoneyBalanceUuid &&
bi.AccountId == other.AccountId
}
@@ -191,7 +192,7 @@ func (incr *Increment) Clone() *Increment {
Duration: incr.Duration,
Cost: incr.Cost,
BalanceRateInterval: incr.BalanceRateInterval,
MinuteInfo: incr.MinuteInfo,
UnitInfo: incr.UnitInfo,
BalanceInfo: incr.BalanceInfo,
}
return nIncr
@@ -202,7 +203,7 @@ func (incr *Increment) Equal(other *Increment) bool {
incr.Cost == other.Cost &&
((incr.BalanceInfo == nil && other.BalanceInfo == nil) || incr.BalanceInfo.Equal(other.BalanceInfo)) &&
((incr.BalanceRateInterval == nil && other.BalanceRateInterval == nil) || reflect.DeepEqual(incr.BalanceRateInterval, other.BalanceRateInterval)) &&
((incr.MinuteInfo == nil && other.MinuteInfo == nil) || incr.MinuteInfo.Equal(other.MinuteInfo))
((incr.UnitInfo == nil && other.UnitInfo == nil) || incr.UnitInfo.Equal(other.UnitInfo))
}
func (incr *Increment) GetCompressFactor() int {
@@ -317,7 +318,6 @@ a new timespan starting from the end of the received one.
The interval will attach itself to the timespan that overlaps the interval.
*/
func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) {
//Logger.Debug("here: ", ts, " +++ ", i)
// if the span is not in interval return nil
if !(i.Contains(ts.TimeStart, false) || i.Contains(ts.TimeEnd, true)) {
//Logger.Debug("Not in interval")
@@ -328,7 +328,7 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) {
if i.Rating != nil {
i.Rating.Rates.Sort()
for _, rate := range i.Rating.Rates {
// Logger.Debug(fmt.Sprintf("Rate: %+v", rate))
//Logger.Debug(fmt.Sprintf("Rate: %+v", rate))
if ts.GetGroupStart() < rate.GroupIntervalStart && ts.GetGroupEnd() > rate.GroupIntervalStart {
// Logger.Debug(fmt.Sprintf("Splitting"))
ts.SetRateInterval(i)
@@ -340,8 +340,8 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) {
nts.copyRatingInfo(ts)
ts.TimeEnd = splitTime
nts.SetRateInterval(i)
nts.CallDuration = ts.CallDuration
ts.SetNewCallDuration(nts)
nts.DurationIndex = ts.DurationIndex
ts.SetNewDurationIndex(nts)
// Logger.Debug(fmt.Sprintf("Group splitting: %+v %+v", ts, nts))
return
}
@@ -368,8 +368,8 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) {
}
nts.copyRatingInfo(ts)
ts.TimeEnd = splitTime
nts.CallDuration = ts.CallDuration
ts.SetNewCallDuration(nts)
nts.DurationIndex = ts.DurationIndex
ts.SetNewDurationIndex(nts)
// Logger.Debug(fmt.Sprintf("right: %+v %+v", ts, nts))
return
}
@@ -390,8 +390,8 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) {
ts.TimeEnd = splitTime
nts.SetRateInterval(i)
nts.CallDuration = ts.CallDuration
ts.SetNewCallDuration(nts)
nts.DurationIndex = ts.DurationIndex
ts.SetNewDurationIndex(nts)
// Logger.Debug(fmt.Sprintf("left: %+v %+v", ts, nts))
return
}
@@ -410,11 +410,11 @@ func (ts *TimeSpan) SplitByIncrement(index int) *TimeSpan {
TimeEnd: ts.TimeEnd,
}
newTs.copyRatingInfo(ts)
newTs.CallDuration = ts.CallDuration
newTs.DurationIndex = ts.DurationIndex
ts.TimeEnd = timeStart
newTs.Increments = ts.Increments[index:]
ts.Increments = ts.Increments[:index]
ts.SetNewCallDuration(newTs)
ts.SetNewDurationIndex(newTs)
return newTs
}
@@ -430,7 +430,7 @@ func (ts *TimeSpan) SplitByDuration(duration time.Duration) *TimeSpan {
TimeEnd: ts.TimeEnd,
}
newTs.copyRatingInfo(ts)
newTs.CallDuration = ts.CallDuration
newTs.DurationIndex = ts.DurationIndex
ts.TimeEnd = timeStart
// split the increment
for incrIndex, incr := range ts.Increments {
@@ -449,7 +449,7 @@ func (ts *TimeSpan) SplitByDuration(duration time.Duration) *TimeSpan {
break
}
}
ts.SetNewCallDuration(newTs)
ts.SetNewDurationIndex(newTs)
return newTs
}
@@ -463,16 +463,16 @@ func (ts *TimeSpan) SplitByRatingPlan(rp *RatingInfo) (newTs *TimeSpan) {
TimeEnd: ts.TimeEnd,
}
newTs.copyRatingInfo(ts)
newTs.CallDuration = ts.CallDuration
newTs.DurationIndex = ts.DurationIndex
ts.TimeEnd = rp.ActivationTime
ts.SetNewCallDuration(newTs)
ts.SetNewDurationIndex(newTs)
// Logger.Debug(fmt.Sprintf("RP SPLITTING: %+v %+v", ts, newTs))
return
}
// Returns the starting time of this timespan
func (ts *TimeSpan) GetGroupStart() time.Duration {
s := ts.CallDuration - ts.GetDuration()
s := ts.DurationIndex - ts.GetDuration()
if s < 0 {
s = 0
}
@@ -480,16 +480,16 @@ func (ts *TimeSpan) GetGroupStart() time.Duration {
}
func (ts *TimeSpan) GetGroupEnd() time.Duration {
return ts.CallDuration
return ts.DurationIndex
}
// sets the CallDuration attribute to reflect new timespan
func (ts *TimeSpan) SetNewCallDuration(nts *TimeSpan) {
d := ts.CallDuration - nts.GetDuration()
// sets the DurationIndex attribute to reflect new timespan
func (ts *TimeSpan) SetNewDurationIndex(nts *TimeSpan) {
d := ts.DurationIndex - nts.GetDuration()
if d < 0 {
d = 0
}
ts.CallDuration = d
ts.DurationIndex = d
}
func (nts *TimeSpan) copyRatingInfo(ts *TimeSpan) {
@@ -514,6 +514,6 @@ func (ts *TimeSpan) RoundToDuration(duration time.Duration) {
if duration > ts.GetDuration() {
initialDuration := ts.GetDuration()
ts.TimeEnd = ts.TimeStart.Add(duration)
ts.CallDuration = ts.CallDuration + (duration - initialDuration)
ts.DurationIndex = ts.DurationIndex + (duration - initialDuration)
}
}

View File

@@ -265,7 +265,7 @@ func TestTimespanSplitGroupedRates(t *testing.T) {
}
t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 18, 00, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 1800 * time.Second, ratingInfo: &RatingInfo{}}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, DurationIndex: 1800 * time.Second, ratingInfo: &RatingInfo{}}
oldDuration := ts.GetDuration()
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 45, 00, 0, time.UTC)
@@ -313,7 +313,7 @@ func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
}
t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 31, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 60 * time.Second, ratingInfo: &RatingInfo{}}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, DurationIndex: 60 * time.Second, ratingInfo: &RatingInfo{}}
oldDuration := ts.GetDuration()
nts := ts.SplitByRateInterval(i)
cd := &CallDescriptor{}
@@ -395,7 +395,7 @@ func TestTimespanSplitGroupSecondSplit(t *testing.T) {
}
t1 := time.Date(2012, time.February, 3, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 04, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 240 * time.Second, ratingInfo: &RatingInfo{}}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, DurationIndex: 240 * time.Second, ratingInfo: &RatingInfo{}}
oldDuration := ts.GetDuration()
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 01, 00, 0, time.UTC)
@@ -440,7 +440,7 @@ func TestTimespanSplitLong(t *testing.T) {
}
t1 := time.Date(2013, time.October, 9, 9, 0, 0, 0, time.UTC)
t2 := time.Date(2013, time.October, 10, 20, 0, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: t2.Sub(t1), ratingInfo: &RatingInfo{}}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, DurationIndex: t2.Sub(t1), ratingInfo: &RatingInfo{}}
oldDuration := ts.GetDuration()
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2013, time.October, 9, 18, 0, 0, 0, time.UTC)
@@ -472,7 +472,7 @@ func TestTimespanSplitMultipleGroup(t *testing.T) {
}
t1 := time.Date(2012, time.February, 3, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 04, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 240 * time.Second, ratingInfo: &RatingInfo{}}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, DurationIndex: 240 * time.Second, ratingInfo: &RatingInfo{}}
oldDuration := ts.GetDuration()
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 01, 00, 0, time.UTC)
@@ -533,7 +533,7 @@ func TestTimespanExpandingPastEnd(t *testing.T) {
}
}
func TestTimespanExpandingCallDuration(t *testing.T) {
func TestTimespanExpandingDurationIndex(t *testing.T) {
timespans := []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
@@ -736,10 +736,10 @@ func TestTimespanCreateIncrements(t *testing.T) {
func TestTimespanSplitByIncrement(t *testing.T) {
ts := &TimeSpan{
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 31, 00, 0, time.UTC),
CallDuration: 60 * time.Second,
ratingInfo: &RatingInfo{},
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 31, 00, 0, time.UTC),
DurationIndex: 60 * time.Second,
ratingInfo: &RatingInfo{},
RateInterval: &RateInterval{
Rating: &RIRate{
RoundingMethod: utils.ROUNDING_MIDDLE,
@@ -761,8 +761,8 @@ func TestTimespanSplitByIncrement(t *testing.T) {
if ts.GetDuration() != 50*time.Second || newTs.GetDuration() != 10*time.Second {
t.Error("Error spliting by increment: ", ts.GetDuration(), newTs.GetDuration())
}
if ts.CallDuration != 50*time.Second || newTs.CallDuration != 60*time.Second {
t.Error("Error spliting by increment at setting call duration: ", ts.CallDuration, newTs.CallDuration)
if ts.DurationIndex != 50*time.Second || newTs.DurationIndex != 60*time.Second {
t.Error("Error spliting by increment at setting call duration: ", ts.DurationIndex, newTs.DurationIndex)
}
if len(ts.Increments) != 5 || len(newTs.Increments) != 1 {
t.Error("Error spliting increments: ", ts.Increments, newTs.Increments)
@@ -771,9 +771,9 @@ func TestTimespanSplitByIncrement(t *testing.T) {
func TestTimespanSplitByIncrementStart(t *testing.T) {
ts := &TimeSpan{
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 31, 00, 0, time.UTC),
CallDuration: 60 * time.Second,
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 31, 00, 0, time.UTC),
DurationIndex: 60 * time.Second,
RateInterval: &RateInterval{
Rating: &RIRate{
RoundingMethod: utils.ROUNDING_MIDDLE,
@@ -795,8 +795,8 @@ func TestTimespanSplitByIncrementStart(t *testing.T) {
if ts.GetDuration() != 60*time.Second || newTs != nil {
t.Error("Error spliting by increment: ", ts.GetDuration())
}
if ts.CallDuration != 60*time.Second {
t.Error("Error spliting by incrementat setting call duration: ", ts.CallDuration)
if ts.DurationIndex != 60*time.Second {
t.Error("Error spliting by incrementat setting call duration: ", ts.DurationIndex)
}
if len(ts.Increments) != 6 {
t.Error("Error spliting increments: ", ts.Increments)
@@ -805,9 +805,9 @@ func TestTimespanSplitByIncrementStart(t *testing.T) {
func TestTimespanSplitByIncrementEnd(t *testing.T) {
ts := &TimeSpan{
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 31, 00, 0, time.UTC),
CallDuration: 60 * time.Second,
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 31, 00, 0, time.UTC),
DurationIndex: 60 * time.Second,
RateInterval: &RateInterval{
Rating: &RIRate{
RoundingMethod: utils.ROUNDING_MIDDLE,
@@ -829,8 +829,8 @@ func TestTimespanSplitByIncrementEnd(t *testing.T) {
if ts.GetDuration() != 60*time.Second || newTs != nil {
t.Error("Error spliting by increment: ", ts.GetDuration())
}
if ts.CallDuration != 60*time.Second {
t.Error("Error spliting by increment at setting call duration: ", ts.CallDuration)
if ts.DurationIndex != 60*time.Second {
t.Error("Error spliting by increment at setting call duration: ", ts.DurationIndex)
}
if len(ts.Increments) != 6 {
t.Error("Error spliting increments: ", ts.Increments)
@@ -839,10 +839,10 @@ func TestTimespanSplitByIncrementEnd(t *testing.T) {
func TestTimespanSplitByDuration(t *testing.T) {
ts := &TimeSpan{
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 31, 00, 0, time.UTC),
CallDuration: 60 * time.Second,
ratingInfo: &RatingInfo{},
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 31, 00, 0, time.UTC),
DurationIndex: 60 * time.Second,
ratingInfo: &RatingInfo{},
RateInterval: &RateInterval{
Rating: &RIRate{
RoundingMethod: utils.ROUNDING_MIDDLE,
@@ -864,8 +864,8 @@ func TestTimespanSplitByDuration(t *testing.T) {
if ts.GetDuration() != 46*time.Second || newTs.GetDuration() != 14*time.Second {
t.Error("Error spliting by duration: ", ts.GetDuration(), newTs.GetDuration())
}
if ts.CallDuration != 46*time.Second || newTs.CallDuration != 60*time.Second {
t.Error("Error spliting by duration at setting call duration: ", ts.CallDuration, newTs.CallDuration)
if ts.DurationIndex != 46*time.Second || newTs.DurationIndex != 60*time.Second {
t.Error("Error spliting by duration at setting call duration: ", ts.DurationIndex, newTs.DurationIndex)
}
if len(ts.Increments) != 5 || len(newTs.Increments) != 2 {
t.Error("Error spliting increments: ", ts.Increments, newTs.Increments)
@@ -1501,35 +1501,35 @@ func TestTSCompressDecompress(t *testing.T) {
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
},
},
@@ -1553,35 +1553,35 @@ func TestTSMultipleCompressDecompress(t *testing.T) {
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
MinuteInfo: &MinuteInfo{"1", 2.3},
UnitInfo: &UnitInfo{"1", 2.3, MINUTES},
},
},
},

View File

@@ -273,7 +273,7 @@ func (self *TPCSVImporter) importRatingProfiles(fn string) error {
rp := &utils.TPRatingProfile{
LoadId: loadId,
Tenant: tenant,
TOR: tor,
Category: tor,
Direction: direction,
Subject: subject,
RatingPlanActivations: []*utils.TPRatingActivation{

View File

@@ -48,11 +48,11 @@ RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8`
DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5`
ratingPlans := `RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10
RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10`
ratingProfiles := `cgrates.org,call,*out,*any,2013-01-06T00:00:00Z,RP_UK,
cgrates.org,call,*out,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,`
ratingProfiles := `*out,cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_UK,
*out,cgrates.org,call,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,`
sharedGroups := ``
actions := `TOPUP10_AC,*topup_reset,*monetary,*out,10,*unlimited,*any,,10,,,10
TOPUP10_AC1,*topup_reset,*minutes,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10`
TOPUP10_AC1,*topup_reset,*call_duration,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10`
actionPlans := `TOPUP10_AT,TOPUP10_AC,ASAP,10
TOPUP10_AT,TOPUP10_AC1,ASAP,10`
actionTriggers := ``
@@ -135,7 +135,7 @@ func TestExecuteActions(t *testing.T) {
func TestDebit(t *testing.T) {
cd := &engine.CallDescriptor{
Direction: "*out",
TOR: "call",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",

View File

@@ -48,11 +48,11 @@ RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8`
DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5`
ratingPlans := `RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10
RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10`
ratingProfiles := `cgrates.org,call,*out,*any,2013-01-06T00:00:00Z,RP_UK,
cgrates.org,call,*out,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,`
ratingProfiles := `*out,cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_UK,
*out,cgrates.org,call,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,`
sharedGroups := ``
actions := `TOPUP10_AC,*topup_reset,*monetary,*out,0,*unlimited,*any,,10,,,10
TOPUP10_AC1,*topup_reset,*minutes,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10`
TOPUP10_AC1,*topup_reset,*call_duration,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10`
actionPlans := `TOPUP10_AT,TOPUP10_AC,ASAP,10
TOPUP10_AT,TOPUP10_AC1,ASAP,10`
actionTriggers := ``
@@ -135,7 +135,7 @@ func TestExecuteActions2(t *testing.T) {
func TestDebit2(t *testing.T) {
cd := &engine.CallDescriptor{
Direction: "*out",
TOR: "call",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",

View File

@@ -48,10 +48,10 @@ RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8`
DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5`
ratingPlans := `RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10
RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10`
ratingProfiles := `cgrates.org,call,*out,*any,2013-01-06T00:00:00Z,RP_UK,
cgrates.org,call,*out,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,`
ratingProfiles := `*out,cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_UK,
*out,cgrates.org,call,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,`
sharedGroups := ``
actions := `TOPUP10_AC1,*topup_reset,*minutes,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10`
actions := `TOPUP10_AC1,*topup_reset,*call_duration,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10`
actionPlans := `TOPUP10_AT,TOPUP10_AC1,ASAP,10`
actionTriggers := ``
accountActions := `cgrates.org,12345,*out,TOPUP10_AT,`
@@ -131,7 +131,7 @@ func TestExecuteActions3(t *testing.T) {
func TestDebit3(t *testing.T) {
cd := &engine.CallDescriptor{
Direction: "*out",
TOR: "call",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",

View File

@@ -65,16 +65,16 @@ func (self *Mediator) getCostsFromRater(cdr *utils.StoredCdr) (*engine.CallCost,
return cc, nil
}
cd := engine.CallDescriptor{
Direction: "*out", //record[m.directionFields[runIdx]] TODO: fix me
Tenant: cdr.Tenant,
TOR: cdr.TOR,
Subject: cdr.Subject,
Account: cdr.Account,
Destination: cdr.Destination,
TimeStart: cdr.AnswerTime,
TimeEnd: cdr.AnswerTime.Add(cdr.Duration),
LoopIndex: 0,
CallDuration: cdr.Duration,
Direction: "*out", //record[m.directionFields[runIdx]] TODO: fix me
Tenant: cdr.Tenant,
Category: cdr.Category,
Subject: cdr.Subject,
Account: cdr.Account,
Destination: cdr.Destination,
TimeStart: cdr.AnswerTime,
TimeEnd: cdr.AnswerTime.Add(cdr.Duration),
LoopIndex: 0,
DurationIndex: cdr.Duration,
}
if cdr.ReqType == utils.PSEUDOPREPAID {
err = self.connector.Debit(cd, cc)
@@ -114,8 +114,8 @@ func (self *Mediator) RateCdr(dbcdr utils.RawCDR) error {
if err != nil {
return err
}
cdrs := []*utils.StoredCdr{rtCdr} // Start with initial dbcdr, will add here all to be mediated
attrsDC := utils.AttrDerivedChargers{Direction: rtCdr.Direction, Tenant: rtCdr.Tenant, Category: rtCdr.TOR,
cdrs := []*utils.StoredCdr{rtCdr} // Start with initial dbcdr, will add here all to be mediated
attrsDC := utils.AttrDerivedChargers{Tenant: rtCdr.Tenant, Category: rtCdr.Category, Direction: rtCdr.Direction,
Account: rtCdr.Account, Subject: rtCdr.Subject}
var dcs utils.DerivedChargers
if err := self.connector.GetDerivedChargers(attrsDC, &dcs); err != nil {

View File

@@ -32,7 +32,7 @@ type Event interface {
GetAccount(string) string
GetDestination(string) string
GetCallDestNr(string) string
GetTOR(string) string
GetCategory(string) string
GetTenant(string) string
GetReqType(string) string
GetSetupTime(string) (time.Time, error)

View File

@@ -20,12 +20,13 @@ package sessionmanager
import (
"fmt"
"strings"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/fsock"
"strings"
"time"
)
// ToDo: Introduce support for RSRFields
@@ -40,7 +41,7 @@ const (
ACCOUNT = "variable_cgr_account"
DESTINATION = "variable_cgr_destination"
REQTYPE = "variable_cgr_reqtype" //prepaid or postpaid
TOR = "variable_cgr_tor"
Category = "variable_cgr_tor"
UUID = "Unique-ID" // -Unique ID for this call leg
CSTMID = "variable_cgr_tenant"
CALL_DEST_NR = "Caller-Destination-Number"
@@ -126,13 +127,13 @@ func (fsev FSEvent) GetCallDestNr(fieldName string) string {
}
return utils.FirstNonEmpty(fsev[fieldName], fsev[CALL_DEST_NR])
}
func (fsev FSEvent) GetTOR(fieldName string) string {
func (fsev FSEvent) GetCategory(fieldName string) string {
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
} else if fieldName == utils.META_DEFAULT {
return utils.FirstNonEmpty(fsev[TOR], config.CgrConfig().DefaultTOR)
return utils.FirstNonEmpty(fsev[Category], config.CgrConfig().DefaultCategory)
}
return utils.FirstNonEmpty(fsev[fieldName], fsev[TOR], config.CgrConfig().DefaultTOR)
return utils.FirstNonEmpty(fsev[fieldName], fsev[Category], config.CgrConfig().DefaultCategory)
}
func (fsev FSEvent) GetCgrId() string {
setupTime, _ := fsev.GetSetupTime(utils.META_DEFAULT)
@@ -162,7 +163,7 @@ func (fsev FSEvent) MissingParameter() bool {
strings.TrimSpace(fsev.GetSubject(utils.META_DEFAULT)) == "" ||
strings.TrimSpace(fsev.GetAccount(utils.META_DEFAULT)) == "" ||
strings.TrimSpace(fsev.GetDestination(utils.META_DEFAULT)) == "" ||
strings.TrimSpace(fsev.GetTOR(utils.META_DEFAULT)) == "" ||
strings.TrimSpace(fsev.GetCategory(utils.META_DEFAULT)) == "" ||
strings.TrimSpace(fsev.GetUUID()) == "" ||
strings.TrimSpace(fsev.GetTenant(utils.META_DEFAULT)) == "" ||
strings.TrimSpace(fsev.GetCallDestNr(utils.META_DEFAULT)) == ""

View File

@@ -19,10 +19,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package sessionmanager
import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
)
func TestEventCreation(t *testing.T) {
@@ -62,7 +63,7 @@ func TestEventParseStatic(t *testing.T) {
if ev.GetReqType("^test") != "test" ||
ev.GetDirection("^test") != "test" ||
ev.GetTenant("^test") != "test" ||
ev.GetTOR("^test") != "test" ||
ev.GetCategory("^test") != "test" ||
ev.GetAccount("^test") != "test" ||
ev.GetSubject("^test") != "test" ||
ev.GetDestination("^test") != "test" ||
@@ -73,7 +74,7 @@ func TestEventParseStatic(t *testing.T) {
ev.GetReqType("^test") != "test",
ev.GetDirection("^test") != "test",
ev.GetTenant("^test") != "test",
ev.GetTOR("^test") != "test",
ev.GetCategory("^test") != "test",
ev.GetAccount("^test") != "test",
ev.GetSubject("^test") != "test",
ev.GetDestination("^test") != "test",
@@ -111,7 +112,7 @@ Task-Runtime: 1349437318`
if ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetDirection("FreeSWITCH-Hostname") != "*out" ||
ev.GetTenant("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetTOR("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetCategory("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetAccount("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetSubject("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetDestination("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
@@ -122,7 +123,7 @@ Task-Runtime: 1349437318`
ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetDirection("FreeSWITCH-Hostname") != "*out",
ev.GetTenant("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetTOR("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetCategory("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetAccount("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetSubject("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetDestination("FreeSWITCH-Hostname") != "h1.ip-switch.net",
@@ -477,7 +478,7 @@ variable_rtp_audio_rtcp_octet_count: 0
if ev.GetReqType(utils.META_DEFAULT) != utils.PSEUDOPREPAID ||
ev.GetDirection(utils.META_DEFAULT) != "*out" ||
ev.GetTenant(utils.META_DEFAULT) != "cgrates.org" ||
ev.GetTOR(utils.META_DEFAULT) != "call" ||
ev.GetCategory(utils.META_DEFAULT) != "call" ||
ev.GetAccount(utils.META_DEFAULT) != "1003" ||
ev.GetSubject(utils.META_DEFAULT) != "1003" ||
ev.GetDestination(utils.META_DEFAULT) != "1002" ||
@@ -488,7 +489,7 @@ variable_rtp_audio_rtcp_octet_count: 0
ev.GetReqType(utils.META_DEFAULT) != utils.PSEUDOPREPAID,
ev.GetDirection(utils.META_DEFAULT) != "*out",
ev.GetTenant(utils.META_DEFAULT) != "cgrates.org",
ev.GetTOR(utils.META_DEFAULT) != "call",
ev.GetCategory(utils.META_DEFAULT) != "call",
ev.GetAccount(utils.META_DEFAULT) != "1003",
ev.GetSubject(utils.META_DEFAULT) != "1003",
ev.GetDestination(utils.META_DEFAULT) != "1002",

View File

@@ -151,7 +151,7 @@ func (sm *FSSessionManager) OnHeartBeat(ev Event) {
func (sm *FSSessionManager) OnChannelPark(ev Event) {
var maxCallDuration time.Duration // This will be the maximum duration this channel will be allowed to last
var durInitialized bool
attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetTOR(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
var dcs utils.DerivedChargers
if err := sm.connector.GetDerivedChargers(attrsDC, &dcs); err != nil {
@@ -178,7 +178,7 @@ func (sm *FSSessionManager) OnChannelPark(ev Event) {
cd := engine.CallDescriptor{
Direction: ev.GetDirection(dc.DirectionField),
Tenant: ev.GetTenant(dc.TenantField),
TOR: ev.GetTOR(dc.TorField),
Category: ev.GetCategory(dc.TorField),
Subject: ev.GetSubject(dc.SubjectField),
Account: ev.GetAccount(dc.AccountField),
Destination: ev.GetDestination(dc.DestinationField),
@@ -219,7 +219,7 @@ func (sm *FSSessionManager) OnChannelAnswer(ev Event) {
if _, err := fsock.FS.SendApiCmd(fmt.Sprintf("uuid_setvar %s cgr_reqtype %s\n\n", ev.GetUUID(), ev.GetReqType(""))); err != nil {
engine.Logger.Err(fmt.Sprintf("Error on attempting to overwrite cgr_type in chan variables: %v", err))
}
attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetTOR(utils.META_DEFAULT),
attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT),
Direction: ev.GetDirection(utils.META_DEFAULT), Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
var dcs utils.DerivedChargers
if err := sm.connector.GetDerivedChargers(attrsDC, &dcs); err != nil {
@@ -242,7 +242,7 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
sm.RemoveSession(s.uuid) // Unreference it early so we avoid concurrency
}
defer s.Close(ev) // Stop loop and save the costs deducted so far to database
attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetTOR(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
var dcs utils.DerivedChargers
if err := sm.connector.GetDerivedChargers(attrsDC, &dcs); err != nil {
@@ -264,16 +264,16 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
return
}
cd := engine.CallDescriptor{
Direction: ev.GetDirection(dc.DirectionField),
Tenant: ev.GetTenant(dc.TenantField),
TOR: ev.GetTOR(dc.TorField),
Subject: ev.GetSubject(dc.SubjectField),
Account: ev.GetAccount(dc.AccountField),
LoopIndex: 0,
CallDuration: duration,
Destination: ev.GetDestination(dc.DestinationField),
TimeStart: startTime,
TimeEnd: startTime.Add(duration),
Direction: ev.GetDirection(dc.DirectionField),
Tenant: ev.GetTenant(dc.TenantField),
Category: ev.GetCategory(dc.TorField),
Subject: ev.GetSubject(dc.SubjectField),
Account: ev.GetAccount(dc.AccountField),
LoopIndex: 0,
DurationIndex: duration,
Destination: ev.GetDestination(dc.DestinationField),
TimeStart: startTime,
TimeEnd: startTime.Add(duration),
}
cc := &engine.CallCost{}
err = sm.connector.Debit(cd, cc)
@@ -333,7 +333,7 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
cd := &engine.CallDescriptor{
Direction: lastCC.Direction,
Tenant: lastCC.Tenant,
TOR: lastCC.TOR,
Category: lastCC.Category,
Subject: lastCC.Subject,
Account: lastCC.Account,
Destination: lastCC.Destination,

View File

@@ -61,7 +61,7 @@ func NewSession(ev Event, sm SessionManager, dcs utils.DerivedChargers) *Session
cd := &engine.CallDescriptor{
Direction: ev.GetDirection(dc.DirectionField),
Tenant: ev.GetTenant(dc.TenantField),
TOR: ev.GetTOR(dc.TorField),
Category: ev.GetCategory(dc.TorField),
Subject: ev.GetSubject(dc.SubjectField),
Account: ev.GetAccount(dc.AccountField),
Destination: ev.GetDestination(dc.DestinationField),
@@ -97,7 +97,7 @@ func (s *Session) debitLoop(runIdx int) {
}
nextCd.TimeEnd = nextCd.TimeStart.Add(debitPeriod)
nextCd.LoopIndex = index
nextCd.CallDuration += debitPeriod // first presumed duration
nextCd.DurationIndex += debitPeriod // first presumed duration
cc := &engine.CallCost{}
if err := s.sessionManager.MaxDebit(&nextCd, cc); err != nil {
engine.Logger.Err(fmt.Sprintf("Could not complete debit opperation: %v", err))
@@ -112,8 +112,8 @@ func (s *Session) debitLoop(runIdx int) {
s.sessionRuns[runIdx].callCosts = append(s.sessionRuns[runIdx].callCosts, cc)
nextCd.TimeEnd = cc.GetEndTime() // set debited timeEnd
// update call duration with real debited duration
nextCd.CallDuration -= debitPeriod
nextCd.CallDuration += nextCd.GetDuration()
nextCd.DurationIndex -= debitPeriod
nextCd.DurationIndex += nextCd.GetDuration()
time.Sleep(cc.GetDuration())
index++
}
@@ -129,7 +129,7 @@ func (s *Session) Close(ev Event) {
if _, err := ev.GetEndTime(); err != nil {
engine.Logger.Err("Error parsing answer event stop time.")
for idx := range s.sessionRuns {
s.sessionRuns[idx].callDescriptor.TimeEnd = s.sessionRuns[idx].callDescriptor.TimeStart.Add(s.sessionRuns[idx].callDescriptor.CallDuration)
s.sessionRuns[idx].callDescriptor.TimeEnd = s.sessionRuns[idx].callDescriptor.TimeStart.Add(s.sessionRuns[idx].callDescriptor.DurationIndex)
}
}
s.SaveOperations()

View File

@@ -146,14 +146,14 @@ func NewTPRatingProfileFromKeyId(tpid, loadId, keyId string) (*TPRatingProfile,
if len(s) != 4 {
return nil, fmt.Errorf("Cannot parse key %s into RatingProfile", keyId)
}
return &TPRatingProfile{TPid: tpid, LoadId: loadId, Tenant: s[1], TOR: s[2], Direction: s[0], Subject: s[3]}, nil
return &TPRatingProfile{TPid: tpid, LoadId: loadId, Tenant: s[1], Category: s[2], Direction: s[0], Subject: s[3]}, nil
}
type TPRatingProfile struct {
TPid string // Tariff plan id
LoadId string // Gives ability to load specific RatingProfile based on load identifier, hence being able to keep history also in stordb
Tenant string // Tenant's Id
TOR string // TypeOfRecord
Category string // TypeOfRecord
Direction string // Traffic direction, OUT is the only one supported for now
Subject string // Rating subject, usually the same as account
RatingPlanActivations []*TPRatingActivation // Activate rate profiles at specific time
@@ -161,7 +161,7 @@ type TPRatingProfile struct {
// Used as key in nosql db (eg: redis)
func (self *TPRatingProfile) KeyId() string {
return fmt.Sprintf("%s:%s:%s:%s", self.Direction, self.Tenant, self.TOR, self.Subject)
return fmt.Sprintf("%s:%s:%s:%s", self.Direction, self.Tenant, self.Category, self.Subject)
}
type TPRatingActivation struct {
@@ -196,7 +196,7 @@ func FallbackSubjKeys(direction, tenant, tor, fallbackSubjects string) []string
type AttrTPRatingProfileIds struct {
TPid string // Tariff plan id
Tenant string // Tenant's Id
TOR string // TypeOfRecord
Category string // TypeOfRecord
Direction string // Traffic direction
Subject string // Rating subject, usually the same as account
}
@@ -245,6 +245,7 @@ type TPActionTrigger struct {
Direction string // Traffic direction
ThresholdType string // This threshold type
ThresholdValue float64 // Threshold
Recurrent bool // reset executed flag each run
DestinationId string // Id of the destination profile
ActionsId string // Actions which will execute on threshold reached
Weight float64 // weight

View File

@@ -77,8 +77,8 @@ func (cgrCdr CgrCdr) GetDestination() string {
return cgrCdr[DESTINATION]
}
func (cgrCdr CgrCdr) GetTOR() string {
return cgrCdr[TOR]
func (cgrCdr CgrCdr) GetCategory() string {
return cgrCdr[Category]
}
func (cgrCdr CgrCdr) GetTenant() string {
@@ -136,7 +136,7 @@ func (cgrCdr CgrCdr) ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld,
tenantFld = TENANT
}
if torFld == META_DEFAULT {
torFld = TOR
torFld = Category
}
if accountFld == META_DEFAULT {
accountFld = ACCOUNT
@@ -178,8 +178,8 @@ func (cgrCdr CgrCdr) ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld,
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, tenantFld))
}
if strings.HasPrefix(torFld, STATIC_VALUE_PREFIX) {
rtCdr.TOR = torFld[1:]
} else if rtCdr.TOR, hasKey = cgrCdr[torFld]; !hasKey && fieldsMandatory {
rtCdr.Category = torFld[1:]
} else if rtCdr.Category, hasKey = cgrCdr[torFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, torFld))
}
if strings.HasPrefix(accountFld, STATIC_VALUE_PREFIX) {

View File

@@ -54,7 +54,7 @@ func TestCgrCdrFields(t *testing.T) {
if cgrCdr.GetDestination() != "1002" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetTOR() != "call" {
if cgrCdr.GetCategory() != "call" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetTenant() != "cgrates.org" {
@@ -96,7 +96,7 @@ func TestCgrCdrForkCdr(t *testing.T) {
}
setupTime1 := time.Date(2013, 11, 7, 8, 42, 24, 0, time.UTC)
expctSplRatedCdr := &StoredCdr{CgrId: Sha1("dsafdsaf", setupTime1.String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: setupTime1, AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: 10000000000, ExtraFields: map[string]string{}, MediationRunId: "sample_run1", Cost: -1}
if !reflect.DeepEqual(expctSplRatedCdr, rtSampleCdrOut) {
@@ -112,7 +112,7 @@ func TestCgrCdrForkCdr(t *testing.T) {
}
setupTime, _ := ParseTimeDetectLayout("2013-11-07T08:42:24Z")
expctRatedCdr := &StoredCdr{CgrId: Sha1("dsafdsaf", setupTime.String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Unix(1383813744, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: 10000000000, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) {
@@ -124,7 +124,7 @@ func TestCgrCdrForkCdr(t *testing.T) {
t.Error("Unexpected error received", err)
}
expctRatedCdr2 := &StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "postpaid",
Direction: "*in", Tenant: "cgrates.com", TOR: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "1002",
Direction: "*in", Tenant: "cgrates.com", Category: "premium_call", Account: "first_account", Subject: "first_subject", 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), Duration: time.Duration(12) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
@@ -144,7 +144,7 @@ func TestCgrCdrForkCdrFromMetaDefaults(t *testing.T) {
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
setupTime := time.Date(2013, 11, 7, 8, 42, 24, 0, time.UTC)
expctCdr := &StoredCdr{CgrId: Sha1("dsafdsaf", setupTime.String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: setupTime, AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Duration: time.Duration(10) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}

View File

@@ -56,7 +56,7 @@ const (
SHARED_GROUPS_NRCOLS = 4
ACTIONS_NRCOLS = 12
ACTION_PLANS_NRCOLS = 4
ACTION_TRIGGERS_NRCOLS = 8
ACTION_TRIGGERS_NRCOLS = 9
ACCOUNT_ACTIONS_NRCOLS = 5
DERIVED_CHARGERS_NRCOLS = 16
ROUNDING_UP = "*up"
@@ -78,7 +78,7 @@ const (
REQTYPE = "reqtype"
DIRECTION = "direction"
TENANT = "tenant"
TOR = "tor"
Category = "tor"
ACCOUNT = "account"
SUBJECT = "subject"
DESTINATION = "destination"

View File

@@ -40,9 +40,7 @@ func MirrorMap(mapIn map[string]string) (map[string]string, error) {
func MissingMapKeys(inMap map[string]string, requiredKeys []string) []string {
missingKeys := []string{}
for _, reqKey := range requiredKeys {
if val, hasKey := inMap[reqKey]; !hasKey {
missingKeys = append(missingKeys, reqKey)
} else if val == "" {
if val, hasKey := inMap[reqKey]; !hasKey || val == "" {
missingKeys = append(missingKeys, reqKey)
}
}

View File

@@ -22,7 +22,7 @@ import (
"time"
)
var PrimaryCdrFields []string = []string{ACCID, CDRHOST, CDRSOURCE, REQTYPE, DIRECTION, TENANT, TOR, ACCOUNT, SUBJECT, DESTINATION, SETUP_TIME, ANSWER_TIME, DURATION}
var PrimaryCdrFields []string = []string{ACCID, CDRHOST, CDRSOURCE, REQTYPE, DIRECTION, TENANT, Category, ACCOUNT, SUBJECT, DESTINATION, SETUP_TIME, ANSWER_TIME, DURATION}
// RawCDR is the type containing all the original CDR fields, needs it as it is for later usage
type RawCDR interface {
@@ -34,7 +34,7 @@ type RawCDR interface {
GetSubject() string
GetAccount() string
GetDestination() string
GetTOR() string
GetCategory() string
GetTenant() string
GetReqType() string
GetSetupTime() (time.Time, error) // Time when the call was set-up

View File

@@ -35,7 +35,7 @@ func NewStoredCdrFromRawCDR(rawcdr RawCDR) (*StoredCdr, error) {
strCdr.ReqType = rawcdr.GetReqType()
strCdr.Direction = rawcdr.GetDirection()
strCdr.Tenant = rawcdr.GetTenant()
strCdr.TOR = rawcdr.GetTOR()
strCdr.Category = rawcdr.GetCategory()
strCdr.Account = rawcdr.GetAccount()
strCdr.Subject = rawcdr.GetSubject()
strCdr.Destination = rawcdr.GetDestination()
@@ -62,7 +62,7 @@ type StoredCdr struct {
ReqType string
Direction string
Tenant string
TOR string
Category string
Account string
Subject string
Destination string
@@ -108,8 +108,8 @@ func (storedCdr *StoredCdr) GetDestination() string {
return storedCdr.Destination
}
func (storedCdr *StoredCdr) GetTOR() string {
return storedCdr.TOR
func (storedCdr *StoredCdr) GetCategory() string {
return storedCdr.Category
}
func (storedCdr *StoredCdr) GetTenant() string {
@@ -154,7 +154,7 @@ func (storedCdr *StoredCdr) AsRawCdrHttpForm() url.Values {
v.Set(REQTYPE, storedCdr.ReqType)
v.Set(DIRECTION, storedCdr.Direction)
v.Set(TENANT, storedCdr.Tenant)
v.Set(TOR, storedCdr.TOR)
v.Set(Category, storedCdr.Category)
v.Set(ACCOUNT, storedCdr.Account)
v.Set(SUBJECT, storedCdr.Subject)
v.Set(DESTINATION, storedCdr.Destination)
@@ -186,8 +186,8 @@ func (storedCdr *StoredCdr) ExportFieldValue(fldName string) string {
return storedCdr.Direction
case TENANT:
return storedCdr.Tenant
case TOR:
return storedCdr.TOR
case Category:
return storedCdr.Category
case ACCOUNT:
return storedCdr.Account
case SUBJECT:

View File

@@ -35,7 +35,7 @@ func TestNewStoredCdrFromRawCDR(t *testing.T) {
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
setupTime, _ := ParseTimeDetectLayout(cgrCdr["setup_time"])
expctRtCdr := &StoredCdr{CgrId: Sha1(cgrCdr["accid"], setupTime.String()), AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"], ReqType: cgrCdr["reqtype"],
Direction: cgrCdr["direction"], Tenant: cgrCdr["tenant"], TOR: cgrCdr["tor"], Account: cgrCdr["account"], Subject: cgrCdr["subject"],
Direction: cgrCdr["direction"], Tenant: cgrCdr["tenant"], Category: cgrCdr["tor"], Account: cgrCdr["account"], Subject: cgrCdr["subject"],
Destination: cgrCdr["destination"], SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(10) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: DEFAULT_RUNID, Cost: -1}
if rt, err := NewStoredCdrFromRawCDR(cgrCdr); err != nil {
@@ -47,7 +47,7 @@ func TestNewStoredCdrFromRawCDR(t *testing.T) {
func TestStoredCdrFields(t *testing.T) {
ratedCdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Unix(1383813746, 0).String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813746, 0), AnswerTime: time.Unix(1383813746, 0), Duration: 10,
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813746, 0), AnswerTime: time.Unix(1383813746, 0), Duration: 10,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
if ratedCdr.GetCgrId() != Sha1("dsafdsaf", time.Unix(1383813746, 0).String()) {
@@ -71,7 +71,7 @@ func TestStoredCdrFields(t *testing.T) {
if ratedCdr.GetDestination() != "1002" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetTOR() != "call" {
if ratedCdr.GetCategory() != "call" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetTenant() != "cgrates.org" {
@@ -108,7 +108,7 @@ func TestStoredCdrFields(t *testing.T) {
func TestAsRawCdrHttpForm(t *testing.T) {
ratedCdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
cdrForm := ratedCdr.AsRawCdrHttpForm()
@@ -130,8 +130,8 @@ func TestAsRawCdrHttpForm(t *testing.T) {
if cdrForm.Get(TENANT) != ratedCdr.Tenant {
t.Errorf("Expected: %s, received: %s", ratedCdr.Tenant, cdrForm.Get(TENANT))
}
if cdrForm.Get(TOR) != ratedCdr.TOR {
t.Errorf("Expected: %s, received: %s", ratedCdr.TOR, cdrForm.Get(TOR))
if cdrForm.Get(Category) != ratedCdr.Category {
t.Errorf("Expected: %s, received: %s", ratedCdr.Category, cdrForm.Get(Category))
}
if cdrForm.Get(ACCOUNT) != ratedCdr.Account {
t.Errorf("Expected: %s, received: %s", ratedCdr.Account, cdrForm.Get(ACCOUNT))
@@ -161,7 +161,7 @@ func TestAsRawCdrHttpForm(t *testing.T) {
func TestExportFieldValue(t *testing.T) {
cdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "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: DEFAULT_RUNID,
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: DEFAULT_RUNID,
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
if cdr.ExportFieldValue(CGRID) != cdr.CgrId ||
@@ -172,7 +172,7 @@ func TestExportFieldValue(t *testing.T) {
cdr.ExportFieldValue(REQTYPE) != cdr.ReqType ||
cdr.ExportFieldValue(DIRECTION) != cdr.Direction ||
cdr.ExportFieldValue(TENANT) != cdr.Tenant ||
cdr.ExportFieldValue(TOR) != cdr.TOR ||
cdr.ExportFieldValue(Category) != cdr.Category ||
cdr.ExportFieldValue(ACCOUNT) != cdr.Account ||
cdr.ExportFieldValue(SUBJECT) != cdr.Subject ||
cdr.ExportFieldValue(DESTINATION) != cdr.Destination ||