diff --git a/.gitignore b/.gitignore index 029f8c7de..78ccff556 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ docs/_* bin .idea dean* +data/vagrant/.vagrant diff --git a/apier/accounts.go b/apier/accounts.go index a9bc9d5d8..2ac23ae95 100644 --- a/apier/accounts.go +++ b/apier/accounts.go @@ -51,7 +51,7 @@ func (self *ApierV1) GetAccountActionPlan(attrs AttrAcntAction, reply *[]*Accoun } for _, ats := range allATs { for _, at := range ats { - if utils.IsSliceMember(at.UserBalanceIds, utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)) { + if utils.IsSliceMember(at.AccountIds, utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)) { accountATs = append(accountATs, &AccountActionTiming{Id: at.Id, ActionPlanId: at.Tag, ActionsId: at.ActionsId, NextExecTime: at.GetNextStartTime(time.Now())}) } } @@ -108,7 +108,7 @@ func (self *ApierV1) GetAccountActionTriggers(attrs AttrAcntAction, reply *engin if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account", "Direction"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if balance, err := self.AccountDb.GetUserBalance(utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)); err != nil { + if balance, err := self.AccountDb.GetAccount(utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else { *reply = balance.ActionTriggers @@ -130,7 +130,7 @@ func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, r } balanceId := utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction) _, err := engine.AccLock.Guard(balanceId, func() (float64, error) { - ub, err := self.AccountDb.GetUserBalance(balanceId) + ub, err := self.AccountDb.GetAccount(balanceId) if err != nil { return 0, err } @@ -144,7 +144,7 @@ func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, r ub.ActionTriggers = make(engine.ActionTriggerPriotityList, 0) } } - if err := self.AccountDb.SetUserBalance(ub); err != nil { + if err := self.AccountDb.SetAccount(ub); err != nil { return 0, err } return 0, nil @@ -157,11 +157,11 @@ func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, r } type AttrSetAccount struct { - Tenant string - Direction string - Account string - Type string // <*prepaid|*postpaid> - ActionPlanId string + Tenant string + Direction string + Account string + ActionPlanId string + AllowNegative bool } // Ads a new account into dataDb. If already defined, returns success. @@ -170,20 +170,15 @@ func (self *ApierV1) SetAccount(attr AttrSetAccount, reply *string) error { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } balanceId := utils.BalanceKey(attr.Tenant, attr.Account, attr.Direction) - var ub *engine.UserBalance + var ub *engine.Account var ats engine.ActionPlan _, err := engine.AccLock.Guard(balanceId, func() (float64, error) { - if bal, _ := self.AccountDb.GetUserBalance(balanceId); bal != nil { + if bal, _ := self.AccountDb.GetAccount(balanceId); bal != nil { ub = bal } else { // Not found in db, create it here - if len(attr.Type) == 0 { - attr.Type = engine.UB_TYPE_PREPAID - } else if !utils.IsSliceMember([]string{engine.UB_TYPE_POSTPAID, engine.UB_TYPE_PREPAID}, attr.Type) { - return 0, fmt.Errorf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, "Type") - } - ub = &engine.UserBalance{ - Id: balanceId, - Type: attr.Type, + ub = &engine.Account{ + Id: balanceId, + AllowNegative: attr.AllowNegative, } } @@ -194,11 +189,11 @@ func (self *ApierV1) SetAccount(attr AttrSetAccount, reply *string) error { return 0, err } for _, at := range ats { - at.UserBalanceIds = append(at.UserBalanceIds, balanceId) + at.AccountIds = append(at.AccountIds, balanceId) } } // All prepared, save account - if err := self.AccountDb.SetUserBalance(ub); err != nil { + if err := self.AccountDb.SetAccount(ub); err != nil { return 0, err } return 0, nil diff --git a/apier/apier.go b/apier/apier.go index 665bcd85f..0f4d3086b 100644 --- a/apier/apier.go +++ b/apier/apier.go @@ -62,17 +62,17 @@ func (self *ApierV1) GetRatingPlan(rplnId string, reply *engine.RatingPlan) erro return nil } -type AttrGetUserBalance struct { - Tenant string - Account string - BalanceId string - Direction string +type AttrGetAccount struct { + Tenant string + Account string + BalanceType string + Direction string } // Get balance -func (self *ApierV1) GetUserBalance(attr *AttrGetUserBalance, reply *engine.UserBalance) error { +func (self *ApierV1) GetAccount(attr *AttrGetAccount, reply *engine.Account) error { tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account) - userBalance, err := self.AccountDb.GetUserBalance(tag) + userBalance, err := self.AccountDb.GetAccount(tag) if err != nil { return err } @@ -84,7 +84,7 @@ func (self *ApierV1) GetUserBalance(attr *AttrGetUserBalance, reply *engine.User type AttrAddBalance struct { Tenant string Account string - BalanceId string + BalanceType string Direction string Value float64 ExpirationDate time.Time @@ -96,18 +96,18 @@ type AttrAddBalance struct { func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account) - if _, err := self.AccountDb.GetUserBalance(tag); err != nil { + if _, err := self.AccountDb.GetAccount(tag); err != nil { // create user balance if not exists - ub := &engine.UserBalance{ + ub := &engine.Account{ Id: tag, } - if err := self.AccountDb.SetUserBalance(ub); err != nil { + if err := self.AccountDb.SetAccount(ub); err != nil { *reply = err.Error() return err } } at := &engine.ActionTiming{ - UserBalanceIds: []string{tag}, + AccountIds: []string{tag}, } if attr.Direction == "" { @@ -119,9 +119,9 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { } at.SetActions(engine.Actions{ &engine.Action{ - ActionType: aType, - BalanceId: attr.BalanceId, - Direction: attr.Direction, + ActionType: aType, + BalanceType: attr.BalanceType, + Direction: attr.Direction, Balance: &engine.Balance{ Value: attr.Value, ExpirationDate: attr.ExpirationDate, @@ -149,8 +149,8 @@ type AttrExecuteAction struct { func (self *ApierV1) ExecuteAction(attr *AttrExecuteAction, reply *string) error { tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account) at := &engine.ActionTiming{ - UserBalanceIds: []string{tag}, - ActionsId: attr.ActionsId, + AccountIds: []string{tag}, + ActionsId: attr.ActionsId, } if err := at.Execute(); err != nil { @@ -274,7 +274,7 @@ func (self *ApierV1) SetActions(attrs AttrSetActions, reply *string) error { a := &engine.Action{ Id: utils.GenUUID(), ActionType: apiAct.Identifier, - BalanceId: apiAct.BalanceType, + BalanceType: apiAct.BalanceType, Direction: apiAct.Direction, Weight: apiAct.Weight, ExpirationString: apiAct.ExpiryTime, @@ -370,7 +370,7 @@ type AttrAddActionTrigger struct { Tenant string Account string Direction string - BalanceId string + BalanceType string ThresholdType string ThresholdValue float64 DestinationId string @@ -385,7 +385,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string at := &engine.ActionTrigger{ Id: utils.GenUUID(), - BalanceId: attr.BalanceId, + BalanceType: attr.BalanceType, Direction: attr.Direction, ThresholdType: attr.ThresholdType, ThresholdValue: attr.ThresholdValue, @@ -397,14 +397,14 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string tag := utils.BalanceKey(attr.Tenant, attr.Account, attr.Direction) _, err := engine.AccLock.Guard(tag, func() (float64, error) { - userBalance, err := self.AccountDb.GetUserBalance(tag) + userBalance, err := self.AccountDb.GetAccount(tag) if err != nil { return 0, err } userBalance.ActionTriggers = append(userBalance.ActionTriggers, at) - if err = self.AccountDb.SetUserBalance(userBalance); err != nil { + if err = self.AccountDb.SetAccount(userBalance); err != nil { return 0, err } return 0, nil diff --git a/apier/apier_local_test.go b/apier/apier_local_test.go index d49eb9aa3..26c0f3600 100644 --- a/apier/apier_local_test.go +++ b/apier/apier_local_test.go @@ -887,43 +887,43 @@ func TestApierAddBalance(t *testing.T) { return } reply := "" - attrs := &AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceId: "*monetary", Direction: "*out", Value: 1.5} + attrs := &AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Direction: "*out", Value: 1.5} if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { t.Error("Got error on ApierV1.AddBalance: ", err.Error()) } else if reply != "OK" { t.Errorf("Calling ApierV1.AddBalance received: %s", reply) } - attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan", BalanceId: "*monetary", Direction: "*out", Value: 1.5} + attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan", BalanceType: "*monetary", Direction: "*out", Value: 1.5} if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { t.Error("Got error on ApierV1.AddBalance: ", err.Error()) } else if reply != "OK" { t.Errorf("Calling ApierV1.AddBalance received: %s", reply) } - attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan2", BalanceId: "*monetary", Direction: "*out", Value: 1.5} + attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan2", BalanceType: "*monetary", Direction: "*out", Value: 1.5} if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { t.Error("Got error on ApierV1.AddBalance: ", err.Error()) } else if reply != "OK" { t.Errorf("Calling ApierV1.AddBalance received: %s", reply) } - attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan3", BalanceId: "*monetary", Direction: "*out", Value: 1.5} + attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan3", BalanceType: "*monetary", Direction: "*out", Value: 1.5} if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { t.Error("Got error on ApierV1.AddBalance: ", err.Error()) } else if reply != "OK" { t.Errorf("Calling ApierV1.AddBalance received: %s", reply) } - attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan3", BalanceId: "*monetary", Direction: "*out", Value: 2.1} + attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan3", BalanceType: "*monetary", Direction: "*out", Value: 2.1} if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { t.Error("Got error on ApierV1.AddBalance: ", err.Error()) } else if reply != "OK" { t.Errorf("Calling ApierV1.AddBalance received: %s", reply) } - attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan6", BalanceId: "*monetary", Direction: "*out", Value: 2.1} + attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan6", BalanceType: "*monetary", Direction: "*out", Value: 2.1} if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { t.Error("Got error on ApierV1.AddBalance: ", err.Error()) } else if reply != "OK" { t.Errorf("Calling ApierV1.AddBalance received: %s", reply) } - attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan6", BalanceId: "*monetary", Direction: "*out", Value: 1, Overwrite: true} + attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan6", BalanceType: "*monetary", Direction: "*out", Value: 1, Overwrite: true} if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { t.Error("Got error on ApierV1.AddBalance: ", err.Error()) } else if reply != "OK" { @@ -996,7 +996,7 @@ func TestApierAddTriggeredAction(t *testing.T) { } reply := "" // Add balance to a previously known account - attrs := &AttrAddActionTrigger{Tenant: "cgrates.org", Account: "dan2", Direction: "*out", BalanceId: "*monetary", + attrs := &AttrAddActionTrigger{Tenant: "cgrates.org", Account: "dan2", Direction: "*out", BalanceType: "*monetary", ThresholdType: "*min_balance", ThresholdValue: 2, DestinationId: "*any", Weight: 10, ActionsId: "WARN_VIA_HTTP"} if err := rater.Call("ApierV1.AddTriggeredAction", attrs, &reply); err != nil { t.Error("Got error on ApierV1.AddTriggeredAction: ", err.Error()) @@ -1060,7 +1060,7 @@ func TestApierSetAccount(t *testing.T) { return } reply := "" - attrs := &AttrSetAccount{Tenant: "cgrates.org", Direction: "*out", Account: "dan7", Type: "*prepaid", ActionPlanId: "ATMS_1"} + attrs := &AttrSetAccount{Tenant: "cgrates.org", Direction: "*out", Account: "dan7", ActionPlanId: "ATMS_1"} if err := rater.Call("ApierV1.SetAccount", attrs, &reply); err != nil { t.Error("Got error on ApierV1.SetAccount: ", err.Error()) } else if reply != "OK" { @@ -1115,42 +1115,42 @@ func TestApierRemActionTiming(t *testing.T) { } } -// Test here GetUserBalance -func TestApierGetUserBalance(t *testing.T) { +// Test here GetAccount +func TestApierGetAccount(t *testing.T) { if !*testLocal { return } - var reply *engine.UserBalance - attrs := &AttrGetUserBalance{Tenant: "cgrates.org", Account: "1001", BalanceId: "*monetary", Direction: "*out"} - if err := rater.Call("ApierV1.GetUserBalance", attrs, &reply); err != nil { - t.Error("Got error on ApierV1.GetUserBalance: ", err.Error()) - } else if reply.BalanceMap[attrs.BalanceId+attrs.Direction].GetTotalValue() != 11.5 { // We expect 11.5 since we have added in the previous test 1.5 + var reply *engine.Account + attrs := &AttrGetAccount{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetAccount", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccount: ", err.Error()) + } else if reply.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue() != 11.5 { // We expect 11.5 since we have added in the previous test 1.5 t.Errorf("Calling ApierV1.GetBalance expected: 11.5, received: %f", reply) } - attrs = &AttrGetUserBalance{Tenant: "cgrates.org", Account: "dan", BalanceId: "*monetary", Direction: "*out"} - if err := rater.Call("ApierV1.GetUserBalance", attrs, &reply); err != nil { - t.Error("Got error on ApierV1.GetUserBalance: ", err.Error()) - } else if reply.BalanceMap[attrs.BalanceId+attrs.Direction].GetTotalValue() != 1.5 { - t.Errorf("Calling ApierV1.GetUserBalance expected: 1.5, received: %f", reply) + attrs = &AttrGetAccount{Tenant: "cgrates.org", Account: "dan", BalanceType: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetAccount", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccount: ", err.Error()) + } else if reply.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue() != 1.5 { + t.Errorf("Calling ApierV1.GetAccount expected: 1.5, received: %f", reply) } // The one we have topped up though executeAction - attrs = &AttrGetUserBalance{Tenant: "cgrates.org", Account: "dan2", BalanceId: "*monetary", Direction: "*out"} - if err := rater.Call("ApierV1.GetUserBalance", attrs, &reply); err != nil { - t.Error("Got error on ApierV1.GetUserBalance: ", err.Error()) - } else if reply.BalanceMap[attrs.BalanceId+attrs.Direction].GetTotalValue() != 10 { - t.Errorf("Calling ApierV1.GetUserBalance expected: 10, received: %f", reply) + attrs = &AttrGetAccount{Tenant: "cgrates.org", Account: "dan2", BalanceType: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetAccount", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccount: ", err.Error()) + } else if reply.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue() != 10 { + t.Errorf("Calling ApierV1.GetAccount expected: 10, received: %f", reply) } - attrs = &AttrGetUserBalance{Tenant: "cgrates.org", Account: "dan3", BalanceId: "*monetary", Direction: "*out"} - if err := rater.Call("ApierV1.GetUserBalance", attrs, &reply); err != nil { - t.Error("Got error on ApierV1.GetUserBalance: ", err.Error()) - } else if reply.BalanceMap[attrs.BalanceId+attrs.Direction].GetTotalValue() != 3.6 { - t.Errorf("Calling ApierV1.GetUserBalance expected: 3.6, received: %f", reply) + attrs = &AttrGetAccount{Tenant: "cgrates.org", Account: "dan3", BalanceType: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetAccount", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccount: ", err.Error()) + } else if reply.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue() != 3.6 { + t.Errorf("Calling ApierV1.GetAccount expected: 3.6, received: %f", reply) } - attrs = &AttrGetUserBalance{Tenant: "cgrates.org", Account: "dan6", BalanceId: "*monetary", Direction: "*out"} - if err := rater.Call("ApierV1.GetUserBalance", attrs, &reply); err != nil { - t.Error("Got error on ApierV1.GetUserBalance: ", err.Error()) - } else if reply.BalanceMap[attrs.BalanceId+attrs.Direction].GetTotalValue() != 1 { - t.Errorf("Calling ApierV1.GetUserBalance expected: 1, received: %f", reply) + attrs = &AttrGetAccount{Tenant: "cgrates.org", Account: "dan6", BalanceType: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetAccount", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccount: ", err.Error()) + } else if reply.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue() != 1 { + t.Errorf("Calling ApierV1.GetAccount expected: 1, received: %f", reply) } } @@ -1160,13 +1160,13 @@ func TestTriggersExecute(t *testing.T) { return } reply := "" - attrs := &AttrSetAccount{Tenant: "cgrates.org", Direction: "*out", Account: "dan8", Type: "*prepaid"} + attrs := &AttrSetAccount{Tenant: "cgrates.org", Direction: "*out", Account: "dan8"} if err := rater.Call("ApierV1.SetAccount", attrs, &reply); err != nil { t.Error("Got error on ApierV1.SetAccount: ", err.Error()) } else if reply != "OK" { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } - attrAddBlnc := &AttrAddBalance{Tenant: "cgrates.org", Account: "1008", BalanceId: "*monetary", Direction: "*out", Value: 2} + attrAddBlnc := &AttrAddBalance{Tenant: "cgrates.org", Account: "1008", BalanceType: "*monetary", Direction: "*out", Value: 2} if err := rater.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil { t.Error("Got error on ApierV1.AddBalance: ", err.Error()) } else if reply != "OK" { diff --git a/console/add_account.go b/console/add_account.go index 86c68f069..d4e025990 100644 --- a/console/add_account.go +++ b/console/add_account.go @@ -20,6 +20,8 @@ package console import ( "fmt" + "strconv" + "github.com/cgrates/cgrates/apier" ) @@ -36,7 +38,7 @@ type CmdAddAccount struct { // name should be exec's name func (self *CmdAddAccount) Usage(name string) string { - return fmt.Sprintf("\n\tUsage: cgr-console [cfg_opts...{-h}] add_account []") + return fmt.Sprintf("\n\tUsage: cgr-console [cfg_opts...{-h}] add_account []") } // set param defaults @@ -55,7 +57,11 @@ func (self *CmdAddAccount) FromArgs(args []string) error { self.defaults() self.rpcParams.Tenant = args[2] self.rpcParams.Account = args[3] - self.rpcParams.Type = args[4] + if an, err := strconv.ParseBool(args[4]); err != nil { + return fmt.Errorf("Error parsing allownegative boolean: ", args[4]) + } else { + self.rpcParams.AllowNegative = an + } self.rpcParams.ActionPlanId = args[5] if len(args) > 6 { self.rpcParams.Direction = args[6] diff --git a/console/add_balance.go b/console/add_balance.go index d025c796c..22a0a53c7 100644 --- a/console/add_balance.go +++ b/console/add_balance.go @@ -45,7 +45,7 @@ func (self *CmdAddBalance) Usage(name string) string { // set param defaults func (self *CmdAddBalance) defaults() error { self.rpcMethod = "ApierV1.AddBalance" - self.rpcParams = &apier.AttrAddBalance{BalanceId: engine.CREDIT} + self.rpcParams = &apier.AttrAddBalance{BalanceType: engine.CREDIT} self.rpcParams.Direction = "*out" return nil } @@ -66,7 +66,7 @@ func (self *CmdAddBalance) FromArgs(args []string) error { } self.rpcParams.Value = value if len(args) > 5 { - self.rpcParams.BalanceId = args[5] + self.rpcParams.BalanceType = args[5] } if len(args) > 6 { self.rpcParams.DestinationId = args[6] diff --git a/console/add_triggeredaction.go b/console/add_triggeredaction.go index a88a7ec8d..5e3ad07c1 100644 --- a/console/add_triggeredaction.go +++ b/console/add_triggeredaction.go @@ -20,8 +20,9 @@ package console import ( "fmt" - "github.com/cgrates/cgrates/apier" "strconv" + + "github.com/cgrates/cgrates/apier" ) func init() { @@ -56,7 +57,7 @@ func (self *CmdAddTriggeredAction) FromArgs(args []string) error { self.defaults() self.rpcParams.Tenant = args[2] self.rpcParams.Account = args[3] - self.rpcParams.BalanceId = args[4] + self.rpcParams.BalanceType = args[4] thresholdvalue, err := strconv.ParseFloat(args[5], 64) if err != nil { return err diff --git a/console/get_balances.go b/console/get_account.go similarity index 88% rename from console/get_balances.go rename to console/get_account.go index ce7a2e2d1..28d05a31e 100644 --- a/console/get_balances.go +++ b/console/get_account.go @@ -26,14 +26,14 @@ import ( ) func init() { - commands["get_balances"] = &CmdGetBalances{} + commands["get_account"] = &CmdGetBalances{} } // Commander implementation type CmdGetBalances struct { rpcMethod string - rpcParams *apier.AttrGetUserBalance - rpcResult *engine.UserBalance + rpcParams *apier.AttrGetAccount + rpcResult *engine.Account } // name should be exec's name @@ -43,8 +43,8 @@ func (self *CmdGetBalances) Usage(name string) string { // set param defaults func (self *CmdGetBalances) defaults() error { - self.rpcMethod = "ApierV1.GetUserBalance" - self.rpcParams = &apier.AttrGetUserBalance{BalanceId: engine.CREDIT} + self.rpcMethod = "ApierV1.GetAccount" + self.rpcParams = &apier.AttrGetAccount{BalanceType: engine.CREDIT} self.rpcParams.Direction = "*out" return nil } diff --git a/data/vagrant/Vagrantfile b/data/vagrant/Vagrantfile new file mode 100644 index 000000000..73bc0bff8 --- /dev/null +++ b/data/vagrant/Vagrantfile @@ -0,0 +1,60 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + # All Vagrant configuration is done here. The most common configuration + # options are documented and commented below. For a complete reference, + # please see the online documentation at vagrantup.com. + + # Every Vagrant virtual environment requires a box to build off of. + config.vm.box = "debian74_64" + config.vm.box_url = "https://s3-eu-west-1.amazonaws.com/ffuenf-vagrant-boxes/debian/debian-7.4.0-amd64_virtualbox.box" + config.vm.network :public_network + + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # config.vm.network :forwarded_port, guest: 80, host: 8080 + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network :private_network, ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network :public_network + + # If true, then any SSH connections made will enable agent forwarding. + # Default value: false + # config.ssh.forward_agent = true + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider :virtualbox do |vb| + # # Don't boot with headless mode + # vb.gui = true + # + # # Use VBoxManage to customize the VM. For example to change memory: + # vb.customize ["modifyvm", :id, "--memory", "1024"] + # end + # + # View the documentation for the provider you're using for more + # information on available options. + config.vm.provision "ansible" do |ansible| + ansible.playbook = "ansible_golang_release.yml" + #ansible.verbose = "vvvv" + end +end diff --git a/data/vagrant/ansible_golang_release.yml b/data/vagrant/ansible_golang_release.yml new file mode 100644 index 000000000..2c499a6dc --- /dev/null +++ b/data/vagrant/ansible_golang_release.yml @@ -0,0 +1,71 @@ +--- + +- hosts: all + user: vagrant + sudo: yes + vars: + root_db_password: CGRateS.org + + tasks: + - name: install dependency + apt: pkg={{ item }} state=latest + with_items: + - git + - bzr + - redis-server + - mysql-server + - python-pycurl + - python-mysqldb + - mercurial + + - name: add freeswitch gpg key + command: gpg --keyserver pool.sks-keyservers.net --recv-key D76EDC7725E010CF + + - name: add freeswitch apt key + shell: gpg -a --export D76EDC7725E010CF | sudo apt-key add - + + - name: add freeswitch apt repo + apt_repository: repo='deb http://files.freeswitch.org/repo/deb/debian/ wheezy main' state=present + + - name: install freeswitch + apt: pkg={{ item }} update_cache=yes state=latest + with_items: + - freeswitch-meta-vanilla + - freeswitch-mod-json-cdr + + - name: add cgrates apt-key + apt_key: url=http://apt.itsyscom.com/conf/cgrates.gpg.key state=present + + - name: add cgrates repo + apt_repository: repo='deb http://apt.itsyscom.com/debian wheezy main' state=present + + - name: install cgrates + apt: pkg=cgrates update_cache=yes state=latest + + - name: update mysql root password for root account + mysql_user: name=root host=localhost password={{ root_db_password }} + + - name: copy .my.cnf + template: src=my.cnf dest=/root/.my.cnf mode=0600 + + - name: setup database tables + shell: chdir=/usr/share/cgrates/storage/mysql ./setup_cgr_db.sh root {{ root_db_password }} localhost + +- hosts: all + user: vagrant + + tasks: + - name: get golang + get_url: url=http://go.googlecode.com/files/go1.2.linux-amd64.tar.gz dest=~/go1.2.linux-amd64.tar.gz + + - name: unpack go + command: chdir=~/ tar xvf go1.2.linux-amd64.tar.gz + + - name: delete golang archive + file: path=~/go1.2.linux-amd64.tar.gz state=absent + + - name: copy bashrc + copy: src=bashrc dest=~/.bashrc + + - name: get cgrates + shell: GOROOT=~/go GOPATH=~/go_home ~/go/bin/go get -u -v github.com/cgrates/cgrates diff --git a/data/vagrant/bashrc b/data/vagrant/bashrc new file mode 100644 index 000000000..5695ae03e --- /dev/null +++ b/data/vagrant/bashrc @@ -0,0 +1,115 @@ +# ~/.bashrc: executed by bash(1) for non-login shells. +# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) +# for examples + +# If not running interactively, don't do anything +case $- in + *i*) ;; + *) return;; +esac + +# don't put duplicate lines or lines starting with space in the history. +# See bash(1) for more options +HISTCONTROL=ignoreboth + +# append to the history file, don't overwrite it +shopt -s histappend + +# for setting history length see HISTSIZE and HISTFILESIZE in bash(1) +HISTSIZE=1000 +HISTFILESIZE=2000 + +# check the window size after each command and, if necessary, +# update the values of LINES and COLUMNS. +shopt -s checkwinsize + +# If set, the pattern "**" used in a pathname expansion context will +# match all files and zero or more directories and subdirectories. +#shopt -s globstar + +# make less more friendly for non-text input files, see lesspipe(1) +#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" + +# set variable identifying the chroot you work in (used in the prompt below) +if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then + debian_chroot=$(cat /etc/debian_chroot) +fi + +# set a fancy prompt (non-color, unless we know we "want" color) +case "$TERM" in + xterm-color) color_prompt=yes;; +esac + +# uncomment for a colored prompt, if the terminal has the capability; turned +# off by default to not distract the user: the focus in a terminal window +# should be on the output of commands, not on the prompt +#force_color_prompt=yes + +if [ -n "$force_color_prompt" ]; then + if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then + # We have color support; assume it's compliant with Ecma-48 + # (ISO/IEC-6429). (Lack of such support is extremely rare, and such + # a case would tend to support setf rather than setaf.) + color_prompt=yes + else + color_prompt= + fi +fi + +if [ "$color_prompt" = yes ]; then + PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' +else + PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' +fi +unset color_prompt force_color_prompt + +# If this is an xterm set the title to user@host:dir +case "$TERM" in +xterm*|rxvt*) + PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" + ;; +*) + ;; +esac + +# enable color support of ls and also add handy aliases +if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + #alias dir='dir --color=auto' + #alias vdir='vdir --color=auto' + + #alias grep='grep --color=auto' + #alias fgrep='fgrep --color=auto' + #alias egrep='egrep --color=auto' +fi + +# some more ls aliases +#alias ll='ls -l' +#alias la='ls -A' +#alias l='ls -CF' + +# Alias definitions. +# You may want to put all your additions into a separate file like +# ~/.bash_aliases, instead of adding them here directly. +# See /usr/share/doc/bash-doc/examples in the bash-doc package. + +if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases +fi + +# enable programmable completion features (you don't need to enable +# this, if it's already enabled in /etc/bash.bashrc and /etc/profile +# sources /etc/bash.bashrc). +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi + +export GOROOT=$HOME/go +export GOPATH=$HOME/go_home + +export PATH=$PATH:$GOROOT/bin:$GOPATH/bin diff --git a/data/vagrant/my.cnf b/data/vagrant/my.cnf new file mode 100644 index 000000000..f86974c39 --- /dev/null +++ b/data/vagrant/my.cnf @@ -0,0 +1,3 @@ +[client] +user=root +password={{ root_db_password }} diff --git a/data/vagrant/vagrant_ansible_inventory_default b/data/vagrant/vagrant_ansible_inventory_default new file mode 100644 index 000000000..3a8f94913 --- /dev/null +++ b/data/vagrant/vagrant_ansible_inventory_default @@ -0,0 +1,3 @@ +# Generated by Vagrant + +default ansible_ssh_host=127.0.0.1 ansible_ssh_port=2222 diff --git a/engine/userbalance.go b/engine/account.go similarity index 80% rename from engine/userbalance.go rename to engine/account.go index 79c86971b..8a6eefb82 100644 --- a/engine/userbalance.go +++ b/engine/account.go @@ -30,8 +30,6 @@ import ( ) const ( - UB_TYPE_POSTPAID = "*postpaid" - UB_TYPE_PREPAID = "*prepaid" // Direction type INBOUND = "*in" OUTBOUND = "*out" @@ -59,23 +57,23 @@ var ( Structure containing information about user's credit (minutes, cents, sms...).' This can represent a user or a shared group. */ -type UserBalance struct { +type Account struct { Id string - Type string // prepaid-postpaid BalanceMap map[string]BalanceChain UnitCounters []*UnitsCounter ActionTriggers ActionTriggerPriotityList Groups GroupLinks // user info about groups // group information - UserIds []string // group info about users - Disabled bool + UserIds []string // group info about users + AllowNegative bool + Disabled bool } // Returns user's available minutes for the specified destination -func (ub *UserBalance) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances BalanceChain) { + +func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances BalanceChain) { credit = ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[CREDIT+cd.Direction], "").GetTotalValue() balances = ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[MINUTES+cd.Direction], "") - for _, b := range balances { d, c := b.GetMinutesForCredit(cd, credit) credit = c @@ -86,7 +84,7 @@ func (ub *UserBalance) getCreditForPrefix(cd *CallDescriptor) (duration time.Dur // Debits some amount of user's specified balance adding the balance if it does not exists. // Returns the remaining credit in user's balance. -func (ub *UserBalance) debitBalanceAction(a *Action) error { +func (ub *Account) debitBalanceAction(a *Action) error { if a == nil { return errors.New("nil minute action!") } @@ -94,10 +92,10 @@ func (ub *UserBalance) debitBalanceAction(a *Action) error { a.Balance.Uuid = utils.GenUUID() } if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]BalanceChain, 0) + ub.BalanceMap = make(map[string]BalanceChain, 1) } found := false - id := a.BalanceId + a.Direction + id := a.BalanceType + a.Direction for _, b := range ub.BalanceMap[id] { if b.IsExpired() { continue // we can clean expired balances balances here @@ -108,9 +106,8 @@ func (ub *UserBalance) debitBalanceAction(a *Action) error { break } } - // if it is not found and the Seconds are negative (topup) - // then we add it to the list - if !found && a.Balance.Value <= 0 { + // if it is not found then we add it to the list + if !found { a.Balance.Value = -a.Balance.Value ub.BalanceMap[id] = append(ub.BalanceMap[id], a.Balance) if a.Balance.SharedGroup != "" { @@ -130,10 +127,10 @@ func (ub *UserBalance) debitBalanceAction(a *Action) error { return nil //ub.BalanceMap[id].GetTotalValue() } -func (ub *UserBalance) getBalancesForPrefix(prefix string, balances BalanceChain, sharedGroup string) BalanceChain { +func (ub *Account) getBalancesForPrefix(prefix string, balances BalanceChain, sharedGroup string) BalanceChain { var usefulBalances BalanceChain for _, b := range balances { - if b.IsExpired() || (ub.Type != UB_TYPE_POSTPAID && b.Value <= 0) { + if b.IsExpired() || (ub.AllowNegative == false && b.Value <= 0) { continue } if b.SharedGroup != sharedGroup { @@ -142,7 +139,7 @@ func (ub *UserBalance) getBalancesForPrefix(prefix string, balances BalanceChain if b.SharedGroup != "" { // we are asking for balances from a sharing user so // attach ub info to the balance - b.userBalance = ub + b.account = ub } if b.DestinationId != "" && b.DestinationId != utils.ANY { for _, p := range utils.SplitPrefix(prefix, MIN_PREFIX_MATCH) { @@ -169,11 +166,10 @@ func (ub *UserBalance) getBalancesForPrefix(prefix string, balances BalanceChain return usefulBalances } -func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) (err error) { +func (ub *Account) debitCreditBalance(cc *CallCost, count bool) (err error) { usefulMinuteBalances := ub.getBalancesForPrefix(cc.Destination, ub.BalanceMap[MINUTES+cc.Direction], "") usefulMoneyBalances := ub.getBalancesForPrefix(cc.Destination, ub.BalanceMap[CREDIT+cc.Direction], "") defaultMoneyBalance := ub.GetDefaultMoneyBalance(cc.Direction) - // debit minutes for _, balance := range usefulMinuteBalances { balance.DebitMinutes(cc, count, ub, usefulMoneyBalances) @@ -231,7 +227,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) (err error) cost := increment.Cost defaultMoneyBalance.Value -= cost if count { - ub.countUnits(&Action{BalanceId: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: cost, DestinationId: cc.Destination}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: cost, DestinationId: cc.Destination}}) } err = errors.New("not enough credit") } @@ -246,7 +242,7 @@ CONNECT_FEE: b.Value -= amount // the conect fee is not refundable! if count { - ub.countUnits(&Action{BalanceId: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) } connectFeePaid = true break @@ -258,14 +254,14 @@ CONNECT_FEE: defaultMoneyBalance.Value -= amount // the conect fee is not refundable! if count { - ub.countUnits(&Action{BalanceId: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) } } } return } -func (ub *UserBalance) debitMinutesFromSharedBalances(sharedGroupName string, cc *CallCost, moneyBalances BalanceChain, count bool) { +func (ub *Account) debitMinutesFromSharedBalances(sharedGroupName string, cc *CallCost, moneyBalances BalanceChain, count bool) { sharedGroup, err := accountingStorage.GetSharedGroup(sharedGroupName, false) if err != nil { Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sharedGroupName)) @@ -279,7 +275,7 @@ func (ub *UserBalance) debitMinutesFromSharedBalances(sharedGroupName string, cc continue } - nUb, err := accountingStorage.GetUserBalance(ubId) + nUb, err := accountingStorage.GetAccount(ubId) if err != nil { Logger.Warning(fmt.Sprintf("Could not get user balance: %s", ubId)) } @@ -293,9 +289,9 @@ func (ub *UserBalance) debitMinutesFromSharedBalances(sharedGroupName string, cc for sharedBalance := sharedGroup.PopBalanceByStrategy(ub.Id, &allMinuteSharedBalances); sharedBalance != nil; sharedBalance = sharedGroup.PopBalanceByStrategy(ub.Id, &allMinuteSharedBalances) { initialValue := sharedBalance.Value - sharedBalance.DebitMinutes(cc, count, sharedBalance.userBalance, moneyBalances) + sharedBalance.DebitMinutes(cc, count, sharedBalance.account, moneyBalances) if sharedBalance.Value != initialValue { - accountingStorage.SetUserBalance(sharedBalance.userBalance) + accountingStorage.SetAccount(sharedBalance.account) } if cc.IsPaid() { return 0, nil @@ -305,7 +301,7 @@ func (ub *UserBalance) debitMinutesFromSharedBalances(sharedGroupName string, cc }) } -func (ub *UserBalance) debitMoneyFromSharedBalances(sharedGroupName string, cc *CallCost, count bool) { +func (ub *Account) debitMoneyFromSharedBalances(sharedGroupName string, cc *CallCost, count bool) { sharedGroup, err := accountingStorage.GetSharedGroup(sharedGroupName, false) if err != nil { Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sharedGroup)) @@ -319,7 +315,7 @@ func (ub *UserBalance) debitMoneyFromSharedBalances(sharedGroupName string, cc * continue } - nUb, err := accountingStorage.GetUserBalance(ubId) + nUb, err := accountingStorage.GetAccount(ubId) if err != nil { Logger.Warning(fmt.Sprintf("Could not get user balance: %s", ubId)) } @@ -332,9 +328,9 @@ func (ub *UserBalance) debitMoneyFromSharedBalances(sharedGroupName string, cc * } for sharedBalance := sharedGroup.PopBalanceByStrategy(ub.Id, &allMoneySharedBalances); sharedBalance != nil; sharedBalance = sharedGroup.PopBalanceByStrategy(ub.Id, &allMoneySharedBalances) { initialValue := sharedBalance.Value - sharedBalance.DebitMoney(cc, count, sharedBalance.userBalance) + sharedBalance.DebitMoney(cc, count, sharedBalance.account) if sharedBalance.Value != initialValue { - accountingStorage.SetUserBalance(sharedBalance.userBalance) + accountingStorage.SetAccount(sharedBalance.account) } if cc.IsPaid() { return 0, nil @@ -344,7 +340,7 @@ func (ub *UserBalance) debitMoneyFromSharedBalances(sharedGroupName string, cc * }) } -func (ub *UserBalance) GetDefaultMoneyBalance(direction string) *Balance { +func (ub *Account) GetDefaultMoneyBalance(direction string) *Balance { for _, balance := range ub.BalanceMap[CREDIT+direction] { if balance.IsDefault() { return balance @@ -357,7 +353,7 @@ func (ub *UserBalance) GetDefaultMoneyBalance(direction string) *Balance { return defaultBalance } -func (ub *UserBalance) refundIncrements(increments Increments, direction string, count bool) { +func (ub *Account) refundIncrements(increments Increments, direction string, count bool) { for _, increment := range increments { var balance *Balance if increment.BalanceInfo.MinuteBalanceUuid != "" { @@ -366,7 +362,7 @@ func (ub *UserBalance) refundIncrements(increments Increments, direction string, } balance.Value += increment.Duration.Seconds() if count { - ub.countUnits(&Action{BalanceId: MINUTES, Direction: direction, Balance: &Balance{Value: -increment.Duration.Seconds()}}) + ub.countUnits(&Action{BalanceType: MINUTES, Direction: direction, Balance: &Balance{Value: -increment.Duration.Seconds()}}) } } // check money too @@ -376,7 +372,7 @@ func (ub *UserBalance) refundIncrements(increments Increments, direction string, } balance.Value += increment.Cost if count { - ub.countUnits(&Action{BalanceId: CREDIT, Direction: direction, Balance: &Balance{Value: -increment.Cost}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: direction, Balance: &Balance{Value: -increment.Cost}}) } } } @@ -385,16 +381,16 @@ func (ub *UserBalance) refundIncrements(increments Increments, direction string, /* Debits some amount of user's specified balance. Returns the remaining credit in user's balance. */ -func (ub *UserBalance) debitGenericBalance(balanceId string, direction string, amount float64, count bool) float64 { +func (ub *Account) debitGenericBalance(balanceId string, direction string, amount float64, count bool) float64 { if count { - ub.countUnits(&Action{BalanceId: balanceId, Direction: direction, Balance: &Balance{Value: amount}}) + ub.countUnits(&Action{BalanceType: balanceId, Direction: direction, Balance: &Balance{Value: amount}}) } ub.BalanceMap[balanceId+direction].Debit(amount) return ub.BalanceMap[balanceId+direction].GetTotalValue() } // Scans the action trigers and execute the actions for which trigger is met -func (ub *UserBalance) executeActionTriggers(a *Action) { +func (ub *Account) executeActionTriggers(a *Action) { ub.ActionTriggers.Sort() for _, at := range ub.ActionTriggers { if at.Executed { @@ -407,7 +403,7 @@ func (ub *UserBalance) executeActionTriggers(a *Action) { } if strings.Contains(at.ThresholdType, "counter") { for _, uc := range ub.UnitCounters { - if uc.BalanceId == at.BalanceId { + if uc.BalanceType == at.BalanceType { for _, mb := range uc.Balances { if strings.Contains(at.ThresholdType, "*max") { if mb.MatchDestination(at.DestinationId) && mb.Value >= at.ThresholdValue { @@ -424,7 +420,7 @@ func (ub *UserBalance) executeActionTriggers(a *Action) { } } } else { // BALANCE - for _, b := range ub.BalanceMap[at.BalanceId+at.Direction] { + for _, b := range ub.BalanceMap[at.BalanceType+at.Direction] { if strings.Contains(at.ThresholdType, "*max") { if b.MatchDestination(at.DestinationId) && b.Value >= at.ThresholdValue { // run the actions @@ -443,7 +439,7 @@ func (ub *UserBalance) executeActionTriggers(a *Action) { // Mark all action trigers as ready for execution // If the action is not nil it acts like a filter -func (ub *UserBalance) resetActionTriggers(a *Action) { +func (ub *Account) resetActionTriggers(a *Action) { for _, at := range ub.ActionTriggers { if !at.Match(a) { continue @@ -454,13 +450,13 @@ func (ub *UserBalance) resetActionTriggers(a *Action) { } // Returns the unit counter that matches the specified action type -func (ub *UserBalance) getUnitCounter(a *Action) *UnitsCounter { +func (ub *Account) getUnitCounter(a *Action) *UnitsCounter { for _, uc := range ub.UnitCounters { direction := a.Direction if direction == "" { direction = OUTBOUND } - if uc.BalanceId == a.BalanceId && uc.Direction == direction { + if uc.BalanceType == a.BalanceType && uc.Direction == direction { return uc } } @@ -469,7 +465,7 @@ func (ub *UserBalance) getUnitCounter(a *Action) *UnitsCounter { // Increments the counter for the type specified in the received Action // with the actions values -func (ub *UserBalance) countUnits(a *Action) { +func (ub *Account) countUnits(a *Action) { unitsCounter := ub.getUnitCounter(a) // if not found add the counter if unitsCounter == nil { @@ -477,7 +473,7 @@ func (ub *UserBalance) countUnits(a *Action) { if direction == "" { direction = OUTBOUND } - unitsCounter = &UnitsCounter{BalanceId: a.BalanceId, Direction: direction} + unitsCounter = &UnitsCounter{BalanceType: a.BalanceType, Direction: direction} ub.UnitCounters = append(ub.UnitCounters, unitsCounter) } @@ -485,8 +481,8 @@ func (ub *UserBalance) countUnits(a *Action) { ub.executeActionTriggers(nil) } -// Create minute counters for all triggered actions that have actions operating on minute buckets -func (ub *UserBalance) initCounters() { +// Create minute counters for all triggered actions that have actions opertating on balances +func (ub *Account) initCounters() { ucTempMap := make(map[string]*UnitsCounter, 2) for _, at := range ub.ActionTriggers { acs, err := accountingStorage.GetActions(at.ActionsId, false) @@ -501,7 +497,7 @@ func (ub *UserBalance) initCounters() { } uc, exists := ucTempMap[direction] if !exists { - uc = &UnitsCounter{BalanceId: a.BalanceId, Direction: direction} + uc = &UnitsCounter{BalanceType: a.BalanceType, Direction: direction} ucTempMap[direction] = uc uc.Balances = BalanceChain{} ub.UnitCounters = append(ub.UnitCounters, uc) @@ -515,7 +511,7 @@ func (ub *UserBalance) initCounters() { } } -func (ub *UserBalance) CleanExpiredBalancesAndBuckets() { +func (ub *Account) CleanExpiredBalancesAndBuckets() { for key, bm := range ub.BalanceMap { for i := 0; i < len(bm); i++ { if bm[i].IsExpired() { @@ -528,7 +524,7 @@ func (ub *UserBalance) CleanExpiredBalancesAndBuckets() { } // returns the shared groups that this user balance belnongs to -func (ub *UserBalance) GetSharedGroups() (groups []string) { +func (ub *Account) GetSharedGroups() (groups []string) { for _, balanceChain := range ub.BalanceMap { for _, b := range balanceChain { if b.SharedGroup != "" { diff --git a/engine/userbalance_test.go b/engine/account_test.go similarity index 79% rename from engine/userbalance_test.go rename to engine/account_test.go index afce7782c..db6bdccb9 100644 --- a/engine/userbalance_test.go +++ b/engine/account_test.go @@ -28,23 +28,6 @@ var ( RET = &Destination{Id: "RET", Prefixes: []string{"0723", "0724"}} ) -func init() { - populateTestActionsForTriggers() -} - -func populateTestActionsForTriggers() { - ats := []*Action{ - &Action{ActionType: "*topup", BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}, - &Action{ActionType: "*topup", BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Weight: 20, Value: 10, DestinationId: "NAT"}}, - } - accountingStorage.SetActions("TEST_ACTIONS", ats) - ats1 := []*Action{ - &Action{ActionType: "*topup", BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}, Weight: 20}, - &Action{ActionType: "*reset_prepaid", Weight: 10}, - } - accountingStorage.SetActions("TEST_ACTIONS_ORDER", ats1) -} - func TestBalanceStoreRestore(t *testing.T) { b := &Balance{Value: 14, Weight: 1, Uuid: "test", ExpirationDate: time.Date(2013, time.July, 15, 17, 48, 0, 0, time.UTC)} marsh := NewCodecMsgpackMarshaler() @@ -95,12 +78,12 @@ func TestBalanceChainStoreRestore(t *testing.T) { } } -func TestUserBalanceStorageStoreRestore(t *testing.T) { +func TestAccountStorageStoreRestore(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} - accountingStorage.SetUserBalance(rifsBalance) - ub1, err := accountingStorage.GetUserBalance("other") + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + accountingStorage.SetAccount(rifsBalance) + ub1, err := accountingStorage.GetAccount("other") if err != nil || !ub1.BalanceMap[CREDIT+OUTBOUND].Equal(rifsBalance.BalanceMap[CREDIT+OUTBOUND]) { t.Log("UB: ", ub1) t.Errorf("Expected %v was %v", rifsBalance.BalanceMap[CREDIT+OUTBOUND], ub1.BalanceMap[CREDIT+OUTBOUND]) @@ -110,7 +93,7 @@ func TestUserBalanceStorageStoreRestore(t *testing.T) { func TestGetSecondsForPrefix(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 200}}}} + ub1 := &Account{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 200}}}} cd := &CallDescriptor{ TOR: "0", Tenant: "vdf", @@ -133,7 +116,7 @@ func TestGetSpecialPricedSeconds(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: "minu"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET", RateSubject: "minu"} - ub1 := &UserBalance{ + ub1 := &Account{ Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1, b2}, @@ -157,12 +140,12 @@ func TestGetSpecialPricedSeconds(t *testing.T) { } } -func TestUserBalanceStorageStore(t *testing.T) { +func TestAccountStorageStore(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} - accountingStorage.SetUserBalance(rifsBalance) - result, err := accountingStorage.GetUserBalance(rifsBalance.Id) + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + accountingStorage.SetAccount(rifsBalance) + result, err := accountingStorage.GetAccount(rifsBalance.Id) if err != nil || rifsBalance.Id != result.Id || len(rifsBalance.BalanceMap[MINUTES+OUTBOUND]) < 2 || len(result.BalanceMap[MINUTES+OUTBOUND]) < 2 || !(rifsBalance.BalanceMap[MINUTES+OUTBOUND][0].Equal(result.BalanceMap[MINUTES+OUTBOUND][0])) || @@ -175,7 +158,7 @@ func TestUserBalanceStorageStore(t *testing.T) { func TestDebitMoneyBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} result := rifsBalance.debitGenericBalance(CREDIT, OUTBOUND, 6, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 15 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 15, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) @@ -185,7 +168,7 @@ func TestDebitMoneyBalance(t *testing.T) { func TestDebitAllMoneyBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} rifsBalance.debitGenericBalance(CREDIT, OUTBOUND, 21, false) result := rifsBalance.debitGenericBalance(CREDIT, OUTBOUND, 0, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 0 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value { @@ -196,7 +179,7 @@ func TestDebitAllMoneyBalance(t *testing.T) { func TestDebitMoreMoneyBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} result := rifsBalance.debitGenericBalance(CREDIT, OUTBOUND, 22, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != -1 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", -1, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) @@ -206,7 +189,7 @@ func TestDebitMoreMoneyBalance(t *testing.T) { func TestDebitNegativeMoneyBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} result := rifsBalance.debitGenericBalance(CREDIT, OUTBOUND, -15, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 36 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 36, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) @@ -227,7 +210,7 @@ func TestDebitCreditZeroSecond(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitCreditBalance(cc, false) if err != nil { t.Error("Error debiting balance: ", err) @@ -256,7 +239,7 @@ func TestDebitCreditZeroMinute(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, }} @@ -291,7 +274,7 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, }} @@ -330,7 +313,7 @@ func TestDebitCreditNoCredit(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, }} err := rifsBalance.debitCreditBalance(cc, false) @@ -370,7 +353,7 @@ func TestDebitCreditHasCredit(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50}}, }} @@ -407,7 +390,7 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50}}, }} @@ -449,7 +432,7 @@ func TestDebitCreditMoreTimespans(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, }} err := rifsBalance.debitCreditBalance(cc, false) @@ -487,7 +470,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1, b2}, }} err := rifsBalance.debitCreditBalance(cc, false) @@ -525,7 +508,7 @@ func TestDebitCreditNoConectFeeCredit(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, }} err := rifsBalance.debitCreditBalance(cc, false) @@ -558,7 +541,7 @@ func TestDebitCreditMoneyOnly(t *testing.T) { }, }, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "money", Value: 50}}, }} err := rifsBalance.debitCreditBalance(cc, false) @@ -599,7 +582,7 @@ func TestDebitCreditSubjectMinutes(t *testing.T) { }, deductConnectFee: true, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 350}}, }} @@ -641,7 +624,7 @@ func TestDebitCreditSubjectMoney(t *testing.T) { }, deductConnectFee: true, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 75, DestinationId: "NAT", RateSubject: "minu"}}, }} err := rifsBalance.debitCreditBalance(cc, false) @@ -678,7 +661,7 @@ func TestDebitCreditSubjectMixed(t *testing.T) { }, deductConnectFee: true, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 150, RateSubject: "minu"}}, }} @@ -727,7 +710,7 @@ func TestDebitCreditSubjectMixedMoreTS(t *testing.T) { }, deductConnectFee: true, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50, RateSubject: "minu"}}, }} @@ -778,7 +761,7 @@ func TestDebitCreditSubjectMixedPartPay(t *testing.T) { }, deductConnectFee: true, } - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{b1}, CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 75, RateSubject: "minu"}}, }} @@ -809,7 +792,7 @@ func TestDebitCreditSubjectMixedPartPay(t *testing.T) { func TestDebitSMSBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} result := rifsBalance.debitGenericBalance(SMS, OUTBOUND, 12, false) if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != 88 || result != rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 88, rifsBalance.BalanceMap[SMS+OUTBOUND]) @@ -819,7 +802,7 @@ func TestDebitSMSBalance(t *testing.T) { func TestDebitAllSMSBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} result := rifsBalance.debitGenericBalance(SMS, OUTBOUND, 100, false) if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != 0 || result != rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 0, rifsBalance.BalanceMap[SMS+OUTBOUND]) @@ -829,7 +812,7 @@ func TestDebitAllSMSBalance(t *testing.T) { func TestDebitMoreSMSBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} result := rifsBalance.debitGenericBalance(SMS, OUTBOUND, 110, false) if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != -10 || result != rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", -10, rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value) @@ -839,47 +822,47 @@ func TestDebitMoreSMSBalance(t *testing.T) { func TestDebitNegativeSMSBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} result := rifsBalance.debitGenericBalance(SMS, OUTBOUND, -15, false) if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != 115 || result != rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 115, rifsBalance.BalanceMap[SMS+OUTBOUND]) } } -func TestUserBalancedebitBalance(t *testing.T) { - ub := &UserBalance{ - Id: "rif", - Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{SMS: BalanceChain{&Balance{Value: 14}}, TRAFFIC: BalanceChain{&Balance{Value: 1204}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, +func TestAccountdebitBalance(t *testing.T) { + ub := &Account{ + Id: "rif", + AllowNegative: true, + BalanceMap: map[string]BalanceChain{SMS: BalanceChain{&Balance{Value: 14}}, TRAFFIC: BalanceChain{&Balance{Value: 1204}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, } newMb := &Balance{Weight: 20, DestinationId: "NEW"} - a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: newMb} + a := &Action{BalanceType: MINUTES, Direction: OUTBOUND, Balance: newMb} ub.debitBalanceAction(a) if len(ub.BalanceMap[MINUTES+OUTBOUND]) != 3 || ub.BalanceMap[MINUTES+OUTBOUND][2] != newMb { t.Error("Error adding minute bucket!", len(ub.BalanceMap[MINUTES+OUTBOUND]), ub.BalanceMap[MINUTES+OUTBOUND]) } } -func TestUserBalancedebitBalanceExists(t *testing.T) { +func TestAccountdebitBalanceExists(t *testing.T) { - ub := &UserBalance{ - Id: "rif", - Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, + ub := &Account{ + Id: "rif", + AllowNegative: true, + BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, } newMb := &Balance{Value: -10, Weight: 20, DestinationId: "NAT"} - a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: newMb} + a := &Action{BalanceType: MINUTES, Direction: OUTBOUND, Balance: newMb} ub.debitBalanceAction(a) if len(ub.BalanceMap[MINUTES+OUTBOUND]) != 2 || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 25 { t.Error("Error adding minute bucket!") } } -func TestUserBalanceAddMinuteNil(t *testing.T) { - ub := &UserBalance{ - Id: "rif", - Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, +func TestAccountAddMinuteNil(t *testing.T) { + ub := &Account{ + Id: "rif", + AllowNegative: true, + BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, } ub.debitBalanceAction(nil) if len(ub.BalanceMap[MINUTES+OUTBOUND]) != 2 { @@ -887,80 +870,80 @@ func TestUserBalanceAddMinuteNil(t *testing.T) { } } -func TestUserBalanceAddMinutBucketEmpty(t *testing.T) { +func TestAccountAddMinutBucketEmpty(t *testing.T) { mb1 := &Balance{Value: -10, DestinationId: "NAT"} mb2 := &Balance{Value: -10, DestinationId: "NAT"} mb3 := &Balance{Value: -10, DestinationId: "OTHER"} - ub := &UserBalance{} - a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: mb1} + ub := &Account{} + a := &Action{BalanceType: MINUTES, Direction: OUTBOUND, Balance: mb1} ub.debitBalanceAction(a) if len(ub.BalanceMap[MINUTES+OUTBOUND]) != 1 { t.Error("Error adding minute bucket: ", ub.BalanceMap[MINUTES+OUTBOUND]) } - a = &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: mb2} + a = &Action{BalanceType: MINUTES, Direction: OUTBOUND, Balance: mb2} ub.debitBalanceAction(a) if len(ub.BalanceMap[MINUTES+OUTBOUND]) != 1 || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 20 { t.Error("Error adding minute bucket: ", ub.BalanceMap[MINUTES+OUTBOUND]) } - a = &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: mb3} + a = &Action{BalanceType: MINUTES, Direction: OUTBOUND, Balance: mb3} ub.debitBalanceAction(a) if len(ub.BalanceMap[MINUTES+OUTBOUND]) != 2 { t.Error("Error adding minute bucket: ", ub.BalanceMap[MINUTES+OUTBOUND]) } } -func TestUserBalanceExecuteTriggeredActions(t *testing.T) { - ub := &UserBalance{ +func TestAccountExecuteTriggeredActions(t *testing.T) { + ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS"}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS"}}, } - ub.countUnits(&Action{BalanceId: CREDIT, Balance: &Balance{Value: 1}}) + ub.countUnits(&Action{BalanceType: CREDIT, Balance: &Balance{Value: 1}}) if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 20 { t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.BalanceMap[MINUTES+OUTBOUND][0].Value) } // are set to executed - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 1}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 1}}) if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 20 { t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.BalanceMap[MINUTES+OUTBOUND][0].Value) } // we can reset them ub.resetActionTriggers(nil) - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 120 || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 30 { t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.BalanceMap[MINUTES+OUTBOUND][0].Value) } } -func TestUserBalanceExecuteTriggeredActionsBalance(t *testing.T) { - ub := &UserBalance{ +func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { + ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: TRIGGER_MIN_COUNTER, ActionsId: "TEST_ACTIONS"}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: TRIGGER_MIN_COUNTER, ActionsId: "TEST_ACTIONS"}}, } - ub.countUnits(&Action{BalanceId: CREDIT, Balance: &Balance{Value: 1}}) + ub.countUnits(&Action{BalanceType: CREDIT, Balance: &Balance{Value: 1}}) if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 20 { t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.BalanceMap[MINUTES+OUTBOUND][0].Value) } } -func TestUserBalanceExecuteTriggeredActionsOrder(t *testing.T) { - ub := &UserBalance{ +func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { + ub := &Account{ Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 1}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 1}}) if len(ub.BalanceMap[CREDIT+OUTBOUND]) != 1 || ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 10 { - t.Error("Error executing triggered actions in order", ub.BalanceMap[CREDIT+OUTBOUND]) + t.Error("Error executing triggered actions in order", ub.BalanceMap[CREDIT+OUTBOUND][0].Value) } } func TestCleanExpired(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{ &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}, @@ -979,52 +962,52 @@ func TestCleanExpired(t *testing.T) { } } -func TestUserBalanceUnitCounting(t *testing.T) { - ub := &UserBalance{} - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Balances[0].Value != 10 { +func TestAccountUnitCounting(t *testing.T) { + ub := &Account{} + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) + if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != CREDIT || ub.UnitCounters[0].Balances[0].Value != 10 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Balances[0].Value != 20 { + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) + if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != CREDIT || ub.UnitCounters[0].Balances[0].Value != 20 { t.Error("Error counting units") } } -func TestUserBalanceUnitCountingOutbound(t *testing.T) { - ub := &UserBalance{} - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Balances[0].Value != 10 { +func TestAccountUnitCountingOutbound(t *testing.T) { + ub := &Account{} + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) + if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != CREDIT || ub.UnitCounters[0].Balances[0].Value != 10 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Balances[0].Value != 20 { + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) + if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != CREDIT || ub.UnitCounters[0].Balances[0].Value != 20 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Balances[0].Value != 30 { + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) + if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != CREDIT || ub.UnitCounters[0].Balances[0].Value != 30 { t.Error("Error counting units") } } -func TestUserBalanceUnitCountingOutboundInbound(t *testing.T) { - ub := &UserBalance{} - ub.countUnits(&Action{BalanceId: CREDIT, Balance: &Balance{Value: 10}}) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Balances[0].Value != 10 { +func TestAccountUnitCountingOutboundInbound(t *testing.T) { + ub := &Account{} + ub.countUnits(&Action{BalanceType: CREDIT, Balance: &Balance{Value: 10}}) + if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != CREDIT || ub.UnitCounters[0].Balances[0].Value != 10 { t.Errorf("Error counting units: %+v", ub.UnitCounters[0]) } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Balances[0].Value != 20 { + ub.countUnits(&Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) + if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != CREDIT || ub.UnitCounters[0].Balances[0].Value != 20 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: INBOUND, Balance: &Balance{Value: 10}}) - if len(ub.UnitCounters) != 2 && ub.UnitCounters[1].BalanceId != CREDIT || ub.UnitCounters[0].Balances[0].Value != 20 || ub.UnitCounters[1].Balances[0].Value != 10 { + ub.countUnits(&Action{BalanceType: CREDIT, Direction: INBOUND, Balance: &Balance{Value: 10}}) + if len(ub.UnitCounters) != 2 && ub.UnitCounters[1].BalanceType != CREDIT || ub.UnitCounters[0].Balances[0].Value != 20 || ub.UnitCounters[1].Balances[0].Value != 10 { t.Error("Error counting units") } } -func TestUserBalanceRefund(t *testing.T) { - ub := &UserBalance{ +func TestAccountRefund(t *testing.T) { + ub := &Account{ BalanceMap: map[string]BalanceChain{ CREDIT + OUTBOUND: BalanceChain{ &Balance{Uuid: "moneya", Value: 100}, @@ -1049,6 +1032,29 @@ func TestUserBalanceRefund(t *testing.T) { } } +func TestTopupAction(t *testing.T) { + initialUb, _ := accountingStorage.GetAccount("*out:vdf:minu") + a := &Action{ + ActionType: "*topup", + BalanceType: CREDIT, + Direction: OUTBOUND, + Balance: &Balance{Value: 25, DestinationId: "RET", Weight: 20}, + } + + at := &ActionTiming{ + AccountIds: []string{"*out:vdf:minu"}, + actions: Actions{a}, + } + + at.Execute() + afterUb, _ := accountingStorage.GetAccount("*out:vdf:minu") + initialValue := initialUb.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() + afterValue := afterUb.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() + if initialValue != 50 || afterValue != 75 { + t.Error("Bad topup before and after: ", initialValue, afterValue) + } +} + /*********************************** Benchmarks *******************************/ func BenchmarkGetSecondForPrefix(b *testing.B) { @@ -1056,7 +1062,7 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - ub1 := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + ub1 := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} cd := &CallDescriptor{ Destination: "0723", } @@ -1066,20 +1072,20 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { } } -func BenchmarkUserBalanceStorageStoreRestore(b *testing.B) { +func BenchmarkAccountStorageStoreRestore(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} for i := 0; i < b.N; i++ { - accountingStorage.SetUserBalance(rifsBalance) - accountingStorage.GetUserBalance(rifsBalance.Id) + accountingStorage.SetAccount(rifsBalance) + accountingStorage.GetAccount(rifsBalance.Id) } } func BenchmarkGetSecondsForPrefix(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} - ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + ub1 := &Account{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} cd := &CallDescriptor{ Destination: "0723", } diff --git a/engine/action.go b/engine/action.go index 0f839d554..7e4fc5b1b 100644 --- a/engine/action.go +++ b/engine/action.go @@ -39,7 +39,7 @@ Structure to be filled for each tariff plan with the bonus value for received ca type Action struct { Id string ActionType string - BalanceId string + BalanceType string Direction string ExtraParameters string ExpirationString string @@ -51,10 +51,9 @@ type Action struct { const ( LOG = "*log" RESET_TRIGGERS = "*reset_triggers" - SET_POSTPAID = "*set_postpaid" - RESET_POSTPAID = "*reset_postpaid" - SET_PREPAID = "*set_prepaid" - RESET_PREPAID = "*reset_prepaid" + ALLOW_NEGATIVE = "*allow_negative" + DENY_NEGATIVE = "*deny_negative" + RESET_ACCOUNT = "*reset_account" TOPUP_RESET = "*topup_reset" TOPUP = "*topup" DEBIT = "*debit" @@ -68,7 +67,7 @@ const ( UNLIMITED = "*unlimited" ) -type actionTypeFunc func(*UserBalance, *Action) error +type actionTypeFunc func(*Account, *Action) error func getActionFunc(typ string) (actionTypeFunc, bool) { switch typ { @@ -76,14 +75,12 @@ func getActionFunc(typ string) (actionTypeFunc, bool) { return logAction, true case RESET_TRIGGERS: return resetTriggersAction, true - case SET_POSTPAID: - return setPostpaidAction, true - case RESET_POSTPAID: - return resetPostpaidAction, true - case SET_PREPAID: - return setPrepaidAction, true - case RESET_PREPAID: - return resetPrepaidAction, 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: @@ -108,68 +105,62 @@ func getActionFunc(typ string) (actionTypeFunc, bool) { return nil, false } -func logAction(ub *UserBalance, a *Action) (err error) { +func logAction(ub *Account, a *Action) (err error) { ubMarshal, _ := json.Marshal(ub) Logger.Info(fmt.Sprintf("Threshold reached, balance: %s", ubMarshal)) return } -func resetTriggersAction(ub *UserBalance, a *Action) (err error) { +func resetTriggersAction(ub *Account, a *Action) (err error) { ub.resetActionTriggers(a) return } -func setPostpaidAction(ub *UserBalance, a *Action) (err error) { - ub.Type = UB_TYPE_POSTPAID +func allowNegativeAction(ub *Account, a *Action) (err error) { + ub.AllowNegative = true return } -func resetPostpaidAction(ub *UserBalance, a *Action) (err error) { - genericReset(ub) - return setPostpaidAction(ub, a) -} - -func setPrepaidAction(ub *UserBalance, a *Action) (err error) { - ub.Type = UB_TYPE_PREPAID +func denyNegativeAction(ub *Account, a *Action) (err error) { + ub.AllowNegative = false return } -func resetPrepaidAction(ub *UserBalance, a *Action) (err error) { - genericReset(ub) - return setPrepaidAction(ub, a) +func resetAccountAction(ub *Account, a *Action) (err error) { + return genericReset(ub) } -func topupResetAction(ub *UserBalance, a *Action) (err error) { +func topupResetAction(ub *Account, a *Action) (err error) { if ub.BalanceMap == nil { // Init the map since otherwise will get error if nil ub.BalanceMap = make(map[string]BalanceChain, 0) } - ub.BalanceMap[a.BalanceId+a.Direction] = BalanceChain{} + ub.BalanceMap[a.BalanceType+a.Direction] = BalanceChain{} genericMakeNegative(a) genericDebit(ub, a) return } -func topupAction(ub *UserBalance, a *Action) (err error) { +func topupAction(ub *Account, a *Action) (err error) { genericMakeNegative(a) genericDebit(ub, a) return } -func debitAction(ub *UserBalance, a *Action) (err error) { +func debitAction(ub *Account, a *Action) (err error) { return genericDebit(ub, a) } -func resetCounterAction(ub *UserBalance, a *Action) (err error) { +func resetCounterAction(ub *Account, a *Action) (err error) { uc := ub.getUnitCounter(a) if uc == nil { - uc = &UnitsCounter{BalanceId: a.BalanceId, Direction: a.Direction} + uc = &UnitsCounter{BalanceType: a.BalanceType, Direction: a.Direction} ub.UnitCounters = append(ub.UnitCounters, uc) } uc.initBalances(ub.ActionTriggers) return } -func resetCountersAction(ub *UserBalance, a *Action) (err error) { +func resetCountersAction(ub *Account, a *Action) (err error) { ub.UnitCounters = make([]*UnitsCounter, 0) ub.initCounters() return @@ -181,7 +172,7 @@ func genericMakeNegative(a *Action) { } } -func genericDebit(ub *UserBalance, a *Action) (err error) { +func genericDebit(ub *Account, a *Action) (err error) { if ub.BalanceMap == nil { ub.BalanceMap = make(map[string]BalanceChain) } @@ -189,25 +180,26 @@ func genericDebit(ub *UserBalance, a *Action) (err error) { return } -func enableUserAction(ub *UserBalance, a *Action) (err error) { +func enableUserAction(ub *Account, a *Action) (err error) { ub.Disabled = false return } -func disableUserAction(ub *UserBalance, a *Action) (err error) { +func disableUserAction(ub *Account, a *Action) (err error) { ub.Disabled = true return } -func genericReset(ub *UserBalance) { +func genericReset(ub *Account) error { for k, _ := range ub.BalanceMap { ub.BalanceMap[k] = BalanceChain{&Balance{Value: 0}} } ub.UnitCounters = make([]*UnitsCounter, 0) ub.resetActionTriggers(nil) + return nil } -func callUrl(ub *UserBalance, a *Action) error { +func callUrl(ub *Account, a *Action) error { body, err := json.Marshal(ub) if err != nil { return err @@ -217,7 +209,7 @@ func callUrl(ub *UserBalance, a *Action) error { } // Does not block for posts, no error reports -func callUrlAsync(ub *UserBalance, a *Action) error { +func callUrlAsync(ub *Account, a *Action) error { body, err := json.Marshal(ub) if err != nil { return err @@ -238,7 +230,7 @@ func callUrlAsync(ub *UserBalance, a *Action) error { } // Mails the balance hitting the threshold towards predefined list of addresses -func mailAsync(ub *UserBalance, a *Action) error { +func mailAsync(ub *Account, a *Action) error { ubJson, err := json.Marshal(ub) if err != nil { return err diff --git a/engine/action_timing.go b/engine/action_timing.go index b2743039b..652541540 100644 --- a/engine/action_timing.go +++ b/engine/action_timing.go @@ -35,14 +35,14 @@ const ( ) type ActionTiming struct { - Id string // uniquely identify the timing - Tag string // informative purpose only - UserBalanceIds []string - Timing *RateInterval - Weight float64 - ActionsId string - actions Actions - stCache time.Time // cached time of the next start + Id string // uniquely identify the timing + Tag string // informative purpose only + AccountIds []string + Timing *RateInterval + Weight float64 + ActionsId string + actions Actions + stCache time.Time // cached time of the next start } type ActionPlan []*ActionTiming @@ -232,9 +232,9 @@ func (at *ActionTiming) Execute() (err error) { Logger.Crit(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) return } - for _, ubId := range at.UserBalanceIds { + for _, ubId := range at.AccountIds { _, err := AccLock.Guard(ubId, func() (float64, error) { - ub, err := accountingStorage.GetUserBalance(ubId) + ub, err := accountingStorage.GetAccount(ubId) if ub.Disabled { return 0, fmt.Errorf("User %s is disabled", ubId) } @@ -245,7 +245,7 @@ func (at *ActionTiming) Execute() (err error) { Logger.Info(fmt.Sprintf("Executing %v on %v", a.ActionType, ub.Id)) err = actionFunction(ub, a) - accountingStorage.SetUserBalance(ub) + accountingStorage.SetAccount(ub) return 0, nil }) if err != nil { @@ -317,15 +317,15 @@ func RemActionTiming(ats ActionPlan, actionTimingId, balanceId string) ActionPla ats[idx], ats = ats[len(ats)-1], ats[:len(ats)-1] continue } - for iBlnc, blncId := range at.UserBalanceIds { + for iBlnc, blncId := range at.AccountIds { if blncId == balanceId { - if len(at.UserBalanceIds) == 1 { // Only one balance, remove complete at + if len(at.AccountIds) == 1 { // Only one balance, remove complete at if len(ats) == 1 { // Removing last item, by init empty return make([]*ActionTiming, 0) } ats[idx], ats = ats[len(ats)-1], ats[:len(ats)-1] } else { - at.UserBalanceIds[iBlnc], at.UserBalanceIds = at.UserBalanceIds[len(at.UserBalanceIds)-1], at.UserBalanceIds[:len(at.UserBalanceIds)-1] + at.AccountIds[iBlnc], at.AccountIds = at.AccountIds[len(at.AccountIds)-1], at.AccountIds[:len(at.AccountIds)-1] } } } diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 3187fc5f1..676b08a02 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -28,7 +28,7 @@ import ( type ActionTrigger struct { Id string // uniquely identify the trigger - BalanceId string + BalanceType string Direction string ThresholdType string //*min_counter, *max_counter, *min_balance, *max_balance ThresholdValue float64 @@ -38,7 +38,7 @@ type ActionTrigger struct { Executed bool } -func (at *ActionTrigger) Execute(ub *UserBalance) (err error) { +func (at *ActionTrigger) Execute(ub *Account) (err error) { if ub.Disabled { return fmt.Errorf("User %s is disabled", ub.Id) } @@ -72,7 +72,7 @@ func (at *ActionTrigger) Execute(ub *UserBalance) (err error) { at.Executed = false } storageLogger.LogActionTrigger(ub.Id, RATER_SOURCE, at, aac) - accountingStorage.SetUserBalance(ub) + accountingStorage.SetAccount(ub) return } @@ -82,7 +82,7 @@ func (at *ActionTrigger) Match(a *Action) bool { if a == nil { return true } - id := a.BalanceId == "" || at.BalanceId == a.BalanceId + id := a.BalanceType == "" || at.BalanceType == a.BalanceType direction := a.Direction == "" || at.Direction == a.Direction thresholdType, thresholdValue := true, true if a.ExtraParameters != "" { diff --git a/engine/actions_test.go b/engine/actions_test.go index 4c234121b..5321c06f2 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -360,9 +360,9 @@ func TestActionTimingOneTimeRun(t *testing.T) { func TestActionTimingLogFunction(t *testing.T) { a := &Action{ - ActionType: "*log", - BalanceId: "test", - Balance: &Balance{Value: 1.1}, + ActionType: "*log", + BalanceType: "test", + Balance: &Balance{Value: 1.1}, } at := &ActionTiming{ actions: []*Action{a}, @@ -431,20 +431,20 @@ func TestActionTimingPriotityListWeight(t *testing.T) { func TestActionTimingsRemoveMember(t *testing.T) { at1 := &ActionTiming{ - Id: "some uuid", - Tag: "test", - UserBalanceIds: []string{"one", "two", "three"}, - ActionsId: "TEST_ACTIONS", + Id: "some uuid", + Tag: "test", + AccountIds: []string{"one", "two", "three"}, + ActionsId: "TEST_ACTIONS", } at2 := &ActionTiming{ - Id: "some uuid22", - Tag: "test2", - UserBalanceIds: []string{"three", "four"}, - ActionsId: "TEST_ACTIONS2", + Id: "some uuid22", + Tag: "test2", + AccountIds: []string{"three", "four"}, + ActionsId: "TEST_ACTIONS2", } ats := ActionPlan{at1, at2} - if outAts := RemActionTiming(ats, "", "four"); len(outAts[1].UserBalanceIds) != 1 { - t.Error("Expecting fewer balance ids", outAts[1].UserBalanceIds) + if outAts := RemActionTiming(ats, "", "four"); len(outAts[1].AccountIds) != 1 { + t.Error("Expecting fewer balance ids", outAts[1].AccountIds) } if ats = RemActionTiming(ats, "", "three"); len(ats) != 1 { t.Error("Expecting fewer actionTimings", ats) @@ -461,7 +461,7 @@ func TestActionTimingsRemoveMember(t *testing.T) { func TestActionTriggerMatchNil(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, - BalanceId: CREDIT, + BalanceType: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } @@ -474,7 +474,7 @@ func TestActionTriggerMatchNil(t *testing.T) { func TestActionTriggerMatchAllBlank(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, - BalanceId: CREDIT, + BalanceType: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } @@ -487,11 +487,11 @@ func TestActionTriggerMatchAllBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, - BalanceId: CREDIT, + BalanceType: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: OUTBOUND, BalanceId: CREDIT} + a := &Action{Direction: OUTBOUND, BalanceType: CREDIT} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -500,7 +500,7 @@ func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, - BalanceId: CREDIT, + BalanceType: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } @@ -513,11 +513,11 @@ func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { func TestActionTriggerMatchAllFull(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, - BalanceId: CREDIT, + BalanceType: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: OUTBOUND, BalanceId: CREDIT, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, TRIGGER_MAX_BALANCE, 2)} + a := &Action{Direction: OUTBOUND, BalanceType: CREDIT, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, TRIGGER_MAX_BALANCE, 2)} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -526,11 +526,11 @@ func TestActionTriggerMatchAllFull(t *testing.T) { func TestActionTriggerMatchSomeFalse(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, - BalanceId: CREDIT, + BalanceType: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: INBOUND, BalanceId: CREDIT, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, TRIGGER_MAX_BALANCE, 2)} + a := &Action{Direction: INBOUND, BalanceType: CREDIT, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, TRIGGER_MAX_BALANCE, 2)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -539,11 +539,11 @@ func TestActionTriggerMatchSomeFalse(t *testing.T) { func TestActionTriggerMatcBalanceFalse(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, - BalanceId: CREDIT, + BalanceType: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: OUTBOUND, BalanceId: CREDIT, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, TRIGGER_MAX_BALANCE, 3.0)} + a := &Action{Direction: OUTBOUND, BalanceType: CREDIT, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, TRIGGER_MAX_BALANCE, 3.0)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -552,11 +552,11 @@ func TestActionTriggerMatcBalanceFalse(t *testing.T) { func TestActionTriggerMatcAllFalse(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, - BalanceId: CREDIT, + BalanceType: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: INBOUND, BalanceId: MINUTES, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, TRIGGER_MAX_COUNTER, 3)} + a := &Action{Direction: INBOUND, BalanceType: MINUTES, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, TRIGGER_MAX_COUNTER, 3)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -575,11 +575,11 @@ func TestActionTriggerPriotityList(t *testing.T) { } func TestActionResetTriggres(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil) if ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { @@ -588,11 +588,11 @@ func TestActionResetTriggres(t *testing.T) { } func TestActionResetTriggresExecutesThem(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil) if ub.ActionTriggers[0].Executed == true || ub.BalanceMap[CREDIT][0].Value == 12 { @@ -601,56 +601,55 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { } func TestActionResetTriggresActionFilter(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - resetTriggersAction(ub, &Action{BalanceId: SMS}) + resetTriggersAction(ub, &Action{BalanceType: SMS}) if ub.ActionTriggers[0].Executed == false || ub.ActionTriggers[1].Executed == false { t.Error("Reset triggers action failed!") } } func TestActionSetPostpaid(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - setPostpaidAction(ub, nil) - if ub.Type != UB_TYPE_POSTPAID { + allowNegativeAction(ub, nil) + if !ub.AllowNegative { t.Error("Set postpaid action failed!") } } func TestActionSetPrepaid(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_POSTPAID, + AllowNegative: true, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - setPrepaidAction(ub, nil) - if ub.Type != UB_TYPE_PREPAID { + denyNegativeAction(ub, nil) + if ub.AllowNegative { t.Error("Set prepaid action failed!") } } func TestActionResetPrepaid(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_POSTPAID, + AllowNegative: true, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - resetPrepaidAction(ub, nil) - if ub.Type != UB_TYPE_PREPAID || + resetAccountAction(ub, nil) + if !ub.AllowNegative || ub.BalanceMap[CREDIT].GetTotalValue() != 0 || len(ub.UnitCounters) != 0 || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 0 || @@ -661,16 +660,14 @@ func TestActionResetPrepaid(t *testing.T) { } func TestActionResetPostpaid(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - resetPostpaidAction(ub, nil) - if ub.Type != UB_TYPE_POSTPAID || - ub.BalanceMap[CREDIT].GetTotalValue() != 0 || + resetAccountAction(ub, nil) + if ub.BalanceMap[CREDIT].GetTotalValue() != 0 || len(ub.UnitCounters) != 0 || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { @@ -679,16 +676,15 @@ func TestActionResetPostpaid(t *testing.T) { } func TestActionTopupResetCredit(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} + a := &Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} topupResetAction(ub, a) - if ub.Type != UB_TYPE_PREPAID || + if ub.AllowNegative || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 10 || len(ub.UnitCounters) != 1 || len(ub.BalanceMap[MINUTES+OUTBOUND]) != 2 || @@ -698,18 +694,17 @@ func TestActionTopupResetCredit(t *testing.T) { } func TestActionTopupResetMinutes(t *testing.T) { - ub := &UserBalance{ - Id: "TEST_UB", - Type: UB_TYPE_PREPAID, + ub := &Account{ + Id: "TEST_UB", BalanceMap: map[string]BalanceChain{ CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: 5, Weight: 20, DestinationId: "NAT"}} + a := &Action{BalanceType: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: 5, Weight: 20, DestinationId: "NAT"}} topupResetAction(ub, a) - if ub.Type != UB_TYPE_PREPAID || + if ub.AllowNegative || ub.BalanceMap[MINUTES+OUTBOUND].GetTotalValue() != 5 || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || @@ -720,16 +715,15 @@ func TestActionTopupResetMinutes(t *testing.T) { } func TestActionTopupCredit(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} + a := &Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} topupAction(ub, a) - if ub.Type != UB_TYPE_PREPAID || + if ub.AllowNegative || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 110 || len(ub.UnitCounters) != 1 || len(ub.BalanceMap[MINUTES+OUTBOUND]) != 2 || @@ -739,16 +733,15 @@ func TestActionTopupCredit(t *testing.T) { } func TestActionTopupMinutes(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: 5, Weight: 20, DestinationId: "NAT"}} + a := &Action{BalanceType: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: 5, Weight: 20, DestinationId: "NAT"}} topupAction(ub, a) - if ub.Type != UB_TYPE_PREPAID || + if ub.AllowNegative || ub.BalanceMap[MINUTES+OUTBOUND].GetTotalValue() != 15 || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || @@ -759,16 +752,15 @@ func TestActionTopupMinutes(t *testing.T) { } func TestActionDebitCredit(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} + a := &Action{BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} debitAction(ub, a) - if ub.Type != UB_TYPE_PREPAID || + if ub.AllowNegative || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 90 || len(ub.UnitCounters) != 1 || len(ub.BalanceMap[MINUTES+OUTBOUND]) != 2 || @@ -778,16 +770,15 @@ func TestActionDebitCredit(t *testing.T) { } func TestActionDebitMinutes(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: 5, Weight: 20, DestinationId: "NAT"}} + a := &Action{BalanceType: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: 5, Weight: 20, DestinationId: "NAT"}} debitAction(ub, a) - if ub.Type != UB_TYPE_PREPAID || + if ub.AllowNegative || ub.BalanceMap[MINUTES+OUTBOUND][0].Value != 5 || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || @@ -798,19 +789,19 @@ func TestActionDebitMinutes(t *testing.T) { } func TestActionResetAllCounters(t *testing.T) { - ub := &UserBalance{ - Id: "TEST_UB", - Type: UB_TYPE_POSTPAID, + ub := &Account{ + Id: "TEST_UB", + AllowNegative: true, BalanceMap: map[string]BalanceChain{ CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{ &Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetCountersAction(ub, nil) - if ub.Type != UB_TYPE_POSTPAID || + if !ub.AllowNegative || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || len(ub.UnitCounters[0].Balances) != 2 || @@ -828,18 +819,18 @@ func TestActionResetAllCounters(t *testing.T) { } func TestActionResetCounterMinutes(t *testing.T) { - ub := &UserBalance{ - Id: "TEST_UB", - Type: UB_TYPE_POSTPAID, + ub := &Account{ + Id: "TEST_UB", + AllowNegative: true, BalanceMap: map[string]BalanceChain{ CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: MINUTES} + a := &Action{BalanceType: MINUTES} resetCounterAction(ub, a) - if ub.Type != UB_TYPE_POSTPAID || + if !ub.AllowNegative || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 2 || len(ub.UnitCounters[1].Balances) != 2 || @@ -860,16 +851,16 @@ func TestActionResetCounterMinutes(t *testing.T) { } func TestActionResetCounterCREDIT(t *testing.T) { - ub := &UserBalance{ + ub := &Account{ Id: "TEST_UB", - Type: UB_TYPE_POSTPAID, + AllowNegative: true, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES + OUTBOUND: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, - UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}, &UnitsCounter{BalanceId: SMS, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: CREDIT, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}, &UnitsCounter{BalanceType: SMS, Direction: OUTBOUND, Balances: BalanceChain{&Balance{Value: 1}}}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: CREDIT, Direction: OUTBOUND} + a := &Action{BalanceType: CREDIT, Direction: OUTBOUND} resetCounterAction(ub, a) - if ub.Type != UB_TYPE_POSTPAID || + if !ub.AllowNegative || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 2 || len(ub.BalanceMap[MINUTES+OUTBOUND]) != 2 || @@ -881,7 +872,7 @@ func TestActionResetCounterCREDIT(t *testing.T) { func TestActionTriggerLogging(t *testing.T) { at := &ActionTrigger{ Id: "some_uuid", - BalanceId: CREDIT, + BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 100.0, DestinationId: "NAT", @@ -925,12 +916,12 @@ func TestActionTimingLogging(t *testing.T) { }, } at := &ActionTiming{ - Id: "some uuid", - Tag: "test", - UserBalanceIds: []string{"one", "two", "three"}, - Timing: i, - Weight: 10.0, - ActionsId: "TEST_ACTIONS", + Id: "some uuid", + Tag: "test", + AccountIds: []string{"one", "two", "three"}, + Timing: i, + Weight: 10.0, + ActionsId: "TEST_ACTIONS", } as, err := accountingStorage.GetActions(at.ActionsId, false) if err != nil { @@ -965,7 +956,7 @@ func TestActionMakeNegative(t *testing.T) { } func TestMinBalanceTriggerSimple(t *testing.T) { - //ub := &UserBalance{} + //ub := &Account{} } /********************************** Benchmarks ********************************/ diff --git a/engine/balances.go b/engine/balances.go index e535e1390..30c545033 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -37,7 +37,7 @@ type Balance struct { RateSubject string SharedGroup string precision int - userBalance *UserBalance // used to store ub reference for shared balances + account *Account // used to store ub reference for shared balances } func (b *Balance) Equal(o *Balance) bool { @@ -118,7 +118,7 @@ func (b *Balance) GetCost(cd *CallDescriptor) (*CallCost, error) { return cc, nil } -func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *UserBalance, moneyBalances BalanceChain) error { +func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalances BalanceChain) error { for tsIndex := 0; tsIndex < len(cc.Timespans); tsIndex++ { if b.Value <= 0 { return nil @@ -183,7 +183,7 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *UserBalance, moneyB inc.Cost = 0 inc.paid = true if count { - ub.countUnits(&Action{BalanceId: MINUTES, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) + ub.countUnits(&Action{BalanceType: MINUTES, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) } } continue @@ -223,8 +223,8 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *UserBalance, moneyB nInc.MinuteInfo = &MinuteInfo{newCC.Destination, seconds} nInc.paid = true if count { - ub.countUnits(&Action{BalanceId: MINUTES, Direction: newCC.Direction, Balance: &Balance{Value: seconds, DestinationId: newCC.Destination}}) - ub.countUnits(&Action{BalanceId: CREDIT, Direction: newCC.Direction, Balance: &Balance{Value: cost, DestinationId: newCC.Destination}}) + ub.countUnits(&Action{BalanceType: MINUTES, Direction: newCC.Direction, Balance: &Balance{Value: seconds, DestinationId: newCC.Destination}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: newCC.Direction, Balance: &Balance{Value: cost, DestinationId: newCC.Destination}}) } } else { increment.paid = false @@ -258,7 +258,7 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *UserBalance, moneyB return nil } -func (b *Balance) DebitMoney(cc *CallCost, count bool, ub *UserBalance) error { +func (b *Balance) DebitMoney(cc *CallCost, count bool, ub *Account) error { for tsIndex := 0; tsIndex < len(cc.Timespans); tsIndex++ { if b.Value <= 0 { return nil @@ -286,7 +286,7 @@ func (b *Balance) DebitMoney(cc *CallCost, count bool, ub *UserBalance) error { increment.BalanceInfo.MoneyBalanceUuid = b.Uuid increment.paid = true if count { - ub.countUnits(&Action{BalanceId: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) } } } else { @@ -314,7 +314,7 @@ func (b *Balance) DebitMoney(cc *CallCost, count bool, ub *UserBalance) error { nInc.BalanceInfo.MoneyBalanceUuid = b.Uuid nInc.paid = true if count { - ub.countUnits(&Action{BalanceId: CREDIT, Direction: newCC.Direction, Balance: &Balance{Value: amount, DestinationId: newCC.Destination}}) + ub.countUnits(&Action{BalanceType: CREDIT, Direction: newCC.Direction, Balance: &Balance{Value: amount, DestinationId: newCC.Destination}}) } } else { increment.paid = false diff --git a/engine/calldesc.go b/engine/calldesc.go index eebf85693..3f8c79bba 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -116,7 +116,7 @@ type CallDescriptor struct { FallbackSubject string // the subject to check for destination if not found on primary subject RatingInfos RatingInfos Increments Increments - userBalance *UserBalance + userBalance *Account } func (cd *CallDescriptor) ValidateCallData() error { @@ -135,7 +135,7 @@ func (cd *CallDescriptor) AddRatingInfo(ris ...*RatingInfo) { } // Returns the key used to retrive the user balance involved in this call -func (cd *CallDescriptor) GetUserBalanceKey() string { +func (cd *CallDescriptor) GetAccountKey() string { subj := cd.Subject if cd.Account != "" { subj = cd.Account @@ -144,9 +144,9 @@ func (cd *CallDescriptor) GetUserBalanceKey() string { } // Gets and caches the user balance information. -func (cd *CallDescriptor) getUserBalance() (ub *UserBalance, err error) { +func (cd *CallDescriptor) getAccount() (ub *Account, err error) { if cd.userBalance == nil { - cd.userBalance, err = accountingStorage.GetUserBalance(cd.GetUserBalanceKey()) + cd.userBalance, err = accountingStorage.GetAccount(cd.GetAccountKey()) } if cd.userBalance != nil && cd.userBalance.Disabled { return nil, fmt.Errorf("User %s is disabled", ub.Id) @@ -388,7 +388,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) { } err := cd.LoadRatingPlans() if err != nil { - Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetUserBalanceKey(), err)) + Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetAccountKey(), err)) return &CallCost{Cost: -1}, err } timespans := cd.splitInTimeSpans(nil) @@ -432,20 +432,20 @@ func (origCd *CallDescriptor) GetMaxSessionDuration() (time.Duration, error) { //Logger.Debug(fmt.Sprintf("MAX SESSION cd: %+v", cd)) err := cd.LoadRatingPlans() if err != nil { - Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetUserBalanceKey(), err)) + Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetAccountKey(), err)) return 0, err } var availableDuration time.Duration availableCredit := 0.0 - if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { - if userBalance.Type == UB_TYPE_POSTPAID { + if userBalance, err := cd.getAccount(); err == nil && userBalance != nil { + if userBalance.AllowNegative { return -1, nil } else { availableDuration, availableCredit, _ = userBalance.getCreditForPrefix(cd) // Logger.Debug(fmt.Sprintf("available sec: %v credit: %v", availableSeconds, availableCredit)) } } else { - Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetUserBalanceKey(), err.Error())) + Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetAccountKey(), err.Error())) return 0, err } //Logger.Debug(fmt.Sprintf("availableDuration: %v, availableCredit: %v", availableDuration, availableCredit)) @@ -494,18 +494,18 @@ func (origCd *CallDescriptor) GetMaxSessionDuration() (time.Duration, error) { func (cd *CallDescriptor) Debit() (cc *CallCost, err error) { cc, err = cd.GetCost() if err != nil { - Logger.Err(fmt.Sprintf(" Error getting cost for account key %v: %v", cd.GetUserBalanceKey(), err)) + Logger.Err(fmt.Sprintf(" Error getting cost for account key %v: %v", cd.GetAccountKey(), err)) return } - if userBalance, err := cd.getUserBalance(); err != nil { + if userBalance, err := cd.getAccount(); err != nil { Logger.Err(fmt.Sprintf(" Error retrieving user balance: %v", err)) } else if userBalance == nil { - // Logger.Debug(fmt.Sprintf(" No user balance defined: %v", cd.GetUserBalanceKey())) + // Logger.Debug(fmt.Sprintf(" No user balance defined: %v", cd.GetAccountKey())) } else { - //Logger.Debug(fmt.Sprintf(" Attempting to debit from %v, value: %v", cd.GetUserBalanceKey(), cc.Cost+cc.ConnectFee)) - defer accountingStorage.SetUserBalance(userBalance) + //Logger.Debug(fmt.Sprintf(" Attempting to debit from %v, value: %v", cd.GetAccountKey(), cc.Cost+cc.ConnectFee)) + defer accountingStorage.SetAccount(userBalance) //ub, _ := json.Marshal(userBalance) - //Logger.Debug(fmt.Sprintf("UserBalance: %s", ub)) + //Logger.Debug(fmt.Sprintf("Account: %s", ub)) //cCost, _ := json.Marshal(cc) //Logger.Debug(fmt.Sprintf("CallCost: %s", cCost)) if cc.Cost != 0 || cc.GetConnectFee() != 0 { @@ -538,8 +538,8 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { } func (cd *CallDescriptor) RefundIncrements() (left float64, err error) { - if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { - defer accountingStorage.SetUserBalance(userBalance) + if userBalance, err := cd.getAccount(); err == nil && userBalance != nil { + defer accountingStorage.SetAccount(userBalance) userBalance.refundIncrements(cd.Increments, cd.Direction, true) } return 0.0, err @@ -550,8 +550,8 @@ Interface method used to add/substract an amount of cents from user's money bala The amount filed has to be filled in call descriptor. */ func (cd *CallDescriptor) DebitCents() (left float64, err error) { - if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { - defer accountingStorage.SetUserBalance(userBalance) + if userBalance, err := cd.getAccount(); err == nil && userBalance != nil { + defer accountingStorage.SetAccount(userBalance) return userBalance.debitGenericBalance(CREDIT, cd.Direction, cd.Amount, true), nil } return 0.0, err @@ -562,8 +562,8 @@ Interface method used to add/substract an amount of units from user's sms balanc The amount filed has to be filled in call descriptor. */ func (cd *CallDescriptor) DebitSMS() (left float64, err error) { - if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { - defer accountingStorage.SetUserBalance(userBalance) + if userBalance, err := cd.getAccount(); err == nil && userBalance != nil { + defer accountingStorage.SetAccount(userBalance) return userBalance.debitGenericBalance(SMS, cd.Direction, cd.Amount, true), nil } return 0, err @@ -574,8 +574,8 @@ Interface method used to add/substract an amount of seconds from user's minutes The amount filed has to be filled in call descriptor. */ func (cd *CallDescriptor) DebitSeconds() (err error) { - if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { - defer accountingStorage.SetUserBalance(userBalance) + if userBalance, err := cd.getAccount(); err == nil && userBalance != nil { + defer accountingStorage.SetAccount(userBalance) return userBalance.debitCreditBalance(cd.CreateCallCost(), true) } return err @@ -588,7 +588,7 @@ specified in the tariff plan is applied. The amount filed has to be filled in call descriptor. */ func (cd *CallDescriptor) AddRecievedCallSeconds() (err error) { - if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { + if userBalance, err := cd.getAccount(); err == nil && userBalance != nil { a := &Action{ Direction: INBOUND, Balance: &Balance{Value: cd.Amount, DestinationId: cd.Destination}, diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 0180a8a6b..658ed26de 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -37,19 +37,25 @@ func init() { } func populateDB() { - minu := &UserBalance{ - Id: "*out:vdf:minu", - Type: UB_TYPE_PREPAID, + ats := []*Action{ + &Action{ActionType: "*topup", BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}, + &Action{ActionType: "*topup", BalanceType: MINUTES, Direction: OUTBOUND, Balance: &Balance{Weight: 20, Value: 10, DestinationId: "NAT"}}, + } + ats1 := []*Action{ + &Action{ActionType: "*topup", BalanceType: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}, Weight: 20}, + &Action{ActionType: "*reset_account", Weight: 10}, + } + minu := &Account{ + Id: "*out:vdf:minu", BalanceMap: map[string]BalanceChain{ - CREDIT: BalanceChain{&Balance{Value: 0}}, + CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 50}}, MINUTES + OUTBOUND: BalanceChain{ &Balance{Value: 200, DestinationId: "NAT", Weight: 10}, &Balance{Value: 100, DestinationId: "RET", Weight: 20}, }}, } - broker := &UserBalance{ - Id: "*out:vdf:broker", - Type: UB_TYPE_PREPAID, + broker := &Account{ + Id: "*out:vdf:broker", BalanceMap: map[string]BalanceChain{ MINUTES + OUTBOUND: BalanceChain{ &Balance{Value: 20, DestinationId: "NAT", Weight: 10, RateSubject: "rif"}, @@ -57,9 +63,10 @@ func populateDB() { }}, } if accountingStorage != nil { - accountingStorage.(Storage).Flush() - accountingStorage.SetUserBalance(broker) - accountingStorage.SetUserBalance(minu) + accountingStorage.SetActions("TEST_ACTIONS", ats) + accountingStorage.SetActions("TEST_ACTIONS_ORDER", ats1) + accountingStorage.SetAccount(broker) + accountingStorage.SetAccount(minu) } else { log.Fatal("Could not connect to db!") } @@ -270,7 +277,7 @@ func TestMinutesCost(t *testing.T) { } } -func TestMaxSessionTimeNoUserBalance(t *testing.T) { +func TestMaxSessionTimeNoAccount(t *testing.T) { cd := &CallDescriptor{ TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC), TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC), @@ -285,7 +292,7 @@ func TestMaxSessionTimeNoUserBalance(t *testing.T) { } } -func TestMaxSessionTimeWithUserBalance(t *testing.T) { +func TestMaxSessionTimeWithAccount(t *testing.T) { cd := &CallDescriptor{ TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC), TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC), @@ -302,7 +309,7 @@ func TestMaxSessionTimeWithUserBalance(t *testing.T) { } } -func TestMaxSessionTimeWithUserBalanceAccount(t *testing.T) { +func TestMaxSessionTimeWithAccountAccount(t *testing.T) { cd := &CallDescriptor{ TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC), TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC), diff --git a/engine/loader_csv.go b/engine/loader_csv.go index 7542c6da5..ec2a3783a 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -38,7 +38,7 @@ type CSVReader struct { actions map[string][]*Action actionsTimings map[string][]*ActionTiming actionsTriggers map[string][]*ActionTrigger - accountActions []*UserBalance + accountActions []*Account destinations []*Destination timings map[string]*utils.TPTiming rates map[string]*utils.TPRate @@ -236,7 +236,7 @@ func (csvr *CSVReader) WriteToDatabase(flush, verbose bool) (err error) { log.Print("Account actions") } for _, ub := range csvr.accountActions { - err = accountingStorage.SetUserBalance(ub) + err = accountingStorage.SetAccount(ub) if err != nil { return err } @@ -526,7 +526,7 @@ func (csvr *CSVReader) LoadActions() (err error) { a := &Action{ Id: utils.GenUUID(), ActionType: record[1], - BalanceId: record[2], + BalanceType: record[2], Direction: record[3], Weight: weight, ExpirationString: record[5], @@ -614,7 +614,7 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) { } at := &ActionTrigger{ Id: utils.GenUUID(), - BalanceId: record[1], + BalanceType: record[1], Direction: record[2], ThresholdType: record[3], ThresholdValue: value, @@ -644,8 +644,7 @@ func (csvr *CSVReader) LoadAccountActions() (err error) { // only return error if there was something ther for the tag return errors.New(fmt.Sprintf("Could not get action triggers for tag %v", record[4])) } - ub := &UserBalance{ - Type: UB_TYPE_PREPAID, + ub := &Account{ Id: tag, ActionTriggers: aTriggers, } @@ -656,7 +655,7 @@ func (csvr *CSVReader) LoadAccountActions() (err error) { // must not continue here } for _, at := range aTimings { - at.UserBalanceIds = append(at.UserBalanceIds, tag) + at.AccountIds = append(at.AccountIds, tag) } } return nil diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 99aff8d00..cc4ce8a9d 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -576,7 +576,7 @@ func TestLoadActions(t *testing.T) { &Action{ Id: as[0].Id, ActionType: TOPUP_RESET, - BalanceId: CREDIT, + BalanceType: CREDIT, Direction: OUTBOUND, ExpirationString: UNLIMITED, ExtraParameters: "", @@ -590,7 +590,7 @@ func TestLoadActions(t *testing.T) { &Action{ Id: as[1].Id, ActionType: TOPUP, - BalanceId: MINUTES, + BalanceType: MINUTES, Direction: OUTBOUND, ExpirationString: UNLIMITED, ExtraParameters: "", @@ -615,9 +615,9 @@ func TestLoadActionTimings(t *testing.T) { } atm := csvr.actionsTimings["MORE_MINUTES"][0] expected := &ActionTiming{ - Id: atm.Id, - Tag: "MORE_MINUTES", - UserBalanceIds: []string{"*out:vdf:minitsboy"}, + Id: atm.Id, + Tag: "MORE_MINUTES", + AccountIds: []string{"*out:vdf:minitsboy"}, Timing: &RateInterval{ Timing: &RITiming{ Years: utils.Years{2012}, @@ -642,7 +642,7 @@ func TestLoadActionTriggers(t *testing.T) { atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0] expected := &ActionTrigger{ Id: atr.Id, - BalanceId: MINUTES, + BalanceType: MINUTES, Direction: OUTBOUND, ThresholdType: TRIGGER_MIN_COUNTER, ThresholdValue: 10, @@ -657,7 +657,7 @@ func TestLoadActionTriggers(t *testing.T) { atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ Id: atr.Id, - BalanceId: MINUTES, + BalanceType: MINUTES, Direction: OUTBOUND, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 200, @@ -676,9 +676,8 @@ func TestLoadAccountActions(t *testing.T) { t.Error("Failed to load account actions: ", csvr.accountActions) } aa := csvr.accountActions[0] - expected := &UserBalance{ + expected := &Account{ Id: "*out:vdf:minitsboy", - Type: UB_TYPE_PREPAID, ActionTriggers: csvr.actionsTriggers["STANDARD_TRIGGER"], } if !reflect.DeepEqual(aa, expected) { diff --git a/engine/loader_db.go b/engine/loader_db.go index b39354da2..78d41894f 100644 --- a/engine/loader_db.go +++ b/engine/loader_db.go @@ -34,7 +34,7 @@ type DbReader struct { actions map[string][]*Action actionsTimings map[string][]*ActionTiming actionsTriggers map[string][]*ActionTrigger - accountActions []*UserBalance + accountActions []*Account destinations []*Destination timings map[string]*utils.TPTiming rates map[string]*utils.TPRate @@ -180,7 +180,7 @@ func (dbr *DbReader) WriteToDatabase(flush, verbose bool) (err error) { log.Print("Account actions") } for _, ub := range dbr.accountActions { - err = accountingStorage.SetUserBalance(ub) + err = accountingStorage.SetAccount(ub) if err != nil { return err } @@ -405,7 +405,7 @@ func (dbr *DbReader) LoadActions() (err error) { acts[idx] = &Action{ Id: utils.GenUUID(), ActionType: tpact.Identifier, - BalanceId: tpact.BalanceType, + BalanceType: tpact.BalanceType, Direction: tpact.Direction, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, @@ -470,7 +470,7 @@ func (dbr *DbReader) LoadActionTriggers() (err error) { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, apiAtr := range atrsLst { atrs[idx] = &ActionTrigger{Id: utils.GenUUID(), - BalanceId: apiAtr.BalanceType, + BalanceType: apiAtr.BalanceType, Direction: apiAtr.Direction, ThresholdType: apiAtr.ThresholdType, ThresholdValue: apiAtr.ThresholdValue, @@ -494,8 +494,7 @@ func (dbr *DbReader) LoadAccountActions() (err error) { if !exists { return errors.New(fmt.Sprintf("Could not get action triggers for tag %v", aa.ActionTriggersId)) } - ub := &UserBalance{ - Type: UB_TYPE_PREPAID, + ub := &Account{ Id: aa.KeyId(), ActionTriggers: aTriggers, } @@ -506,7 +505,7 @@ func (dbr *DbReader) LoadAccountActions() (err error) { // must not continue here } for _, at := range aTimings { - at.UserBalanceIds = append(at.UserBalanceIds, aa.KeyId()) + at.AccountIds = append(at.AccountIds, aa.KeyId()) } } return nil @@ -523,11 +522,11 @@ func (dbr *DbReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions) // action timings if accountAction.ActionPlanId != "" { // get old userBalanceIds - var exitingUserBalanceIds []string + var exitingAccountIds []string existingActionTimings, err := dbr.accountDb.GetActionTimings(accountAction.ActionPlanId) if err == nil && len(existingActionTimings) > 0 { // all action timings from a specific tag shuld have the same list of user balances from the first one - exitingUserBalanceIds = existingActionTimings[0].UserBalanceIds + exitingAccountIds = existingActionTimings[0].AccountIds } actionTimingsMap, err := dbr.storDb.GetTPActionTimings(dbr.tpid, accountAction.ActionPlanId) @@ -571,14 +570,14 @@ func (dbr *DbReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions) actionsIds = append(actionsIds, actTmg.ActionsId) //add user balance id if no already in found := false - for _, ubId := range exitingUserBalanceIds { + for _, ubId := range exitingAccountIds { if ubId == id { found = true break } } if !found { - actTmg.UserBalanceIds = append(exitingUserBalanceIds, id) + actTmg.AccountIds = append(exitingAccountIds, id) } actionTimings = append(actionTimings, actTmg) } @@ -602,7 +601,7 @@ func (dbr *DbReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions) atrs := make([]*ActionTrigger, len(atrsLst)) for idx, apiAtr := range atrsLst { atrs[idx] = &ActionTrigger{Id: utils.GenUUID(), - BalanceId: apiAtr.BalanceType, + BalanceType: apiAtr.BalanceType, Direction: apiAtr.Direction, ThresholdType: apiAtr.ThresholdType, ThresholdValue: apiAtr.ThresholdValue, @@ -633,7 +632,7 @@ func (dbr *DbReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions) enacts[idx] = &Action{ Id: utils.GenUUID(), ActionType: tpact.Identifier, - BalanceId: tpact.BalanceType, + BalanceType: tpact.BalanceType, Direction: tpact.Direction, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, @@ -657,16 +656,15 @@ func (dbr *DbReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions) return err } } - ub, err := dbr.accountDb.GetUserBalance(id) + ub, err := dbr.accountDb.GetAccount(id) if err != nil { - ub = &UserBalance{ - Type: UB_TYPE_PREPAID, - Id: id, + ub = &Account{ + Id: id, } } ub.ActionTriggers = actionTriggers - if err := dbr.accountDb.SetUserBalance(ub); err != nil { + if err := dbr.accountDb.SetAccount(ub); err != nil { return err } } diff --git a/engine/realcalls_test.go b/engine/realcalls_test.go index c62c7ff9d..cf459464b 100644 --- a/engine/realcalls_test.go +++ b/engine/realcalls_test.go @@ -28,7 +28,7 @@ var balance1 string = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","Balance var callCost1 string = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":0.6,"ConnectFee":0,"Timespans":[{"TimeStart":"2013-12-03T14:36:48+01:00","TimeEnd":"2013-12-03T14:37:48+01:00","Cost":0.6,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":0.6,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}` func TestDebitBalanceForCall1(t *testing.T) { - b1 := new(UserBalance) + b1 := new(Account) if err := json.Unmarshal([]byte(balance1), b1); err != nil { t.Error("Error restoring balance1: ", err) } @@ -50,7 +50,7 @@ var balanceInsufficient = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","Bal var costInsufficient = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":1,"ConnectFee":3,"Timespans":[{"TimeStart":"2013-12-05T09:52:17+01:00","TimeEnd":"2013-12-05T09:53:17+01:00","Cost":1,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":3,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}` func TestDebitInsufficientBalance(t *testing.T) { - b1 := new(UserBalance) + b1 := new(Account) if err := json.Unmarshal([]byte(balanceInsufficient), b1); err != nil { t.Error("Error restoring balance1: ", err) } diff --git a/engine/responder.go b/engine/responder.go index 548be6c21..f135b8378 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -43,7 +43,7 @@ func (rs *Responder) GetCost(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.GetCost") *reply, err = *r, e } else { - r, e := AccLock.GuardGetCost(arg.GetUserBalanceKey(), func() (*CallCost, error) { + r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { return arg.GetCost() }) *reply, err = *r, e @@ -56,7 +56,7 @@ func (rs *Responder) Debit(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.Debit") *reply, err = *r, e } else { - r, e := AccLock.GuardGetCost(arg.GetUserBalanceKey(), func() (*CallCost, error) { + r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { return arg.Debit() }) *reply, err = *r, e @@ -69,7 +69,7 @@ func (rs *Responder) MaxDebit(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.MaxDebit") *reply, err = *r, e } else { - r, e := AccLock.GuardGetCost(arg.GetUserBalanceKey(), func() (*CallCost, error) { + r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { return arg.MaxDebit() }) *reply, err = *r, e @@ -81,7 +81,7 @@ func (rs *Responder) RefundIncrements(arg CallDescriptor, reply *float64) (err e if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.RefundIncrements") } else { - r, e := AccLock.Guard(arg.GetUserBalanceKey(), func() (float64, error) { + r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { return arg.RefundIncrements() }) *reply, err = r, e @@ -93,7 +93,7 @@ func (rs *Responder) DebitCents(arg CallDescriptor, reply *float64) (err error) if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.DebitCents") } else { - r, e := AccLock.Guard(arg.GetUserBalanceKey(), func() (float64, error) { + r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { return arg.DebitCents() }) *reply, err = r, e @@ -105,7 +105,7 @@ func (rs *Responder) DebitSMS(arg CallDescriptor, reply *float64) (err error) { if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.DebitSMS") } else { - r, e := AccLock.Guard(arg.GetUserBalanceKey(), func() (float64, error) { + r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { return arg.DebitSMS() }) *reply, err = r, e @@ -117,7 +117,7 @@ func (rs *Responder) DebitSeconds(arg CallDescriptor, reply *float64) (err error if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.DebitSeconds") } else { - r, e := AccLock.Guard(arg.GetUserBalanceKey(), func() (float64, error) { + r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { return 0, arg.DebitSeconds() }) *reply, err = r, e @@ -129,7 +129,7 @@ func (rs *Responder) GetMaxSessionTime(arg CallDescriptor, reply *float64) (err if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.GetMaxSessionTime") } else { - r, e := AccLock.Guard(arg.GetUserBalanceKey(), func() (float64, error) { + r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { d, err := arg.GetMaxSessionDuration() return float64(d), err }) @@ -143,7 +143,7 @@ func (rs *Responder) AddRecievedCallSeconds(arg CallDescriptor, reply *float64) *reply, err = rs.callMethod(&arg, "Responder.AddRecievedCallSeconds") } else { - r, e := AccLock.Guard(arg.GetUserBalanceKey(), func() (float64, error) { + r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { return 0, arg.AddRecievedCallSeconds() }) *reply, err = r, e @@ -155,7 +155,7 @@ func (rs *Responder) FlushCache(arg CallDescriptor, reply *float64) (err error) if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.FlushCache") } else { - r, e := AccLock.Guard(arg.GetUserBalanceKey(), func() (float64, error) { + r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { return 0, arg.FlushCache() }) *reply, err = r, e @@ -220,7 +220,7 @@ func (rs *Responder) getBalance(arg *CallDescriptor, balanceId string, reply *Ca return errors.New("No balancer supported for this command right now") } ubKey := arg.Direction + ":" + arg.Tenant + ":" + arg.Account - userBalance, err := accountingStorage.GetUserBalance(ubKey) + userBalance, err := accountingStorage.GetAccount(ubKey) if err != nil { return err } @@ -248,7 +248,7 @@ func (rs *Responder) getCallCost(key *CallDescriptor, method string) (reply *Cal time.Sleep(1 * time.Second) // wait one second and retry } else { reply = &CallCost{} - reply, err = AccLock.GuardGetCost(key.GetUserBalanceKey(), func() (*CallCost, error) { + reply, err = AccLock.GuardGetCost(key.GetAccountKey(), func() (*CallCost, error) { err = client.Call(method, *key, reply) return reply, err }) @@ -271,7 +271,7 @@ func (rs *Responder) callMethod(key *CallDescriptor, method string) (reply float Logger.Info("Waiting for raters to register...") time.Sleep(1 * time.Second) // wait one second and retry } else { - reply, err = AccLock.Guard(key.GetUserBalanceKey(), func() (float64, error) { + reply, err = AccLock.Guard(key.GetAccountKey(), func() (float64, error) { err = client.Call(method, *key, &reply) return reply, err }) diff --git a/engine/storage_interface.go b/engine/storage_interface.go index d98fa11dd..d2057d5cb 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -37,7 +37,7 @@ const ( RATING_PROFILE_PREFIX = "rpf_" ACTION_PREFIX = "act_" SHARED_GROUP_PREFIX = "shg_" - USER_BALANCE_PREFIX = "ubl_" + ACCOUNT_PREFIX = "ubl_" DESTINATION_PREFIX = "dst_" TEMP_DESTINATION_PREFIX = "tmp_" LOG_CALL_COST_PREFIX = "cco_" @@ -87,8 +87,8 @@ type AccountingStorage interface { SetActions(string, Actions) error GetSharedGroup(string, bool) (*SharedGroup, error) SetSharedGroup(string, *SharedGroup) error - GetUserBalance(string) (*UserBalance, error) - SetUserBalance(*UserBalance) error + GetAccount(string) (*Account, error) + SetAccount(*Account) error GetActionTimings(string) (ActionPlan, error) SetActionTimings(string, ActionPlan) error GetAllActionTimings() (map[string]ActionPlan, error) diff --git a/engine/storage_map.go b/engine/storage_map.go index d2c0810a3..435d79763 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -24,6 +24,7 @@ import ( "strings" "time" + "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/utils" ) @@ -249,9 +250,9 @@ func (ms *MapStorage) SetSharedGroup(key string, sg *SharedGroup) (err error) { return } -func (ms *MapStorage) GetUserBalance(key string) (ub *UserBalance, err error) { - if values, ok := ms.dict[USER_BALANCE_PREFIX+key]; ok { - ub = &UserBalance{Id: key} +func (ms *MapStorage) GetAccount(key string) (ub *Account, err error) { + if values, ok := ms.dict[ACCOUNT_PREFIX+key]; ok { + ub = &Account{Id: key} err = ms.ms.Unmarshal(values, ub) } else { return nil, errors.New("not found") @@ -259,9 +260,9 @@ func (ms *MapStorage) GetUserBalance(key string) (ub *UserBalance, err error) { return } -func (ms *MapStorage) SetUserBalance(ub *UserBalance) (err error) { +func (ms *MapStorage) SetAccount(ub *Account) (err error) { result, err := ms.ms.Marshal(ub) - ms.dict[USER_BALANCE_PREFIX+ub.Id] = result + ms.dict[ACCOUNT_PREFIX+ub.Id] = result return } diff --git a/engine/storage_mongo.go b/engine/storage_mongo.go index f136ad71b..92b387f2b 100644 --- a/engine/storage_mongo.go +++ b/engine/storage_mongo.go @@ -183,13 +183,13 @@ func (ms *MongoStorage) SetActions(key string, as Actions) error { return ms.db.C("actions").Insert(&AcKeyValue{Key: key, Value: as}) } -func (ms *MongoStorage) GetUserBalance(key string) (result *UserBalance, err error) { - result = new(UserBalance) +func (ms *MongoStorage) GetAccount(key string) (result *Account, err error) { + result = new(Account) err = ms.db.C("userbalances").Find(bson.M{"id": key}).One(result) return } -func (ms *MongoStorage) SetUserBalance(ub *UserBalance) error { +func (ms *MongoStorage) SetAccount(ub *Account) error { return ms.db.C("userbalances").Insert(ub) } diff --git a/engine/storage_redis.go b/engine/storage_redis.go index d874cd121..6db56f9ae 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -171,7 +171,7 @@ func (rs *RedisStorage) CacheAccounting(actKeys, shgKeys []string) (err error) { // Used to check if specific subject is stored using prefix key attached to entity func (rs *RedisStorage) HasData(category, subject string) (bool, error) { switch category { - case DESTINATION_PREFIX, RATING_PLAN_PREFIX, RATING_PROFILE_PREFIX, ACTION_PREFIX, ACTION_TIMING_PREFIX, USER_BALANCE_PREFIX: + case DESTINATION_PREFIX, RATING_PLAN_PREFIX, RATING_PROFILE_PREFIX, ACTION_PREFIX, ACTION_TIMING_PREFIX, ACCOUNT_PREFIX: return rs.db.Exists(category + subject) } return false, errors.New("Unsupported category in ExistsData") @@ -342,19 +342,19 @@ func (rs *RedisStorage) SetSharedGroup(key string, sg *SharedGroup) (err error) return } -func (rs *RedisStorage) GetUserBalance(key string) (ub *UserBalance, err error) { +func (rs *RedisStorage) GetAccount(key string) (ub *Account, err error) { var values []byte - if values, err = rs.db.Get(USER_BALANCE_PREFIX + key); err == nil { - ub = &UserBalance{Id: key} + if values, err = rs.db.Get(ACCOUNT_PREFIX + key); err == nil { + ub = &Account{Id: key} err = rs.ms.Unmarshal(values, ub) } return } -func (rs *RedisStorage) SetUserBalance(ub *UserBalance) (err error) { +func (rs *RedisStorage) SetAccount(ub *Account) (err error) { result, err := rs.ms.Marshal(ub) - err = rs.db.Set(USER_BALANCE_PREFIX+ub.Id, result) + err = rs.db.Set(ACCOUNT_PREFIX+ub.Id, result) return } diff --git a/engine/storage_test.go b/engine/storage_test.go index 29985a548..1ed0cd72d 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -117,18 +117,17 @@ func TestStoreInterfaces(t *testing.T) { var _ LogStorage = sql } - /************************** Benchmarks *****************************/ -func GetUB() *UserBalance { +func GetUB() *Account { uc := &UnitsCounter{ - Direction: OUTBOUND, - BalanceId: SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}, + Direction: OUTBOUND, + BalanceType: SMS, + Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}, } at := &ActionTrigger{ Id: "some_uuid", - BalanceId: CREDIT, + BalanceType: CREDIT, Direction: OUTBOUND, ThresholdValue: 100.0, DestinationId: "NAT", @@ -137,9 +136,9 @@ func GetUB() *UserBalance { } var zeroTime time.Time zeroTime = zeroTime.UTC() // for deep equal to find location - ub := &UserBalance{ + ub := &Account{ Id: "rif", - Type: UB_TYPE_POSTPAID, + AllowNegative: true, BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, MINUTES: BalanceChain{&Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{uc, uc}, ActionTriggers: ActionTriggerPriotityList{at, at, at}, @@ -161,7 +160,7 @@ func BenchmarkMarshallerJSONStoreRestore(b *testing.B) { ub := GetUB() ap1 := RatingPlan{} - ub1 := &UserBalance{} + ub1 := &Account{} b.StartTimer() ms := new(JSONMarshaler) for i := 0; i < b.N; i++ { @@ -186,7 +185,7 @@ func BenchmarkMarshallerBSONStoreRestore(b *testing.B) { ub := GetUB() ap1 := RatingPlan{} - ub1 := &UserBalance{} + ub1 := &Account{} b.StartTimer() ms := new(BSONMarshaler) for i := 0; i < b.N; i++ { @@ -210,7 +209,7 @@ func BenchmarkMarshallerJSONBufStoreRestore(b *testing.B) { ub := GetUB() ap1 := RatingPlan{} - ub1 := &UserBalance{} + ub1 := &Account{} b.StartTimer() ms := new(JSONBufMarshaler) for i := 0; i < b.N; i++ { @@ -234,7 +233,7 @@ func BenchmarkMarshallerGOBStoreRestore(b *testing.B) { ub := GetUB() ap1 := RatingPlan{} - ub1 := &UserBalance{} + ub1 := &Account{} b.StartTimer() ms := new(GOBMarshaler) for i := 0; i < b.N; i++ { @@ -259,7 +258,7 @@ func BenchmarkMarshallerCodecMsgpackStoreRestore(b *testing.B) { ub := GetUB() ap1 := RatingPlan{} - ub1 := &UserBalance{} + ub1 := &Account{} b.StartTimer() ms := NewCodecMsgpackMarshaler() for i := 0; i < b.N; i++ { @@ -284,7 +283,7 @@ func BenchmarkMarshallerBincStoreRestore(b *testing.B) { ub := GetUB() ap1 := RatingPlan{} - ub1 := &UserBalance{} + ub1 := &Account{} b.StartTimer() ms := NewBincMarshaler() for i := 0; i < b.N; i++ { diff --git a/engine/units_counter.go b/engine/units_counter.go index 42742f6c9..4bc76ac12 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -25,12 +25,14 @@ import ( // Amount of a trafic of a certain type type UnitsCounter struct { - Direction string - BalanceId string + Direction string + BalanceType string // Units float64 Balances BalanceChain // first balance is the general one (no destination) } +// clears balances for this counter +// makes sure there are balances for all action triggers func (uc *UnitsCounter) initBalances(ats []*ActionTrigger) { uc.Balances = BalanceChain{&Balance{}} // general balance for _, at := range ats { diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index a902348c5..4f944c443 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -24,9 +24,9 @@ import ( func TestUnitsCounterAddBalance(t *testing.T) { uc := &UnitsCounter{ - Direction: OUTBOUND, - BalanceId: SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}, + Direction: OUTBOUND, + BalanceType: SMS, + Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}, } uc.addUnits(20, "test") if len(uc.Balances) != 3 { @@ -36,9 +36,9 @@ func TestUnitsCounterAddBalance(t *testing.T) { func TestUnitsCounterAddBalanceExists(t *testing.T) { uc := &UnitsCounter{ - Direction: OUTBOUND, - BalanceId: SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}, + Direction: OUTBOUND, + BalanceType: SMS, + Balances: BalanceChain{&Balance{Value: 1}, &Balance{Value: 10, Weight: 20, DestinationId: "NAT"}, &Balance{Weight: 10, DestinationId: "RET"}}, } uc.addUnits(5, "0723") if len(uc.Balances) != 3 || uc.Balances[1].Value != 15 { diff --git a/utils/coreutils.go b/utils/coreutils.go index 80ebb4d5b..ac033245a 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -135,8 +135,12 @@ func ParseDate(date string) (expDate time.Time, err error) { return expDate, err } expDate = time.Now().Add(d) + case date == "*daily": + expDate = time.Now().AddDate(0, 0, 1) // add one day case date == "*monthly": expDate = time.Now().AddDate(0, 1, 0) // add one month + case date == "*yearly": + expDate = time.Now().AddDate(1, 0, 0) // add one year case strings.HasSuffix(date, "Z"): expDate, err = time.Parse(time.RFC3339, date) default: