diff --git a/apier/v1/accounts_it_test.go b/apier/v1/accounts_it_test.go index 37e601da9..d802a90b0 100644 --- a/apier/v1/accounts_it_test.go +++ b/apier/v1/accounts_it_test.go @@ -76,6 +76,7 @@ var ( testAccITMultipleBalanceWithoutTenant, testAccITRemoveBalances, testAccITAddVoiceBalanceWithDestinations, + testAccITSetBalanceWithTimeSuffix, testAccITStopCgrEngine, } ) @@ -1350,3 +1351,44 @@ func testAccITAddVoiceBalanceWithDestinations(t *testing.T) { t.Errorf("Expecting %+v, received: %+v", 0, rply) } } + +func testAccITSetBalanceWithTimeSuffix(t *testing.T) { + var reply string + args := &utils.AttrSetBalance{ + Tenant: "cgrates.org", + Account: "testAccSetBalanceTimeSuffix", + BalanceType: "*voice", + Balance: map[string]interface{}{ + utils.ID: "testAccSetBalanceTimeSuffix", + utils.Weight: 10, + utils.Value: "120ms", + }, + } + if err := accRPC.Call(utils.APIerSv1SetBalance, args, &reply); err != nil { + t.Error("Got error on SetBalance: ", err.Error()) + } else if reply != utils.OK { + t.Errorf("Calling SetBalance received: %s", reply) + } + + var acnt engine.Account + attrAcc := &utils.AttrGetAccount{ + Tenant: accTenant, + Account: "testAccSetBalanceTimeSuffix", + } + if err := accRPC.Call(utils.APIerSv2GetAccount, attrAcc, &acnt); err != nil { + t.Fatal(err) + } + + for _, value := range acnt.BalanceMap[utils.MetaVoice] { + // check only where balance ID is testBalanceID (SetBalance function call was made with this Balance ID) + if value.ID == "testAccSetBalanceTimeSuffix" { + if value.GetValue() != 120000000. { + t.Errorf("Expecting %+v, received: %+v", 120000000., value.GetValue()) + } + if value.Weight != 10 { + t.Errorf("Expecting %+v, received: %+v", 10, value.Weight) + } + break + } + } +} diff --git a/engine/balance_filter.go b/engine/balance_filter.go index de2ccbec7..d554922e6 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -58,7 +58,7 @@ func NewBalanceFilter(filter map[string]interface{}, defaultTimezone string) (bf // } if val, has := filter[utils.Value]; has { var value float64 - if value, err = utils.IfaceAsFloat64(val); err != nil { + if value, err = utils.IfaceAsTFloat64(val); err != nil { return } bf.Value = &utils.ValueFormula{Static: math.Abs(value)} diff --git a/utils/reflect.go b/utils/reflect.go index 1215f6a78..fd1d4d598 100644 --- a/utils/reflect.go +++ b/utils/reflect.go @@ -277,6 +277,30 @@ func IfaceAsFloat64(itm interface{}) (f float64, err error) { } return } +func IfaceAsTFloat64(itm interface{}) (f float64, err error) { + switch it := itm.(type) { + case float64: + return it, nil + case time.Duration: + return float64(it.Nanoseconds()), nil + case int: + return float64(it), nil + case int64: + return float64(it), nil + case string: + if strings.HasSuffix(it, SSuffix) || strings.HasSuffix(it, MSuffix) || strings.HasSuffix(it, HSuffix) { + var tm time.Duration + if tm, err = time.ParseDuration(it); err != nil { + return + } + return float64(tm), nil + } + return strconv.ParseFloat(it, 64) + default: + err = fmt.Errorf("cannot convert field: %+v to float64", it) + } + return +} func IfaceAsBool(itm interface{}) (b bool, err error) { switch v := itm.(type) { diff --git a/utils/reflect_test.go b/utils/reflect_test.go index 8b13d2970..ab78f4368 100644 --- a/utils/reflect_test.go +++ b/utils/reflect_test.go @@ -1833,3 +1833,58 @@ func TestIfaceAsBigDefault(t *testing.T) { t.Errorf("Expected %v but received %v", errExpect, err) } } + +func TestIfaceAsTFloat64(t *testing.T) { + eFloat := 6.0 + val := interface{}(6.0) + if itmConvert, err := IfaceAsTFloat64(val); err != nil { + t.Error(err) + } else if itmConvert != eFloat { + t.Errorf("received: %+v", itmConvert) + } + val = interface{}(6) + if itmConvert, err := IfaceAsTFloat64(val); err != nil { + t.Error(err) + } else if itmConvert != eFloat { + t.Errorf("received: %+v", itmConvert) + } + val = interface{}("6") + if itmConvert, err := IfaceAsTFloat64(val); err != nil { + t.Error(err) + } else if itmConvert != eFloat { + t.Errorf("received: %+v", itmConvert) + } + val = interface{}(int64(6)) + if itmConvert, err := IfaceAsTFloat64(val); err != nil { + t.Error(err) + } else if itmConvert != eFloat { + t.Errorf("received: %+v", itmConvert) + } + val = interface{}("This is not a float") + if _, err := IfaceAsTFloat64(val); err == nil { + t.Error("expecting error") + } + val = interface{}(time.Duration(6)) + if itmConvert, err := IfaceAsTFloat64(val); err != nil { + t.Error(err) + } else if itmConvert != eFloat { + t.Errorf("received: %+v", itmConvert) + } + eFloat = 6000000. + val = interface{}("6ms") + if itmConvert, err := IfaceAsTFloat64(val); err != nil { + t.Error(err) + } else if itmConvert != eFloat { + t.Errorf("received: %+v", itmConvert) + } + val = interface{}("6sss") + experr := `time: unknown unit "sss" in duration "6sss"` + if _, err := IfaceAsTFloat64(val); err == nil || err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + val = false + experr = `cannot convert field: false to float64` + if _, err := IfaceAsTFloat64(val); err == nil || err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +}