diff --git a/apier/v1/apier.go b/apier/v1/apier.go
index 1f5c2ef35..dcdfeda45 100644
--- a/apier/v1/apier.go
+++ b/apier/v1/apier.go
@@ -556,6 +556,11 @@ func (self *ApierV1) GetActions(actsId string, reply *[]*utils.TPAction) error {
act.RatingSubject = engAct.Balance.RatingSubject
act.SharedGroups = engAct.Balance.SharedGroups.String()
act.BalanceWeight = engAct.Balance.Weight
+ act.TimingTags = engAct.Balance.TimingIDs.String()
+ act.BalanceId = engAct.Balance.Id
+ act.Categories = engAct.Balance.Categories.String()
+ act.BalanceBlocker = engAct.Balance.Blocker
+ act.BalanceDisabled = engAct.Balance.Disabled
}
acts = append(acts, act)
}
@@ -658,71 +663,6 @@ func (self *ApierV1) GetActionPlan(attr AttrGetActionPlan, reply *[]*engine.Acti
return nil
}
-type AttrAddActionTrigger struct {
- ActionTriggersId string
- ActionTriggersUniqueId string
- Tenant string
- Account string
- ThresholdType string
- ThresholdValue float64
- BalanceId string
- BalanceType string
- BalanceDirection string
- BalanceDestinationIds string
- BalanceRatingSubject string //ToDo
- BalanceWeight float64
- BalanceExpiryTime string
- BalanceSharedGroup string //ToDo
- Weight float64
- ActionsId string
-}
-
-func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string) error {
- if attr.BalanceDirection == "" {
- attr.BalanceDirection = utils.OUT
- }
- balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime, self.Config.DefaultTimezone)
- if err != nil {
- return utils.NewErrServerError(err)
- }
- at := &engine.ActionTrigger{
- ID: attr.ActionTriggersId,
- UniqueID: attr.ActionTriggersUniqueId,
- ThresholdType: attr.ThresholdType,
- ThresholdValue: attr.ThresholdValue,
- BalanceId: attr.BalanceId,
- BalanceType: attr.BalanceType,
- BalanceDirections: utils.ParseStringMap(attr.BalanceDirection),
- BalanceDestinationIds: utils.ParseStringMap(attr.BalanceDestinationIds),
- BalanceWeight: attr.BalanceWeight,
- BalanceExpirationDate: balExpiryTime,
- Weight: attr.Weight,
- ActionsId: attr.ActionsId,
- Executed: false,
- }
-
- tag := utils.AccountKey(attr.Tenant, attr.Account)
- _, err = engine.Guardian.Guard(func() (interface{}, error) {
- userBalance, err := self.AccountDb.GetAccount(tag)
- if err != nil {
- return 0, err
- }
-
- userBalance.ActionTriggers = append(userBalance.ActionTriggers, at)
-
- if err = self.AccountDb.SetAccount(userBalance); err != nil {
- return 0, err
- }
- return 0, nil
- }, 0, tag)
- if err != nil {
- *reply = err.Error()
- return err
- }
- *reply = OK
- return nil
-}
-
type AttrResetTriggeredAction struct {
Id string
Tenant string
diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go
index 571f4f44c..1ea6a0fae 100644
--- a/apier/v1/triggers.go
+++ b/apier/v1/triggers.go
@@ -5,30 +5,95 @@ import (
"github.com/cgrates/cgrates/utils"
)
-type AttrSetActionTriggers struct {
+type AttrAddActionTrigger struct {
+ ActionTriggersId string
+ ActionTriggersUniqueId string
Tenant string
Account string
- ActionTriggersIds *[]string
+ ThresholdType string
+ ThresholdValue float64
+ BalanceId string
+ BalanceType string
+ BalanceDirection string
+ BalanceDestinationIds string
+ BalanceRatingSubject string //ToDo
+ BalanceWeight float64
+ BalanceExpiryTime string
+ BalanceSharedGroup string //ToDo
+ Weight float64
+ ActionsId string
+}
+
+func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string) error {
+ if attr.BalanceDirection == "" {
+ attr.BalanceDirection = utils.OUT
+ }
+ balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime, self.Config.DefaultTimezone)
+ if err != nil {
+ return utils.NewErrServerError(err)
+ }
+ at := &engine.ActionTrigger{
+ ID: attr.ActionTriggersId,
+ UniqueID: attr.ActionTriggersUniqueId,
+ ThresholdType: attr.ThresholdType,
+ ThresholdValue: attr.ThresholdValue,
+ BalanceId: attr.BalanceId,
+ BalanceType: attr.BalanceType,
+ BalanceDirections: utils.ParseStringMap(attr.BalanceDirection),
+ BalanceDestinationIds: utils.ParseStringMap(attr.BalanceDestinationIds),
+ BalanceWeight: attr.BalanceWeight,
+ BalanceExpirationDate: balExpiryTime,
+ Weight: attr.Weight,
+ ActionsId: attr.ActionsId,
+ Executed: false,
+ }
+
+ tag := utils.AccountKey(attr.Tenant, attr.Account)
+ _, err = engine.Guardian.Guard(func() (interface{}, error) {
+ userBalance, err := self.AccountDb.GetAccount(tag)
+ if err != nil {
+ return 0, err
+ }
+
+ userBalance.ActionTriggers = append(userBalance.ActionTriggers, at)
+
+ if err = self.AccountDb.SetAccount(userBalance); err != nil {
+ return 0, err
+ }
+ return 0, nil
+ }, 0, tag)
+ if err != nil {
+ *reply = err.Error()
+ return err
+ }
+ *reply = OK
+ return nil
+}
+
+type AttrSetAccountActionTriggers struct {
+ Tenant string
+ Account string
+ ActionTriggersIDs *[]string
ActionTriggerOverwrite bool
}
-func (self *ApierV1) SetActionTriggers(attr AttrSetActionTriggers, reply *string) error {
+func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
accID := utils.AccountKey(attr.Tenant, attr.Account)
var account *engine.Account
_, err := engine.Guardian.Guard(func() (interface{}, error) {
- if acc, err := self.AccountDb.GetAccount(accID); err != nil {
+ if acc, err := self.AccountDb.GetAccount(accID); err == nil {
account = acc
} else {
return 0, err
}
- if attr.ActionTriggersIds != nil {
+ if attr.ActionTriggersIDs != nil {
if attr.ActionTriggerOverwrite {
account.ActionTriggers = make(engine.ActionTriggers, 0)
}
- for _, actionTriggerID := range *attr.ActionTriggersIds {
+ for _, actionTriggerID := range *attr.ActionTriggersIDs {
atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID)
if err != nil {
@@ -49,6 +114,9 @@ func (self *ApierV1) SetActionTriggers(attr AttrSetActionTriggers, reply *string
}
}
account.InitCounters()
+ if err := self.AccountDb.SetAccount(account); err != nil {
+ return 0, err
+ }
return 0, nil
}, 0, accID)
if err != nil {
@@ -59,21 +127,21 @@ func (self *ApierV1) SetActionTriggers(attr AttrSetActionTriggers, reply *string
return nil
}
-type AttrRemoveActionTriggers struct {
+type AttrRemoveAccountActionTriggers struct {
Tenant string
Account string
GroupID string
UniqueID string
}
-func (self *ApierV1) RemoveActionTriggers(attr AttrRemoveActionTriggers, reply *string) error {
+func (self *ApierV1) RemoveAccountActionTriggers(attr AttrRemoveAccountActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
accID := utils.AccountKey(attr.Tenant, attr.Account)
- var account *engine.Account
_, err := engine.Guardian.Guard(func() (interface{}, error) {
- if acc, err := self.AccountDb.GetAccount(accID); err != nil {
+ var account *engine.Account
+ if acc, err := self.AccountDb.GetAccount(accID); err == nil {
account = acc
} else {
return 0, err
@@ -88,8 +156,10 @@ func (self *ApierV1) RemoveActionTriggers(attr AttrRemoveActionTriggers, reply *
newActionTriggers = append(newActionTriggers, at)
}
account.ActionTriggers = newActionTriggers
-
account.InitCounters()
+ if err := self.AccountDb.SetAccount(account); err != nil {
+ return 0, err
+ }
return 0, nil
}, 0, accID)
if err != nil {
@@ -100,14 +170,7 @@ func (self *ApierV1) RemoveActionTriggers(attr AttrRemoveActionTriggers, reply *
return nil
}
-type AttrResetActionTriggers struct {
- Tenant string
- Account string
- GroupID string
- UniqueID string
-}
-
-func (self *ApierV1) ResetActionTriggers(attr AttrResetActionTriggers, reply *string) error {
+func (self *ApierV1) ResetAccountActionTriggers(attr AttrRemoveAccountActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
@@ -115,7 +178,7 @@ func (self *ApierV1) ResetActionTriggers(attr AttrResetActionTriggers, reply *st
accID := utils.AccountKey(attr.Tenant, attr.Account)
var account *engine.Account
_, err := engine.Guardian.Guard(func() (interface{}, error) {
- if acc, err := self.AccountDb.GetAccount(accID); err != nil {
+ if acc, err := self.AccountDb.GetAccount(accID); err == nil {
account = acc
} else {
return 0, err
@@ -129,6 +192,9 @@ func (self *ApierV1) ResetActionTriggers(attr AttrResetActionTriggers, reply *st
}
account.ExecuteActionTriggers(nil)
+ if err := self.AccountDb.SetAccount(account); err != nil {
+ return 0, err
+ }
return 0, nil
}, 0, accID)
if err != nil {
diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go
index e18c90eeb..b852b212d 100644
--- a/apier/v2/accounts.go
+++ b/apier/v2/accounts.go
@@ -81,9 +81,9 @@ func (self *ApierV2) GetAccount(attr *utils.AttrGetAccount, reply *engine.Accoun
type AttrSetAccount struct {
Tenant string
Account string
- ActionPlanIds *[]string
+ ActionPlanIDs *[]string
ActionPlansOverwrite bool
- ActionTriggersIds *[]string
+ ActionTriggerIDs *[]string
ActionTriggerOverwrite bool
AllowNegative *bool
Disabled *bool
@@ -105,7 +105,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error {
Id: accID,
}
}
- if attr.ActionPlanIds != nil {
+ if attr.ActionPlanIDs != nil {
_, err := engine.Guardian.Guard(func() (interface{}, error) {
actionPlansMap, err := self.RatingDb.GetAllActionPlans()
if err != nil {
@@ -121,7 +121,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error {
}
}
- for _, actionPlanID := range *attr.ActionPlanIds {
+ for _, actionPlanID := range *attr.ActionPlanIDs {
ap, ok := actionPlansMap[actionPlanID]
if !ok {
return 0, utils.ErrNotFound
@@ -162,11 +162,11 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error {
}
}
- if attr.ActionTriggersIds != nil {
+ if attr.ActionTriggerIDs != nil {
if attr.ActionTriggerOverwrite {
ub.ActionTriggers = make(engine.ActionTriggers, 0)
}
- for _, actionTriggerID := range *attr.ActionTriggersIds {
+ for _, actionTriggerID := range *attr.ActionTriggerIDs {
atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID)
if err != nil {
return 0, err
diff --git a/console/trigger_remove.go b/console/trigger_remove.go
new file mode 100644
index 000000000..a460d8528
--- /dev/null
+++ b/console/trigger_remove.go
@@ -0,0 +1,63 @@
+/*
+Rating system designed to be used in VoIP Carriers World
+Copyright (C) 2012-2015 ITsysCOM
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package console
+
+import "github.com/cgrates/cgrates/apier/v1"
+
+func init() {
+ c := &CmdRemoveTriggers{
+ name: "triggers_remove",
+ rpcMethod: "ApierV1.RemoveAccountActionTriggers",
+ rpcParams: &v1.AttrRemoveAccountActionTriggers{},
+ }
+ commands[c.Name()] = c
+ c.CommandExecuter = &CommandExecuter{c}
+}
+
+// Commander implementation
+type CmdRemoveTriggers struct {
+ name string
+ rpcMethod string
+ rpcParams *v1.AttrRemoveAccountActionTriggers
+ *CommandExecuter
+}
+
+func (self *CmdRemoveTriggers) Name() string {
+ return self.name
+}
+
+func (self *CmdRemoveTriggers) RpcMethod() string {
+ return self.rpcMethod
+}
+
+func (self *CmdRemoveTriggers) RpcParams(reset bool) interface{} {
+ if reset || self.rpcParams == nil {
+ self.rpcParams = &v1.AttrRemoveAccountActionTriggers{}
+ }
+ return self.rpcParams
+}
+
+func (self *CmdRemoveTriggers) PostprocessRpcParams() error {
+ return nil
+}
+
+func (self *CmdRemoveTriggers) RpcResult() interface{} {
+ var s string
+ return &s
+}
diff --git a/console/trigger_reset.go b/console/trigger_reset.go
new file mode 100644
index 000000000..0d5be9f24
--- /dev/null
+++ b/console/trigger_reset.go
@@ -0,0 +1,63 @@
+/*
+Rating system designed to be used in VoIP Carriers World
+Copyright (C) 2012-2015 ITsysCOM
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package console
+
+import "github.com/cgrates/cgrates/apier/v1"
+
+func init() {
+ c := &CmdResetTriggers{
+ name: "triggers_reset",
+ rpcMethod: "ApierV1.ResetAccountActionTriggers",
+ rpcParams: &v1.AttrRemoveAccountActionTriggers{},
+ }
+ commands[c.Name()] = c
+ c.CommandExecuter = &CommandExecuter{c}
+}
+
+// Commander implementation
+type CmdResetTriggers struct {
+ name string
+ rpcMethod string
+ rpcParams *v1.AttrRemoveAccountActionTriggers
+ *CommandExecuter
+}
+
+func (self *CmdResetTriggers) Name() string {
+ return self.name
+}
+
+func (self *CmdResetTriggers) RpcMethod() string {
+ return self.rpcMethod
+}
+
+func (self *CmdResetTriggers) RpcParams(reset bool) interface{} {
+ if reset || self.rpcParams == nil {
+ self.rpcParams = &v1.AttrRemoveAccountActionTriggers{}
+ }
+ return self.rpcParams
+}
+
+func (self *CmdResetTriggers) PostprocessRpcParams() error {
+ return nil
+}
+
+func (self *CmdResetTriggers) RpcResult() interface{} {
+ var s string
+ return &s
+}
diff --git a/console/trigger_set.go b/console/trigger_set.go
new file mode 100644
index 000000000..2e9095d95
--- /dev/null
+++ b/console/trigger_set.go
@@ -0,0 +1,63 @@
+/*
+Rating system designed to be used in VoIP Carriers World
+Copyright (C) 2012-2015 ITsysCOM
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package console
+
+import "github.com/cgrates/cgrates/apier/v1"
+
+func init() {
+ c := &CmdSetTriggers{
+ name: "triggers_set",
+ rpcMethod: "ApierV1.SetAccountActionTriggers",
+ rpcParams: &v1.AttrSetAccountActionTriggers{},
+ }
+ commands[c.Name()] = c
+ c.CommandExecuter = &CommandExecuter{c}
+}
+
+// Commander implementation
+type CmdSetTriggers struct {
+ name string
+ rpcMethod string
+ rpcParams *v1.AttrSetAccountActionTriggers
+ *CommandExecuter
+}
+
+func (self *CmdSetTriggers) Name() string {
+ return self.name
+}
+
+func (self *CmdSetTriggers) RpcMethod() string {
+ return self.rpcMethod
+}
+
+func (self *CmdSetTriggers) RpcParams(reset bool) interface{} {
+ if reset || self.rpcParams == nil {
+ self.rpcParams = &v1.AttrSetAccountActionTriggers{}
+ }
+ return self.rpcParams
+}
+
+func (self *CmdSetTriggers) PostprocessRpcParams() error {
+ return nil
+}
+
+func (self *CmdSetTriggers) RpcResult() interface{} {
+ var s string
+ return &s
+}
diff --git a/engine/action_plan.go b/engine/action_plan.go
index a633c8386..f3d2b4563 100644
--- a/engine/action_plan.go
+++ b/engine/action_plan.go
@@ -294,6 +294,7 @@ func (at *ActionTiming) Execute() (err error) {
// check action filter
if len(a.Filter) > 0 {
matched, err := ub.matchActionFilter(a.Filter)
+ //log.Print("Checkng: ", a.Filter, matched)
if err != nil {
return 0, err
}
diff --git a/engine/actions_test.go b/engine/actions_test.go
index 1c6da1a7d..1eb5367d6 100644
--- a/engine/actions_test.go
+++ b/engine/actions_test.go
@@ -1759,6 +1759,144 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) {
}
}
+func TestActionConditionalDisabledIfNegative(t *testing.T) {
+ err := accountingStorage.SetAccount(
+ &Account{
+ Id: "cgrates.org:af",
+ BalanceMap: map[string]BalanceChain{
+ "*data": BalanceChain{
+ &Balance{
+ Uuid: "fc927edb-1bd6-425e-a2a3-9fd8bafaa524",
+ Id: "for_v3hsillmilld500m_data_500_m",
+ Value: 5.242,
+ Weight: 10,
+ RatingSubject: "for_v3hsillmilld500m_data_forfait",
+ Categories: utils.StringMap{
+ "data_france": true,
+ },
+ },
+ },
+ "*monetary": BalanceChain{
+ &Balance{
+ Uuid: "9fa1847a-f36a-41a7-8ec0-dfaab370141e",
+ Id: "*default",
+ Value: -1.95001,
+ },
+ },
+ "*sms": BalanceChain{
+ &Balance{
+ Uuid: "d348d15d-2988-4ee4-b847-6a552f94e2ec",
+ Id: "for_v3hsillmilld500m_mms_ill",
+ Value: 20000,
+ Weight: 10,
+ DestinationIds: utils.StringMap{
+ "FRANCE_NATIONAL": true,
+ },
+ Categories: utils.StringMap{
+ "mms_france": true,
+ "tmms_france": true,
+ "vmms_france": true,
+ },
+ },
+ &Balance{
+ Uuid: "f4643517-31f6-4199-980f-04cf535471ed",
+ Id: "for_v3hsillmilld500m_sms_ill",
+ Value: 20000,
+ Weight: 10,
+ DestinationIds: utils.StringMap{
+ "FRANCE_NATIONAL": true,
+ },
+ Categories: utils.StringMap{
+ "ms_france": true,
+ },
+ },
+ },
+ "*voice": BalanceChain{
+ &Balance{
+ Uuid: "079ab190-77f4-44f3-9c6f-3a0dd1a59dfd",
+ Id: "for_v3hsillmilld500m_voice_3_h",
+ Value: 10800,
+ Weight: 10,
+ DestinationIds: utils.StringMap{
+ "FRANCE_NATIONAL": true,
+ },
+ Categories: utils.StringMap{
+ "call_france": true,
+ },
+ },
+ },
+ },
+ })
+ if err != nil {
+ t.Errorf("error setting account: %v", err)
+ }
+
+ a1 := &Action{
+ ActionType: "*enable_disable_balance",
+ BalanceType: "*sms",
+ Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}",
+ Balance: &Balance{
+ Weight: 10,
+ Disabled: true,
+ },
+ Weight: 9,
+ }
+ a2 := &Action{
+ ActionType: "*enable_disable_balance",
+ BalanceType: "*sms",
+ Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}",
+ Balance: &Balance{
+ DestinationIds: utils.NewStringMap("FRANCE_NATIONAL"),
+ Weight: 10,
+ Disabled: true,
+ },
+ Weight: 8,
+ }
+ a3 := &Action{
+ ActionType: "*enable_disable_balance",
+ BalanceType: "*data",
+ Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}",
+ Balance: &Balance{
+ RatingSubject: "for_v3hsillmilld500m_data_forfait",
+ Weight: 10,
+ Disabled: true,
+ },
+ Weight: 7,
+ }
+ a4 := &Action{
+ ActionType: "*enable_disable_balance",
+ BalanceType: "*voice",
+ Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}",
+ Balance: &Balance{
+ DestinationIds: utils.NewStringMap("FRANCE_NATIONAL"),
+ Weight: 10,
+ Disabled: true,
+ },
+ Weight: 6,
+ }
+
+ at := &ActionTiming{
+ accountIDs: utils.StringMap{"cgrates.org:af": true},
+ actions: Actions{a1, a2, a3, a4},
+ }
+ at.Execute()
+
+ afterUb, err := accountingStorage.GetAccount("cgrates.org:af")
+ if err != nil {
+ t.Error("account not found: ", err, afterUb)
+ }
+
+ for btype, chain := range afterUb.BalanceMap {
+ if btype != utils.MONETARY {
+ for _, b := range chain {
+ if b.Disabled != true {
+ t.Errorf("Failed to disabled balance (%s): %+v", btype, b)
+ }
+ }
+ }
+ }
+}
+
func TestActionSetBalance(t *testing.T) {
err := accountingStorage.SetAccount(
&Account{