Merge branch 'master' into shared_balances

Conflicts:
	engine/account.go
	engine/loader_csv_test.go
	engine/storage_interface.go
	engine/storage_map.go
	engine/storage_redis.go
This commit is contained in:
Radu Ioan Fericean
2014-02-21 16:36:48 +02:00
35 changed files with 829 additions and 580 deletions

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@ docs/_*
bin
.idea
dean*
data/vagrant/.vagrant

View File

@@ -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

View File

@@ -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

View File

@@ -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" {

View File

@@ -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 <tenant> <account> <type=prepaid|postpaid> <actiontimingsid> [<direction>]")
return fmt.Sprintf("\n\tUsage: cgr-console [cfg_opts...{-h}] add_account <tenant> <account> <allownegative> <actiontimingsid> [<direction>]")
}
// 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]

View File

@@ -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]

View File

@@ -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

View File

@@ -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
}

60
data/vagrant/Vagrantfile vendored Normal file
View File

@@ -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

View File

@@ -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

115
data/vagrant/bashrc Normal file
View File

@@ -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

3
data/vagrant/my.cnf Normal file
View File

@@ -0,0 +1,3 @@
[client]
user=root
password={{ root_db_password }}

View File

@@ -0,0 +1,3 @@
# Generated by Vagrant
default ansible_ssh_host=127.0.0.1 ansible_ssh_port=2222

View File

@@ -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 != "" {

View File

@@ -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",
}

View File

@@ -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

View File

@@ -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]
}
}
}

View File

@@ -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 != "" {

View File

@@ -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 ********************************/

View File

@@ -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

View File

@@ -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("<Rater> Error getting cost for account key %v: %v", cd.GetUserBalanceKey(), err))
Logger.Err(fmt.Sprintf("<Rater> 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("<Rater> Error retrieving user balance: %v", err))
} else if userBalance == nil {
// Logger.Debug(fmt.Sprintf("<Rater> No user balance defined: %v", cd.GetUserBalanceKey()))
// Logger.Debug(fmt.Sprintf("<Rater> No user balance defined: %v", cd.GetAccountKey()))
} else {
//Logger.Debug(fmt.Sprintf("<Rater> Attempting to debit from %v, value: %v", cd.GetUserBalanceKey(), cc.Cost+cc.ConnectFee))
defer accountingStorage.SetUserBalance(userBalance)
//Logger.Debug(fmt.Sprintf("<Rater> 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},

View File

@@ -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),

View File

@@ -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

View File

@@ -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) {

View File

@@ -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
}
}

View File

@@ -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)
}

View File

@@ -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
})

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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++ {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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: