From 88137b75dbe04cd72793af0906f15e7ef0fb3698 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 30 Oct 2015 17:03:54 +0200 Subject: [PATCH] *generic balance used by all tors fixes #250 --- config/config_json_test.go | 2 +- engine/account.go | 18 +++++--- engine/account_test.go | 86 ++++++++++++++++++++++++++++++++++++++ engine/balances.go | 32 +++++++++----- engine/loader_csv_test.go | 26 ++++++------ engine/sharedgroup.go | 2 +- engine/timespans_test.go | 2 +- 7 files changed, 136 insertions(+), 32 deletions(-) diff --git a/config/config_json_test.go b/config/config_json_test.go index 4c1c0e690..e7a622993 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -39,7 +39,7 @@ func TestDfNewdfCgrJsonCfgFromReader(t *testing.T) { func TestDfGeneralJsonCfg(t *testing.T) { eCfg := &GeneralJsonCfg{ Http_skip_tls_verify: utils.BoolPointer(false), - Rounding_decimals: utils.IntPointer(10), + Rounding_decimals: utils.IntPointer(5), Dbdata_encoding: utils.StringPointer("msgpack"), Tpexport_dir: utils.StringPointer("/var/log/cgrates/tpe"), Http_failed_dir: utils.StringPointer("/var/log/cgrates/http_failed"), diff --git a/engine/account.go b/engine/account.go index 73b4dbbed..1c1954c53 100644 --- a/engine/account.go +++ b/engine/account.go @@ -52,8 +52,9 @@ type Account struct { // User's available minutes for the specified destination func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances BalanceChain) { - creditBalances := ub.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, ub.BalanceMap[utils.MONETARY], "") - unitBalances := ub.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, ub.BalanceMap[cd.TOR], "") + creditBalances := ub.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, utils.MONETARY, "") + + unitBalances := ub.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, cd.TOR, "") // gather all balances from shared groups var extendedCreditBalances BalanceChain for _, cb := range creditBalances { @@ -182,7 +183,12 @@ func (ub *Account) enableDisableBalanceAction(a *Action) error { return nil } -func (ub *Account) getBalancesForPrefix(prefix, category string, direction string, balances BalanceChain, sharedGroup string) BalanceChain { +func (ub *Account) getBalancesForPrefix(prefix, category, direction, tor string, sharedGroup string) BalanceChain { + var balances BalanceChain + balances = append(balances, ub.BalanceMap[tor]...) + if tor != utils.MONETARY && tor != utils.GENERIC { + balances = append(balances, ub.BalanceMap[utils.GENERIC]...) + } var usefulBalances BalanceChain for _, b := range balances { if b.Disabled { @@ -235,7 +241,7 @@ func (ub *Account) getBalancesForPrefix(prefix, category string, direction strin // like getBalancesForPrefix but expanding shared balances func (account *Account) getAlldBalancesForPrefix(destination, category, direction, balanceType string) (bc BalanceChain) { - balances := account.getBalancesForPrefix(destination, category, direction, account.BalanceMap[balanceType], "") + balances := account.getBalancesForPrefix(destination, category, direction, balanceType, "") for _, b := range balances { if len(b.SharedGroups) > 0 { for sgId := range b.SharedGroups { @@ -615,8 +621,8 @@ func (ub *Account) GetSharedGroups() (groups []string) { func (account *Account) GetUniqueSharedGroupMembers(cd *CallDescriptor) ([]string, error) { var balances []*Balance - balances = append(balances, account.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, account.BalanceMap[utils.MONETARY], "")...) - balances = append(balances, account.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, account.BalanceMap[cd.TOR], "")...) + balances = append(balances, account.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, utils.MONETARY, "")...) + balances = append(balances, account.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, cd.TOR, "")...) // gather all shared group ids var sharedGroupIds []string for _, b := range balances { diff --git a/engine/account_test.go b/engine/account_test.go index fbeddfb73..799177a78 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -1231,6 +1231,92 @@ func TestDebitGeneric(t *testing.T) { } } +func TestDebitGenericBalance(t *testing.T) { + cc := &CallCost{ + Direction: utils.OUT, + Destination: "0723045326", + Timespans: []*TimeSpan{ + &TimeSpan{ + TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 9, 24, 10, 48, 30, 0, time.UTC), + ratingInfo: &RatingInfo{}, + DurationIndex: 0, + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1 * time.Second, RateUnit: time.Second}}}}, + }, + }, + TOR: utils.VOICE, + } + cd := &CallDescriptor{ + TimeStart: cc.Timespans[0].TimeStart, + TimeEnd: cc.Timespans[0].TimeEnd, + Direction: cc.Direction, + Destination: cc.Destination, + TOR: cc.TOR, + DurationIndex: cc.GetDuration(), + testCallcost: cc, + } + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}}}, + utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + }} + var err error + cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) + if err != nil { + t.Error("Error debiting balance: ", err) + } + if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { + t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) + } + if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.4999 || + rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 { + t.Logf("%+v", cc.Timespans[0].Increments[0]) + t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.GENERIC][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) + } +} + +func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { + cc := &CallCost{ + Direction: utils.OUT, + Destination: "0723045326", + Timespans: []*TimeSpan{ + &TimeSpan{ + TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 9, 24, 10, 48, 30, 0, time.UTC), + ratingInfo: &RatingInfo{}, + DurationIndex: 0, + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 0, RateIncrement: time.Second, RateUnit: time.Second}}}}, + }, + }, + TOR: utils.VOICE, + } + cd := &CallDescriptor{ + TimeStart: cc.Timespans[0].TimeStart, + TimeEnd: cc.Timespans[0].TimeEnd, + Direction: cc.Direction, + Destination: cc.Destination, + TOR: cc.TOR, + DurationIndex: cc.GetDuration(), + testCallcost: cc, + } + rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}, RatingSubject: "free"}}, + utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + }} + var err error + cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) + if err != nil { + t.Error("Error debiting balance: ", err) + } + if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { + t.Error("Error setting balance id to increment: ", cc.Timespans[0]) + } + if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.4999 || + rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 { + t.Logf("%+v", cc.Timespans[0].Increments[0]) + t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.GENERIC][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) + } +} + func TestDebitDataUnits(t *testing.T) { cc := &CallCost{ Direction: utils.OUT, diff --git a/engine/balances.go b/engine/balances.go index 7f7c49a14..f5a565656 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -45,6 +45,7 @@ type Balance struct { Timings []*RITiming TimingIDs utils.StringMap Disabled bool + Factor ValueFactor precision int account *Account // used to store ub reference for shared balances dirty bool @@ -319,8 +320,6 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala TimeEnd: cd.TimeEnd, }) - seconds := duration.Seconds() - amount := seconds ts := cc.Timespans[0] ts.RoundToDuration(duration) ts.RateInterval = &RateInterval{ @@ -352,8 +351,10 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala //log.Printf("CC: %+v", ts) for incIndex, inc := range ts.Increments { //log.Printf("INCREMENET: %+v", inc) - if seconds == 1 { - amount = inc.Duration.Seconds() + + amount := inc.Duration.Seconds() + if b.Factor != nil { + amount = utils.Round(amount/b.Factor.GetValue(cd.TOR), globalRoundingDecimals, utils.ROUNDING_UP) } if b.GetValue() >= amount { b.SubstractValue(amount) @@ -401,9 +402,11 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala maxCost, strategy := ts.RateInterval.GetMaxCost() for incIndex, inc := range ts.Increments { // debit minutes and money - seconds := inc.Duration.Seconds() + amount := inc.Duration.Seconds() + if b.Factor != nil { + amount = utils.Round(amount/b.Factor.GetValue(cd.TOR), globalRoundingDecimals, utils.ROUNDING_UP) + } cost := inc.Cost - //log.Printf("INC: %+v", inc) inc.paid = false if strategy == utils.MAX_COST_DISCONNECT && cd.MaxCostSoFar >= maxCost { // cat the entire current timespan @@ -437,11 +440,11 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala break } } - if (cost == 0 || moneyBal != nil) && b.GetValue() >= seconds { - b.SubstractValue(seconds) + if (cost == 0 || moneyBal != nil) && b.GetValue() >= amount { + b.SubstractValue(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.Id - inc.UnitInfo = &UnitInfo{cc.Destination, seconds, cc.TOR} + inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} if cost != 0 { inc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid moneyBal.SubstractValue(cost) @@ -449,7 +452,7 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala } inc.paid = true if count { - ub.countUnits(&Action{BalanceType: cc.TOR, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: seconds, DestinationIds: utils.StringMap{cc.Destination: true}}}) + ub.countUnits(&Action{BalanceType: cc.TOR, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: amount, DestinationIds: utils.StringMap{cc.Destination: true}}}) if cost != 0 { ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: cost, DestinationIds: utils.StringMap{cc.Destination: true}}}) } @@ -678,3 +681,12 @@ func (bc BalanceChain) SaveDirtyBalances(acc *Account) { } } } + +type ValueFactor map[string]float64 + +func (f ValueFactor) GetValue(tor string) float64 { + if value, ok := f[tor]; ok { + return value + } + return 1.0 +} diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 9e7ca337b..84b6069f4 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -817,12 +817,12 @@ func TestLoadActions(t *testing.T) { ExtraParameters: "", Weight: 10, Balance: &Balance{ - Uuid: as1[0].Balance.Uuid, - Directions: utils.NewStringMap(utils.OUT), - Value: 10, - Weight: 10, - TimingIDs: utils.StringMap{}, - SharedGroups: utils.StringMap{}, + Uuid: as1[0].Balance.Uuid, + Directions: utils.NewStringMap(utils.OUT), + Value: 10, + Weight: 10, + TimingIDs: utils.StringMap{}, + SharedGroups: utils.StringMap{}, }, }, &Action{ @@ -840,7 +840,7 @@ func TestLoadActions(t *testing.T) { RatingSubject: "test", DestinationIds: utils.NewStringMap("NAT"), TimingIDs: utils.StringMap{}, - SharedGroups: utils.StringMap{}, + SharedGroups: utils.StringMap{}, }, }, } @@ -861,7 +861,7 @@ func TestLoadActions(t *testing.T) { Uuid: as2[0].Balance.Uuid, Value: 100, Weight: 10, - SharedGroups: utils.NewStringMap("SG1"), + SharedGroups: utils.NewStringMap("SG1"), TimingIDs: utils.StringMap{}, }, }, @@ -881,7 +881,7 @@ func TestLoadActions(t *testing.T) { Directions: utils.StringMap{}, DestinationIds: utils.StringMap{}, TimingIDs: utils.StringMap{}, - SharedGroups: utils.StringMap{}, + SharedGroups: utils.StringMap{}, }, }, } @@ -1014,8 +1014,8 @@ func TestLoadActionTriggers(t *testing.T) { ThresholdValue: 10, BalanceDestinationIds: utils.NewStringMap("GERMANY_O2"), BalanceCategories: utils.StringMap{}, - BalanceTimingTags: utils.StringMap{}, - BalanceSharedGroups: utils.StringMap{}, + BalanceTimingTags: utils.StringMap{}, + BalanceSharedGroups: utils.StringMap{}, Weight: 10, ActionsId: "SOME_1", Executed: false, @@ -1031,8 +1031,8 @@ func TestLoadActionTriggers(t *testing.T) { ThresholdValue: 200, BalanceDestinationIds: utils.NewStringMap("GERMANY"), BalanceCategories: utils.StringMap{}, - BalanceTimingTags: utils.StringMap{}, - BalanceSharedGroups: utils.StringMap{}, + BalanceTimingTags: utils.StringMap{}, + BalanceSharedGroups: utils.StringMap{}, Weight: 10, ActionsId: "SOME_2", Executed: false, diff --git a/engine/sharedgroup.go b/engine/sharedgroup.go index e3f08ad34..4ec1bde23 100644 --- a/engine/sharedgroup.go +++ b/engine/sharedgroup.go @@ -103,7 +103,7 @@ func (sg *SharedGroup) GetBalances(destination, category, direction, balanceType } } //sg.members = append(sg.members, nUb) - sb := nUb.getBalancesForPrefix(destination, category, direction, nUb.BalanceMap[balanceType], sg.Id) + sb := nUb.getBalancesForPrefix(destination, category, direction, balanceType, sg.Id) bc = append(bc, sb...) } /* } else { diff --git a/engine/timespans_test.go b/engine/timespans_test.go index e2b21d1b2..1a4fea010 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -742,7 +742,7 @@ func TestTimespanCreateIncrements(t *testing.T) { if len(ts.Increments) != 3 { t.Error("Error creating increment slice: ", len(ts.Increments)) } - if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.0666666667 { + if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.06667 { t.Error("Wrong second slice: ", ts.Increments[2].Cost) } }