Rated and Cost fields inside derived charging

This commit is contained in:
DanB
2015-10-05 08:33:27 +02:00
parent 5a614711f2
commit abbfd8270c
17 changed files with 114 additions and 38 deletions

View File

@@ -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`)

View File

@@ -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);

View File

@@ -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
1 #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]
2 *out cgrates.org call dan dan extra1 ^prepaid ^rif ^rif ^1s *default *default *default *default
3 *out cgrates.org call dan dan extra2 ^ivo ^ivo *default *default *default *default
4 *out cgrates.org call dan dan extra3 ~filterhdr1:s/(.+)/special_run3/ ^runusr3 ^runusr3 *default *default *default *default
5 *out cgrates.org call dan *any extra1 ^rif2 ^rif2 *default *default *default *default

View File

@@ -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
1 #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]
2 *out cgrates.org call 1001 1001 derived_run1 ^*rated *default *default *default *default ^1002 *default *default *default *default *default *default *default *default *default

View File

@@ -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("<CDRReplicator> Replicating CDR: %+v, got error: %s", cdr, err.Error()))

View File

@@ -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")

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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}

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -446,6 +446,8 @@ type TPDerivedCharger struct {
UsageField string
SupplierField string
DisconnectCauseField string
CostField string
RatedField string
}
type TPActionPlan struct {

View File

@@ -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
}

View File

@@ -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])