diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go index a12e992c5..1427d357b 100644 --- a/cdrc/cdrc.go +++ b/cdrc/cdrc.go @@ -118,7 +118,7 @@ func (self *Cdrc) parseFieldsConfig() error { } // Takes the record out of csv and turns it into http form which can be posted -func (self *Cdrc) recordAsStoredCdr(record []string) (*utils.StoredCdr, error) { +func (self *Cdrc) recordForkCdr(record []string) (*utils.StoredCdr, error) { ratedCdr := &utils.StoredCdr{CdrSource: self.cgrCfg.CdrcSourceId, ExtraFields: map[string]string{}, Cost: -1} var err error for cfgFieldName, cfgFieldVal := range self.cfgCdrFields { @@ -233,7 +233,7 @@ func (self *Cdrc) processFile(filePath string) error { engine.Logger.Err(fmt.Sprintf(" Error in csv file: %s", err.Error())) continue // Other csv related errors, ignore } - rawCdr, err := self.recordAsStoredCdr(record) + rawCdr, err := self.recordForkCdr(record) if err != nil { engine.Logger.Err(fmt.Sprintf(" Error in csv file: %s", err.Error())) continue diff --git a/cdrc/cdrc_test.go b/cdrc/cdrc_test.go index 8bf6fb028..b0cd8d71b 100644 --- a/cdrc/cdrc_test.go +++ b/cdrc/cdrc_test.go @@ -56,7 +56,7 @@ func TestParseFieldsConfig(t *testing.T) { } } -func TestRecordAsStoredCdr(t *testing.T) { +func TestRecordForkCdr(t *testing.T) { cgrConfig, _ := config.NewDefaultCGRConfig() cgrConfig.CdrcExtraFields = []string{"supplier:11"} cdrc := &Cdrc{cgrCfg: cgrConfig} @@ -64,13 +64,13 @@ func TestRecordAsStoredCdr(t *testing.T) { t.Error("Failed parsing default fieldIndexesFromConfig", err) } cdrRow := []string{"firstField", "secondField"} - _, err := cdrc.recordAsStoredCdr(cdrRow) + _, err := cdrc.recordForkCdr(cdrRow) if err == nil { t.Error("Failed to corectly detect missing fields from record") } cdrRow = []string{"acc1", "prepaid", "*out", "cgrates.org", "call", "1001", "1001", "+4986517174963", "2013-02-03 19:50:00", "2013-02-03 19:54:00", "62", "supplier1", "172.16.1.1"} - rtCdr, err := cdrc.recordAsStoredCdr(cdrRow) + rtCdr, err := cdrc.recordForkCdr(cdrRow) if err != nil { t.Error("Failed to parse CDR in rated cdr", err) } diff --git a/cdrs/fscdr.go b/cdrs/fscdr.go index b56b97a39..f6181c4d0 100644 --- a/cdrs/fscdr.go +++ b/cdrs/fscdr.go @@ -201,7 +201,7 @@ func (fsCdr FSCdr) Restore(input string) error { } // Used in extra mediation -func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*utils.StoredCdr, error) { +func (fsCdr FSCdr) ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*utils.StoredCdr, error) { if utils.IsSliceMember([]string{runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld}, "") { return nil, errors.New(fmt.Sprintf("%s:FieldName", utils.ERR_MANDATORY_IE_MISSING)) // All input field names are mandatory } diff --git a/cdrs/fscdr_test.go b/cdrs/fscdr_test.go index 53b30a5b8..59b152e58 100644 --- a/cdrs/fscdr_test.go +++ b/cdrs/fscdr_test.go @@ -102,13 +102,13 @@ func TestCDRFields(t *testing.T) { } -func TestFsCdrAsStoredCdr(t *testing.T) { +func TestFsCdrForkCdr(t *testing.T) { cfg, _ = config.NewDefaultCGRConfig() fsCdr, err := new(FSCdr).New(body) if err != nil { t.Errorf("Error loading cdr: %v", err) } - rtCdrOut, err := fsCdr.AsStoredCdr("wholesale_run", "^"+utils.RATED, "^*out", "cgr_tenant", "cgr_tor", "cgr_account", "cgr_subject", "cgr_destination", "start_epoch", + rtCdrOut, err := fsCdr.ForkCdr("wholesale_run", "^"+utils.RATED, "^*out", "cgr_tenant", "cgr_tor", "cgr_account", "cgr_subject", "cgr_destination", "start_epoch", "answer_epoch", "billsec", []string{"effective_caller_id_number"}, true) if err != nil { t.Error("Unexpected error received", err) @@ -122,7 +122,7 @@ func TestFsCdrAsStoredCdr(t *testing.T) { if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) { t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr) } - rtCdrOut2, err := fsCdr.AsStoredCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "cgr_destination", + rtCdrOut2, err := fsCdr.ForkCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "cgr_destination", "^2013-12-07T08:42:24Z", "^2013-12-07T08:42:26Z", "^12s", []string{"effective_caller_id_number"}, true) if err != nil { t.Error("Unexpected error received", err) @@ -136,7 +136,7 @@ func TestFsCdrAsStoredCdr(t *testing.T) { if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) { t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2) } - _, err = fsCdr.AsStoredCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true) + _, err = fsCdr.ForkCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true) if err == nil { t.Error("Failed to detect missing header") } diff --git a/data/conf/samples/derived_charging1.cfg b/data/conf/samples/derived_charging1.cfg new file mode 100644 index 000000000..53a2fee6e --- /dev/null +++ b/data/conf/samples/derived_charging1.cfg @@ -0,0 +1,32 @@ +# CGRateS Configuration file +# +# Used in mediator_local_test +# Starts rater, cdrs and mediator connecting over internal channel + +[rater] +enabled = true # Enable RaterCDRSExportPath service: . + +[cdrs] +enabled = true # Start the CDR Server service: . +mediator = internal # Address where to reach the Mediator. Empty for disabling mediation. <""|internal> + +[cdre] +export_dir = /tmp/cgrates/cdr/cdrexport/csv # Path where the exported CDRs will be placed + +[mediator] +enabled = true # Starts Mediator service: . +rater = internal # Address where to reach the Rater: + +[derived_charging] +run_ids = run2 # Identifiers of additional sessions control. +reqtype_fields = *default # Name of request type fields to be used during additional sessions control <""|*default|field_name>. +direction_fields = *default # Name of direction fields to be used during additional sessions control <""|*default|field_name>. +tenant_fields = *default # Name of tenant fields to be used during additional sessions control <""|*default|field_name>. +tor_fields = *default # Name of tor fields to be used during additional sessions control <""|*default|field_name>. +account_fields = ^dc2 # Name of account fields to be used during additional sessions control <""|*default|field_name>. +subject_fields = ^dc2 # Name of fields to be used during additional sessions control <""|*default|field_name>. +destination_fields = *default # Name of destination fields to be used during additional sessions control <""|*default|field_name>. +# setup_time_fields = # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. +# answer_time_fields = # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. +# duration_fields = # Name of duration fields to be used during additional sessions control <""|*default|field_name>. +# combined_chargers = true # Combine accounts specific derived_chargers with server configured ones . diff --git a/data/conf/samples/mediator_test1.cfg b/data/conf/samples/mediator_test1.cfg index f6d93966d..a15950aea 100644 --- a/data/conf/samples/mediator_test1.cfg +++ b/data/conf/samples/mediator_test1.cfg @@ -17,4 +17,18 @@ export_dir = /tmp/cgrates/cdr/cdrexport/csv # Path where the exported CDRs will enabled = true # Starts Mediator service: . rater = internal # Address where to reach the Rater: +[derived_charging] +run_ids = run2 # Identifiers of additional sessions control. +reqtype_fields = *default # Name of request type fields to be used during additional sessions control <""|*default|field_name>. +direction_fields = *default # Name of direction fields to be used during additional sessions control <""|*default|field_name>. +tenant_fields = *default # Name of tenant fields to be used during additional sessions control <""|*default|field_name>. +tor_fields = *default # Name of tor fields to be used during additional sessions control <""|*default|field_name>. +account_fields = ^dc2 # Name of account fields to be used during additional sessions control <""|*default|field_name>. +subject_fields = ^dc2 # Name of fields to be used during additional sessions control <""|*default|field_name>. +destination_fields = *default # Name of destination fields to be used during additional sessions control <""|*default|field_name>. +setup_time_fields = *default # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. +answer_time_fields = *default # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. +duration_fields = *default # Name of duration fields to be used during additional sessions control <""|*default|field_name>. +# combined_chargers = true # Combine accounts specific derived_chargers with server configured ones . + diff --git a/mediator/mediator.go b/mediator/mediator.go index 28714903f..ff60e9dd8 100644 --- a/mediator/mediator.go +++ b/mediator/mediator.go @@ -124,8 +124,9 @@ func (self *Mediator) RateCdr(dbcdr utils.RawCDR) error { return errors.New(errText) } for _, dc := range dcs { - forkedCdr, err := dbcdr.AsStoredCdr(dc.RunId, dc.ReqTypeField, dc.DirectionField, + forkedCdr, err := dbcdr.ForkCdr(dc.RunId, dc.ReqTypeField, dc.DirectionField, dc.TenantField, dc.TorField, dc.AccountField, dc.SubjectField, dc.DestinationField, dc.SetupTimeField, dc.AnswerTimeField, dc.DurationField, []string{}, true) + engine.Logger.Debug(fmt.Sprintf("Forked CDR for dc: %v, is: %v", dc, forkedCdr)) if err != nil { // Errors on fork, cannot calculate further, write that into db for later analysis self.cdrDb.SetRatedCdr(&utils.StoredCdr{CgrId: dbcdr.GetCgrId(), MediationRunId: dc.RunId, Cost: -1.0}, err.Error()) // Cannot fork CDR, important just runid and error continue diff --git a/mediator/mediator_local_test.go b/mediator/mediator_local_test.go index b572e5d17..d7fcfc594 100644 --- a/mediator/mediator_local_test.go +++ b/mediator/mediator_local_test.go @@ -237,6 +237,38 @@ func TestRateCdrs(t *testing.T) { } } +func TestDerivedCharging(t *testing.T) { + if !*testLocal { + return + } + var reply string + if err := cgrRpc.Call("MediatorV1.RateCdrs", utils.AttrRateCdrs{}, &reply); err != nil { + t.Error(err.Error()) + } else if reply != utils.OK { + t.Errorf("Unexpected reply: %s", reply) + } + if nonRatedCdrs, err := cdrStor.GetStoredCdrs(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0, 0, time.Time{}, time.Time{}, true, true); err != nil { + t.Error(err) + } else if len(nonRatedCdrs) != 0 { // Just two of them should be non-rated + t.Error(fmt.Sprintf("Unexpected number of CDRs non-rated: %d", len(nonRatedCdrs))) + } + if errRatedCdrs, err := cdrStor.GetStoredCdrs(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0, 0, time.Time{}, time.Time{}, false, true); err != nil { + t.Error(err) + } else if len(errRatedCdrs) != 2 { // The first 2 with errors should be still there before rerating + t.Error(fmt.Sprintf("Unexpected number of CDRs with errors: %d", len(errRatedCdrs))) + } + if err := cgrRpc.Call("MediatorV1.RateCdrs", utils.AttrRateCdrs{RerateErrors: true}, &reply); err != nil { + t.Error(err.Error()) + } else if reply != utils.OK { + t.Errorf("Unexpected reply: %s", reply) + } + if errRatedCdrs, err := cdrStor.GetStoredCdrs(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0, 0, time.Time{}, time.Time{}, false, true); err != nil { + t.Error(err) + } else if len(errRatedCdrs) != 1 { // One CDR with errors should be fixed now by rerating + t.Error(fmt.Sprintf("Unexpected number of CDRs with errors: %d", len(errRatedCdrs))) + } +} + // Simply kill the engine after we are done with tests within this file func TestStopEngine(t *testing.T) { if !*testLocal { diff --git a/utils/cgrcdr.go b/utils/cgrcdr.go index 732dbf354..8b1fb3bc7 100644 --- a/utils/cgrcdr.go +++ b/utils/cgrcdr.go @@ -110,7 +110,7 @@ func (cgrCdr CgrCdr) GetDuration() (time.Duration, error) { // Used in mediation, fieldsMandatory marks whether missing field out of request represents error or can be ignored // If the fields in parameters start with ^ their value is considered instead of dynamically retrieving it from CDR -func (cgrCdr CgrCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) { +func (cgrCdr CgrCdr) ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) { if IsSliceMember([]string{runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld}, "") { return nil, errors.New(fmt.Sprintf("%s:FieldName", ERR_MANDATORY_IE_MISSING)) // All input field names are mandatory } diff --git a/utils/cgrcdr_test.go b/utils/cgrcdr_test.go index 9d36c7704..1a84da7da 100644 --- a/utils/cgrcdr_test.go +++ b/utils/cgrcdr_test.go @@ -85,11 +85,27 @@ func TestCgrCdrFields(t *testing.T) { } } -func TestCgrCdrAsStoredCdr(t *testing.T) { +func TestCgrCdrForkCdr(t *testing.T) { + sampleCdr1 := &CgrCdr{"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", "setup_time": "2013-11-07T08:42:24Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10", + "field_extr1": "val_extr1", "fieldextr2": "valextr2"} + rtSampleCdrOut, err := sampleCdr1.ForkCdr("sample_run1", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration", + []string{}, true) + if err != nil { + t.Error("Unexpected error received", err) + } + 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", + SetupTime: setupTime1, AnswerTime: time.Unix(1383813746, 0).UTC(), + Duration: 10000000000, ExtraFields: map[string]string{}, MediationRunId: "sample_run1", Cost: -1} + if !reflect.DeepEqual(expctSplRatedCdr, rtSampleCdrOut) { + t.Errorf("Expected: %v, received: %v", expctSplRatedCdr, rtSampleCdrOut) + } cgrCdr := &CgrCdr{"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", "setup_time": "2013-11-07T08:42:24Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10", "field_extr1": "val_extr1", "fieldextr2": "valextr2"} - rtCdrOut, err := cgrCdr.AsStoredCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration", + rtCdrOut, err := cgrCdr.ForkCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true) if err != nil { t.Error("Unexpected error received", err) @@ -102,7 +118,7 @@ func TestCgrCdrAsStoredCdr(t *testing.T) { if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) { t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr) } - rtCdrOut2, err := cgrCdr.AsStoredCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "destination", + rtCdrOut2, err := cgrCdr.ForkCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "destination", "^2013-12-07T08:42:24Z", "^2013-12-07T08:42:26Z", "^12s", []string{"field_extr1", "fieldextr2"}, true) if err != nil { t.Error("Unexpected error received", err) @@ -115,14 +131,14 @@ func TestCgrCdrAsStoredCdr(t *testing.T) { if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) { t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2) } - _, err = cgrCdr.AsStoredCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration", + _, err = cgrCdr.ForkCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true) if err == nil { t.Error("Failed to detect missing header") } } -func TestCgrCdrAsStoredCdrFromMetaDefaults(t *testing.T) { +func TestCgrCdrForkCdrFromMetaDefaults(t *testing.T) { cgrCdr := &CgrCdr{"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", "setup_time": "2013-11-07T08:42:24Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10", "field_extr1": "val_extr1", "fieldextr2": "valextr2"} @@ -132,7 +148,7 @@ func TestCgrCdrAsStoredCdrFromMetaDefaults(t *testing.T) { 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} - cdrOut, err := cgrCdr.AsStoredCdr("wholesale_run", META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, + cdrOut, err := cgrCdr.ForkCdr("wholesale_run", META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, []string{"field_extr1", "fieldextr2"}, true) if err != nil { t.Fatal("Unexpected error received", err) diff --git a/utils/consts.go b/utils/consts.go index 90b0bc19a..0f0ad9c26 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -100,6 +100,7 @@ const ( MASK_CHAR = "*" CONCATENATED_KEY_SEP = ":" META_DEFAULT = "*default" + FORKED_CDR = "forked_cdr" ) var ( diff --git a/utils/rawcdr.go b/utils/rawcdr.go index 59ad618ca..11f2815d3 100644 --- a/utils/rawcdr.go +++ b/utils/rawcdr.go @@ -40,6 +40,6 @@ type RawCDR interface { GetSetupTime() (time.Time, error) // Time when the call was set-up GetAnswerTime() (time.Time, error) // Time when the call was answered GetDuration() (time.Duration, error) - GetExtraFields() map[string]string //Stores extra CDR Fields - AsStoredCdr(string, string, string, string, string, string, string, string, string, string, string, []string, bool) (*StoredCdr, error) // Based on fields queried will return a particular instance of RatedCDR + GetExtraFields() map[string]string //Stores extra CDR Fields + ForkCdr(string, string, string, string, string, string, string, string, string, string, string, []string, bool) (*StoredCdr, error) // Based on fields queried will return a particular instance of RatedCDR } diff --git a/utils/storedcdr.go b/utils/storedcdr.go index 18ab5a5a3..7f1180165 100644 --- a/utils/storedcdr.go +++ b/utils/storedcdr.go @@ -27,29 +27,29 @@ import ( func NewStoredCdrFromRawCDR(rawcdr RawCDR) (*StoredCdr, error) { var err error - rtCdr := new(StoredCdr) - rtCdr.CgrId = rawcdr.GetCgrId() - rtCdr.AccId = rawcdr.GetAccId() - rtCdr.CdrHost = rawcdr.GetCdrHost() - rtCdr.CdrSource = rawcdr.GetCdrSource() - rtCdr.ReqType = rawcdr.GetReqType() - rtCdr.Direction = rawcdr.GetDirection() - rtCdr.Tenant = rawcdr.GetTenant() - rtCdr.TOR = rawcdr.GetTOR() - rtCdr.Account = rawcdr.GetAccount() - rtCdr.Subject = rawcdr.GetSubject() - rtCdr.Destination = rawcdr.GetDestination() - if rtCdr.SetupTime, err = rawcdr.GetSetupTime(); err != nil { + strCdr := new(StoredCdr) + strCdr.CgrId = rawcdr.GetCgrId() + strCdr.AccId = rawcdr.GetAccId() + strCdr.CdrHost = rawcdr.GetCdrHost() + strCdr.CdrSource = rawcdr.GetCdrSource() + strCdr.ReqType = rawcdr.GetReqType() + strCdr.Direction = rawcdr.GetDirection() + strCdr.Tenant = rawcdr.GetTenant() + strCdr.TOR = rawcdr.GetTOR() + strCdr.Account = rawcdr.GetAccount() + strCdr.Subject = rawcdr.GetSubject() + strCdr.Destination = rawcdr.GetDestination() + if strCdr.SetupTime, err = rawcdr.GetSetupTime(); err != nil { return nil, err } - if rtCdr.AnswerTime, err = rawcdr.GetAnswerTime(); err != nil { + if strCdr.AnswerTime, err = rawcdr.GetAnswerTime(); err != nil { return nil, err } - rtCdr.Duration, _ = rawcdr.GetDuration() - rtCdr.ExtraFields = rawcdr.GetExtraFields() - rtCdr.MediationRunId = DEFAULT_RUNID - rtCdr.Cost = -1 - return rtCdr, nil + strCdr.Duration, _ = rawcdr.GetDuration() + strCdr.ExtraFields = rawcdr.GetExtraFields() + strCdr.MediationRunId = DEFAULT_RUNID + strCdr.Cost = -1 + return strCdr, nil } // Rated CDR as extracted from StorDb. Kinda standard of internal CDR, complies to CDR interface also @@ -145,10 +145,6 @@ func (storedCdr *StoredCdr) FormatCost(shiftDecimals, roundDecimals int) string return strconv.FormatFloat(cost, 'f', roundDecimals, 64) } -func (storedCdr *StoredCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) { - return storedCdr, nil -} - // Converts part of the rated Cdr as httpForm used to post remotely to CDRS func (storedCdr *StoredCdr) AsRawCdrHttpForm() url.Values { v := url.Values{} @@ -212,3 +208,18 @@ func (storedCdr *StoredCdr) ExportFieldValue(fldName string) string { return storedCdr.ExtraFields[fldName] } } + +// Converts to CgrCdr, so we can fork with less code +func (storedCdr *StoredCdr) AsCgrCdr() CgrCdr { + cgrCdr := make(CgrCdr) + for _, fldName := range PrimaryCdrFields { + cgrCdr[fldName] = storedCdr.ExportFieldValue(fldName) + } + return cgrCdr +} + +func (storedCdr *StoredCdr) ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, + setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) { + return storedCdr.AsCgrCdr().ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, + setupTimeFld, answerTimeFld, durationFld, extraFlds, fieldsMandatory) +}