mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
max session caclulus to consider shared groups
This commit is contained in:
@@ -72,8 +72,35 @@ type Account struct {
|
||||
// Returns user's available minutes for the specified destination
|
||||
|
||||
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], "")
|
||||
creditBalances := ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[CREDIT+cd.Direction], "")
|
||||
minuteBalances := ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[MINUTES+cd.Direction], "")
|
||||
// gather all balances from shared groups
|
||||
var extendedCreditBalances BalanceChain
|
||||
for _, cb := range creditBalances {
|
||||
if cb.SharedGroup != "" {
|
||||
if sharedGroup, _ := accountingStorage.GetSharedGroup(cb.SharedGroup, false); sharedGroup != nil {
|
||||
sgb := sharedGroup.GetBalances(cd.Destination, CREDIT+cd.Direction, ub)
|
||||
sgb = sharedGroup.SortBalancesByStrategy(cb, sgb)
|
||||
extendedCreditBalances = append(extendedCreditBalances, sgb...)
|
||||
}
|
||||
} else {
|
||||
extendedCreditBalances = append(extendedCreditBalances, cb)
|
||||
}
|
||||
}
|
||||
var extendedMinuteBalances BalanceChain
|
||||
for _, mb := range minuteBalances {
|
||||
if mb.SharedGroup != "" {
|
||||
if sharedGroup, _ := accountingStorage.GetSharedGroup(mb.SharedGroup, false); sharedGroup != nil {
|
||||
sgb := sharedGroup.GetBalances(cd.Destination, MINUTES+cd.Direction, ub)
|
||||
sgb = sharedGroup.SortBalancesByStrategy(mb, sgb)
|
||||
extendedMinuteBalances = append(extendedMinuteBalances, sgb...)
|
||||
}
|
||||
} else {
|
||||
extendedMinuteBalances = append(extendedMinuteBalances, mb)
|
||||
}
|
||||
}
|
||||
credit = extendedCreditBalances.GetTotalValue()
|
||||
balances = extendedMinuteBalances
|
||||
for _, b := range balances {
|
||||
d, c := b.GetMinutesForCredit(cd, credit)
|
||||
credit = c
|
||||
@@ -119,7 +146,7 @@ func (ub *Account) debitBalanceAction(a *Action) error {
|
||||
Logger.Warning(fmt.Sprintf("Could not get shared group: %v", a.Balance.SharedGroup))
|
||||
} else {
|
||||
// add membere and save
|
||||
sg.Members = append(sg.Members, ub.Id)
|
||||
sg.MemberIds = append(sg.MemberIds, ub.Id)
|
||||
accountingStorage.SetSharedGroup(sg.Id, sg)
|
||||
}
|
||||
}
|
||||
@@ -268,27 +295,8 @@ func (ub *Account) debitMinutesFromSharedBalances(myBalance *Balance, cc *CallCo
|
||||
Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sharedGroupName))
|
||||
return
|
||||
}
|
||||
sharingMembers := sharedGroup.Members
|
||||
|
||||
var allMinuteSharedBalances BalanceChain
|
||||
for _, ubId := range sharingMembers {
|
||||
var nUb *Account
|
||||
if ubId == ub.Id { // skip the initiating user
|
||||
nUb = ub
|
||||
} else {
|
||||
nUb, err = accountingStorage.GetAccount(ubId)
|
||||
if err != nil {
|
||||
Logger.Warning(fmt.Sprintf("Could not get user balance: %s", ubId))
|
||||
}
|
||||
if nUb.Disabled {
|
||||
Logger.Warning(fmt.Sprintf("Disabled user in shared group: %s (%s)", ubId, sharedGroupName))
|
||||
continue
|
||||
}
|
||||
}
|
||||
sharedMinuteBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[MINUTES+cc.Direction], sharedGroupName)
|
||||
allMinuteSharedBalances = append(allMinuteSharedBalances, sharedMinuteBalances...)
|
||||
}
|
||||
for _, sharedBalance := range sharedGroup.GetBalancesByStrategy(myBalance, allMinuteSharedBalances) {
|
||||
allMinuteSharedBalances := sharedGroup.GetBalances(cc.Destination, MINUTES+cc.Direction, ub)
|
||||
for _, sharedBalance := range sharedGroup.SortBalancesByStrategy(myBalance, allMinuteSharedBalances) {
|
||||
initialValue := sharedBalance.Value
|
||||
sharedBalance.DebitMinutes(cc, count, sharedBalance.account, moneyBalances)
|
||||
if sharedBalance.Value != initialValue {
|
||||
@@ -308,26 +316,8 @@ func (ub *Account) debitMoneyFromSharedBalances(myBalance *Balance, cc *CallCost
|
||||
Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sharedGroup))
|
||||
return
|
||||
}
|
||||
sharingMembers := sharedGroup.Members
|
||||
var allMoneySharedBalances BalanceChain
|
||||
for _, ubId := range sharingMembers {
|
||||
var nUb *Account
|
||||
if ubId == ub.Id { // skip the initiating user
|
||||
nUb = ub
|
||||
} else {
|
||||
nUb, err = accountingStorage.GetAccount(ubId)
|
||||
if err != nil {
|
||||
Logger.Warning(fmt.Sprintf("Could not get user balance: %s", ubId))
|
||||
}
|
||||
if nUb.Disabled {
|
||||
Logger.Warning(fmt.Sprintf("Disabled user in shared group: %s (%s)", ubId, sharedGroupName))
|
||||
continue
|
||||
}
|
||||
}
|
||||
sharedMoneyBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[CREDIT+cc.Direction], sharedGroupName)
|
||||
allMoneySharedBalances = append(allMoneySharedBalances, sharedMoneyBalances...)
|
||||
}
|
||||
for _, sharedBalance := range sharedGroup.GetBalancesByStrategy(myBalance, allMoneySharedBalances) {
|
||||
allMoneySharedBalances := sharedGroup.GetBalances(cc.Destination, CREDIT+cc.Direction, ub)
|
||||
for _, sharedBalance := range sharedGroup.SortBalancesByStrategy(myBalance, allMoneySharedBalances) {
|
||||
initialValue := sharedBalance.Value
|
||||
sharedBalance.DebitMoney(cc, count, sharedBalance.account)
|
||||
if sharedBalance.Value != initialValue {
|
||||
|
||||
@@ -1058,7 +1058,7 @@ func TestDebitShared(t *testing.T) {
|
||||
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneyc", Value: 70, SharedGroup: "SG_TEST"}},
|
||||
}}
|
||||
|
||||
sg := &SharedGroup{Id: "SG_TEST", Members: []string{rif.Id, groupie.Id}, AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}}
|
||||
sg := &SharedGroup{Id: "SG_TEST", MemberIds: []string{rif.Id, groupie.Id}, AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}}
|
||||
|
||||
accountingStorage.SetAccount(groupie)
|
||||
accountingStorage.SetSharedGroup("SG_TEST", sg)
|
||||
|
||||
@@ -115,7 +115,7 @@ type CallDescriptor struct {
|
||||
FallbackSubject string // the subject to check for destination if not found on primary subject
|
||||
RatingInfos RatingInfos
|
||||
Increments Increments
|
||||
userBalance *Account
|
||||
account *Account
|
||||
}
|
||||
|
||||
func (cd *CallDescriptor) ValidateCallData() error {
|
||||
@@ -148,13 +148,13 @@ func (cd *CallDescriptor) GetAccountKey() string {
|
||||
|
||||
// Gets and caches the user balance information.
|
||||
func (cd *CallDescriptor) getAccount() (ub *Account, err error) {
|
||||
if cd.userBalance == nil {
|
||||
cd.userBalance, err = accountingStorage.GetAccount(cd.GetAccountKey())
|
||||
if cd.account == nil {
|
||||
cd.account, err = accountingStorage.GetAccount(cd.GetAccountKey())
|
||||
}
|
||||
if cd.userBalance != nil && cd.userBalance.Disabled {
|
||||
if cd.account != nil && cd.account.Disabled {
|
||||
return nil, fmt.Errorf("User %s is disabled", ub.Id)
|
||||
}
|
||||
return cd.userBalance, err
|
||||
return cd.account, err
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -496,13 +496,20 @@ func (origCD *CallDescriptor) getMaxSessionDuration(account *Account) (time.Dura
|
||||
return utils.MinDuration(initialDuration, availableDuration), nil
|
||||
}
|
||||
|
||||
func (origCD *CallDescriptor) GetMaxSessionDuration() (time.Duration, error) {
|
||||
cd := origCD.Clone()
|
||||
func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err error) {
|
||||
if account, err := cd.getAccount(); err != nil || account == nil {
|
||||
Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetAccountKey(), err.Error()))
|
||||
return 0, err
|
||||
} else {
|
||||
return cd.getMaxSessionDuration(account)
|
||||
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction); err == nil {
|
||||
AccLock.GuardMany(memberIds, func() (float64, error) {
|
||||
duration, err = cd.getMaxSessionDuration(account)
|
||||
return 0, err
|
||||
})
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
return duration, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,15 +570,24 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
|
||||
Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetAccountKey(), err.Error()))
|
||||
return nil, err
|
||||
} else {
|
||||
remainingDuration, err := cd.getMaxSessionDuration(account)
|
||||
if err != nil || remainingDuration == 0 {
|
||||
return new(CallCost), fmt.Errorf("no more credit: %v", err)
|
||||
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction); err == nil {
|
||||
AccLock.GuardMany(memberIds, func() (float64, error) {
|
||||
remainingDuration, err := cd.getMaxSessionDuration(account)
|
||||
if err != nil || remainingDuration == 0 {
|
||||
cc, err = new(CallCost), fmt.Errorf("no more credit: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
if remainingDuration > 0 { // for postpaying client returns -1
|
||||
cd.TimeEnd = cd.TimeStart.Add(remainingDuration)
|
||||
}
|
||||
cc, err = cd.debit(account)
|
||||
return 0, err
|
||||
})
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
if remainingDuration > 0 { // for postpaying client returns -1
|
||||
cd.TimeEnd = cd.TimeStart.Add(remainingDuration)
|
||||
}
|
||||
return cd.debit(account)
|
||||
}
|
||||
return cc, err
|
||||
}
|
||||
|
||||
func (cd *CallDescriptor) RefundIncrements() (left float64, err error) {
|
||||
|
||||
@@ -314,7 +314,7 @@ func TestMaxSessionTimeWithAccount(t *testing.T) {
|
||||
Tenant: "vdf",
|
||||
Subject: "minu",
|
||||
Destination: "0723",
|
||||
Amount: 1000}
|
||||
}
|
||||
result, err := cd.GetMaxSessionDuration()
|
||||
expected := time.Minute
|
||||
if result != expected || err != nil {
|
||||
@@ -330,8 +330,9 @@ func TestMaxSessionTimeWithAccountAlias(t *testing.T) {
|
||||
TOR: "0",
|
||||
Tenant: "vdf",
|
||||
Subject: "a1",
|
||||
Account: "a1",
|
||||
Destination: "0723",
|
||||
Amount: 1000}
|
||||
}
|
||||
result, err := cd.GetMaxSessionDuration()
|
||||
expected := time.Minute
|
||||
if result != expected || err != nil {
|
||||
@@ -389,6 +390,7 @@ func TestMaxSessionModifiesCallDesc(t *testing.T) {
|
||||
Amount: 5400}
|
||||
initial := cd.Clone()
|
||||
cd.GetMaxSessionDuration()
|
||||
cd.account = nil // it's OK to cache the account
|
||||
if !reflect.DeepEqual(cd, initial) {
|
||||
t.Errorf("GetMaxSessionDuration is changing the call descriptor %+v != %+v", cd, initial)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ const (
|
||||
type SharedGroup struct {
|
||||
Id string
|
||||
AccountParameters map[string]*SharingParameters
|
||||
Members []string
|
||||
MemberIds []string
|
||||
members []*Account // accounts caching
|
||||
}
|
||||
|
||||
type SharingParameters struct {
|
||||
@@ -49,18 +50,18 @@ type SharingParameters struct {
|
||||
}
|
||||
|
||||
func (sg *SharedGroup) GetMembersExceptUser(ubId string) []string {
|
||||
for i, m := range sg.Members {
|
||||
for i, m := range sg.MemberIds {
|
||||
if m == ubId {
|
||||
a := make([]string, len(sg.Members))
|
||||
copy(a, sg.Members)
|
||||
a := make([]string, len(sg.MemberIds))
|
||||
copy(a, sg.MemberIds)
|
||||
a[i], a = a[len(a)-1], a[:len(a)-1]
|
||||
return a
|
||||
}
|
||||
}
|
||||
return sg.Members
|
||||
return sg.MemberIds
|
||||
}
|
||||
|
||||
func (sg *SharedGroup) GetBalancesByStrategy(myBalance *Balance, bc BalanceChain) BalanceChain {
|
||||
func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc BalanceChain) BalanceChain {
|
||||
sharingParameters := sg.AccountParameters[utils.ANY]
|
||||
if sp, hasParamsForAccount := sg.AccountParameters[myBalance.account.Id]; hasParamsForAccount {
|
||||
sharingParameters = sp
|
||||
@@ -100,6 +101,32 @@ func (sg *SharedGroup) GetBalancesByStrategy(myBalance *Balance, bc BalanceChain
|
||||
return bc
|
||||
}
|
||||
|
||||
// Returns all shared group's balances collected from user accounts'
|
||||
func (sg *SharedGroup) GetBalances(destination, balanceType string, ub *Account) (bc BalanceChain) {
|
||||
if len(sg.members) == 0 {
|
||||
for _, ubId := range sg.MemberIds {
|
||||
var nUb *Account
|
||||
if ubId == ub.Id { // skip the initiating user
|
||||
nUb = ub
|
||||
} else {
|
||||
nUb, _ = accountingStorage.GetAccount(ubId)
|
||||
if nUb == nil || nUb.Disabled {
|
||||
continue
|
||||
}
|
||||
}
|
||||
sg.members = append(sg.members, nUb)
|
||||
sb := nUb.getBalancesForPrefix(destination, nUb.BalanceMap[balanceType], sg.Id)
|
||||
bc = append(bc, sb...)
|
||||
}
|
||||
} else {
|
||||
for _, m := range sg.members {
|
||||
sb := m.getBalancesForPrefix(destination, m.BalanceMap[balanceType], sg.Id)
|
||||
bc = append(bc, sb...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type LowestBalanceChainSorter []*Balance
|
||||
|
||||
func (lbcs LowestBalanceChainSorter) Len() int {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
|
||||
func TestSharedGroupGetMembersExcept(t *testing.T) {
|
||||
sg := &SharedGroup{
|
||||
Members: []string{"1", "2", "3"},
|
||||
MemberIds: []string{"1", "2", "3"},
|
||||
}
|
||||
a1 := sg.GetMembersExceptUser("1")
|
||||
a2 := sg.GetMembersExceptUser("2")
|
||||
@@ -46,7 +46,7 @@ func TestSharedPopBalanceByStrategyLow(t *testing.T) {
|
||||
sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{
|
||||
"test": &SharingParameters{Strategy: STRATEGY_LOWEST}},
|
||||
}
|
||||
sbc := sg.GetBalancesByStrategy(bc[1], bc)
|
||||
sbc := sg.SortBalancesByStrategy(bc[1], bc)
|
||||
if len(sbc) != 3 ||
|
||||
sbc[0].Value != 1.0 ||
|
||||
sbc[1].Value != 2.0 {
|
||||
@@ -63,7 +63,7 @@ func TestSharedPopBalanceByStrategyHigh(t *testing.T) {
|
||||
sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{
|
||||
"test": &SharingParameters{Strategy: STRATEGY_HIGHEST}},
|
||||
}
|
||||
sbc := sg.GetBalancesByStrategy(bc[0], bc)
|
||||
sbc := sg.SortBalancesByStrategy(bc[0], bc)
|
||||
if len(sbc) != 3 ||
|
||||
sbc[0].Value != 3.0 ||
|
||||
sbc[1].Value != 2.0 {
|
||||
@@ -80,7 +80,7 @@ func TestSharedPopBalanceByStrategyMineHigh(t *testing.T) {
|
||||
sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{
|
||||
"test": &SharingParameters{Strategy: STRATEGY_MINE_HIGHEST}},
|
||||
}
|
||||
sbc := sg.GetBalancesByStrategy(bc[0], bc)
|
||||
sbc := sg.SortBalancesByStrategy(bc[0], bc)
|
||||
if len(sbc) != 3 ||
|
||||
sbc[0].Value != 2.0 ||
|
||||
sbc[1].Value != 3.0 {
|
||||
@@ -88,7 +88,7 @@ func TestSharedPopBalanceByStrategyMineHigh(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedPopBalanceByStrategyRandomHigh(t *testing.T) {
|
||||
/*func TestSharedPopBalanceByStrategyRandomHigh(t *testing.T) {
|
||||
bc := BalanceChain{
|
||||
&Balance{Uuid: "uuuu", Value: 2.0, account: &Account{Id: "test"}},
|
||||
&Balance{Value: 1.0},
|
||||
@@ -98,15 +98,15 @@ func TestSharedPopBalanceByStrategyRandomHigh(t *testing.T) {
|
||||
"test": &SharingParameters{Strategy: STRATEGY_RANDOM}},
|
||||
}
|
||||
x := bc[0]
|
||||
sbc := sg.GetBalancesByStrategy(bc[0], bc)
|
||||
sbc := sg.SortBalancesByStrategy(bc[0], bc)
|
||||
firstTest := (sbc[0].Uuid == x.Uuid)
|
||||
sbc = sg.GetBalancesByStrategy(bc[0], bc)
|
||||
sbc = sg.SortBalancesByStrategy(bc[0], bc)
|
||||
secondTest := (sbc[0].Uuid == x.Uuid)
|
||||
sbc = sg.GetBalancesByStrategy(bc[0], bc)
|
||||
sbc = sg.SortBalancesByStrategy(bc[0], bc)
|
||||
thirdTest := (sbc[0].Uuid == x.Uuid)
|
||||
sbc = sg.GetBalancesByStrategy(bc[0], bc)
|
||||
sbc = sg.SortBalancesByStrategy(bc[0], bc)
|
||||
fourthTest := (sbc[0].Uuid == x.Uuid)
|
||||
if firstTest && secondTest && thirdTest && fourthTest {
|
||||
t.Error("Something is wrong with balance randomizer")
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
Reference in New Issue
Block a user