diff --git a/apier/cdrs.go b/apier/cdrs.go index 77ba81af9..3f9f0acfa 100644 --- a/apier/cdrs.go +++ b/apier/cdrs.go @@ -64,6 +64,9 @@ func (apier *ApierV1) GetCdrs(attrs utils.AttrGetCdrs, reply *[]*utils.StoredCdr attrs.OrderIdStart, attrs.OrderIdEnd, tStart, tEnd, attrs.SkipErrors, attrs.SkipRated, false); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else { + for _, cdr := range cdrs { + cdr.MangleDataUsage(utils.CDR_EXPORT) // Convert data usage to the right format + } *reply = cdrs } return nil diff --git a/cdre/fixedwidth.go b/cdre/fixedwidth.go index 30b7ba9f3..323333839 100644 --- a/cdre/fixedwidth.go +++ b/cdre/fixedwidth.go @@ -304,7 +304,9 @@ func (fwv *FixedWidthCdrWriter) WriteCdr(cdr *utils.StoredCdr) error { fwv.lastCdrATime = cdr.AnswerTime } fwv.numberOfRecords += 1 - fwv.totalDuration += cdr.Usage + if !utils.IsSliceMember([]string{utils.DATA, utils.SMS}, cdr.TOR) { // Only count duration for non data cdrs + fwv.totalDuration += cdr.Usage + } fwv.totalCost += cdr.Cost fwv.totalCost = utils.Round(fwv.totalCost, fwv.roundDecimals, utils.ROUNDING_MIDDLE) if fwv.firstExpOrderId > cdr.OrderId || fwv.firstExpOrderId == 0 { diff --git a/cdrs/cdrs.go b/cdrs/cdrs.go index 7122b20cc..abc086921 100644 --- a/cdrs/cdrs.go +++ b/cdrs/cdrs.go @@ -37,6 +37,7 @@ var ( // Returns error if not able to properly store the CDR, mediation is async since we can always recover offline func storeAndMediate(storedCdr *utils.StoredCdr) error { + storedCdr.MangleDataUsage(utils.CDR_IMPORT) // Fix the data usage here if err := storage.SetCdr(storedCdr); err != nil { return err } diff --git a/mediator/mediator_local_test.go b/mediator/mediator_local_test.go index e3776f4ef..5c54deeb4 100644 --- a/mediator/mediator_local_test.go +++ b/mediator/mediator_local_test.go @@ -268,7 +268,7 @@ func TestMediatePseudoprepaid(t *testing.T) { } } time.Sleep(time.Duration(*startDelay) * time.Millisecond) // Give time for debits to happen - expectBalance := 5.998 + expectBalance := 6.0 if err := cgrRpc.Call("ApierV1.GetAccount", attrs, &reply); err != nil { t.Error("Got error on ApierV1.GetAccount: ", err.Error()) } else if reply.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue() != expectBalance { // 5 from voice, 0.002 from DATA diff --git a/utils/consts.go b/utils/consts.go index 6dee4842c..263ae2023 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -117,6 +117,8 @@ const ( NANOSECONDS = "nanoseconds" SECONDS = "seconds" OUT = "*out" + CDR_IMPORT = "cdr_import" + CDR_EXPORT = "cdr_export" ) var ( diff --git a/utils/storedcdr.go b/utils/storedcdr.go index ad721446c..866f40d3c 100644 --- a/utils/storedcdr.go +++ b/utils/storedcdr.go @@ -51,9 +51,14 @@ type StoredCdr struct { } // Should only be used for display purposes, bad otherwise. -func (storedCdr *StoredCdr) MangleDataUsage() { +// cdrDirection: CDR_IMPORT or CDR_EXPORT +func (storedCdr *StoredCdr) MangleDataUsage(cdrDirection string) { if IsSliceMember([]string{DATA, SMS}, storedCdr.TOR) { - storedCdr.Usage = time.Duration(int(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE))) // 0.1 should be reflected as 1 and not 0 + if cdrDirection == CDR_IMPORT { // On import CDRs usages are converted to nanoseconds, for data we need seconds, fix it here. + storedCdr.Usage = time.Duration(storedCdr.Usage.Nanoseconds()) * time.Second + } else if cdrDirection == CDR_EXPORT { // On exports we need to show the data back in seconds instead of internally stored as nanoseconds + storedCdr.Usage = time.Duration(int(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE))) + } } } @@ -117,6 +122,9 @@ func (storedCdr *StoredCdr) FieldAsString(rsrFld *RSRField) string { case ANSWER_TIME: return rsrFld.ParseValue(storedCdr.AnswerTime.String()) case USAGE: + if IsSliceMember([]string{DATA, SMS}, storedCdr.TOR) { + return strconv.FormatFloat(Round(storedCdr.Usage.Seconds(), 0, ROUNDING_MIDDLE), 'f', -1, 64) + } return rsrFld.ParseValue(strconv.FormatInt(storedCdr.Usage.Nanoseconds(), 10)) case MEDI_RUNID: return rsrFld.ParseValue(storedCdr.MediationRunId) diff --git a/utils/storedcdr_test.go b/utils/storedcdr_test.go index 4d81a8890..65c704094 100644 --- a/utils/storedcdr_test.go +++ b/utils/storedcdr_test.go @@ -76,15 +76,27 @@ func TestFieldAsString(t *testing.T) { cdr.FieldAsString(&RSRField{Id: "fieldextr2"}) != cdr.ExtraFields["fieldextr2"], cdr.FieldAsString(&RSRField{Id: "dummy_field"}) != "") } + cdr.TOR = DATA + if formated := cdr.FieldAsString(&RSRField{Id: USAGE}); formated != "10" { + t.Error("Wrong exported value for data field: ", formated) + } } func TestMangleDataUsage(t *testing.T) { - cdr := StoredCdr{TOR: DATA, Usage: time.Duration(1640113000000000)} - if cdr.MangleDataUsage(); cdr.Usage != time.Duration(1640113) { + cdr := StoredCdr{TOR: DATA, Usage: time.Duration(1640113)} + if cdr.MangleDataUsage(CDR_IMPORT); cdr.Usage != time.Duration(1640113000000000) { t.Error("Unexpected usage after mangling: ", cdr.Usage) } cdr = StoredCdr{TOR: VOICE, Usage: time.Duration(1640113000000000)} - if cdr.MangleDataUsage(); cdr.Usage != time.Duration(1640113000000000) { + if cdr.MangleDataUsage(CDR_IMPORT); cdr.Usage != time.Duration(1640113000000000) { + t.Error("Unexpected usage after mangling: ", cdr.Usage) + } + cdr = StoredCdr{TOR: DATA, Usage: time.Duration(1640113000000000)} + if cdr.MangleDataUsage(CDR_EXPORT); cdr.Usage != time.Duration(1640113) { + t.Error("Unexpected usage after mangling: ", cdr.Usage) + } + cdr = StoredCdr{TOR: VOICE, Usage: time.Duration(1640113000000000)} + if cdr.MangleDataUsage(CDR_EXPORT); cdr.Usage != time.Duration(1640113000000000) { t.Error("Unexpected usage after mangling: ", cdr.Usage) } }