triggers improvements

work in progress
This commit is contained in:
Radu Ioan Fericean
2015-11-02 22:44:48 +02:00
parent c11a8b279b
commit a44916465d
11 changed files with 153 additions and 135 deletions

View File

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

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
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 ||

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,83 +19,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
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)
}*/

View File

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

View File

@@ -237,6 +237,8 @@ const (
EXTRA_FIELDS = "ExtraFields"
META_SURETAX = "*sure_tax"
SURETAX = "suretax"
COUNTER_EVENT = "*event"
COUNTER_BALANCE = "*balance"
)
var (