diff --git a/engine/account.go b/engine/account.go index cc535a7e1..90b7ce49b 100644 --- a/engine/account.go +++ b/engine/account.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "log" "time" "github.com/cgrates/cgrates/cache2go" @@ -32,10 +33,12 @@ import ( const ( // action trigger threshold types - TRIGGER_MIN_COUNTER = "*min_counter" - TRIGGER_MAX_COUNTER = "*max_counter" - TRIGGER_MIN_BALANCE = "*min_balance" - TRIGGER_MAX_BALANCE = "*max_balance" + TRIGGER_MIN_EVENT_COUNTER = "*min_event_counter" + TRIGGER_MIN_BALANCE_COUNTER = "*min_balance_counter" + TRIGGER_MAX_EVENT_COUNTER = "*max_event_counter" + TRIGGER_MAX_BALANCE_COUNTER = "*max_balance_counter" + TRIGGER_MIN_BALANCE = "*min_balance" + TRIGGER_MAX_BALANCE = "*max_balance" ) /* @@ -45,7 +48,7 @@ This can represent a user or a shared group. type Account struct { Id string BalanceMap map[string]BalanceChain - UnitCounters []*UnitsCounter + UnitCounters UnitCounters ActionTriggers ActionTriggers AllowNegative bool Disabled bool @@ -407,7 +410,15 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo increment.BalanceInfo.AccountId = ub.Id increment.paid = true if count { - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Directions: utils.StringMap{leftCC.Direction: true}, Value: cost, DestinationIds: utils.NewStringMap(leftCC.Destination)}}) + ub.countUnits( + cost, + utils.MONETARY, + leftCC, + &Balance{ + Directions: utils.StringMap{leftCC.Direction: true}, + Value: cost, + DestinationIds: utils.NewStringMap(leftCC.Destination), + }) } } } @@ -441,15 +452,17 @@ func (ub *Account) GetDefaultMoneyBalance() *Balance { return defaultBalance } -func (ub *Account) refundIncrement(increment *Increment, unitType string, count bool) { +func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, count bool) { var balance *Balance + unitType := cd.TOR + cc := cd.CreateCallCost() if increment.BalanceInfo.UnitBalanceUuid != "" { if balance = ub.BalanceMap[unitType].GetBalance(increment.BalanceInfo.UnitBalanceUuid); balance == nil { return } balance.AddValue(increment.Duration.Seconds()) if count { - ub.countUnits(&Action{BalanceType: unitType, Balance: &Balance{Value: -increment.Duration.Seconds()}}) + ub.countUnits(-increment.Duration.Seconds(), unitType, cc, balance) } } // check money too @@ -459,7 +472,7 @@ func (ub *Account) refundIncrement(increment *Increment, unitType string, count } balance.AddValue(increment.Cost) if count { - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: -increment.Cost}}) + ub.countUnits(-increment.Cost, utils.MONETARY, cc, balance) } } } @@ -468,9 +481,9 @@ func (ub *Account) refundIncrement(increment *Increment, unitType string, count func (ub *Account) executeActionTriggers(a *Action) { ub.ActionTriggers.Sort() for _, at := range ub.ActionTriggers { + limit, counter, kind := at.GetThresholdTypeInfo() // sanity check - if !strings.Contains(at.ThresholdType, "counter") && - !strings.Contains(at.ThresholdType, "balance") { + if kind != "counter" && kind != "balance" { continue } if at.Executed { @@ -483,9 +496,11 @@ func (ub *Account) executeActionTriggers(a *Action) { } if strings.Contains(at.ThresholdType, "counter") { for _, uc := range ub.UnitCounters { - if uc.BalanceType == at.BalanceType { + if uc.BalanceType == at.BalanceType && + uc.CounterType == counter { for _, mb := range uc.Balances { - if strings.Contains(at.ThresholdType, "*max") { + if limit == "*max" { + log.Print("HERE: ", mb.MatchActionTrigger(at)) if mb.MatchActionTrigger(at) && mb.GetValue() >= at.ThresholdValue { // run the actions at.Execute(ub, nil) @@ -504,7 +519,7 @@ func (ub *Account) executeActionTriggers(a *Action) { if !b.dirty { // do not check clean balances continue } - if strings.Contains(at.ThresholdType, "*max") { + if limit == "*max" { if b.MatchActionTrigger(at) && b.GetValue() >= at.ThresholdValue { // run the actions at.Execute(ub, nil) @@ -542,27 +557,9 @@ func (ub *Account) SetRecurrent(a *Action, recurrent bool) { } } -// Returns the unit counter that matches the specified action type -func (ub *Account) getUnitCounter(a *Action) *UnitsCounter { - for _, uc := range ub.UnitCounters { - if uc.BalanceType == a.BalanceType { - return uc - } - } - return nil -} - -// Increments the counter for the type specified in the received Action -// with the actions values -func (ub *Account) countUnits(a *Action) { - unitsCounter := ub.getUnitCounter(a) - // if not found add the counter - if unitsCounter == nil { - unitsCounter = &UnitsCounter{BalanceType: a.BalanceType} - ub.UnitCounters = append(ub.UnitCounters, unitsCounter) - } - - unitsCounter.addUnits(a.Balance.GetValue(), a.Balance.DestinationIds) // DestinationIds is actually a destination (number or prefix) +// Increments the counter for the type +func (ub *Account) countUnits(amount float64, kind string, cc *CallCost, b *Balance) { + ub.UnitCounters.addUnits(amount, kind, cc, b) ub.executeActionTriggers(nil) } @@ -577,16 +574,16 @@ func (acc *Account) initCounters() { } uc, exists := ucTempMap[at.BalanceType] if !exists { - uc = &UnitsCounter{BalanceType: at.BalanceType} + _, ct, _ := at.GetThresholdTypeInfo() + uc = &UnitsCounter{ + BalanceType: at.BalanceType, + CounterType: ct, + } ucTempMap[at.BalanceType] = uc - uc.Balances = BalanceChain{&Balance{}} // init chain with default balance + uc.Balances = BalanceChain{} acc.UnitCounters = append(acc.UnitCounters, uc) } - b := at.CreateBalance() - if !uc.Balances.HasBalance(b) { - uc.Balances = append(uc.Balances, b) - } - //uc.Balances.Sort() // do not sort, default balance first + uc.Balances = append(uc.Balances, at.CreateBalance()) } } @@ -681,7 +678,7 @@ func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balance b.SubstractValue(connectFee) // the conect fee is not refundable! if count { - acc.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: connectFee, DestinationIds: utils.StringMap{cc.Destination: true}}}) + acc.countUnits(connectFee, utils.MONETARY, cc, b) } connectFeePaid = true break @@ -690,10 +687,11 @@ func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balance // debit connect fee if connectFee > 0 && !connectFeePaid { // there are no money for the connect fee; go negative - acc.GetDefaultMoneyBalance().SubstractValue(connectFee) + b := acc.GetDefaultMoneyBalance() + b.SubstractValue(connectFee) // the conect fee is not refundable! if count { - acc.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: connectFee, DestinationIds: utils.StringMap{cc.Destination: true}}}) + acc.countUnits(connectFee, utils.MONETARY, cc, b) } } } diff --git a/engine/account_test.go b/engine/account_test.go index 799177a78..4712b5a69 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -19,6 +19,7 @@ along with this program. If not, see package engine import ( + "log" "testing" "time" @@ -889,20 +890,22 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.StringMap{utils.OUT: true}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}) + log.Print("==============") + ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) + log.Print("==============") if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue()) } // are set to executed - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}) + ub.countUnits(1, utils.MONETARY, nil, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue()) } // we can reset them ub.ResetActionTriggers(nil) - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}}) + ub.countUnits(10, utils.MONETARY, nil, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 120 || ub.BalanceMap[utils.VOICE][0].GetValue() != 30 { t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue()) } @@ -913,9 +916,9 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 100, ThresholdType: TRIGGER_MIN_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 100, ThresholdType: TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 1}}) + ub.countUnits(1, utils.MONETARY, nil, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue(), len(ub.BalanceMap[utils.MONETARY])) } @@ -926,9 +929,9 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 1}}) + ub.countUnits(1, utils.MONETARY, nil, nil) if len(ub.BalanceMap[utils.MONETARY]) != 1 || ub.BalanceMap[utils.MONETARY][0].GetValue() != 10 { t.Errorf("Error executing triggered actions in order %v BAL: %+v", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.MONETARY][1]) @@ -957,11 +960,11 @@ func TestCleanExpired(t *testing.T) { func TestAccountUnitCounting(t *testing.T) { ub := &Account{} - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}}) + ub.countUnits(10, utils.MONETARY, nil, nil) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}}) + ub.countUnits(10, utils.MONETARY, nil, nil) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { t.Error("Error counting units") } @@ -969,15 +972,15 @@ func TestAccountUnitCounting(t *testing.T) { func TestAccountUnitCountingOutbound(t *testing.T) { ub := &Account{} - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}}) + ub.countUnits(10, utils.MONETARY, nil, nil) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}}) + ub.countUnits(10, utils.MONETARY, nil, nil) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}}) + ub.countUnits(10, utils.MONETARY, nil, nil) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 30 { t.Error("Error counting units") } @@ -985,15 +988,15 @@ func TestAccountUnitCountingOutbound(t *testing.T) { func TestAccountUnitCountingOutboundInbound(t *testing.T) { ub := &Account{} - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10}}) + ub.countUnits(1, utils.MONETARY, nil, nil) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { t.Errorf("Error counting units: %+v", ub.UnitCounters[0]) } - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}}) + ub.countUnits(10, utils.MONETARY, nil, nil) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.IN)}}) + ub.countUnits(10, utils.MONETARY, nil, nil) if len(ub.UnitCounters) != 1 || (ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 30) { // for the moment no in/out distinction t.Error("Error counting units") } @@ -1017,7 +1020,7 @@ func TestAccountRefund(t *testing.T) { &Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: ""}}, } for _, increment := range increments { - ub.refundIncrement(increment, utils.VOICE, false) + ub.refundIncrement(increment, &CallDescriptor{TOR: utils.VOICE}, false) } if ub.BalanceMap[utils.MONETARY][0].GetValue() != 104 || ub.BalanceMap[utils.VOICE][0].GetValue() != 13 || diff --git a/engine/action.go b/engine/action.go index 0a2e9d592..619af3fbc 100644 --- a/engine/action.go +++ b/engine/action.go @@ -358,7 +358,8 @@ func resetCounterAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Act if ub == nil { return errors.New("nil user balance") } - uc := ub.getUnitCounter(a) + //uc := ub.getUnitCounter(a.ActionType) + var uc *UnitsCounter if uc == nil { uc = &UnitsCounter{BalanceType: a.BalanceType} ub.UnitCounters = append(ub.UnitCounters, uc) diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 919a895c8..54f225d6b 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -23,6 +23,7 @@ import ( "fmt" "regexp" "sort" + "strings" "time" "github.com/cgrates/cgrates/utils" @@ -36,7 +37,7 @@ type ActionTrigger struct { Recurrent bool // reset eexcuted flag each run MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers BalanceId string - BalanceType string + BalanceType string // *monetary/*voice etc BalanceDirections utils.StringMap // filter for balance BalanceDestinationIds utils.StringMap // filter for balance BalanceWeight float64 // filter for balance @@ -53,6 +54,14 @@ type ActionTrigger struct { lastExecutionTime time.Time } +func (at *ActionTrigger) GetThresholdTypeInfo() (limit, counter, kind string) { + slice := strings.Split(at.ThresholdType, "_") + if len(slice) != 3 { + return "", "", "" + } + return slice[0], "*" + slice[1], slice[2] +} + func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err error) { // check for min sleep time if at.Recurrent && !at.lastExecutionTime.IsZero() && time.Since(at.lastExecutionTime) < at.MinSleep { diff --git a/engine/actions_test.go b/engine/actions_test.go index 75233f9a1..28c15dcf8 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -610,7 +610,7 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{BalanceType: utils.VOICE, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, TRIGGER_MAX_COUNTER, 3)} + a := &Action{BalanceType: utils.VOICE, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, TRIGGER_MAX_EVENT_COUNTER, 3)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } diff --git a/engine/balances.go b/engine/balances.go index f5a565656..a93b2e87e 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -93,6 +93,40 @@ func (b *Balance) MatchFilter(o *Balance) bool { (o.RatingSubject == "" || b.RatingSubject == o.RatingSubject) } +func (b *Balance) MatchCCFilter(cc *CallCost) bool { + if len(b.Categories) > 0 && cc.Category != "" && b.Categories[cc.Category] == false { + return false + } + if len(b.Directions) > 0 && cc.Direction != "" && b.Directions[cc.Direction] == false { + return false + } + + // match destination ids + foundMatchingDestId := false + if len(b.DestinationIds) > 0 && cc.Destination != "" { + for _, p := range utils.SplitPrefix(cc.Destination, MIN_PREFIX_MATCH) { + if x, err := cache2go.Get(utils.DESTINATION_PREFIX + p); err == nil { + destIds := x.(map[interface{}]struct{}) + for filterDestId := range b.DestinationIds { + if _, ok := destIds[filterDestId]; ok { + foundMatchingDestId = true + break + } + } + } + if foundMatchingDestId { + break + } + } + } else { + foundMatchingDestId = true + } + if !foundMatchingDestId { + return false + } + return true +} + // the default balance has standard Id func (b *Balance) IsDefault() bool { return b.Id == utils.META_DEFAULT @@ -364,7 +398,7 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala inc.Cost = 0 inc.paid = true if count { - ub.countUnits(&Action{BalanceType: cc.TOR, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: amount, DestinationIds: utils.StringMap{cc.Destination: true}}}) + ub.countUnits(amount, cc.TOR, cc, b) } } else { inc.paid = false @@ -428,7 +462,7 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala inc.BalanceInfo.AccountId = ub.Id inc.paid = true if count { - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: cost, DestinationIds: utils.StringMap{cc.Destination: true}}}) + ub.countUnits(cost, utils.MONETARY, cc, b) } // go to nextincrement continue @@ -452,9 +486,9 @@ 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: amount, DestinationIds: utils.StringMap{cc.Destination: true}}}) + ub.countUnits(amount, cc.TOR, cc, b) 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}}}) + ub.countUnits(cost, utils.MONETARY, cc, moneyBal) } } } else { @@ -529,7 +563,7 @@ func (b *Balance) DebitMoney(cd *CallDescriptor, ub *Account, count bool, dryRun inc.BalanceInfo.AccountId = ub.Id inc.paid = true if count { - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: amount, DestinationIds: utils.StringMap{cc.Destination: true}}}) + ub.countUnits(amount, utils.MONETARY, cc, b) } //log.Printf("TS: %+v", cc.Cost) @@ -544,7 +578,7 @@ func (b *Balance) DebitMoney(cd *CallDescriptor, ub *Account, count bool, dryRun inc.BalanceInfo.AccountId = ub.Id inc.paid = true if count { - ub.countUnits(&Action{BalanceType: utils.MONETARY, Balance: &Balance{Directions: utils.StringMap{cc.Direction: true}, Value: amount, DestinationIds: utils.StringMap{cc.Destination: true}}}) + ub.countUnits(amount, utils.MONETARY, cc, b) } } else { inc.paid = false diff --git a/engine/calldesc.go b/engine/calldesc.go index d0c4517ff..fe9db8c9f 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -709,7 +709,7 @@ func (cd *CallDescriptor) RefundIncrements() (left float64, err error) { defer accountingStorage.SetAccount(account) } } - account.refundIncrement(increment, cd.TOR, true) + account.refundIncrement(increment, cd, true) } return 0.0, err } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 84b6069f4..9ec034544 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -182,11 +182,11 @@ POST_AT,NEG,*asap,10 ` actionTriggers = ` -STANDARD_TRIGGER,st0,*min_counter,10,false,0,,*voice,*out,,GERMANY_O2,,,,,,,,SOME_1,10 -STANDARD_TRIGGER,st1,*max_balance,200,false,0,,*voice,*out,,GERMANY,,,,,,,,SOME_2,10 -STANDARD_TRIGGERS,,*min_balance,2,false,0,,*monetary,*out,,,,,,,,,,LOG_WARNING,10 -STANDARD_TRIGGERS,,*max_balance,20,false,0,,*monetary,*out,,,,,,,,,,LOG_WARNING,10 -STANDARD_TRIGGERS,,*max_counter,5,false,0,,*monetary,*out,,FS_USERS,,,,,,,,LOG_WARNING,10 +STANDARD_TRIGGER,st0,*min_event_counter,10,false,0,,*voice,*out,,GERMANY_O2,,,,,,,,SOME_1,10 +STANDARD_TRIGGER,st1,*max_event_balance,200,false,0,,*voice,*out,,GERMANY,,,,,,,,SOME_2,10 +STANDARD_TRIGGERS,,*min_event_balance,2,false,0,,*monetary,*out,,,,,,,,,,LOG_WARNING,10 +STANDARD_TRIGGERS,,*max_event_balance,20,false,0,,*monetary,*out,,,,,,,,,,LOG_WARNING,10 +STANDARD_TRIGGERS,,*max_event_counter,5,false,0,,*monetary,*out,,FS_USERS,,,,,,,,LOG_WARNING,10 CDRST1_WARN_ASR,,*min_asr,45,true,1h,,,,,,,,,,,,3,CDRST_WARN_HTTP,10 CDRST1_WARN_ACD,,*min_acd,10,true,1h,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 CDRST1_WARN_ACC,,*max_acc,10,true,10m,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 @@ -1010,7 +1010,7 @@ func TestLoadActionTriggers(t *testing.T) { expected := &ActionTrigger{ BalanceType: utils.VOICE, BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdType: TRIGGER_MIN_COUNTER, + ThresholdType: TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, BalanceDestinationIds: utils.NewStringMap("GERMANY_O2"), BalanceCategories: utils.StringMap{}, diff --git a/engine/units_counter.go b/engine/units_counter.go index 0a4404965..2cc4b9304 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -19,83 +19,54 @@ along with this program. If not, see package engine import ( + "log" "strings" - "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/utils" ) // Amount of a trafic of a certain type type UnitsCounter struct { - BalanceType string - // Units float64 - Balances BalanceChain // first balance is the general one (no destination) + BalanceType string // *monetary/*voice/*sms/etc + CounterType string // *event or *balance + 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 + uc.Balances = BalanceChain{} for _, at := range ats { if !strings.Contains(at.ThresholdType, "counter") || at.BalanceType != uc.BalanceType { // only get actions for counter type action triggers and with the same type continue } - b := at.CreateBalance() - if !uc.Balances.HasBalance(b) { - uc.Balances = append(uc.Balances, b) + uc.Balances = append(uc.Balances, at.CreateBalance()) + } +} + +type UnitCounters []*UnitsCounter + +func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *Balance) { + for _, uc := range ucs { + if uc.BalanceType != kind { + continue } - - } - //uc.Balances.Sort() // should not be sorted, leave default in first position -} - -// returns the first balance that has no destination attached -func (uc *UnitsCounter) GetGeneralBalance() *Balance { - if len(uc.Balances) == 0 { // general balance not present for some reason - uc.Balances = append(uc.Balances, &Balance{}) - } - return uc.Balances[0] -} - -// Adds the units from the received balance to an existing balance if the destination -// is the same or ads the balance to the list if none matches. -func (uc *UnitsCounter) addUnits(amount float64, prefixMap utils.StringMap) { - counted := false - prefix := prefixMap.String() - if prefix != "" { - for _, mb := range uc.Balances { - if !mb.HasDestination() { + if uc.CounterType == "" { + uc.CounterType = utils.COUNTER_EVENT + } + for _, bal := range uc.Balances { + if uc.CounterType == utils.COUNTER_EVENT && cc != nil && bal.MatchCCFilter(cc) { + log.Print("MATCHCC") + bal.AddValue(amount) continue } - for _, p := range utils.SplitPrefix(prefix, MIN_PREFIX_MATCH) { - if x, err := cache2go.Get(utils.DESTINATION_PREFIX + p); err == nil { - destIds := x.(map[interface{}]struct{}) - for key := range mb.DestinationIds { - if _, found := destIds[key]; found { - mb.AddValue(amount) - counted = true - break - } - if counted { - break - } - } - } - if counted { - break - } + if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bal) { + bal.AddValue(amount) + continue } } - } - if !counted { - // use general balance - b := uc.GetGeneralBalance() - b.AddValue(amount) + } } - -/*func (uc *UnitsCounter) String() string { - return fmt.Sprintf("%s %s %v", uc.BalanceId, uc.Direction, uc.Units) -}*/ diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index 76763c41e..48ff87578 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -29,7 +29,7 @@ func TestUnitsCounterAddBalance(t *testing.T) { BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, } - uc.addUnits(20, utils.NewStringMap("test")) + UnitCounters{uc}.addUnits(20, utils.SMS, &CallCost{Destination: "test"}, nil) if len(uc.Balances) != 3 { t.Error("Error adding minute bucket: ", uc.Balances) } @@ -40,7 +40,7 @@ func TestUnitsCounterAddBalanceExists(t *testing.T) { BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1}, &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, } - uc.addUnits(5, utils.NewStringMap("0723")) + UnitCounters{uc}.addUnits(5, utils.SMS, &CallCost{Destination: "0723"}, nil) if len(uc.Balances) != 3 || uc.Balances[1].GetValue() != 15 { t.Error("Error adding minute bucket!") } diff --git a/utils/consts.go b/utils/consts.go index ce4326b46..3e3b3ad50 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -237,6 +237,8 @@ const ( EXTRA_FIELDS = "ExtraFields" META_SURETAX = "*sure_tax" SURETAX = "suretax" + COUNTER_EVENT = "*event" + COUNTER_BALANCE = "*balance" ) var (