From abbfd8270c425f4ce1218e86282dddc51de53293 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 5 Oct 2015 08:33:27 +0200 Subject: [PATCH] Rated and Cost fields inside derived charging --- .../mysql/create_tariffplan_tables.sql | 2 ++ .../postgres/create_tariffplan_tables.sql | 2 ++ .../prepaid1centpsec/DerivedChargers.csv | 10 +++---- data/tariffplans/tutorial/DerivedChargers.csv | 4 +-- engine/cdrs.go | 10 +++++-- engine/loader_csv_test.go | 12 ++++---- engine/model_converters.go | 2 ++ engine/model_helpers.go | 2 ++ engine/model_helpers_test.go | 8 ++++-- engine/models.go | 2 ++ engine/responder_test.go | 2 +- engine/storedcdr.go | 28 ++++++++++++++++++- engine/storedcdr_test.go | 25 +++++++++-------- engine/tp_reader.go | 2 +- utils/apitpdata.go | 2 ++ utils/derivedchargers.go | 25 +++++++++++++++-- utils/derivedchargers_test.go | 14 ++++++++-- 17 files changed, 114 insertions(+), 38 deletions(-) diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 786fc2ec3..08ebd76b4 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -296,6 +296,8 @@ CREATE TABLE tp_derived_chargers ( `usage_field` varchar(24) NOT NULL, `supplier_field` varchar(24) NOT NULL, `disconnect_cause_field` varchar(24) NOT NULL, + `rated_field` varchar(24) NOT NULL, + `cost_field` varchar(24) NOT NULL, `created_at` TIMESTAMP, PRIMARY KEY (`id`), KEY `tpid` (`tpid`) diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index f530064a0..0f0485d02 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -291,6 +291,8 @@ CREATE TABLE tp_derived_chargers ( usage_field VARCHAR(24) NOT NULL, supplier_field VARCHAR(24) NOT NULL, disconnect_cause_field VARCHAR(24) NOT NULL, + rated_field VARCHAR(24) NOT NULL, + cost_field VARCHAR(24) NOT NULL, created_at TIMESTAMP ); CREATE INDEX tpderivedchargers_tpid_idx ON tp_derived_chargers (tpid); diff --git a/data/tariffplans/prepaid1centpsec/DerivedChargers.csv b/data/tariffplans/prepaid1centpsec/DerivedChargers.csv index 2de4ca05b..4e59e4ef0 100644 --- a/data/tariffplans/prepaid1centpsec/DerivedChargers.csv +++ b/data/tariffplans/prepaid1centpsec/DerivedChargers.csv @@ -1,5 +1,5 @@ -#Direction[0],Tenant[1],Category[2],Account[3],Subject[4],RunId[5],RunFilter[6],ReqTypeField[7],DirectionField[8],TenantField[9],CategoryField[10],AccountField[11],SubjectField[12],DestinationField[13],SetupTimeField[14],PddField[15],AnswerTimeField[16],UsageField[17],SupplierField[18],DisconnectCause[19] -*out,cgrates.org,call,dan,dan,extra1,,^prepaid,,,,^rif,^rif,,,,,^1s,*default,*default -*out,cgrates.org,call,dan,dan,extra2,,,,,,^ivo,^ivo,,,,,,*default,*default -*out,cgrates.org,call,dan,dan,extra3,~filterhdr1:s/(.+)/special_run3/,,,,,^runusr3,^runusr3,,,,,,*default,*default -*out,cgrates.org,call,dan,*any,extra1,,,,,,^rif2,^rif2,,,,,,*default,*default +#Direction[0],Tenant[1],Category[2],Account[3],Subject[4],RunId[5],RunFilter[6],ReqTypeField[7],DirectionField[8],TenantField[9],CategoryField[10],AccountField[11],SubjectField[12],DestinationField[13],SetupTimeField[14],PddField[15],AnswerTimeField[16],UsageField[17],SupplierField[18],DisconnectCause[19],RatedField[20],CostField[21] +*out,cgrates.org,call,dan,dan,extra1,,^prepaid,,,,^rif,^rif,,,,,^1s,*default,*default,*default,*default +*out,cgrates.org,call,dan,dan,extra2,,,,,,^ivo,^ivo,,,,,,*default,*default,*default,*default +*out,cgrates.org,call,dan,dan,extra3,~filterhdr1:s/(.+)/special_run3/,,,,,^runusr3,^runusr3,,,,,,*default,*default,*default,*default +*out,cgrates.org,call,dan,*any,extra1,,,,,,^rif2,^rif2,,,,,,*default,*default,*default,*default diff --git a/data/tariffplans/tutorial/DerivedChargers.csv b/data/tariffplans/tutorial/DerivedChargers.csv index 460ea02d8..cd1f4a847 100644 --- a/data/tariffplans/tutorial/DerivedChargers.csv +++ b/data/tariffplans/tutorial/DerivedChargers.csv @@ -1,2 +1,2 @@ -#Direction[0],Tenant[1],Category[2],Account[3],Subject[4],RunId[5],RunFilter[6],ReqTypeField[7],DirectionField[8],TenantField[9],CategoryField[10],AccountField[11],SubjectField[12],DestinationField[13],SetupTimeField[14],PddField[15],AnswerTimeField[16],UsageField[17],SupplierField[18],DisconnectCause[19] -*out,cgrates.org,call,1001,1001,derived_run1,,^*rated,*default,*default,*default,*default,^1002,*default,*default,*default,*default,*default,*default,*default +#Direction[0],Tenant[1],Category[2],Account[3],Subject[4],RunId[5],RunFilter[6],ReqTypeField[7],DirectionField[8],TenantField[9],CategoryField[10],AccountField[11],SubjectField[12],DestinationField[13],SetupTimeField[14],PddField[15],AnswerTimeField[16],UsageField[17],SupplierField[18],DisconnectCause[19],RatedField[20],CostField[21] +*out,cgrates.org,call,1001,1001,derived_run1,,^*rated,*default,*default,*default,*default,^1002,*default,*default,*default,*default,*default,*default,*default,*default,*default diff --git a/engine/cdrs.go b/engine/cdrs.go index 58bf8b8ad..d9226c5e0 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -274,13 +274,18 @@ func (self *CdrServer) deriveCdrs(storedCdr *StoredCdr) ([]*StoredCdr, error) { dcATimeFld, _ := utils.NewRSRField(dc.AnswerTimeField) dcDurFld, _ := utils.NewRSRField(dc.UsageField) dcSupplFld, _ := utils.NewRSRField(dc.SupplierField) - dcDCausseld, _ := utils.NewRSRField(dc.DisconnectCauseField) + dcDCauseFld, _ := utils.NewRSRField(dc.DisconnectCauseField) + dcRatedFld, _ := utils.NewRSRField(dc.RatedField) + dcCostFld, _ := utils.NewRSRField(dc.CostField) forkedCdr, err := storedCdr.ForkCdr(dc.RunId, dcReqTypeFld, dcDirFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld, - dcSTimeFld, dcPddFld, dcATimeFld, dcDurFld, dcSupplFld, dcDCausseld, []*utils.RSRField{}, true, self.cgrCfg.DefaultTimezone) + dcSTimeFld, dcPddFld, dcATimeFld, dcDurFld, dcSupplFld, dcDCauseFld, dcRatedFld, dcCostFld, []*utils.RSRField{}, true, self.cgrCfg.DefaultTimezone) if err != nil { utils.Logger.Err(fmt.Sprintf("Could not fork CGR with cgrid %s, run: %s, error: %s", storedCdr.CgrId, dc.RunId, err.Error())) continue // do not add it to the forked CDR list } + if !forkedCdr.Rated { + forkedCdr.Cost = -1.0 // Make sure that un-rated CDRs start with Cost -1 + } cdrRuns = append(cdrRuns, forkedCdr) } return cdrRuns, nil @@ -365,7 +370,6 @@ func (self *CdrServer) replicateCdr(cdr *StoredCdr) error { case utils.META_HTTP_POST: errChan := make(chan error) go func(cdr *StoredCdr, rplCfg *config.CdrReplicationCfg, errChan chan error) { - utils.Logger.Debug(fmt.Sprintf("Replicating CDR: %+v, attempts: %d", cdr, rplCfg.Attempts)) fallbackPath := path.Join(self.cgrCfg.HttpFailedDir, fmt.Sprintf("cdr_%s_%s_%s.form", rplCfg.Transport, rplCfg.Server, utils.GenUUID())) if _, err := utils.HttpPoster(rplCfg.Server, self.cgrCfg.HttpSkipTlsVerify, cdr.AsHttpForm(), utils.CONTENT_FORM, rplCfg.Attempts, fallbackPath); err != nil { utils.Logger.Err(fmt.Sprintf(" Replicating CDR: %+v, got error: %s", cdr, err.Error())) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 2f271af3d..794836b6c 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -203,10 +203,10 @@ cgrates.org,alodis,*out,TOPUP_EMPTY_AT,,true,true ` derivedCharges = ` -#Direction,Tenant,Category,Account,Subject,RunId,RunFilter,ReqTypeField,DirectionField,TenantField,TorField,AccountField,SubjectField,DestinationField,SetupTimeField,PddField,AnswerTimeField,UsageField -*out,cgrates.org,call,dan,dan,extra1,^filteredHeader1/filterValue1/,^prepaid,,,,rif,rif,,,,,,, -*out,cgrates.org,call,dan,dan,extra2,,,,,,ivo,ivo,,,,,,, -*out,cgrates.org,call,dan,*any,extra1,,,,,,rif2,rif2,,,,,,, +#Direction,Tenant,Category,Account,Subject,RunId,RunFilter,ReqTypeField,DirectionField,TenantField,TorField,AccountField,SubjectField,DestinationField,SetupTimeField,PddField,AnswerTimeField,UsageField,SupplierField,DisconnectCauseField,CostField,RatedField +*out,cgrates.org,call,dan,dan,extra1,^filteredHeader1/filterValue1/,^prepaid,,,,rif,rif,,,,,,,,, +*out,cgrates.org,call,dan,dan,extra2,,,,,,ivo,ivo,,,,,,,,, +*out,cgrates.org,call,dan,*any,extra1,,,,,,rif2,rif2,,,,,,,,, ` cdrStats = ` #Id[0],QueueLength[1],TimeWindow[2],SaveInterval[3],Metric[4],SetupInterval[5],TOR[6],CdrHost[7],CdrSource[8],ReqType[9],Direction[10],Tenant[11],Category[12],Account[13],Subject[14],DestinationPrefix[15],PddInterval[16],UsageInterval[17],Supplier[18],DisconnectCause[19],MediationRunIds[20],RatedAccount[21],RatedSubject[22],CostInterval[23],Triggers[24] @@ -1058,11 +1058,11 @@ func TestLoadDerivedChargers(t *testing.T) { &utils.DerivedCharger{RunId: "extra1", RunFilters: "^filteredHeader1/filterValue1/", ReqTypeField: "^prepaid", DirectionField: utils.META_DEFAULT, TenantField: utils.META_DEFAULT, CategoryField: utils.META_DEFAULT, AccountField: "rif", SubjectField: "rif", DestinationField: utils.META_DEFAULT, SetupTimeField: utils.META_DEFAULT, PddField: utils.META_DEFAULT, AnswerTimeField: utils.META_DEFAULT, UsageField: utils.META_DEFAULT, - SupplierField: utils.META_DEFAULT, DisconnectCauseField: utils.META_DEFAULT}, + SupplierField: utils.META_DEFAULT, DisconnectCauseField: utils.META_DEFAULT, CostField: utils.META_DEFAULT, RatedField: utils.META_DEFAULT}, &utils.DerivedCharger{RunId: "extra2", ReqTypeField: utils.META_DEFAULT, DirectionField: utils.META_DEFAULT, TenantField: utils.META_DEFAULT, CategoryField: utils.META_DEFAULT, AccountField: "ivo", SubjectField: "ivo", DestinationField: utils.META_DEFAULT, SetupTimeField: utils.META_DEFAULT, PddField: utils.META_DEFAULT, AnswerTimeField: utils.META_DEFAULT, UsageField: utils.META_DEFAULT, - SupplierField: utils.META_DEFAULT, DisconnectCauseField: utils.META_DEFAULT}, + SupplierField: utils.META_DEFAULT, DisconnectCauseField: utils.META_DEFAULT, CostField: utils.META_DEFAULT, RatedField: utils.META_DEFAULT}, } keyCharger1 := utils.DerivedChargersKey("*out", "cgrates.org", "call", "dan", "dan") diff --git a/engine/model_converters.go b/engine/model_converters.go index 6035328f6..48afbe762 100644 --- a/engine/model_converters.go +++ b/engine/model_converters.go @@ -302,6 +302,8 @@ func APItoModelDerivedCharger(dcs *utils.TPDerivedChargers) (result []TpDerivedC UsageField: dc.UsageField, SupplierField: dc.SupplierField, DisconnectCauseField: dc.DisconnectCauseField, + CostField: dc.CostField, + RatedField: dc.RatedField, }) } if len(dcs.DerivedChargers) == 0 { diff --git a/engine/model_helpers.go b/engine/model_helpers.go index ae28cb119..c1adc5a85 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -494,6 +494,8 @@ func (tps TpDerivedChargers) GetDerivedChargers() (map[string]*utils.TPDerivedCh UsageField: ValueOrDefault(tpDcMdl.UsageField, utils.META_DEFAULT), SupplierField: ValueOrDefault(tpDcMdl.SupplierField, utils.META_DEFAULT), DisconnectCauseField: ValueOrDefault(tpDcMdl.DisconnectCauseField, utils.META_DEFAULT), + CostField: ValueOrDefault(tpDcMdl.CostField, utils.META_DEFAULT), + RatedField: ValueOrDefault(tpDcMdl.RatedField, utils.META_DEFAULT), } dcs[tag].DerivedChargers = append(dcs[tag].DerivedChargers, nDc) } diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 62d50ad80..e19b13da3 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -468,6 +468,8 @@ func TestTPDerivedChargersAsExportSlice(t *testing.T) { UsageField: utils.META_DEFAULT, SupplierField: utils.META_DEFAULT, DisconnectCauseField: utils.META_DEFAULT, + CostField: utils.META_DEFAULT, + RatedField: utils.META_DEFAULT, }, &utils.TPDerivedCharger{ RunId: "derived_run2", @@ -485,14 +487,16 @@ func TestTPDerivedChargersAsExportSlice(t *testing.T) { UsageField: utils.META_DEFAULT, SupplierField: utils.META_DEFAULT, DisconnectCauseField: utils.META_DEFAULT, + RatedField: utils.META_DEFAULT, + CostField: utils.META_DEFAULT, }, }, } expectedSlc := [][]string{ []string{"*out", "cgrates.org", "call", "1001", "1001", - "derived_run1", "", "^rated", utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, "^1002", utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT}, + "derived_run1", "", "^rated", utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, "^1002", utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT}, []string{"*out", "cgrates.org", "call", "1001", "1001", - "derived_run2", "", "^rated", utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, "^1002", utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT}, + "derived_run2", "", "^rated", utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, "^1002", utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT, utils.META_DEFAULT}, } ms := APItoModelDerivedCharger(dcs) var slc [][]string diff --git a/engine/models.go b/engine/models.go index 3407546d2..c2698ce30 100644 --- a/engine/models.go +++ b/engine/models.go @@ -272,6 +272,8 @@ type TpDerivedCharger struct { UsageField string `index:"17" re:"\*default\s*|[~^]*[0-9A-Za-z_/:().+]+\s*"` SupplierField string `index:"18" re:"\*default\s*|[~^]*[0-9A-Za-z_/:().+]+\s*"` DisconnectCauseField string `index:"19" re:"\*default\s*|[~^]*[0-9A-Za-z_/:().+]+\s*"` + RatedField string `index:"20" re:"\*default\s*|[~^]*[0-9A-Za-z_/:().+]+\s*"` + CostField string `index:"21" re:"\*default\s*|[~^]*[0-9A-Za-z_/:().+]+\s*"` CreatedAt time.Time } diff --git a/engine/responder_test.go b/engine/responder_test.go index d57958d3a..b7c4db05e 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -131,7 +131,7 @@ func TestGetSessionRuns(t *testing.T) { dfDC := &utils.DerivedCharger{RunId: utils.DEFAULT_RUNID, ReqTypeField: utils.META_DEFAULT, DirectionField: utils.META_DEFAULT, TenantField: utils.META_DEFAULT, CategoryField: utils.META_DEFAULT, AccountField: utils.META_DEFAULT, SubjectField: utils.META_DEFAULT, DestinationField: utils.META_DEFAULT, SetupTimeField: utils.META_DEFAULT, PddField: utils.META_DEFAULT, AnswerTimeField: utils.META_DEFAULT, UsageField: utils.META_DEFAULT, SupplierField: utils.META_DEFAULT, - DisconnectCauseField: utils.META_DEFAULT} + DisconnectCauseField: utils.META_DEFAULT, CostField: utils.META_DEFAULT, RatedField: utils.META_DEFAULT} extra1DC := &utils.DerivedCharger{RunId: "extra1", ReqTypeField: "^" + utils.META_PREPAID, DirectionField: utils.META_DEFAULT, TenantField: utils.META_DEFAULT, CategoryField: "^0", AccountField: "^minitsboy", SubjectField: "^rif", DestinationField: "^0256", SetupTimeField: utils.META_DEFAULT, PddField: utils.META_DEFAULT, AnswerTimeField: utils.META_DEFAULT, UsageField: utils.META_DEFAULT, SupplierField: utils.META_DEFAULT} diff --git a/engine/storedcdr.go b/engine/storedcdr.go index a398ad714..2d738642f 100644 --- a/engine/storedcdr.go +++ b/engine/storedcdr.go @@ -183,6 +183,8 @@ func (storedCdr *StoredCdr) FieldAsString(rsrFld *utils.RSRField) string { return rsrFld.ParseValue(storedCdr.RatedAccount) case utils.RATED_SUBJECT: return rsrFld.ParseValue(storedCdr.RatedSubject) + case utils.RATED_FLD: + return rsrFld.ParseValue(strconv.FormatBool(storedCdr.Rated)) case utils.COST: return rsrFld.ParseValue(strconv.FormatFloat(storedCdr.Cost, 'f', -1, 64)) // Recommended to use FormatCost case utils.COST_DETAILS: @@ -270,7 +272,7 @@ func (storedCdr *StoredCdr) AsHttpForm() url.Values { // Used in mediation, primaryMandatory marks whether missing field out of request represents error or can be ignored func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tenantFld, categFld, accountFld, subjectFld, destFld, setupTimeFld, pddFld, - answerTimeFld, durationFld, supplierFld, disconnectCauseFld *utils.RSRField, + answerTimeFld, durationFld, supplierFld, disconnectCauseFld, ratedFld, costFld *utils.RSRField, extraFlds []*utils.RSRField, primaryMandatory bool, timezone string) (*StoredCdr, error) { if reqTypeFld == nil { reqTypeFld, _ = utils.NewRSRField(utils.META_DEFAULT) @@ -350,6 +352,18 @@ func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tena if disconnectCauseFld.Id == utils.META_DEFAULT { disconnectCauseFld.Id = utils.DISCONNECT_CAUSE } + if ratedFld == nil { + ratedFld, _ = utils.NewRSRField(utils.META_DEFAULT) + } + if ratedFld.Id == utils.META_DEFAULT { + ratedFld.Id = utils.RATED_FLD + } + if costFld == nil { + costFld, _ = utils.NewRSRField(utils.META_DEFAULT) + } + if costFld.Id == utils.META_DEFAULT { + costFld.Id = utils.COST + } var err error frkStorCdr := new(StoredCdr) frkStorCdr.CgrId = storedCdr.CgrId @@ -413,6 +427,18 @@ func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tena } frkStorCdr.Supplier = storedCdr.FieldAsString(supplierFld) frkStorCdr.DisconnectCause = storedCdr.FieldAsString(disconnectCauseFld) + ratedStr := storedCdr.FieldAsString(ratedFld) + if primaryMandatory && len(ratedStr) == 0 { + return nil, utils.NewErrMandatoryIeMissing(utils.RATED_FLD, ratedFld.Id) + } else if frkStorCdr.Rated, err = strconv.ParseBool(ratedStr); err != nil { + return nil, err + } + costStr := storedCdr.FieldAsString(costFld) + if primaryMandatory && len(costStr) == 0 { + return nil, utils.NewErrMandatoryIeMissing(utils.COST, costFld.Id) + } else if frkStorCdr.Cost, err = strconv.ParseFloat(costStr, 64); err != nil { + return nil, err + } frkStorCdr.ExtraFields = make(map[string]string, len(extraFlds)) for _, fld := range extraFlds { frkStorCdr.ExtraFields[fld.Id] = storedCdr.FieldAsString(fld) diff --git a/engine/storedcdr_test.go b/engine/storedcdr_test.go index 47b4f3cd6..8345871bd 100644 --- a/engine/storedcdr_test.go +++ b/engine/storedcdr_test.go @@ -370,7 +370,7 @@ func TestStoredCdrForkCdr(t *testing.T) { rtSampleCdrOut, err := storCdr.ForkCdr("sample_run1", &utils.RSRField{Id: utils.REQTYPE}, &utils.RSRField{Id: utils.DIRECTION}, &utils.RSRField{Id: utils.TENANT}, &utils.RSRField{Id: utils.CATEGORY}, &utils.RSRField{Id: utils.ACCOUNT}, &utils.RSRField{Id: utils.SUBJECT}, &utils.RSRField{Id: utils.DESTINATION}, &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, - &utils.RSRField{Id: utils.SUPPLIER}, &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, + &utils.RSRField{Id: utils.SUPPLIER}, &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, &utils.RSRField{Id: utils.RATED_FLD}, &utils.RSRField{Id: utils.COST}, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true, "") if err != nil { t.Error("Unexpected error received", err) @@ -379,7 +379,7 @@ func TestStoredCdrForkCdr(t *testing.T) { Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), Pdd: time.Duration(200) * time.Millisecond, AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10) * time.Second, Supplier: "suppl1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "field_extr2": "valextr2"}, - MediationRunId: "sample_run1", Cost: -1} + MediationRunId: "sample_run1", Rated: false, Cost: 1.01} if !reflect.DeepEqual(expctSplRatedCdr, rtSampleCdrOut) { t.Errorf("Expected: %v, received: %v", expctSplRatedCdr, rtSampleCdrOut) } @@ -404,8 +404,10 @@ func TestStoredCdrForkCdrStaticVals(t *testing.T) { rsrStSuppl, _ := utils.NewRSRField("^supplier1") rsrStDCause, _ := utils.NewRSRField("^HANGUP_COMPLETE") rsrPdd, _ := utils.NewRSRField("^3") + rsrStRated, _ := utils.NewRSRField("^true") + rsrStCost, _ := utils.NewRSRField("^1.2") rtCdrOut2, err := storCdr.ForkCdr("wholesale_run", rsrStPostpaid, rsrStIn, rsrStCgr, rsrStPC, rsrStFA, rsrStFS, &utils.RSRField{Id: utils.DESTINATION}, - rsrStST, rsrPdd, rsrStAT, rsrStDur, rsrStSuppl, rsrStDCause, []*utils.RSRField{}, true, "") + rsrStST, rsrPdd, rsrStAT, rsrStDur, rsrStSuppl, rsrStDCause, rsrStRated, rsrStCost, []*utils.RSRField{}, true, "") if err != nil { t.Error("Unexpected error received", err) } @@ -413,8 +415,8 @@ func TestStoredCdrForkCdrStaticVals(t *testing.T) { 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), Usage: time.Duration(12) * time.Second, Pdd: time.Duration(3) * time.Second, - Supplier: "supplier1", DisconnectCause: "HANGUP_COMPLETE", - ExtraFields: map[string]string{}, MediationRunId: "wholesale_run", Cost: -1} + Supplier: "supplier1", DisconnectCause: "HANGUP_COMPLETE", Rated: true, Cost: 1.2, + ExtraFields: map[string]string{}, MediationRunId: "wholesale_run"} if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) { t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2) } @@ -422,7 +424,7 @@ func TestStoredCdrForkCdrStaticVals(t *testing.T) { &utils.RSRField{Id: utils.TOR}, &utils.RSRField{Id: utils.ACCOUNT}, &utils.RSRField{Id: utils.SUBJECT}, &utils.RSRField{Id: utils.DESTINATION}, &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, &utils.RSRField{Id: utils.SUPPLIER}, - &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, []*utils.RSRField{}, true, "") + &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, &utils.RSRField{Id: utils.RATED_FLD}, &utils.RSRField{Id: utils.COST}, []*utils.RSRField{}, true, "") if err == nil { t.Error("Failed to detect missing header") } @@ -433,17 +435,18 @@ func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) { CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", 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), MediationRunId: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, Pdd: time.Duration(4) * time.Second, Supplier: "SUPPL3", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, + Usage: time.Duration(10) * time.Second, Pdd: time.Duration(4) * time.Second, Supplier: "SUPPL3", + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, } expctCdr := &StoredCdr{CgrId: storCdr.CgrId, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", 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), - Usage: time.Duration(10) * time.Second, Pdd: time.Duration(4) * time.Second, Supplier: "SUPPL3", - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1} + Usage: time.Duration(10) * time.Second, Pdd: time.Duration(4) * time.Second, Supplier: "SUPPL3", Cost: 1.01, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run"} cdrOut, err := storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, - &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, + &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true, "") if err != nil { t.Fatal("Unexpected error received", err) @@ -453,7 +456,7 @@ func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) { t.Errorf("Expected: %v, received: %v", expctCdr, cdrOut) } // Should also accept nil as defaults - if cdrOut, err := storCdr.ForkCdr("wholesale_run", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + if cdrOut, err := storCdr.ForkCdr("wholesale_run", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true, ""); err != nil { t.Fatal("Unexpected error received", err) } else if !reflect.DeepEqual(expctCdr, cdrOut) { diff --git a/engine/tp_reader.go b/engine/tp_reader.go index ade32bdba..2de1adab4 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -895,7 +895,7 @@ func (tpr *TpReader) LoadDerivedChargersFiltered(filter *TpDerivedCharger, save for _, tpDc := range tpDcs.DerivedChargers { dc, err := utils.NewDerivedCharger(tpDc.RunId, tpDc.RunFilters, tpDc.ReqTypeField, tpDc.DirectionField, tpDc.TenantField, tpDc.CategoryField, tpDc.AccountField, tpDc.SubjectField, tpDc.DestinationField, tpDc.SetupTimeField, tpDc.PddField, tpDc.AnswerTimeField, tpDc.UsageField, tpDc.SupplierField, - tpDc.DisconnectCauseField) + tpDc.DisconnectCauseField, tpDc.RatedField, tpDc.CostField) if err != nil { return err } diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 20ca8a813..69442c171 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -446,6 +446,8 @@ type TPDerivedCharger struct { UsageField string SupplierField string DisconnectCauseField string + CostField string + RatedField string } type TPActionPlan struct { diff --git a/utils/derivedchargers.go b/utils/derivedchargers.go index a88eaa7a8..e20847b7e 100644 --- a/utils/derivedchargers.go +++ b/utils/derivedchargers.go @@ -24,7 +24,8 @@ import ( ) // Wraps regexp compiling in case of rsr fields -func NewDerivedCharger(runId, runFilters, reqTypeFld, dirFld, tenantFld, catFld, acntFld, subjFld, dstFld, sTimeFld, pddFld, aTimeFld, durFld, supplFld, dCauseFld string) (dc *DerivedCharger, err error) { +func NewDerivedCharger(runId, runFilters, reqTypeFld, dirFld, tenantFld, catFld, acntFld, subjFld, dstFld, sTimeFld, pddFld, aTimeFld, durFld, + supplFld, dCauseFld, ratedFld, costFld string) (dc *DerivedCharger, err error) { if len(runId) == 0 { return nil, errors.New("Empty run id field") } @@ -113,6 +114,18 @@ func NewDerivedCharger(runId, runFilters, reqTypeFld, dirFld, tenantFld, catFld, return nil, err } } + dc.RatedField = ratedFld + if strings.HasPrefix(dc.RatedField, REGEXP_PREFIX) || strings.HasPrefix(dc.RatedField, STATIC_VALUE_PREFIX) { + if dc.rsrRatedField, err = NewRSRField(dc.RatedField); err != nil { + return nil, err + } + } + dc.CostField = costFld + if strings.HasPrefix(dc.CostField, REGEXP_PREFIX) || strings.HasPrefix(dc.CostField, STATIC_VALUE_PREFIX) { + if dc.rsrCostField, err = NewRSRField(dc.CostField); err != nil { + return nil, err + } + } return dc, nil } @@ -132,6 +145,8 @@ type DerivedCharger struct { UsageField string // Field containing usage information SupplierField string // Field containing supplier information DisconnectCauseField string // Field containing disconnect cause information + CostField string // Field containing cost information + RatedField string // Field marking rated request in CDR rsrRunFilters []*RSRField // Storage for compiled Regexp in case of RSRFields rsrReqTypeField *RSRField rsrDirectionField *RSRField @@ -146,6 +161,8 @@ type DerivedCharger struct { rsrUsageField *RSRField rsrSupplierField *RSRField rsrDisconnectCauseField *RSRField + rsrCostField *RSRField + rsrRatedField *RSRField } func DerivedChargersKey(direction, tenant, category, account, subject string) string { @@ -169,7 +186,7 @@ func (dcs DerivedChargers) Append(dc *DerivedCharger) (DerivedChargers, error) { func (dcs DerivedChargers) AppendDefaultRun() (DerivedChargers, error) { dcDf, _ := NewDerivedCharger(DEFAULT_RUNID, "", META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, - META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT) + META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT) return append(dcs, dcDf), nil } @@ -197,5 +214,7 @@ func (dc *DerivedCharger) Equal(other *DerivedCharger) bool { dc.AnswerTimeField == other.AnswerTimeField && dc.UsageField == other.UsageField && dc.SupplierField == other.SupplierField && - dc.DisconnectCauseField == other.DisconnectCauseField + dc.DisconnectCauseField == other.DisconnectCauseField && + dc.CostField == other.CostField && + dc.RatedField == other.RatedField } diff --git a/utils/derivedchargers_test.go b/utils/derivedchargers_test.go index 428305495..24f621177 100644 --- a/utils/derivedchargers_test.go +++ b/utils/derivedchargers_test.go @@ -61,9 +61,11 @@ func TestNewDerivedCharger(t *testing.T) { UsageField: "duration1", SupplierField: "supplier1", DisconnectCauseField: "NORMAL_CLEARING", + RatedField: "rated1", + CostField: "cost1", } if dc1, err := NewDerivedCharger("test1", "", "reqtype1", "direction1", "tenant1", "tor1", "account1", "subject1", "destination1", - "setuptime1", "pdd1", "answertime1", "duration1", "supplier1", "NORMAL_CLEARING"); err != nil { + "setuptime1", "pdd1", "answertime1", "duration1", "supplier1", "NORMAL_CLEARING", "rated1", "cost1"); err != nil { t.Error("Unexpected error", err.Error) } else if !reflect.DeepEqual(edc1, dc1) { t.Errorf("Expecting: %v, received: %v", edc1, dc1) @@ -84,6 +86,8 @@ func TestNewDerivedCharger(t *testing.T) { UsageField: "~duration2:s/sip:(.+)/$1/", SupplierField: "~supplier2:s/(.+)/$1/", DisconnectCauseField: "~cgr_disconnect:s/(.+)/$1/", + CostField: "~cgr_cost:s/(.+)/$1/", + RatedField: "~cgr_rated:s/(.+)/$1/", } edc2.rsrRunFilters, _ = ParseRSRFields("^cdr_source/tdm_cdrs/", INFIELD_SEP) edc2.rsrReqTypeField, _ = NewRSRField("~reqtype2:s/sip:(.+)/$1/") @@ -99,6 +103,8 @@ func TestNewDerivedCharger(t *testing.T) { edc2.rsrUsageField, _ = NewRSRField("~duration2:s/sip:(.+)/$1/") edc2.rsrSupplierField, _ = NewRSRField("~supplier2:s/(.+)/$1/") edc2.rsrDisconnectCauseField, _ = NewRSRField("~cgr_disconnect:s/(.+)/$1/") + edc2.rsrCostField, _ = NewRSRField("~cgr_cost:s/(.+)/$1/") + edc2.rsrRatedField, _ = NewRSRField("~cgr_rated:s/(.+)/$1/") if dc2, err := NewDerivedCharger("test2", "^cdr_source/tdm_cdrs/", "~reqtype2:s/sip:(.+)/$1/", @@ -113,7 +119,9 @@ func TestNewDerivedCharger(t *testing.T) { "~answertime2:s/sip:(.+)/$1/", "~duration2:s/sip:(.+)/$1/", "~supplier2:s/(.+)/$1/", - "~cgr_disconnect:s/(.+)/$1/"); err != nil { + "~cgr_disconnect:s/(.+)/$1/", + "~cgr_rated:s/(.+)/$1/", + "~cgr_cost:s/(.+)/$1/"); err != nil { t.Error("Unexpected error", err) } else if !reflect.DeepEqual(edc2, dc2) { t.Errorf("Expecting: %v, received: %v", edc2, dc2) @@ -131,7 +139,7 @@ func TestAppendDefaultRun(t *testing.T) { dcDf := &DerivedCharger{RunId: DEFAULT_RUNID, RunFilters: "", ReqTypeField: META_DEFAULT, DirectionField: META_DEFAULT, TenantField: META_DEFAULT, CategoryField: META_DEFAULT, AccountField: META_DEFAULT, SubjectField: META_DEFAULT, DestinationField: META_DEFAULT, SetupTimeField: META_DEFAULT, PddField: META_DEFAULT, AnswerTimeField: META_DEFAULT, UsageField: META_DEFAULT, SupplierField: META_DEFAULT, - DisconnectCauseField: META_DEFAULT} + DisconnectCauseField: META_DEFAULT, CostField: META_DEFAULT, RatedField: META_DEFAULT} eDc1 := DerivedChargers{dcDf} if dc1, _ = dc1.AppendDefaultRun(); !reflect.DeepEqual(dc1, eDc1) { t.Errorf("Expecting: %+v, received: %+v", eDc1[0], dc1[0])