mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
triggers improvements
work in progress
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{},
|
||||
|
||||
@@ -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)
|
||||
}*/
|
||||
|
||||
@@ -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!")
|
||||
}
|
||||
|
||||
@@ -237,6 +237,8 @@ const (
|
||||
EXTRA_FIELDS = "ExtraFields"
|
||||
META_SURETAX = "*sure_tax"
|
||||
SURETAX = "suretax"
|
||||
COUNTER_EVENT = "*event"
|
||||
COUNTER_BALANCE = "*balance"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
Reference in New Issue
Block a user