diff --git a/apier/apier.go b/apier/apier.go index 23bdb1f4f..1e8fe901f 100644 --- a/apier/apier.go +++ b/apier/apier.go @@ -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()) diff --git a/apier/apier_local_test.go b/apier/apier_local_test.go index 914567a48..b6c677ccb 100644 --- a/apier/apier_local_test.go +++ b/apier/apier_local_test.go @@ -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", diff --git a/apier/tpratingprofiles.go b/apier/tpratingprofiles.go index fb91b875e..cf5e74a5f 100644 --- a/apier/tpratingprofiles.go +++ b/apier/tpratingprofiles.go @@ -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" diff --git a/apier/tutfscsv_local_test.go b/apier/tutfscsv_local_test.go index 1205b38e4..a2721457d 100644 --- a/apier/tutfscsv_local_test.go +++ b/apier/tutfscsv_local_test.go @@ -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 { diff --git a/apier/tutfsjson_local_test.go b/apier/tutfsjson_local_test.go index d5346ba9b..13c0a6b90 100644 --- a/apier/tutfsjson_local_test.go +++ b/apier/tutfsjson_local_test.go @@ -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", diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go index 1427d357b..dd563a49c 100644 --- a/cdrc/cdrc.go +++ b/cdrc/cdrc.go @@ -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: diff --git a/cdrc/cdrc_test.go b/cdrc/cdrc_test.go index b0cd8d71b..edf20070d 100644 --- a/cdrc/cdrc_test.go +++ b/cdrc/cdrc_test.go @@ -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], diff --git a/cdre/csv_test.go b/cdre/csv_test.go index 309413a4a..0824b792d 100644 --- a/cdre/csv_test.go +++ b/cdre/csv_test.go @@ -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, } diff --git a/cdre/fixedwidth_test.go b/cdre/fixedwidth_test.go index 9cfb01597..8cc7ccad1 100644 --- a/cdre/fixedwidth_test.go +++ b/cdre/fixedwidth_test.go @@ -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, diff --git a/cdrs/fscdr.go b/cdrs/fscdr.go index f6181c4d0..9a6d73d5f 100644 --- a/cdrs/fscdr.go +++ b/cdrs/fscdr.go @@ -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) { diff --git a/cdrs/fscdr_test.go b/cdrs/fscdr_test.go index a403a07f4..ad7de3ec1 100644 --- a/cdrs/fscdr_test.go +++ b/cdrs/fscdr_test.go @@ -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} diff --git a/config/config.go b/config/config.go index c3a8489c7..9fd86237b 100644 --- a/config/config.go +++ b/config/config.go @@ -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") diff --git a/config/config_test.go b/config/config_test.go index 8daf06179..18b022a4d 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -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" diff --git a/config/test_data.txt b/config/test_data.txt index 206d684e1..6ec72b04f 100644 --- a/config/test_data.txt +++ b/config/test_data.txt @@ -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: diff --git a/console/callcost.go b/console/callcost.go index 6507f8213..34651b13a 100644 --- a/console/callcost.go +++ b/console/callcost.go @@ -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 diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 271bd8e55..edea9fd10 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -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`) -); \ No newline at end of file +); diff --git a/data/tariffplans/prepaid1centpsec/ActionTriggers.csv b/data/tariffplans/prepaid1centpsec/ActionTriggers.csv index 22d063dfa..9e3929114 100644 --- a/data/tariffplans/prepaid1centpsec/ActionTriggers.csv +++ b/data/tariffplans/prepaid1centpsec/ActionTriggers.csv @@ -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 diff --git a/engine/account.go b/engine/account.go index 7f393fdc6..feb500efb 100644 --- a/engine/account.go +++ b/engine/account.go @@ -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) } diff --git a/engine/account_test.go b/engine/account_test.go index 547b464cb..6ac5d42cd 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -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) { diff --git a/engine/action.go b/engine/action.go index 84a016429..32ca17de4 100644 --- a/engine/action.go +++ b/engine/action.go @@ -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 diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 676b08a02..bac755d7e 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -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) diff --git a/engine/balances.go b/engine/balances.go index 4a1fd7f50..b2b53c71b 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -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)) diff --git a/engine/callcost.go b/engine/callcost.go index 3dd1a4aab..ac8843b64 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -18,16 +18,17 @@ along with this program. If not, see 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 +} diff --git a/engine/callcost_test.go b/engine/callcost_test.go index 5b8fa78e2..2cda0c4f6 100644 --- a/engine/callcost_test.go +++ b/engine/callcost_test.go @@ -19,14 +19,17 @@ along with this program. If not, see 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)) + } +} diff --git a/engine/calldesc.go b/engine/calldesc.go index 1756fd9f4..d56d4c6e2 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -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(" 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, } } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 535f6a695..1ea99c30d 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -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() diff --git a/engine/datacost.go b/engine/datacost.go new file mode 100644 index 000000000..d584cab5d --- /dev/null +++ b/engine/datacost.go @@ -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 +*/ +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 +} diff --git a/engine/history_test.go b/engine/history_test.go index bcaac39b6..daf5e628a 100644 --- a/engine/history_test.go +++ b/engine/history_test.go @@ -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()) diff --git a/engine/loader_csv.go b/engine/loader_csv.go index 2df37fb32..1f4d8680f 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -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) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 0242267fb..bf128971e 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -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"] diff --git a/engine/loader_db.go b/engine/loader_db.go index 890bab2a8..75ec379a3 100644 --- a/engine/loader_db.go +++ b/engine/loader_db.go @@ -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 diff --git a/engine/ratingplan_test.go b/engine/ratingplan_test.go index 46876af3b..002e4f594 100644 --- a/engine/ratingplan_test.go +++ b/engine/ratingplan_test.go @@ -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)) diff --git a/engine/ratingprofile_test.go b/engine/ratingprofile_test.go index 3a12f63c9..8e64ef99b 100644 --- a/engine/ratingprofile_test.go +++ b/engine/ratingprofile_test.go @@ -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", diff --git a/engine/realcalls_test.go b/engine/realcalls_test.go index cf459464b..fbe353f1d 100644 --- a/engine/realcalls_test.go +++ b/engine/realcalls_test.go @@ -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) diff --git a/engine/responder.go b/engine/responder.go index efef8bbdc..0f0417e88 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -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. */ diff --git a/engine/storage_sql.go b/engine/storage_sql.go index d8cf07987..45663d835 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -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, ×pansJson, &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 } diff --git a/engine/storage_sql_local_test.go b/engine/storage_sql_local_test.go index eca87edf6..3c5b58207 100644 --- a/engine/storage_sql_local_test.go +++ b/engine/storage_sql_local_test.go @@ -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} diff --git a/engine/timespans.go b/engine/timespans.go index f54026028..2420a2ce8 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -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) } } diff --git a/engine/timespans_test.go b/engine/timespans_test.go index cd4c753b9..8dfd85d94 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -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}, }, }, }, diff --git a/engine/tpimporter_csv.go b/engine/tpimporter_csv.go index e5e046ef1..616112a2d 100644 --- a/engine/tpimporter_csv.go +++ b/engine/tpimporter_csv.go @@ -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{ diff --git a/general_tests/ddazmbl1_test.go b/general_tests/ddazmbl1_test.go index c74a2b02e..a8971ac18 100644 --- a/general_tests/ddazmbl1_test.go +++ b/general_tests/ddazmbl1_test.go @@ -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", diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index 70bf91ed4..a90af17ea 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -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", diff --git a/general_tests/ddazmbl3_test.go b/general_tests/ddazmbl3_test.go index 025e38aa3..a5f595ca4 100644 --- a/general_tests/ddazmbl3_test.go +++ b/general_tests/ddazmbl3_test.go @@ -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", diff --git a/mediator/mediator.go b/mediator/mediator.go index cf415e632..5d0b0202d 100644 --- a/mediator/mediator.go +++ b/mediator/mediator.go @@ -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 { diff --git a/sessionmanager/event.go b/sessionmanager/event.go index 721dfd56d..c39c3a8b8 100644 --- a/sessionmanager/event.go +++ b/sessionmanager/event.go @@ -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) diff --git a/sessionmanager/fsevent.go b/sessionmanager/fsevent.go index f3e85a6a9..b0edaa855 100644 --- a/sessionmanager/fsevent.go +++ b/sessionmanager/fsevent.go @@ -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)) == "" diff --git a/sessionmanager/fsevent_test.go b/sessionmanager/fsevent_test.go index 98c58ee36..961b7aeef 100644 --- a/sessionmanager/fsevent_test.go +++ b/sessionmanager/fsevent_test.go @@ -19,10 +19,11 @@ along with this program. If not, see 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", diff --git a/sessionmanager/fssessionmanager.go b/sessionmanager/fssessionmanager.go index aea91ab48..7622cc4f3 100644 --- a/sessionmanager/fssessionmanager.go +++ b/sessionmanager/fssessionmanager.go @@ -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, diff --git a/sessionmanager/session.go b/sessionmanager/session.go index 52a4c2d67..79ff8a4a4 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -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() diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 8c9ba9863..deb754de2 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -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 diff --git a/utils/cgrcdr.go b/utils/cgrcdr.go index 8b1fb3bc7..b0d60d509 100644 --- a/utils/cgrcdr.go +++ b/utils/cgrcdr.go @@ -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) { diff --git a/utils/cgrcdr_test.go b/utils/cgrcdr_test.go index 1a84da7da..888ed49d2 100644 --- a/utils/cgrcdr_test.go +++ b/utils/cgrcdr_test.go @@ -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} diff --git a/utils/consts.go b/utils/consts.go index 0f0ad9c26..e9de5805a 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -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" diff --git a/utils/map.go b/utils/map.go index 94aab386d..d996bdb99 100644 --- a/utils/map.go +++ b/utils/map.go @@ -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) } } diff --git a/utils/rawcdr.go b/utils/rawcdr.go index 11f2815d3..533467cec 100644 --- a/utils/rawcdr.go +++ b/utils/rawcdr.go @@ -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 diff --git a/utils/storedcdr.go b/utils/storedcdr.go index 7f1180165..0d799567c 100644 --- a/utils/storedcdr.go +++ b/utils/storedcdr.go @@ -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: diff --git a/utils/storedcdr_test.go b/utils/storedcdr_test.go index 03b0fa03b..99a50367b 100644 --- a/utils/storedcdr_test.go +++ b/utils/storedcdr_test.go @@ -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 ||