diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index add9e9b97..39173d652 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -426,7 +426,7 @@ func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *st Uuid: attr.BalanceUuid, ID: attr.BalanceId, Type: utils.StringPointer(attr.BalanceType), - Value: utils.Float64Pointer(attr.Value), + Value: &engine.ValueFormula{Static: attr.Value}, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Weight: attr.Weight, @@ -514,7 +514,6 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { Uuid: attr.BalanceUUID, ID: attr.BalanceID, Type: utils.StringPointer(attr.BalanceType), - Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Weight: attr.Weight, @@ -522,6 +521,9 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { Disabled: attr.Disabled, }, } + if attr.Value != nil { + a.Balance.Value = &engine.ValueFormula{Static: *attr.Value} + } if attr.Directions != nil { a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) } @@ -572,7 +574,6 @@ func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error { Uuid: attr.BalanceUUID, ID: attr.BalanceID, Type: utils.StringPointer(attr.BalanceType), - Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Weight: attr.Weight, @@ -580,6 +581,9 @@ func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error { Disabled: attr.Disabled, }, } + if attr.Value != nil { + a.Balance.Value = &engine.ValueFormula{Static: *attr.Value} + } if attr.Directions != nil { a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) } diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 95fba7081..24a5d3a49 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -504,10 +504,10 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error } storeActions := make(engine.Actions, len(attrs.Actions)) for idx, apiAct := range attrs.Actions { - var units *float64 + var vf *engine.ValueFormula if apiAct.Units != "" { - if x, err := strconv.ParseFloat(apiAct.Units, 64); err == nil { - units = &x + if x, err := engine.ParseBalanceFilterValue(apiAct.Units); err == nil { + vf = x } else { return err } @@ -533,7 +533,7 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error Uuid: utils.StringPointer(utils.GenUUID()), ID: utils.StringPointer(apiAct.BalanceId), Type: utils.StringPointer(apiAct.BalanceType), - Value: units, + Value: vf, Weight: weight, Directions: utils.StringMapPointer(utils.ParseStringMap(apiAct.Directions)), DestinationIDs: utils.StringMapPointer(utils.ParseStringMap(apiAct.DestinationIds)), diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 1ae8ab419..de9a51586 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -22,6 +22,7 @@ import ( "flag" "fmt" "log" + _ "net/http/pprof" "os" "runtime" "runtime/pprof" diff --git a/cmd/cgr-engine/rater.go b/cmd/cgr-engine/rater.go index 0c3f90021..8fe1ee27d 100644 --- a/cmd/cgr-engine/rater.go +++ b/cmd/cgr-engine/rater.go @@ -211,7 +211,6 @@ func startRater(internalRaterChan chan rpcclient.RpcClientConnection, cacheDoneC utils.RegisterRpcParams("PubSubV1", &engine.PubSub{}) utils.RegisterRpcParams("AliasesV1", &engine.AliasHandler{}) utils.RegisterRpcParams("UsersV1", &engine.UserMap{}) - utils.RegisterRpcParams("UsersV1", &engine.UserMap{}) utils.RegisterRpcParams("", &v1.CdrsV1{}) utils.RegisterRpcParams("", &v2.CdrsV2{}) utils.RegisterRpcParams("", &v1.SessionManagerV1{}) @@ -219,6 +218,6 @@ func startRater(internalRaterChan chan rpcclient.RpcClientConnection, cacheDoneC utils.RegisterRpcParams("", responder) utils.RegisterRpcParams("", apierRpcV1) utils.RegisterRpcParams("", apierRpcV2) - + utils.GetRpcParams("") internalRaterChan <- responder // Rater done } diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index 9d085d2f6..f28e04766 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -482,7 +482,7 @@ func (mig MigratorRC8) migrateActions() error { bf.Type = utils.StringPointer(oldAc.BalanceType) } if oldAc.Balance.Value != 0 { - bf.Value = utils.Float64Pointer(oldAc.Balance.Value) + bf.Value = &engine.ValueFormula{Static: oldAc.Balance.Value} } if oldAc.Balance.RatingSubject != "" { bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject) diff --git a/cmd/cgr-loader/migrator_rc8int.go b/cmd/cgr-loader/migrator_rc8int.go index 41f55afb1..3d335c914 100644 --- a/cmd/cgr-loader/migrator_rc8int.go +++ b/cmd/cgr-loader/migrator_rc8int.go @@ -364,7 +364,7 @@ func (mig MigratorRC8) migrateActionsInt() error { bf.Type = utils.StringPointer(oldAc.BalanceType) } if oldAc.Balance.Value != 0 { - bf.Value = utils.Float64Pointer(oldAc.Balance.Value) + bf.Value = &engine.ValueFormula{Static: oldAc.Balance.Value} } if oldAc.Balance.RatingSubject != "" { bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject) diff --git a/engine/account.go b/engine/account.go index b49ef4edd..1bd3ac42e 100644 --- a/engine/account.go +++ b/engine/account.go @@ -127,7 +127,7 @@ func (acc *Account) setBalanceAction(a *Action) error { if a.Balance.ID != nil && *a.Balance.ID == utils.META_DEFAULT { balance.ID = utils.META_DEFAULT if a.Balance.Value != nil { - balance.Value = *a.Balance.Value + balance.Value = a.Balance.GetValue() } } else { a.Balance.ModifyBalance(balance) diff --git a/engine/account_test.go b/engine/account_test.go index 90be903d2..42dd1cb4c 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -856,7 +856,7 @@ func TestAccountdebitBalanceExists(t *testing.T) { BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14}}, utils.DATA: Balances{&Balance{Value: 1024}}, utils.VOICE: Balances{&Balance{Value: 15, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } newMb := &BalanceFilter{ - Value: utils.Float64Pointer(-10), + Value: &ValueFormula{Static: -10}, Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), @@ -883,19 +883,19 @@ func TestAccountAddMinuteNil(t *testing.T) { func TestAccountAddMinutBucketEmpty(t *testing.T) { mb1 := &BalanceFilter{ - Value: utils.Float64Pointer(-10), + Value: &ValueFormula{Static: -10}, Type: utils.StringPointer(utils.VOICE), DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } mb2 := &BalanceFilter{ - Value: utils.Float64Pointer(-10), + Value: &ValueFormula{Static: -10}, Type: utils.StringPointer(utils.VOICE), DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } mb3 := &BalanceFilter{ - Value: utils.Float64Pointer(-10), + Value: &ValueFormula{Static: -10}, Type: utils.StringPointer(utils.VOICE), DestinationIDs: utils.StringMapPointer(utils.StringMap{"OTHER": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), diff --git a/engine/action.go b/engine/action.go index 903ce7d90..5a3c8e754 100644 --- a/engine/action.go +++ b/engine/action.go @@ -93,59 +93,36 @@ func (a *Action) Clone() *Action { type actionTypeFunc func(*Account, *StatsQueueTriggered, *Action, Actions) error func getActionFunc(typ string) (actionTypeFunc, bool) { - switch typ { - case LOG: - return logAction, true - case CDRLOG: - return cdrLogAction, true - case RESET_TRIGGERS: - return resetTriggersAction, true - case SET_RECURRENT: - return setRecurrentAction, true - case UNSET_RECURRENT: - return unsetRecurrentAction, true - case ALLOW_NEGATIVE: - return allowNegativeAction, true - case DENY_NEGATIVE: - return denyNegativeAction, true - case RESET_ACCOUNT: - return resetAccountAction, true - case TOPUP_RESET: - return topupResetAction, true - case TOPUP: - return topupAction, true - case DEBIT_RESET: - return debitResetAction, true - case DEBIT: - return debitAction, true - case RESET_COUNTERS: - return resetCountersAction, true - case ENABLE_ACCOUNT: - return enableUserAction, true - case DISABLE_ACCOUNT: - return disableUserAction, true - //case ENABLE_DISABLE_BALANCE: - // return enableDisableBalanceAction, true - case CALL_URL: - return callUrl, true - case CALL_URL_ASYNC: - return callUrlAsync, true - case MAIL_ASYNC: - return mailAsync, true - case SET_DDESTINATIONS: - return setddestinations, true - case REMOVE_ACCOUNT: - return removeAccountAction, true - case REMOVE_BALANCE: - return removeBalanceAction, true - case SET_BALANCE: - return setBalanceAction, true - case TRANSFER_MONETARY_DEFAULT: - return transferMonetaryDefaultAction, true - case CGR_RPC: - return cgrRPCAction, true + actionFuncMap := map[string]actionTypeFunc{ + LOG: logAction, + CDRLOG: cdrLogAction, + RESET_TRIGGERS: resetTriggersAction, + SET_RECURRENT: setRecurrentAction, + UNSET_RECURRENT: unsetRecurrentAction, + ALLOW_NEGATIVE: allowNegativeAction, + DENY_NEGATIVE: denyNegativeAction, + RESET_ACCOUNT: resetAccountAction, + TOPUP_RESET: topupResetAction, + TOPUP: topupAction, + DEBIT_RESET: debitResetAction, + DEBIT: debitAction, + RESET_COUNTERS: resetCountersAction, + ENABLE_ACCOUNT: enableUserAction, + DISABLE_ACCOUNT: disableUserAction, + //case ENABLE_DISABLE_BALANCE: + // return enableDisableBalanceAction, true + CALL_URL: callUrl, + CALL_URL_ASYNC: callUrlAsync, + MAIL_ASYNC: mailAsync, + SET_DDESTINATIONS: setddestinations, + REMOVE_ACCOUNT: removeAccountAction, + REMOVE_BALANCE: removeBalanceAction, + SET_BALANCE: setBalanceAction, + TRANSFER_MONETARY_DEFAULT: transferMonetaryDefaultAction, + CGR_RPC: cgrRPCAction, } - return nil, false + f, exists := actionFuncMap[typ] + return f, exists } func logAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { diff --git a/engine/actions_test.go b/engine/actions_test.go index fb1481ef4..d37695055 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -413,7 +413,7 @@ func TestActionPlanLogFunction(t *testing.T) { ActionType: "*log", Balance: &BalanceFilter{ Type: utils.StringPointer("test"), - Value: utils.Float64Pointer(1.1), + Value: &ValueFormula{Static: 1.1}, }, } at := &ActionTiming{ @@ -430,7 +430,7 @@ func TestActionPlanFunctionNotAvailable(t *testing.T) { ActionType: "VALID_FUNCTION_TYPE", Balance: &BalanceFilter{ Type: utils.StringPointer("test"), - Value: utils.Float64Pointer(1.1), + Value: &ValueFormula{Static: 1.1}, }, } at := &ActionTiming{ @@ -659,7 +659,7 @@ func TestActionTriggerMatchAll(t *testing.T) { Type: utils.StringPointer(utils.MONETARY), RatingSubject: utils.StringPointer("test1"), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - Value: utils.Float64Pointer(2), + Value: &ValueFormula{Static: 2}, Weight: utils.Float64Pointer(1.0), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")), @@ -669,7 +669,7 @@ func TestActionTriggerMatchAll(t *testing.T) { Type: utils.StringPointer(utils.MONETARY), RatingSubject: utils.StringPointer("test1"), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - Value: utils.Float64Pointer(2), + Value: &ValueFormula{Static: 2}, Weight: utils.Float64Pointer(1.0), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")), @@ -799,7 +799,7 @@ func TestActionTopupResetCredit(t *testing.T) { UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 || @@ -818,7 +818,7 @@ func TestActionTopupValueFactor(t *testing.T) { a := &Action{ Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), - Value: utils.Float64Pointer(10), + Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ExtraParameters: `{"*monetary":2.0}`, @@ -839,7 +839,7 @@ func TestActionTopupResetCreditId(t *testing.T) { }, }, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("TEST_B"), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -858,7 +858,7 @@ func TestActionTopupResetCreditNoId(t *testing.T) { }, }, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 20 || @@ -876,7 +876,7 @@ func TestActionTopupResetMinutes(t *testing.T) { UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 5 || @@ -895,7 +895,7 @@ func TestActionTopupCredit(t *testing.T) { UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -913,7 +913,7 @@ func TestActionTopupMinutes(t *testing.T) { UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 15 || @@ -932,7 +932,7 @@ func TestActionDebitCredit(t *testing.T) { UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 || @@ -950,7 +950,7 @@ func TestActionDebitMinutes(t *testing.T) { UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE][0].GetValue() != 5 || @@ -1119,7 +1119,7 @@ func TestActionPlanLogging(t *testing.T) { } func TestActionMakeNegative(t *testing.T) { - a := &Action{Balance: &BalanceFilter{Value: utils.Float64Pointer(10)}} + a := &Action{Balance: &BalanceFilter{Value: &ValueFormula{Static: 10}}} genericMakeNegative(a) if a.Balance.GetValue() > 0 { t.Error("Failed to make negative: ", a) @@ -1153,7 +1153,7 @@ func TestTopupAction(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minu") a := &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1174,7 +1174,7 @@ func TestTopupActionLoaded(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minitsboy") a := &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1201,7 +1201,7 @@ func TestActionCdrlogEmpty(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1223,11 +1223,11 @@ func TestActionCdrlogWithParams(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1250,11 +1250,11 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1335,11 +1335,11 @@ func TestActionTransactionFuncType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ ActionType: "VALID_FUNCTION_TYPE", - Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer("test")}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer("test")}, }, }, } @@ -1371,7 +1371,7 @@ func TestActionTransactionBalanceType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ ActionType: TOPUP, @@ -1407,7 +1407,7 @@ func TestActionTransactionBalanceNotType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.VOICE)}, + Balance: &BalanceFilter{Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.VOICE)}, }, &Action{ ActionType: TOPUP, @@ -1445,14 +1445,14 @@ func TestActionWithExpireWithoutExpire(t *testing.T) { ActionType: TOPUP, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), - Value: utils.Float64Pointer(15), + Value: &ValueFormula{Static: 15}, }, }, &Action{ ActionType: TOPUP, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), - Value: utils.Float64Pointer(30), + Value: &ValueFormula{Static: 30}, ExpirationDate: utils.TimePointer(time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC)), }, }, @@ -1672,7 +1672,7 @@ func TestActionConditionalTopup(t *testing.T) { Filter: `{"Type":"*monetary","Value":1,"Weight":10}`, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), - Value: utils.Float64Pointer(11), + Value: &ValueFormula{Static: 11}, Weight: utils.Float64Pointer(30), }, } @@ -1736,7 +1736,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { Filter: `{"Type":"*monetary","Value":2,"Weight":10}`, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), - Value: utils.Float64Pointer(11), + Value: &ValueFormula{Static: 11}, Weight: utils.Float64Pointer(30), }, } @@ -1800,7 +1800,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { Filter: `{"Type":"*voice","Value":{"*gte":100}}`, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), - Value: utils.Float64Pointer(11), + Value: &ValueFormula{Static: 11}, Weight: utils.Float64Pointer(10), }, } @@ -2021,7 +2021,7 @@ func TestActionSetBalance(t *testing.T) { Balance: &BalanceFilter{ ID: utils.StringPointer("m2"), Type: utils.StringPointer(utils.MONETARY), - Value: utils.Float64Pointer(11), + Value: &ValueFormula{Static: 11}, Weight: utils.Float64Pointer(10), }, } @@ -2122,7 +2122,7 @@ func TestActionCdrlogBalanceValue(t *testing.T) { Balance: &BalanceFilter{ ID: utils.StringPointer("*default"), Uuid: utils.StringPointer("25a02c82-f09f-4c6e-bacf-8ed4b076475a"), - Value: utils.Float64Pointer(1.1), + Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY), }, }, @@ -2132,7 +2132,7 @@ func TestActionCdrlogBalanceValue(t *testing.T) { Balance: &BalanceFilter{ ID: utils.StringPointer("*default"), Uuid: utils.StringPointer("25a02c82-f09f-4c6e-bacf-8ed4b076475a"), - Value: utils.Float64Pointer(2.1), + Value: &ValueFormula{Static: 2.1}, Type: utils.StringPointer(utils.MONETARY), }, }, diff --git a/engine/balance_filter.go b/engine/balance_filter.go index f9e408d39..bc98c2bae 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -1,7 +1,10 @@ package engine import ( + "encoding/json" + "errors" "reflect" + "strconv" "time" "github.com/cgrates/cgrates/utils" @@ -11,7 +14,7 @@ type BalanceFilter struct { Uuid *string ID *string Type *string - Value *float64 + Value *ValueFormula Directions *utils.StringMap ExpirationDate *time.Time Weight *float64 @@ -58,7 +61,7 @@ func (bf *BalanceFilter) Clone() *BalanceFilter { *result.ID = *bf.ID } if bf.Value != nil { - result.Value = new(float64) + result.Value = new(ValueFormula) *result.Value = *bf.Value } if bf.RatingSubject != nil { @@ -116,7 +119,7 @@ func (bf *BalanceFilter) LoadFromBalance(b *Balance) *BalanceFilter { bf.ID = &b.ID } if b.Value != 0 { - bf.Value = &b.Value + bf.Value.Static = b.Value } if !b.Directions.IsEmpty() { bf.Directions = &b.Directions @@ -173,14 +176,22 @@ func (bp *BalanceFilter) GetValue() float64 { if bp == nil || bp.Value == nil { return 0.0 } - return *bp.Value + if bp.Value.Method == "" { + return bp.Value.Static + } + // calculate using formula + formula, exists := valueFormulas[bp.Value.Method] + if !exists { + return 0.0 + } + return formula(bp.Value.Params) } func (bp *BalanceFilter) SetValue(v float64) { if bp.Value == nil { - bp.Value = new(float64) + bp.Value = new(ValueFormula) } - *bp.Value = v + bp.Value.Static = v } func (bp *BalanceFilter) GetUuid() string { @@ -292,7 +303,7 @@ func (bf *BalanceFilter) ModifyBalance(b *Balance) { b.Directions = *bf.Directions } if bf.Value != nil { - b.Value = *bf.Value + b.Value = bf.GetValue() } if bf.ExpirationDate != nil { b.ExpirationDate = *bf.ExpirationDate @@ -323,3 +334,37 @@ func (bf *BalanceFilter) ModifyBalance(b *Balance) { } b.SetDirty() // Mark the balance as dirty since we have modified and it should be checked by action triggers } + +//for computing a dynamic value for Value field +type ValueFormula struct { + Method string + Params map[string]interface{} + Static float64 +} + +func ParseBalanceFilterValue(val string) (*ValueFormula, error) { + u, err := strconv.ParseFloat(val, 64) + if err == nil { + return &ValueFormula{Static: u}, err + } + var vf ValueFormula + err = json.Unmarshal([]byte(val), &vf) + if err == nil { + return &vf, err + } + return nil, errors.New("Invalid value: " + val) +} + +type valueFormula func(map[string]interface{}) float64 + +const ( + PERIODIC = "*periodic" +) + +var valueFormulas = map[string]valueFormula{ + PERIODIC: periodicFormula, +} + +func periodicFormula(params map[string]interface{}) float64 { + return 0.0 +} diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 607a97a23..d37b417b0 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -41,12 +41,12 @@ func init() { func populateDB() { ats := []*Action{ - &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, - &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}}}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: &ValueFormula{Static: 10}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, } ats1 := []*Action{ - &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}, Weight: 10}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}}, Weight: 10}, &Action{ActionType: "*reset_account", Weight: 20}, } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index d26ebbcc6..851d474e9 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -840,7 +840,7 @@ func TestLoadActions(t *testing.T) { Type: utils.StringPointer(utils.MONETARY), Uuid: as1[0].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - Value: utils.Float64Pointer(10), + Value: &ValueFormula{Static: 10}, Weight: utils.Float64Pointer(10), DestinationIDs: nil, TimingIDs: nil, @@ -860,7 +860,7 @@ func TestLoadActions(t *testing.T) { Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - Value: utils.Float64Pointer(100), + Value: &ValueFormula{Static: 100}, Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), @@ -873,7 +873,7 @@ func TestLoadActions(t *testing.T) { }, } if !reflect.DeepEqual(as1, expected) { - t.Errorf("Error loading action1: %+v", as1[0].Balance) + t.Errorf("Error loading action1: %s", utils.ToIJSON(as1)) } as2 := csvr.actions["SHARED"] expected = []*Action{ @@ -887,7 +887,7 @@ func TestLoadActions(t *testing.T) { Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: nil, Uuid: as2[0].Balance.Uuid, - Value: utils.Float64Pointer(100), + Value: &ValueFormula{Static: 100}, Weight: utils.Float64Pointer(10), SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")), TimingIDs: nil, @@ -898,7 +898,7 @@ func TestLoadActions(t *testing.T) { }, } if !reflect.DeepEqual(as2, expected) { - t.Errorf("Error loading action: %+v", as2[0].Balance) + t.Errorf("Error loading action: %s", utils.ToIJSON(as2)) } as3 := csvr.actions["DEFEE"] expected = []*Action{ diff --git a/engine/tp_reader.go b/engine/tp_reader.go index e458e130e..6f85f3d89 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -531,11 +531,11 @@ func (tpr *TpReader) LoadActions() (err error) { } if tpact.Units != "" && tpact.Units != utils.ANY { - u, err := strconv.ParseFloat(tpact.Units, 64) + vf, err := ParseBalanceFilterValue(tpact.Units) if err != nil { return err } - acts[idx].Balance.Value = utils.Float64Pointer(u) + acts[idx].Balance.Value = vf } if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { @@ -1007,11 +1007,11 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error } if tpact.Units != "" && tpact.Units != utils.ANY { - u, err := strconv.ParseFloat(tpact.Units, 64) + vf, err := ParseBalanceFilterValue(tpact.Units) if err != nil { return err } - acts[idx].Balance.Value = utils.Float64Pointer(u) + acts[idx].Balance.Value = vf } if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { @@ -1355,11 +1355,11 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { } if tpact.Units != "" && tpact.Units != utils.ANY { - u, err := strconv.ParseFloat(tpact.Units, 64) + vf, err := ParseBalanceFilterValue(tpact.Units) if err != nil { return err } - acts[idx].Balance.Value = utils.Float64Pointer(u) + acts[idx].Balance.Value = vf } if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { diff --git a/utils/rpc_params.go b/utils/rpc_params.go index c48e84085..3e5f3f166 100644 --- a/utils/rpc_params.go +++ b/utils/rpc_params.go @@ -1,8 +1,6 @@ package utils -import ( - "reflect" -) +import "reflect" var rpcParamsMap map[string]*RpcParams @@ -39,12 +37,10 @@ func RegisterRpcParams(name string, obj interface{}) { OutParam: reflect.New(out).Interface(), } } - } } func GetRpcParams(method string) (*RpcParams, error) { - Logger.Info("REGISTERED: " + ToJSON(rpcParamsMap)) x, found := rpcParamsMap[method] if !found { return nil, ErrNotFound