Merge branch 'master' into lcr

This commit is contained in:
Radu Ioan Fericean
2014-04-07 13:33:09 +03:00
20 changed files with 331 additions and 134 deletions

View File

@@ -126,7 +126,7 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error {
Balance: &engine.Balance{
Value: attr.Value,
ExpirationDate: attr.ExpirationDate,
RateSubject: attr.RatingSubject,
RatingSubject: attr.RatingSubject,
DestinationId: attr.DestinationId,
Weight: attr.Weight,
},
@@ -300,7 +300,7 @@ func (self *ApierV1) SetActions(attrs AttrSetActions, reply *string) error {
Value: apiAct.Units,
Weight: apiAct.BalanceWeight,
DestinationId: apiAct.DestinationId,
RateSubject: apiAct.RatingSubject,
RatingSubject: apiAct.RatingSubject,
},
}
storeActions[idx] = a

View File

@@ -0,0 +1,156 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012-2014 ITsysCOM GmbH
This program is free software: you can Storagetribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITH*out ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package engine
import (
"testing"
"time"
"github.com/cgrates/cgrates/cache2go"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/scheduler"
)
var ratingDb engine.RatingStorage
var acntDb engine.AccountingStorage
func init() {
ratingDb, _ = engine.NewMapStorageJson()
engine.SetRatingStorage(ratingDb)
acntDb, _ = engine.NewMapStorageJson()
engine.SetAccountingStorage(acntDb)
}
func TestLoadCsvTp(t *testing.T) {
timings := `ALWAYS,*any,*any,*any,*any,00:00:00
ASAP,*any,*any,*any,*any,*asap`
destinations := `DST_UK_Mobile_BIG5,447596
DST_UK_Mobile_BIG5,447956`
rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s,*up,8
RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8`
destinationRates := `DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG
DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5`
ratingPlans := `RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10
RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10`
ratingProfiles := `cgrates.org,call,*out,*any,2013-01-06T00:00:00Z,RP_UK,
cgrates.org,call,*out,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,`
sharedGroups := ``
actions := `TOPUP10_AC,*topup_reset,*monetary,*out,10,*unlimited,*any,,10,,,10
TOPUP10_AC1,*topup_reset,*minutes,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10`
actionPlans := `TOPUP10_AT,TOPUP10_AC,ASAP,10
TOPUP10_AT,TOPUP10_AC1,ASAP,10`
actionTriggers := ``
accountActions := `cgrates.org,12345,*out,TOPUP10_AT,`
csvr := engine.NewStringCSVReader(ratingDb, acntDb, ',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions)
if err := csvr.LoadDestinations(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadTimings(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadRates(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadDestinationRates(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadRatingPlans(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadRatingProfiles(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadSharedGroups(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadActions(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadActionTimings(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadActionTriggers(); err != nil {
t.Fatal(err)
}
if err := csvr.LoadAccountActions(); err != nil {
t.Fatal(err)
}
csvr.WriteToDatabase(false, false)
if acnt, err := acntDb.GetAccount("*out:cgrates.org:12345"); err != nil {
t.Error(err)
} else if acnt == nil {
t.Error("No account saved")
}
ratingDb.CacheRating(nil, nil, nil, nil)
acntDb.CacheAccounting(nil, nil, nil)
if cachedDests := cache2go.CountEntries(engine.DESTINATION_PREFIX); cachedDests != 2 {
t.Error("Wrong number of cached destinations found", cachedDests)
}
if cachedRPlans := cache2go.CountEntries(engine.RATING_PLAN_PREFIX); cachedRPlans != 2 {
t.Error("Wrong number of cached rating plans found", cachedRPlans)
}
if cachedRProfiles := cache2go.CountEntries(engine.RATING_PROFILE_PREFIX); cachedRProfiles != 2 {
t.Error("Wrong number of cached rating profiles found", cachedRProfiles)
}
if cachedActions := cache2go.CountEntries(engine.ACTION_PREFIX); cachedActions != 2 {
t.Error("Wrong number of cached actions found", cachedActions)
}
}
func TestExecuteActions(t *testing.T) {
scheduler.NewScheduler().LoadActionTimings(acntDb)
time.Sleep(time.Duration(1) * time.Microsecond) // Give time to scheduler to topup the account
if acnt, err := acntDb.GetAccount("*out:cgrates.org:12345"); err != nil {
t.Error(err)
} else if len(acnt.BalanceMap) != 2 {
t.Error("Account does not have enough balances: ", acnt.BalanceMap)
} else if acnt.BalanceMap[engine.MINUTES+engine.OUTBOUND][0].Value != 40 {
t.Errorf("Account does not have enough minutes in balance", acnt.BalanceMap[engine.MINUTES+engine.OUTBOUND][0].Value)
} else if acnt.BalanceMap[engine.CREDIT+engine.OUTBOUND][0].Value != 10 {
t.Error("Account does not have enough monetary balance", acnt.BalanceMap[engine.CREDIT+engine.OUTBOUND][0].Value)
}
}
func TestDebit(t *testing.T) {
cd := &engine.CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956933443",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 10, 0, time.UTC),
}
if cc, err := cd.Debit(); err != nil {
t.Error(err)
} else if cc.Cost != 0.01 {
t.Error("Wrong cost returned: ", cc.Cost)
}
acnt, err := acntDb.GetAccount("*out:cgrates.org:12345")
if err != nil {
t.Error(err)
}
if acnt.BalanceMap[engine.MINUTES+engine.OUTBOUND][0].Value != 20 {
t.Error("Account does not have expected minutes in balance", acnt.BalanceMap[engine.MINUTES+engine.OUTBOUND][0].Value)
}
if acnt.BalanceMap[engine.CREDIT+engine.OUTBOUND][0].Value != 9.99 {
t.Error("Account does not have expected monetary balance", acnt.BalanceMap[engine.CREDIT+engine.OUTBOUND][0].Value)
}
}

View File

@@ -53,9 +53,9 @@ var (
dbdata_encoding = flag.String("dbdata_encoding", cgrConfig.DBDataEncoding, "The encoding used to store object data in strings.")
raterAddress = flag.String("rater_address", "", "Rater address for remote tests. Empty for internal rater.")
tor = flag.String("tor", "call", "The type of record to use in queries.")
tenant = flag.String("tenant", "call", "The type of record to use in queries.")
tenant = flag.String("tenant", "cgrates.org", "The type of record to use in queries.")
subject = flag.String("subject", "1001", "The rating subject to use in queries.")
destination = flag.String("destination", "+4986517174963", "The destination to use in queries.")
destination = flag.String("destination", "1002", "The destination to use in queries.")
nilDuration = time.Duration(0)
)
@@ -150,8 +150,8 @@ func main() {
defer pprof.StopCPUProfile()
}
cd := &engine.CallDescriptor{
TimeStart: time.Date(2013, time.December, 13, 22, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, time.December, 13, 22, 31, 0, 0, time.UTC),
TimeStart: time.Date(2014, time.December, 11, 55, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2014, time.December, 11, 55, 31, 0, 0, time.UTC),
CallDuration: 60 * time.Second,
Direction: "*out",
TOR: *tor,

View File

@@ -38,7 +38,7 @@ type CmdDebitBalance struct {
// name should be exec's name
func (self *CmdDebitBalance) Usage(name string) string {
return fmt.Sprintf("\n\tUsage: cgr-console [cfg_opts...{-h}] debit_balance <tor> <tenant> <subject> <destination> <start_time|*now> <duration>")
return fmt.Sprintf("\n\tUsage: cgr-console [cfg_opts...{-h}] debit_balance <tor> <tenant> <account> <subject> <destination> <start_time|*now> <duration>")
}
// set param defaults
@@ -50,17 +50,17 @@ func (self *CmdDebitBalance) defaults() error {
// Parses command line args and builds CmdBalance value
func (self *CmdDebitBalance) FromArgs(args []string) error {
if len(args) != 8 {
if len(args) != 9 {
return fmt.Errorf(self.Usage(""))
}
// Args look OK, set defaults before going further
self.defaults()
var tStart time.Time
var err error
if args[6] == "*now" {
if args[7] == "*now" {
tStart = time.Now()
} else {
tStart, err = utils.ParseDate(args[6])
tStart, err = utils.ParseDate(args[7])
if err != nil {
fmt.Println("\n*start_time* should have one of the formats:")
fmt.Println("\ttime.RFC3339\teg:2013-08-07T17:30:00Z in UTC")
@@ -70,14 +70,15 @@ func (self *CmdDebitBalance) FromArgs(args []string) error {
return fmt.Errorf(self.Usage(""))
}
}
callDur, err := utils.ParseDurationWithSecs(args[7])
callDur, err := utils.ParseDurationWithSecs(args[8])
if err != nil {
fmt.Println("\n\tExample durations: 60s for 60 seconds, 25m for 25minutes, 1m25s for one minute and 25 seconds\n")
}
self.rpcParams.TOR = args[2]
self.rpcParams.Tenant = args[3]
self.rpcParams.Subject = args[4]
self.rpcParams.Destination = args[5]
self.rpcParams.Account = args[4]
self.rpcParams.Subject = args[5]
self.rpcParams.Destination = args[6]
self.rpcParams.TimeStart = tStart
self.rpcParams.CallDuration = callDur
self.rpcParams.TimeEnd = tStart.Add(callDur)

View File

@@ -47,9 +47,9 @@ cd = {"Direction":"*out",
"TOR":"call",
"Tenant": "cgrates.org",
"Subject": "1001",
"Destination": "+49",
"TimeStart": "2013-08-07T17:30:00Z",
"TimeEnd": "2013-08-07T18:30:00Z",
"Destination": "1002",
"TimeStart": "2014-04-03T11:12:23.190554134+02:00",
"TimeEnd": "2014-04-03T11:13:23.190554134+02:00",
"CallDuration": 60000000000,
}
@@ -60,7 +60,7 @@ cd = {"Direction":"*out",
start_time = time.time()
i = 0
runs = 1e5
runs = 5e5
result = ""
for i in range(int(runs) + 1):
result = rpc.call("Responder.GetCost", cd)

View File

@@ -182,9 +182,33 @@ func (ub *Account) getBalancesForPrefix(prefix string, balances BalanceChain, sh
}
// resort by precision
usefulBalances.Sort()
// clear precision
for _, b := range usefulBalances {
b.precision = 0
}
return usefulBalances
}
// like getBalancesForPrefix but expanding shared balances
func (account *Account) getAlldBalancesForPrefix(destination, balanceType string) (bc BalanceChain) {
balances := account.getBalancesForPrefix(destination, account.BalanceMap[balanceType], "")
for _, b := range balances {
if b.SharedGroup != "" {
sharedGroup, err := accountingStorage.GetSharedGroup(b.SharedGroup, false)
if err != nil {
Logger.Warning(fmt.Sprintf("Could not get shared group: %v", b.SharedGroup))
continue
}
sharedBalances := sharedGroup.GetBalances(destination, balanceType, account)
sharedBalances = sharedGroup.SortBalancesByStrategy(b, sharedBalances)
bc = append(bc, sharedBalances...)
} else {
bc = append(bc, b)
}
}
return
}
func (ub *Account) debitCreditBalance(cc *CallCost, count bool) (err error) {
usefulMinuteBalances := ub.getAlldBalancesForPrefix(cc.Destination, MINUTES+cc.Direction)
usefulMoneyBalances := ub.getAlldBalancesForPrefix(cc.Destination, CREDIT+cc.Direction)
@@ -482,23 +506,3 @@ func (account *Account) GetUniqueSharedGroupMembers(destination, direction strin
}
return memberIds, nil
}
// like getBalancesForPrefix but expanding shared balances
func (account *Account) getAlldBalancesForPrefix(destination, balanceType string) (bc BalanceChain) {
balances := account.getBalancesForPrefix(destination, account.BalanceMap[balanceType], "")
for _, b := range balances {
if b.SharedGroup != "" {
sharedGroup, err := accountingStorage.GetSharedGroup(b.SharedGroup, false)
if err != nil {
Logger.Warning(fmt.Sprintf("Could not get shared group: %v", b.SharedGroup))
continue
}
sharedBalances := sharedGroup.GetBalances(destination, balanceType, account)
sharedBalances = sharedGroup.SortBalancesByStrategy(b, sharedBalances)
bc = append(bc, sharedBalances...)
} else {
bc = append(bc, b)
}
}
return
}

View File

@@ -115,8 +115,8 @@ func TestGetSecondsForPrefix(t *testing.T) {
}
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"}
b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET", RatingSubject: "minu"}
ub1 := &Account{
Id: "OUT:CUSTOMER_1:rif",
@@ -158,7 +158,7 @@ func TestAccountStorageStore(t *testing.T) {
}
func TestDebitCreditZeroSecond(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1s"}
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1s"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -187,7 +187,7 @@ func TestDebitCreditZeroSecond(t *testing.T) {
}
func TestDebitCreditZeroMinute(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1m"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -220,8 +220,8 @@ func TestDebitCreditZeroMinute(t *testing.T) {
}
}
func TestDebitCreditZeroMixedMinute(t *testing.T) {
b1 := &Balance{Uuid: "testm", Value: 70, Weight: 5, DestinationId: "NAT", RateSubject: "*zero1m"}
b2 := &Balance{Uuid: "tests", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1s"}
b1 := &Balance{Uuid: "testm", Value: 70, Weight: 5, DestinationId: "NAT", RatingSubject: "*zero1m"}
b2 := &Balance{Uuid: "tests", Value: 10, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1s"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -255,7 +255,7 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) {
}
func TestDebitCreditNoCredit(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1m"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -295,7 +295,7 @@ func TestDebitCreditNoCredit(t *testing.T) {
}
func TestDebitCreditHasCredit(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1m"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -337,7 +337,7 @@ func TestDebitCreditHasCredit(t *testing.T) {
}
func TestDebitCreditSplitMinutesMoney(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1s"}
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1s"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -374,7 +374,7 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) {
}
func TestDebitCreditMoreTimespans(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 150, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
b1 := &Balance{Uuid: "testb", Value: 150, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1m"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -411,8 +411,8 @@ func TestDebitCreditMoreTimespans(t *testing.T) {
}
func TestDebitCreditMoreTimespansMixed(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
b2 := &Balance{Uuid: "testa", Value: 150, Weight: 5, DestinationId: "NAT", RateSubject: "*zero1s"}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1m"}
b2 := &Balance{Uuid: "testa", Value: 150, Weight: 5, DestinationId: "NAT", RatingSubject: "*zero1s"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -450,7 +450,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) {
}
func TestDebitCreditNoConectFeeCredit(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "*zero1m"}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -527,7 +527,7 @@ func TestDebitCreditMoneyOnly(t *testing.T) {
}
func TestDebitCreditSubjectMinutes(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 250, Weight: 10, DestinationId: "NAT", RateSubject: "minu"}
b1 := &Balance{Uuid: "testb", Value: 250, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
@@ -586,7 +586,7 @@ func TestDebitCreditSubjectMoney(t *testing.T) {
deductConnectFee: true,
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 75, DestinationId: "NAT", RateSubject: "minu"}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 75, DestinationId: "NAT", RatingSubject: "minu"}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
@@ -606,7 +606,7 @@ func TestDebitCreditSubjectMoney(t *testing.T) {
}
func TestDebitCreditSubjectMixed(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 40, Weight: 10, DestinationId: "NAT", RateSubject: "minu"}
b1 := &Balance{Uuid: "testb", Value: 40, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
@@ -624,7 +624,7 @@ func TestDebitCreditSubjectMixed(t *testing.T) {
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 150, RateSubject: "minu"}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 150, RatingSubject: "minu"}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
@@ -649,7 +649,7 @@ func TestDebitCreditSubjectMixed(t *testing.T) {
}
func TestDebitCreditSubjectMixedMoreTS(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "minu"}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
@@ -673,7 +673,7 @@ func TestDebitCreditSubjectMixedMoreTS(t *testing.T) {
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50, RateSubject: "minu"}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50, RatingSubject: "minu"}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err == nil {
@@ -700,7 +700,7 @@ func TestDebitCreditSubjectMixedMoreTS(t *testing.T) {
}
func TestDebitCreditSubjectMixedPartPay(t *testing.T) {
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "minu"}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RatingSubject: "minu"}
cc := &CallCost{
Tenant: "vdf",
TOR: "0",
@@ -724,7 +724,7 @@ func TestDebitCreditSubjectMixedPartPay(t *testing.T) {
}
rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 75, RateSubject: "minu"}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 75, RatingSubject: "minu"}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err == nil {

View File

@@ -48,22 +48,22 @@ type Action struct {
}
const (
LOG = "*log"
RESET_TRIGGERS = "*reset_triggers"
ALLOW_NEGATIVE = "*allow_negative"
DENY_NEGATIVE = "*deny_negative"
RESET_ACCOUNT = "*reset_account"
TOPUP_RESET = "*topup_reset"
TOPUP = "*topup"
DEBIT = "*debit"
RESET_COUNTER = "*reset_counter"
RESET_COUNTERS = "*reset_counters"
ENABLE_USER = "*enable_user"
DISABLE_USER = "*disable_user"
CALL_URL = "*call_url"
CALL_URL_ASYNC = "*call_url_async"
MAIL_ASYNC = "*mail_async"
UNLIMITED = "*unlimited"
LOG = "*log"
RESET_TRIGGERS = "*reset_triggers"
ALLOW_NEGATIVE = "*allow_negative"
DENY_NEGATIVE = "*deny_negative"
RESET_ACCOUNT = "*reset_account"
TOPUP_RESET = "*topup_reset"
TOPUP = "*topup"
DEBIT = "*debit"
RESET_COUNTER = "*reset_counter"
RESET_COUNTERS = "*reset_counters"
ENABLE_ACCOUNT = "*enable_account"
DISABLE_ACCOUNT = "*disable_account"
CALL_URL = "*call_url"
CALL_URL_ASYNC = "*call_url_async"
MAIL_ASYNC = "*mail_async"
UNLIMITED = "*unlimited"
)
type actionTypeFunc func(*Account, *Action) error
@@ -90,9 +90,9 @@ func getActionFunc(typ string) (actionTypeFunc, bool) {
return resetCounterAction, true
case RESET_COUNTERS:
return resetCountersAction, true
case ENABLE_USER:
case ENABLE_ACCOUNT:
return enableUserAction, true
case DISABLE_USER:
case DISABLE_ACCOUNT:
return disableUserAction, true
case CALL_URL:
return callUrl, true

View File

@@ -235,7 +235,10 @@ func (at *ActionTiming) Execute() (err error) {
for _, ubId := range at.AccountIds {
_, err := AccLock.Guard(ubId, func() (float64, error) {
ub, err := accountingStorage.GetAccount(ubId)
if ub.Disabled {
if err != nil {
Logger.Warning(fmt.Sprintf("Could not get user balances for this id: %s. Skipping!", ubId))
return 0, err
} else if ub.Disabled {
return 0, fmt.Errorf("User %s is disabled", ubId)
}
if err != nil {

View File

@@ -21,8 +21,8 @@ package engine
import (
"encoding/json"
"fmt"
"sort"
"github.com/cgrates/cgrates/utils"
"sort"
)
type ActionTrigger struct {

View File

@@ -33,7 +33,7 @@ type Balance struct {
ExpirationDate time.Time
Weight float64
DestinationId string
RateSubject string
RatingSubject string
SharedGroup string
precision int
account *Account // used to store ub reference for shared balances
@@ -50,14 +50,14 @@ func (b *Balance) Equal(o *Balance) bool {
return b.ExpirationDate.Equal(o.ExpirationDate) &&
b.Weight == o.Weight &&
b.DestinationId == o.DestinationId &&
b.RateSubject == o.RateSubject &&
b.RatingSubject == o.RatingSubject &&
b.SharedGroup == o.SharedGroup
}
// the default balance has no destinationid, Expirationdate or ratesubject
func (b *Balance) IsDefault() bool {
return (b.DestinationId == "" || b.DestinationId == utils.ANY) &&
b.RateSubject == "" &&
b.RatingSubject == "" &&
b.ExpirationDate.IsZero() &&
b.SharedGroup == ""
}
@@ -81,7 +81,7 @@ func (b *Balance) Clone() *Balance {
DestinationId: b.DestinationId,
ExpirationDate: b.ExpirationDate,
Weight: b.Weight,
RateSubject: b.RateSubject,
RatingSubject: b.RatingSubject,
}
}
@@ -125,8 +125,8 @@ func (b *Balance) GetMinutesForCredit(origCD *CallDescriptor, initialCredit floa
}
func (b *Balance) GetCost(cd *CallDescriptor) (*CallCost, error) {
if b.RateSubject != "" {
cd.Subject = b.RateSubject
if b.RatingSubject != "" {
cd.Subject = b.RatingSubject
cd.Account = cd.Subject
cd.RatingInfos = nil
return cd.GetCost()
@@ -162,13 +162,13 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalan
if increment.paid {
continue
}
if duration, err := utils.ParseZeroRatingSubject(b.RateSubject); err == nil {
if duration, err := utils.ParseZeroRatingSubject(b.RatingSubject); err == nil {
seconds := duration.Seconds()
amount := seconds
if seconds == 1 {
amount = increment.Duration.Seconds()
}
if b.Value >= amount { // balance has at least 60 seconds
if b.Value >= amount {
newTs := ts
inc := increment
if seconds > 1 { // we need to recreate increments
@@ -215,7 +215,7 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalan
}
// get the new rate
cd := cc.CreateCallDescriptor()
cd.Subject = b.RateSubject
cd.Subject = b.RatingSubject
cd.TimeStart = ts.GetTimeStartForIncrement(incrementIndex)
cd.TimeEnd = cc.Timespans[len(cc.Timespans)-1].TimeEnd
cd.CallDuration = cc.Timespans[len(cc.Timespans)-1].CallDuration
@@ -240,7 +240,7 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalan
break
}
}
if moneyBal != nil && b.Value >= seconds {
if (cost == 0 || moneyBal != nil) && b.Value >= seconds {
b.SubstractAmount(seconds)
nInc.BalanceInfo.MinuteBalanceUuid = b.Uuid
nInc.BalanceInfo.AccountId = ub.Id
@@ -310,7 +310,7 @@ func (b *Balance) DebitMoney(cc *CallCost, count bool, ub *Account) error {
continue
}
// check standard subject tags
if b.RateSubject == "" {
if b.RatingSubject == "" {
amount := increment.Cost
if b.Value >= amount {
b.SubstractAmount(amount)
@@ -324,7 +324,7 @@ func (b *Balance) DebitMoney(cc *CallCost, count bool, ub *Account) error {
} else {
// get the new rate
cd := cc.CreateCallDescriptor()
cd.Subject = b.RateSubject
cd.Subject = b.RatingSubject
cd.TimeStart = ts.GetTimeStartForIncrement(incrementIndex)
cd.TimeEnd = cc.Timespans[len(cc.Timespans)-1].TimeEnd
cd.CallDuration = cc.Timespans[len(cc.Timespans)-1].CallDuration

View File

@@ -79,16 +79,16 @@ func TestBalanceSortWeightLess(t *testing.T) {
}
func TestBalanceEqual(t *testing.T) {
mb1 := &Balance{Weight: 1, precision: 1, RateSubject: "1", DestinationId: ""}
mb2 := &Balance{Weight: 1, precision: 1, RateSubject: "1", DestinationId: ""}
mb3 := &Balance{Weight: 1, precision: 1, RateSubject: "2", DestinationId: ""}
mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationId: ""}
mb2 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationId: ""}
mb3 := &Balance{Weight: 1, precision: 1, RatingSubject: "2", DestinationId: ""}
if !mb1.Equal(mb2) || mb2.Equal(mb3) {
t.Error("Equal failure!", mb1 == mb2, mb3)
}
}
func TestBalanceClone(t *testing.T) {
mb1 := &Balance{Value: 1, Weight: 2, RateSubject: "test", DestinationId: "5"}
mb1 := &Balance{Value: 1, Weight: 2, RatingSubject: "test", DestinationId: "5"}
mb2 := mb1.Clone()
if mb1 == mb2 || !reflect.DeepEqual(mb1, mb2) {
t.Errorf("Cloning failure: \n%v\n%v", mb1, mb2)

View File

@@ -58,7 +58,7 @@ func populateDB() {
Id: "*out:vdf:broker",
BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{
&Balance{Value: 20, DestinationId: "NAT", Weight: 10, RateSubject: "rif"},
&Balance{Value: 20, DestinationId: "NAT", Weight: 10, RatingSubject: "rif"},
&Balance{Value: 100, DestinationId: "RET", Weight: 20},
}},
}
@@ -67,7 +67,7 @@ func populateDB() {
Id: "*out:vdf:minitsboy",
BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{
&Balance{Value: 20, DestinationId: "NAT", Weight: 10, RateSubject: "rif"},
&Balance{Value: 20, DestinationId: "NAT", Weight: 10, RatingSubject: "rif"},
&Balance{Value: 100, DestinationId: "RET", Weight: 20},
},
CREDIT + OUTBOUND: BalanceChain{
@@ -575,7 +575,7 @@ func TestMaxDebitZeroDefinedRate(t *testing.T) {
cd1 := &CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.directvoip.co.uk",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
@@ -603,7 +603,7 @@ func TestMaxDebitZeroDefinedRateOnlyMinutes(t *testing.T) {
cd1 := &CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.directvoip.co.uk",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
@@ -613,7 +613,7 @@ func TestMaxDebitZeroDefinedRateOnlyMinutes(t *testing.T) {
CallDuration: 0}
cc, err := cd1.MaxDebit()
if err != nil {
t.Error("Error maxdebiting: ", err)
t.Fatal("Error maxdebiting: ", err)
}
if cc.GetDuration() != 40*time.Second {
t.Error("Error obtaining max debit duration: ", cc.GetDuration())
@@ -623,7 +623,29 @@ func TestMaxDebitZeroDefinedRateOnlyMinutes(t *testing.T) {
}
}
/*********************************** BENCHMARKS ***************************************/
func TestMaxDebitConsumesMinutes(t *testing.T) {
ap, _ := accountingStorage.GetActionTimings("TOPUP10_AT")
for _, at := range ap {
at.Execute()
}
cd1 := &CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 5, 0, time.UTC),
LoopIndex: 0,
CallDuration: 0}
cd1.MaxDebit()
if cd1.account.BalanceMap[MINUTES+OUTBOUND][0].Value != 20 {
t.Error("Error using minutes: ", cd1.account.BalanceMap[MINUTES+OUTBOUND][0].Value)
}
}
/*************** BENCHMARKS ********************/
func BenchmarkStorageGetting(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)

View File

@@ -518,16 +518,16 @@ func (csvr *CSVReader) LoadSharedGroups() (err error) {
sg, found := csvr.sharedGroups[tag]
if found {
sg.AccountParameters[record[1]] = &SharingParameters{
Strategy: record[2],
RateSubject: record[3],
Strategy: record[2],
RatingSubject: record[3],
}
} else {
sg = &SharedGroup{
Id: tag,
AccountParameters: map[string]*SharingParameters{
record[1]: &SharingParameters{
Strategy: record[2],
RateSubject: record[3],
Strategy: record[2],
RatingSubject: record[3],
},
},
}
@@ -584,7 +584,7 @@ func (csvr *CSVReader) LoadActions() (err error) {
Value: units,
Weight: balanceWeight,
DestinationId: record[6],
RateSubject: record[7],
RatingSubject: record[7],
SharedGroup: record[9],
},
}

View File

@@ -118,8 +118,8 @@ vdf,0,*out,fallback1,2013-11-18T13:45:00Z,G,fallback2
vdf,0,*out,fallback1,2013-11-18T13:46:00Z,G,fallback2
vdf,0,*out,fallback1,2013-11-18T13:47:00Z,G,fallback2
vdf,0,*out,fallback2,2013-11-18T13:45:00Z,R,rif
cgrates.directvoip.co.uk,call,*out,*any,2013-01-06T00:00:00Z,RP_UK,
cgrates.directvoip.co.uk,call,*out,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,
cgrates.org,call,*out,*any,2013-01-06T00:00:00Z,RP_UK,
cgrates.org,call,*out,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,
`
sharedGroups = `
SG1,*any,*lowest,
@@ -157,7 +157,7 @@ STANDARD_TRIGGERS,*monetary,*out,*max_counter,5,FS_USERS,LOG_WARNING,10
`
accountActions = `
vdf,minitsboy;a1;a2,*out,MORE_MINUTES,STANDARD_TRIGGER
cgrates.directvoip.co.uk,12345,*out,TOPUP10_AT,STANDARD_TRIGGERS
cgrates.org,12345,*out,TOPUP10_AT,STANDARD_TRIGGERS
vdf,empty0,*out,TOPUP_SHARED0_AT,
vdf,empty10,*out,TOPUP_SHARED10_AT,
vdf,emptyX,*out,TOPUP_EMPTY_AT,
@@ -633,7 +633,7 @@ func TestLoadActions(t *testing.T) {
Uuid: as1[1].Balance.Uuid,
Value: 100,
Weight: 10,
RateSubject: "test",
RatingSubject: "test",
DestinationId: "NAT",
},
},
@@ -673,8 +673,8 @@ func TestLoadSharedGroups(t *testing.T) {
Id: "SG1",
AccountParameters: map[string]*SharingParameters{
"*any": &SharingParameters{
Strategy: "*lowest",
RateSubject: "",
Strategy: "*lowest",
RatingSubject: "",
},
},
}
@@ -686,8 +686,8 @@ func TestLoadSharedGroups(t *testing.T) {
Id: "SG2",
AccountParameters: map[string]*SharingParameters{
"*any": &SharingParameters{
Strategy: "*lowest",
RateSubject: "one",
Strategy: "*lowest",
RatingSubject: "one",
},
},
}

View File

@@ -473,7 +473,7 @@ func (dbr *DbReader) LoadActions() (err error) {
Uuid: utils.GenUUID(),
Value: tpact.Units,
Weight: tpact.BalanceWeight,
RateSubject: tpact.RatingSubject,
RatingSubject: tpact.RatingSubject,
DestinationId: tpact.DestinationId,
},
}
@@ -712,7 +712,7 @@ func (dbr *DbReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions)
Uuid: utils.GenUUID(),
Value: tpact.Units,
Weight: tpact.BalanceWeight,
RateSubject: tpact.RatingSubject,
RatingSubject: tpact.RatingSubject,
DestinationId: tpact.DestinationId,
},
}

View File

@@ -45,8 +45,8 @@ type SharedGroup struct {
}
type SharingParameters struct {
Strategy string
RateSubject string
Strategy string
RatingSubject string
}
func (sg *SharedGroup) GetMembersExceptUser(ubId string) []string {

View File

@@ -41,6 +41,10 @@ func NewMapStorage() (*MapStorage, error) {
return &MapStorage{dict: make(map[string][]byte), ms: NewCodecMsgpackMarshaler()}, nil
}
func NewMapStorageJson() (*MapStorage, error) {
return &MapStorage{dict: make(map[string][]byte), ms: new(JSONBufMarshaler)}, nil
}
func (ms *MapStorage) Close() {}
func (ms *MapStorage) Flush() error {
@@ -182,8 +186,9 @@ func (ms *MapStorage) SetRatingPlan(rp *RatingPlan) (err error) {
w.Close()
ms.dict[RATING_PLAN_PREFIX+rp.Id] = b.Bytes()
response := 0
go historyScribe.Record(rp.GetHistoryRecord(), &response)
if historyScribe != nil {
go historyScribe.Record(rp.GetHistoryRecord(), &response)
}
//cache2go.Cache(RATING_PLAN_PREFIX+rp.Id, rp)
return
}
@@ -211,7 +216,9 @@ func (ms *MapStorage) SetRatingProfile(rpf *RatingProfile) (err error) {
result, err := ms.ms.Marshal(rpf)
ms.dict[RATING_PROFILE_PREFIX+rpf.Id] = result
response := 0
go historyScribe.Record(rpf.GetHistoryRecord(), &response)
if historyScribe != nil {
go historyScribe.Record(rpf.GetHistoryRecord(), &response)
}
//cache2go.Cache(RATING_PROFILE_PREFIX+rpf.Id, rpf)
return
}
@@ -342,7 +349,9 @@ func (ms *MapStorage) SetDestination(dest *Destination) (err error) {
w.Close()
ms.dict[DESTINATION_PREFIX+dest.Id] = b.Bytes()
response := 0
go historyScribe.Record(dest.GetHistoryRecord(), &response)
if historyScribe != nil {
go historyScribe.Record(dest.GetHistoryRecord(), &response)
}
//cache2go.Cache(DESTINATION_PREFIX+dest.Id, dest)
return
}

View File

@@ -294,7 +294,7 @@ func (self *SQLStorage) SetTPSharedGroups(tpid string, sgs map[string]*SharedGro
buffer.WriteRune(',')
}
buffer.WriteString(fmt.Sprintf("('%s','%s','%s','%s','%s')",
tpid, sgId, account, params.Strategy, params.RateSubject))
tpid, sgId, account, params.Strategy, params.RatingSubject))
i++
}
}
@@ -962,7 +962,7 @@ func (self *SQLStorage) GetTpTimings(tpid, tag string) (map[string]*utils.TPTimi
func (self *SQLStorage) GetTpRatingPlans(tpid, tag string) (map[string][]*utils.TPRatingPlanBinding, error) {
rpbns := make(map[string][]*utils.TPRatingPlanBinding)
q := fmt.Sprintf("SELECT * FROM %s WHERE tpid='%s'", utils.TBL_TP_RATING_PLANS, tpid)
q := fmt.Sprintf("SELECT tpid, id, destrates_id, timing_id, weight FROM %s WHERE tpid='%s'", utils.TBL_TP_RATING_PLANS, tpid)
if tag != "" {
q += fmt.Sprintf(" AND id='%s'", tag)
}
@@ -972,10 +972,9 @@ func (self *SQLStorage) GetTpRatingPlans(tpid, tag string) (map[string][]*utils.
}
defer rows.Close()
for rows.Next() {
var id int
var weight float64
var tpid, tag, destination_rates_tag, timings_tag string
if err := rows.Scan(&id, &tpid, &tag, &destination_rates_tag, &timings_tag, &weight); err != nil {
var tpid, id, destination_rates_tag, timings_tag string
if err := rows.Scan(&tpid, &id, &destination_rates_tag, &timings_tag, &weight); err != nil {
return nil, err
}
rpb := &utils.TPRatingPlanBinding{
@@ -983,10 +982,11 @@ func (self *SQLStorage) GetTpRatingPlans(tpid, tag string) (map[string][]*utils.
TimingId: timings_tag,
Weight: weight,
}
if rpBnLst, exists := rpbns[tag]; exists {
rpBnLst = append(rpBnLst, rpb)
// Logger.Debug(fmt.Sprintf("For RatingPlan id: %s, loading RatingPlanBinding: %v", tag, rpb))
if _, exists := rpbns[id]; exists {
rpbns[id] = append(rpbns[id], rpb)
} else { // New
rpbns[tag] = []*utils.TPRatingPlanBinding{rpb}
rpbns[id] = []*utils.TPRatingPlanBinding{rpb}
}
}
return rpbns, nil
@@ -1055,16 +1055,16 @@ func (self *SQLStorage) GetTpSharedGroups(tpid, tag string) (map[string]*SharedG
sg, found := sgs[tag]
if found {
sg.AccountParameters[account] = &SharingParameters{
Strategy: strategy,
RateSubject: rateSubject,
Strategy: strategy,
RatingSubject: rateSubject,
}
} else {
sg = &SharedGroup{
Id: tag,
AccountParameters: map[string]*SharingParameters{
account: &SharingParameters{
Strategy: strategy,
RateSubject: rateSubject,
Strategy: strategy,
RatingSubject: rateSubject,
},
},
}

View File

@@ -15,6 +15,8 @@ go test -i github.com/cgrates/cgrates/cdre
go test github.com/cgrates/cgrates/engine
en=$?
go test github.com/cgrates/cgrates/charging_tests
ct=$?
go test github.com/cgrates/cgrates/sessionmanager
sm=$?
go test github.com/cgrates/cgrates/config
@@ -39,4 +41,4 @@ go test github.com/cgrates/cgrates/cdre
cdre=$?
exit $en && $sm && $cfg && $bl && $cr && $md && $cdrs && $cdrc && $fs && $ut && $hs && $c2g && $cdre
exit $en && $ct && $sm && $cfg && $bl && $cr && $md && $cdrs && $cdrc && $fs && $ut && $hs && $c2g && $cdre