mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-24 08:38:45 +05:00
Merge branch 'master' into hapool
This commit is contained in:
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"github.com/cgrates/cgrates/cache2go"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"github.com/cgrates/structmatcher"
|
||||
|
||||
"strings"
|
||||
)
|
||||
@@ -176,9 +177,11 @@ func (ub *Account) enableDisableBalanceAction(a *Action) error {
|
||||
}
|
||||
found := false
|
||||
id := a.BalanceType
|
||||
disabled := a.Balance.Disabled
|
||||
a.Balance.Disabled = !disabled // match for the opposite
|
||||
for _, b := range ub.BalanceMap[id] {
|
||||
if b.MatchFilter(a.Balance, false) {
|
||||
b.Disabled = a.Balance.Disabled
|
||||
b.Disabled = disabled
|
||||
b.dirty = true
|
||||
found = true
|
||||
}
|
||||
@@ -316,9 +319,13 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
|
||||
return
|
||||
}
|
||||
}
|
||||
// check for blocker
|
||||
if dryRun && balance.Blocker {
|
||||
//log.Print("BLOCKER!")
|
||||
return // don't go to next balances
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// debit money
|
||||
moneyBalanceChecker := true
|
||||
for moneyBalanceChecker {
|
||||
@@ -332,14 +339,13 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
|
||||
return nil, debitErr
|
||||
}
|
||||
//utils.Logger.Info(fmt.Sprintf("CD AFTER MONEY: %+v", cd))
|
||||
//log.Printf("partCC: %+v", partCC)
|
||||
if partCC != nil {
|
||||
cc.Timespans = append(cc.Timespans, partCC.Timespans...)
|
||||
cc.negativeConnectFee = partCC.negativeConnectFee
|
||||
|
||||
//for i, ts := range cc.Timespans {
|
||||
//log.Printf("cc.times[an[%d]: %+v\n", i, ts)
|
||||
//}
|
||||
/*for i, ts := range cc.Timespans {
|
||||
log.Printf("cc.times[an[%d]: %+v\n", i, ts)
|
||||
}*/
|
||||
cd.TimeStart = cc.GetEndTime()
|
||||
//log.Printf("CD: %+v", cd)
|
||||
//log.Printf("CD: %+v - %+v", cd.TimeStart, cd.TimeEnd)
|
||||
@@ -354,6 +360,11 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
|
||||
return
|
||||
}
|
||||
}
|
||||
// check for blocker
|
||||
if dryRun && balance.Blocker {
|
||||
//log.Print("BLOCKER!")
|
||||
return // don't go to next balances
|
||||
}
|
||||
}
|
||||
}
|
||||
//log.Printf("END CD: %+v", cd)
|
||||
@@ -687,6 +698,31 @@ func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balance
|
||||
}
|
||||
}
|
||||
|
||||
func (acc *Account) matchActionFilter(condition string) (bool, error) {
|
||||
sm, err := structmatcher.NewStructMatcher(condition)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for balanceType, balanceChain := range acc.BalanceMap {
|
||||
for _, b := range balanceChain {
|
||||
check, err := sm.Match(&struct {
|
||||
Type string
|
||||
*Balance
|
||||
}{
|
||||
Type: balanceType,
|
||||
Balance: b,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if check {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// used in some api for transition
|
||||
func (acc *Account) AsOldStructure() interface{} {
|
||||
type Balance struct {
|
||||
|
||||
@@ -42,7 +42,8 @@ type Action struct {
|
||||
ActionType string
|
||||
BalanceType string
|
||||
ExtraParameters string
|
||||
ExpirationString string
|
||||
Filter string
|
||||
ExpirationString string // must stay as string because it can have relative values like 1month
|
||||
Weight float64
|
||||
Balance *Balance
|
||||
}
|
||||
@@ -131,9 +132,9 @@ func getActionFunc(typ string) (actionTypeFunc, bool) {
|
||||
case SET_DDESTINATIONS:
|
||||
return setddestinations, true
|
||||
case REMOVE_ACCOUNT:
|
||||
return removeAccount, true
|
||||
return removeAccountAction, true
|
||||
case REMOVE_BALANCE:
|
||||
return removeBalance, true
|
||||
return removeBalanceAction, true
|
||||
case TRANSFER_MONETARY_DEFAULT:
|
||||
return transferMonetaryDefault, true
|
||||
}
|
||||
@@ -522,7 +523,7 @@ func setddestinations(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actio
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeAccount(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
var accID string
|
||||
if ub != nil {
|
||||
accID = ub.Id
|
||||
@@ -568,7 +569,7 @@ func removeAccount(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeBalance(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
func removeBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
if _, exists := ub.BalanceMap[a.BalanceType]; !exists {
|
||||
return utils.ErrNotFound
|
||||
}
|
||||
@@ -592,6 +593,10 @@ func removeBalance(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions)
|
||||
}
|
||||
|
||||
func transferMonetaryDefault(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error {
|
||||
if acc == nil {
|
||||
utils.Logger.Err("*transfer_monetary_default called without account")
|
||||
return utils.ErrAccountNotFound
|
||||
}
|
||||
if _, exists := acc.BalanceMap[utils.MONETARY]; !exists {
|
||||
return utils.ErrNotFound
|
||||
}
|
||||
@@ -599,7 +604,8 @@ func transferMonetaryDefault(acc *Account, sq *StatsQueueTriggered, a *Action, a
|
||||
bChain := acc.BalanceMap[utils.MONETARY]
|
||||
for _, balance := range bChain {
|
||||
if balance.Uuid != defaultBalance.Uuid &&
|
||||
balance.Id != defaultBalance.Id { // extra caution
|
||||
balance.Id != defaultBalance.Id && // extra caution
|
||||
balance.MatchFilter(a.Balance, false) {
|
||||
if balance.Value > 0 {
|
||||
defaultBalance.Value += balance.Value
|
||||
balance.Value = 0
|
||||
|
||||
@@ -291,6 +291,16 @@ func (at *ActionTiming) Execute() (err error) {
|
||||
transactionFailed := false
|
||||
removeAccountActionFound := false
|
||||
for _, a := range aac {
|
||||
// check action filter
|
||||
if len(a.Filter) > 0 {
|
||||
matched, err := ub.matchActionFilter(a.Filter)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ub.Disabled && a.ActionType != ENABLE_ACCOUNT {
|
||||
continue // disabled acocunts are not removed from action plan
|
||||
//return 0, fmt.Errorf("Account %s is disabled", accID)
|
||||
|
||||
@@ -46,7 +46,8 @@ type ActionTrigger struct {
|
||||
BalanceRatingSubject string // filter for balance
|
||||
BalanceCategories utils.StringMap // filter for balance
|
||||
BalanceSharedGroups utils.StringMap // filter for balance
|
||||
BalanceDisabled bool // filter for balance
|
||||
BalanceBlocker bool
|
||||
BalanceDisabled bool // filter for balance
|
||||
Weight float64
|
||||
ActionsId string
|
||||
MinQueuedItems int // Trigger actions only if this number is hit (stats only)
|
||||
@@ -75,6 +76,17 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro
|
||||
transactionFailed := false
|
||||
removeAccountActionFound := false
|
||||
for _, a := range aac {
|
||||
// check action filter
|
||||
if len(a.Filter) > 0 {
|
||||
matched, err := ub.matchActionFilter(a.Filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if a.Balance == nil {
|
||||
a.Balance = &Balance{}
|
||||
}
|
||||
@@ -117,7 +129,7 @@ func (at *ActionTrigger) Match(a *Action) bool {
|
||||
return match
|
||||
}
|
||||
id := a.BalanceType == "" || at.BalanceType == a.BalanceType
|
||||
thresholdType, thresholdValue, direction, destinationId, weight, ratingSubject, categories, sharedGroup, timings, disabled := true, true, true, true, true, true, true, true, true, true
|
||||
thresholdType, thresholdValue, direction, destinationId, weight, ratingSubject, categories, sharedGroup, timings, blocker, disabled := true, true, true, true, true, true, true, true, true, true, true
|
||||
if a.ExtraParameters != "" {
|
||||
t := struct {
|
||||
ThresholdType string
|
||||
@@ -129,6 +141,7 @@ func (at *ActionTrigger) Match(a *Action) bool {
|
||||
BalanceCategories string
|
||||
BalanceSharedGroups string
|
||||
BalanceTimingTags string
|
||||
BalanceBlocker bool
|
||||
BalanceDisabled bool
|
||||
}{}
|
||||
json.Unmarshal([]byte(a.ExtraParameters), &t)
|
||||
@@ -141,9 +154,10 @@ func (at *ActionTrigger) Match(a *Action) bool {
|
||||
sharedGroup = len(t.BalanceSharedGroups) == 0 || at.BalanceSharedGroups.Equal(utils.ParseStringMap(t.BalanceSharedGroups))
|
||||
weight = t.BalanceWeight == 0 || at.BalanceWeight == t.BalanceWeight
|
||||
ratingSubject = t.BalanceRatingSubject == "" || at.BalanceRatingSubject == t.BalanceRatingSubject
|
||||
blocker = at.BalanceBlocker == t.BalanceBlocker
|
||||
disabled = at.BalanceDisabled == t.BalanceDisabled
|
||||
}
|
||||
return id && direction && thresholdType && thresholdValue && destinationId && weight && ratingSubject && categories && sharedGroup && timings && disabled
|
||||
return id && direction && thresholdType && thresholdValue && destinationId && weight && ratingSubject && categories && sharedGroup && timings && blocker && disabled
|
||||
}
|
||||
|
||||
// makes a shallow copy of the receiver
|
||||
@@ -163,6 +177,7 @@ func (at *ActionTrigger) CreateBalance() *Balance {
|
||||
Categories: at.BalanceCategories,
|
||||
SharedGroups: at.BalanceSharedGroups,
|
||||
TimingIDs: at.BalanceTimingTags,
|
||||
Blocker: at.BalanceBlocker,
|
||||
Disabled: at.BalanceDisabled,
|
||||
Weight: at.BalanceWeight,
|
||||
}
|
||||
|
||||
@@ -1507,6 +1507,258 @@ func TestActionTransferMonetaryDefault(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTransferMonetaryDefaultFilter(t *testing.T) {
|
||||
err := accountingStorage.SetAccount(
|
||||
&Account{
|
||||
Id: "cgrates.org:trans",
|
||||
BalanceMap: map[string]BalanceChain{
|
||||
utils.MONETARY: BalanceChain{
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Id: utils.META_DEFAULT,
|
||||
Value: 10,
|
||||
Weight: 20,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 3,
|
||||
Weight: 20,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 1,
|
||||
Weight: 10,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 6,
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("error setting account: %v", err)
|
||||
}
|
||||
|
||||
a := &Action{
|
||||
ActionType: TRANSFER_MONETARY_DEFAULT,
|
||||
Balance: &Balance{Weight: 20},
|
||||
}
|
||||
|
||||
at := &ActionTiming{
|
||||
accountIDs: map[string]struct{}{"cgrates.org:trans": struct{}{}},
|
||||
actions: Actions{a},
|
||||
}
|
||||
at.Execute()
|
||||
|
||||
afterUb, err := accountingStorage.GetAccount("cgrates.org:trans")
|
||||
if err != nil {
|
||||
t.Error("account not found: ", err, afterUb)
|
||||
}
|
||||
if afterUb.BalanceMap[utils.MONETARY].GetTotalValue() != 20 ||
|
||||
afterUb.BalanceMap[utils.MONETARY][0].Value != 19 ||
|
||||
afterUb.BalanceMap[utils.MONETARY][1].Value != 0 ||
|
||||
afterUb.BalanceMap[utils.MONETARY][2].Value != 1 ||
|
||||
afterUb.BalanceMap[utils.MONETARY][3].Value != 0 {
|
||||
for _, b := range afterUb.BalanceMap[utils.MONETARY] {
|
||||
t.Logf("B: %+v", b)
|
||||
}
|
||||
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionConditionalTopup(t *testing.T) {
|
||||
err := accountingStorage.SetAccount(
|
||||
&Account{
|
||||
Id: "cgrates.org:cond",
|
||||
BalanceMap: map[string]BalanceChain{
|
||||
utils.MONETARY: BalanceChain{
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Id: utils.META_DEFAULT,
|
||||
Value: 10,
|
||||
Weight: 20,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 3,
|
||||
Weight: 20,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 1,
|
||||
Weight: 10,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 6,
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("error setting account: %v", err)
|
||||
}
|
||||
|
||||
a := &Action{
|
||||
ActionType: TOPUP,
|
||||
BalanceType: utils.MONETARY,
|
||||
Filter: `{"Type":"*monetary","Value":1,"Weight":10}`,
|
||||
Balance: &Balance{
|
||||
Value: 11,
|
||||
Weight: 30,
|
||||
},
|
||||
}
|
||||
|
||||
at := &ActionTiming{
|
||||
accountIDs: map[string]struct{}{"cgrates.org:cond": struct{}{}},
|
||||
actions: Actions{a},
|
||||
}
|
||||
at.Execute()
|
||||
|
||||
afterUb, err := accountingStorage.GetAccount("cgrates.org:cond")
|
||||
if err != nil {
|
||||
t.Error("account not found: ", err, afterUb)
|
||||
}
|
||||
if len(afterUb.BalanceMap[utils.MONETARY]) != 5 ||
|
||||
afterUb.BalanceMap[utils.MONETARY].GetTotalValue() != 31 ||
|
||||
afterUb.BalanceMap[utils.MONETARY][4].Value != 11 {
|
||||
for _, b := range afterUb.BalanceMap[utils.MONETARY] {
|
||||
t.Logf("B: %+v", b)
|
||||
}
|
||||
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionConditionalTopupNoMatch(t *testing.T) {
|
||||
err := accountingStorage.SetAccount(
|
||||
&Account{
|
||||
Id: "cgrates.org:cond",
|
||||
BalanceMap: map[string]BalanceChain{
|
||||
utils.MONETARY: BalanceChain{
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Id: utils.META_DEFAULT,
|
||||
Value: 10,
|
||||
Weight: 20,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 3,
|
||||
Weight: 20,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 1,
|
||||
Weight: 10,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 6,
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("error setting account: %v", err)
|
||||
}
|
||||
|
||||
a := &Action{
|
||||
ActionType: TOPUP,
|
||||
BalanceType: utils.MONETARY,
|
||||
Filter: `{"Type":"*monetary","Value":2,"Weight":10}`,
|
||||
Balance: &Balance{
|
||||
Value: 11,
|
||||
Weight: 30,
|
||||
},
|
||||
}
|
||||
|
||||
at := &ActionTiming{
|
||||
accountIDs: map[string]struct{}{"cgrates.org:cond": struct{}{}},
|
||||
actions: Actions{a},
|
||||
}
|
||||
at.Execute()
|
||||
|
||||
afterUb, err := accountingStorage.GetAccount("cgrates.org:cond")
|
||||
if err != nil {
|
||||
t.Error("account not found: ", err, afterUb)
|
||||
}
|
||||
if len(afterUb.BalanceMap[utils.MONETARY]) != 4 ||
|
||||
afterUb.BalanceMap[utils.MONETARY].GetTotalValue() != 20 {
|
||||
for _, b := range afterUb.BalanceMap[utils.MONETARY] {
|
||||
t.Logf("B: %+v", b)
|
||||
}
|
||||
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionConditionalTopupExistingBalance(t *testing.T) {
|
||||
err := accountingStorage.SetAccount(
|
||||
&Account{
|
||||
Id: "cgrates.org:cond",
|
||||
BalanceMap: map[string]BalanceChain{
|
||||
utils.MONETARY: BalanceChain{
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 1,
|
||||
Weight: 10,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 6,
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
utils.VOICE: BalanceChain{
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 10,
|
||||
Weight: 10,
|
||||
},
|
||||
&Balance{
|
||||
Uuid: utils.GenUUID(),
|
||||
Value: 100,
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("error setting account: %v", err)
|
||||
}
|
||||
|
||||
a := &Action{
|
||||
ActionType: TOPUP,
|
||||
BalanceType: utils.MONETARY,
|
||||
Filter: `{"Type":"*voice","Value":{"*gte":100}}`,
|
||||
Balance: &Balance{
|
||||
Value: 11,
|
||||
Weight: 10,
|
||||
},
|
||||
}
|
||||
|
||||
at := &ActionTiming{
|
||||
accountIDs: map[string]struct{}{"cgrates.org:cond": struct{}{}},
|
||||
actions: Actions{a},
|
||||
}
|
||||
at.Execute()
|
||||
|
||||
afterUb, err := accountingStorage.GetAccount("cgrates.org:cond")
|
||||
if err != nil {
|
||||
t.Error("account not found: ", err, afterUb)
|
||||
}
|
||||
if len(afterUb.BalanceMap[utils.MONETARY]) != 2 ||
|
||||
afterUb.BalanceMap[utils.MONETARY].GetTotalValue() != 18 {
|
||||
for _, b := range afterUb.BalanceMap[utils.MONETARY] {
|
||||
t.Logf("B: %+v", b)
|
||||
}
|
||||
t.Error("ransfer balance value: ", afterUb.BalanceMap[utils.MONETARY].GetTotalValue())
|
||||
}
|
||||
}
|
||||
|
||||
/**************** Benchmarks ********************************/
|
||||
|
||||
func BenchmarkUUID(b *testing.B) {
|
||||
|
||||
@@ -46,6 +46,7 @@ type Balance struct {
|
||||
TimingIDs utils.StringMap
|
||||
Disabled bool
|
||||
Factor ValueFactor
|
||||
Blocker bool
|
||||
precision int
|
||||
account *Account // used to store ub reference for shared balances
|
||||
dirty bool
|
||||
@@ -67,10 +68,14 @@ func (b *Balance) Equal(o *Balance) bool {
|
||||
b.RatingSubject == o.RatingSubject &&
|
||||
b.Categories.Equal(o.Categories) &&
|
||||
b.SharedGroups.Equal(o.SharedGroups) &&
|
||||
b.Disabled == o.Disabled
|
||||
b.Disabled == o.Disabled &&
|
||||
b.Blocker == o.Blocker
|
||||
}
|
||||
|
||||
func (b *Balance) MatchFilter(o *Balance, skipIds bool) bool {
|
||||
if o == nil {
|
||||
return true
|
||||
}
|
||||
if !skipIds && o.Uuid != "" {
|
||||
return b.Uuid == o.Uuid
|
||||
}
|
||||
@@ -85,6 +90,8 @@ func (b *Balance) MatchFilter(o *Balance, skipIds bool) bool {
|
||||
}
|
||||
return (o.ExpirationDate.IsZero() || b.ExpirationDate.Equal(o.ExpirationDate)) &&
|
||||
(o.Weight == 0 || b.Weight == o.Weight) &&
|
||||
(b.Blocker == o.Blocker) &&
|
||||
(b.Disabled == o.Disabled) &&
|
||||
(len(o.DestinationIds) == 0 || b.DestinationIds.Includes(o.DestinationIds)) &&
|
||||
(len(o.Directions) == 0 || b.Directions.Includes(o.Directions)) &&
|
||||
(len(o.Categories) == 0 || b.Categories.Includes(o.Categories)) &&
|
||||
@@ -231,6 +238,7 @@ func (b *Balance) Clone() *Balance {
|
||||
SharedGroups: b.SharedGroups,
|
||||
TimingIDs: b.TimingIDs,
|
||||
Timings: b.Timings, // should not be a problem with aliasing
|
||||
Blocker: b.Blocker,
|
||||
Disabled: b.Disabled,
|
||||
dirty: b.dirty,
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ type CallCost struct {
|
||||
Direction, Category, Tenant, Subject, Account, Destination, TOR string
|
||||
Cost float64
|
||||
Timespans TimeSpans
|
||||
RatedUsage float64
|
||||
deductConnectFee bool
|
||||
negativeConnectFee bool // the connect fee went negative on default balance
|
||||
maxCostDisconect bool
|
||||
@@ -61,6 +62,15 @@ func (cc *CallCost) GetDuration() (td time.Duration) {
|
||||
return
|
||||
}
|
||||
|
||||
func (cc *CallCost) UpdateRatedUsage() time.Duration {
|
||||
if cc == nil {
|
||||
return 0
|
||||
}
|
||||
totalDuration := cc.GetDuration()
|
||||
cc.RatedUsage = totalDuration.Seconds()
|
||||
return totalDuration
|
||||
}
|
||||
|
||||
func (cc *CallCost) GetConnectFee() float64 {
|
||||
if len(cc.Timespans) == 0 ||
|
||||
cc.Timespans[0].RateInterval == nil ||
|
||||
|
||||
@@ -50,6 +50,10 @@ func TestSingleResultMerge(t *testing.T) {
|
||||
if cc1.Cost != 122 {
|
||||
t.Errorf("Exdpected 120 was %v", cc1.Cost)
|
||||
}
|
||||
d := cc1.UpdateRatedUsage()
|
||||
if d != 2*time.Minute || cc1.RatedUsage != 120.0 {
|
||||
t.Errorf("error updating rating usage: %v, %v", d, cc1.RatedUsage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleResultMerge(t *testing.T) {
|
||||
|
||||
@@ -195,7 +195,11 @@ func (cd *CallDescriptor) LoadRatingPlans() (err error) {
|
||||
err, _ = cd.getRatingPlansForPrefix(cd.GetKey(FALLBACK_SUBJECT), 1)
|
||||
}
|
||||
//load the rating plans
|
||||
if err != nil || !cd.continousRatingInfos() {
|
||||
if err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("Rating plan not found for destination %s and account: %s, subject: %s", cd.Destination, cd.GetAccountKey(), cd.GetKey(cd.Subject)))
|
||||
err = utils.ErrRatingPlanNotFound
|
||||
}
|
||||
if !cd.continousRatingInfos() {
|
||||
utils.Logger.Err(fmt.Sprintf("Destination %s not authorized for account: %s, subject: %s", cd.Destination, cd.GetAccountKey(), cd.GetKey(cd.Subject)))
|
||||
err = utils.ErrUnauthorizedDestination
|
||||
}
|
||||
@@ -522,6 +526,7 @@ func (cd *CallDescriptor) getCost() (*CallCost, error) {
|
||||
cc.Cost = utils.Round(cc.Cost, roundingDecimals, roundingMethod)
|
||||
//utils.Logger.Info(fmt.Sprintf("<Rater> Get Cost: %s => %v", cd.GetKey(), cc))
|
||||
cc.Timespans.Compress()
|
||||
cc.UpdateRatedUsage()
|
||||
return cc, err
|
||||
}
|
||||
|
||||
@@ -555,7 +560,6 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura
|
||||
//utils.Logger.Debug("ACCOUNT: " + utils.ToJSON(account))
|
||||
//utils.Logger.Debug("DEFAULT_BALANCE: " + utils.ToJSON(defaultBalance))
|
||||
|
||||
//
|
||||
cc, err := cd.debit(account, true, false)
|
||||
//utils.Logger.Debug("CC: " + utils.ToJSON(cc))
|
||||
//log.Print("CC: ", utils.ToIJSON(cc))
|
||||
@@ -665,6 +669,7 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool)
|
||||
return nil, err
|
||||
}
|
||||
cc.updateCost()
|
||||
cc.UpdateRatedUsage()
|
||||
cc.Timespans.Compress()
|
||||
//log.Printf("OUT CC: ", cc)
|
||||
return
|
||||
|
||||
@@ -537,6 +537,57 @@ func TestMaxSessionTimeWithMaxCost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMaxSessiontWithBlocker(t *testing.T) {
|
||||
ap, _ := ratingStorage.GetActionPlan("BLOCK_AT", false)
|
||||
for _, at := range ap.ActionTimings {
|
||||
at.accountIDs = ap.AccountIDs
|
||||
at.Execute()
|
||||
}
|
||||
acc, err := accountingStorage.GetAccount("cgrates.org:block")
|
||||
if err != nil {
|
||||
t.Error("error getting account: ", err)
|
||||
}
|
||||
if len(acc.BalanceMap[utils.MONETARY]) != 2 ||
|
||||
acc.BalanceMap[utils.MONETARY][0].Blocker != true {
|
||||
for _, b := range acc.BalanceMap[utils.MONETARY] {
|
||||
t.Logf("B: %+v", b)
|
||||
}
|
||||
t.Error("Error executing action plan on account: ", acc.BalanceMap[utils.MONETARY])
|
||||
}
|
||||
cd := &CallDescriptor{
|
||||
Direction: "*out",
|
||||
Category: "call",
|
||||
Tenant: "cgrates.org",
|
||||
Subject: "block",
|
||||
Account: "block",
|
||||
Destination: "0723",
|
||||
TimeStart: time.Date(2016, 1, 13, 14, 0, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2016, 1, 13, 14, 30, 0, 0, time.UTC),
|
||||
MaxCostSoFar: 0,
|
||||
}
|
||||
result, err := cd.GetMaxSessionDuration()
|
||||
expected := 985 * time.Second
|
||||
if result != expected || err != nil {
|
||||
t.Errorf("Expected %v was %v (%v)", expected, result, err)
|
||||
}
|
||||
cd = &CallDescriptor{
|
||||
Direction: "*out",
|
||||
Category: "call",
|
||||
Tenant: "cgrates.org",
|
||||
Subject: "block",
|
||||
Account: "block",
|
||||
Destination: "444",
|
||||
TimeStart: time.Date(2016, 1, 13, 14, 0, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2016, 1, 13, 14, 30, 0, 0, time.UTC),
|
||||
MaxCostSoFar: 0,
|
||||
}
|
||||
result, err = cd.GetMaxSessionDuration()
|
||||
expected = 30 * time.Minute
|
||||
if result != expected || err != nil {
|
||||
t.Errorf("Expected %v was %v (%v)", expected, result, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCostWithMaxCost(t *testing.T) {
|
||||
ap, _ := ratingStorage.GetActionPlan("TOPUP10_AT", false)
|
||||
for _, at := range ap.ActionTimings {
|
||||
@@ -560,6 +611,7 @@ func TestGetCostWithMaxCost(t *testing.T) {
|
||||
t.Errorf("Expected %v was %v", expected, cc.Cost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCostRoundingIssue(t *testing.T) {
|
||||
ap, _ := ratingStorage.GetActionPlan("TOPUP10_AT", false)
|
||||
for _, at := range ap.ActionTimings {
|
||||
|
||||
@@ -126,7 +126,7 @@ func (cdr *CDR) FormatCost(shiftDecimals, roundDecimals int) string {
|
||||
|
||||
// Formats usage on export
|
||||
func (cdr *CDR) FormatUsage(layout string) string {
|
||||
if utils.IsSliceMember([]string{utils.DATA, utils.SMS, utils.GENERIC}, cdr.ToR) {
|
||||
if utils.IsSliceMember([]string{utils.DATA, utils.SMS, utils.MMS, utils.GENERIC}, cdr.ToR) {
|
||||
return strconv.FormatFloat(utils.Round(cdr.Usage.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64)
|
||||
}
|
||||
switch layout {
|
||||
|
||||
@@ -153,6 +153,7 @@ func (self *CdrServer) LogCallCost(ccl *CallCostLog, reply *string) error {
|
||||
|
||||
// RPC method, used to log callcosts to db
|
||||
func (self *CdrServer) LocalLogCallCost(ccl *CallCostLog) error {
|
||||
ccl.CallCost.UpdateRatedUsage() // make sure rated usage is updated
|
||||
if ccl.CheckDuplicate {
|
||||
_, err := self.guard.Guard(func() (interface{}, error) {
|
||||
cc, err := self.cdrDb.GetCallCostLog(ccl.CgrId, ccl.RunId)
|
||||
@@ -220,6 +221,9 @@ func (self *CdrServer) processCdr(cdr *CDR) (err error) {
|
||||
cdr.RunID = utils.MetaRaw
|
||||
}
|
||||
if self.cgrCfg.CDRSStoreCdrs { // Store RawCDRs, this we do sync so we can reply with the status
|
||||
if cdr.CostDetails != nil {
|
||||
cdr.CostDetails.UpdateRatedUsage()
|
||||
}
|
||||
if err := self.cdrDb.SetCDR(cdr, false); err != nil { // Only original CDR stored in primary table, no derived
|
||||
utils.Logger.Err(fmt.Sprintf("<CDRS> Storing primary CDR %+v, got error: %s", cdr, err.Error()))
|
||||
return err // Error is propagated back and we don't continue processing the CDR if we cannot store it
|
||||
@@ -276,6 +280,9 @@ func (self *CdrServer) rateStoreStatsReplicate(cdr *CDR, sendToStats bool) error
|
||||
}
|
||||
if self.cgrCfg.CDRSStoreCdrs { // Store CDRs
|
||||
// Store RatedCDR
|
||||
if cdr.CostDetails != nil {
|
||||
cdr.CostDetails.UpdateRatedUsage()
|
||||
}
|
||||
if err := self.cdrDb.SetCDR(cdr, true); err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<CDRS> Storing rated CDR %+v, got error: %s", cdr, err.Error()))
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ DY_PLAN,RT_DY,*any,10
|
||||
*in,cgrates.org,LCR_STANDARD,max,2013-03-23T00:00:00Z,RP_MX,,
|
||||
*out,cgrates.org,call,money,2015-02-28T00:00:00Z,EVENING,,
|
||||
*out,cgrates.org,call,dy,2015-02-28T00:00:00Z,DY_PLAN,,
|
||||
*out,cgrates.org,call,block,2015-02-28T00:00:00Z,DY_PLAN,,
|
||||
`
|
||||
sharedGroups = `
|
||||
SG1,*any,*lowest,
|
||||
@@ -159,18 +160,20 @@ SG3,*any,*lowest,
|
||||
*in,cgrates.org,call,*any,*any,*any,LCR_STANDARD,*lowest_cost,,2012-01-01T00:00:00Z,20
|
||||
`
|
||||
actions = `
|
||||
MINI,*topup_reset,,,*monetary,*out,,,,,*unlimited,,10,10,false,10
|
||||
MINI,*topup,,,*voice,*out,,NAT,test,,*unlimited,,100,10,false,10
|
||||
SHARED,*topup,,,*monetary,*out,,,,SG1,*unlimited,,100,10,false,10
|
||||
TOPUP10_AC,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,10
|
||||
TOPUP10_AC1,*topup_reset,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40,10,false,10
|
||||
SE0,*topup_reset,,,*monetary,*out,,,,SG2,*unlimited,,0,10,false,10
|
||||
SE10,*topup_reset,,,*monetary,*out,,,,SG2,*unlimited,,10,5,false,10
|
||||
SE10,*topup,,,*monetary,*out,,,,,*unlimited,,10,10,false,10
|
||||
EE0,*topup_reset,,,*monetary,*out,,,,SG3,*unlimited,,0,10,false,10
|
||||
EE0,*allow_negative,,,*monetary,*out,,,,,*unlimited,,0,10,false,10
|
||||
DEFEE,*cdrlog,"{""Category"":""^ddi"",""MediationRunId"":""^did_run""}",,,,,,,,,,,,false,10
|
||||
NEG,*allow_negative,,,*monetary,*out,,,,,*unlimited,,0,10,false,10
|
||||
MINI,*topup_reset,,,,*monetary,*out,,,,,*unlimited,,10,10,false,false,10
|
||||
MINI,*topup,,,,*voice,*out,,NAT,test,,*unlimited,,100,10,false,false,10
|
||||
SHARED,*topup,,,,*monetary,*out,,,,SG1,*unlimited,,100,10,false,false,10
|
||||
TOPUP10_AC,*topup_reset,,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,false,10
|
||||
TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40,10,false,false,10
|
||||
SE0,*topup_reset,,,,*monetary,*out,,,,SG2,*unlimited,,0,10,false,false,10
|
||||
SE10,*topup_reset,,,,*monetary,*out,,,,SG2,*unlimited,,10,5,false,false,10
|
||||
SE10,*topup,,,,*monetary,*out,,,,,*unlimited,,10,10,false,false,10
|
||||
EE0,*topup_reset,,,,*monetary,*out,,,,SG3,*unlimited,,0,10,false,false,10
|
||||
EE0,*allow_negative,,,,*monetary,*out,,,,,*unlimited,,0,10,false,false,10
|
||||
DEFEE,*cdrlog,"{""Category"":""^ddi"",""MediationRunId"":""^did_run""}",,,,,,,,,,,,,false,false,10
|
||||
NEG,*allow_negative,,,,*monetary,*out,,,,,*unlimited,,0,10,false,false,10
|
||||
BLOCK,*topup,,,bblocker,*monetary,*out,,NAT,,,*unlimited,,10,20,true,false,20
|
||||
BLOCK,*topup,,,bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10
|
||||
`
|
||||
actionPlans = `
|
||||
MORE_MINUTES,MINI,ONE_TIME_RUN,10
|
||||
@@ -181,19 +184,20 @@ TOPUP_SHARED0_AT,SE0,*asap,10
|
||||
TOPUP_SHARED10_AT,SE10,*asap,10
|
||||
TOPUP_EMPTY_AT,EE0,*asap,10
|
||||
POST_AT,NEG,*asap,10
|
||||
BLOCK_AT,BLOCK,*asap,10
|
||||
`
|
||||
|
||||
actionTriggers = `
|
||||
STANDARD_TRIGGER,st0,*min_event_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_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
|
||||
CDRST2_WARN_ASR,,*min_asr,30,true,0,,,,,,,,,,,,5,CDRST_WARN_HTTP,10
|
||||
CDRST2_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,5,CDRST_WARN_HTTP,10
|
||||
STANDARD_TRIGGER,st0,*min_event_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_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
|
||||
CDRST2_WARN_ASR,,*min_asr,30,true,0,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10
|
||||
CDRST2_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10
|
||||
`
|
||||
accountActions = `
|
||||
vdf,minitsboy,MORE_MINUTES,STANDARD_TRIGGER,,
|
||||
@@ -207,6 +211,7 @@ vdf,emptyX,TOPUP_EMPTY_AT,,,
|
||||
vdf,emptyY,TOPUP_EMPTY_AT,,,
|
||||
vdf,post,POST_AT,,,
|
||||
cgrates.org,alodis,TOPUP_EMPTY_AT,,true,true
|
||||
cgrates.org,block,BLOCK_AT,,false,false
|
||||
`
|
||||
|
||||
derivedCharges = `
|
||||
@@ -790,7 +795,7 @@ func TestLoadRatingPlans(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadRatingProfiles(t *testing.T) {
|
||||
if len(csvr.ratingProfiles) != 22 {
|
||||
if len(csvr.ratingProfiles) != 23 {
|
||||
t.Error("Failed to load rating profiles: ", len(csvr.ratingProfiles), csvr.ratingProfiles)
|
||||
}
|
||||
rp := csvr.ratingProfiles["*out:test:0:trp"]
|
||||
@@ -809,7 +814,7 @@ func TestLoadRatingProfiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadActions(t *testing.T) {
|
||||
if len(csvr.actions) != 9 {
|
||||
if len(csvr.actions) != 10 {
|
||||
t.Error("Failed to load actions: ", len(csvr.actions))
|
||||
}
|
||||
as1 := csvr.actions["MINI"]
|
||||
@@ -822,12 +827,14 @@ func TestLoadActions(t *testing.T) {
|
||||
ExtraParameters: "",
|
||||
Weight: 10,
|
||||
Balance: &Balance{
|
||||
Uuid: as1[0].Balance.Uuid,
|
||||
Directions: utils.NewStringMap(utils.OUT),
|
||||
Value: 10,
|
||||
Weight: 10,
|
||||
TimingIDs: utils.StringMap{},
|
||||
SharedGroups: utils.StringMap{},
|
||||
Uuid: as1[0].Balance.Uuid,
|
||||
Directions: utils.NewStringMap(utils.OUT),
|
||||
Value: 10,
|
||||
Weight: 10,
|
||||
DestinationIds: utils.StringMap{},
|
||||
TimingIDs: utils.StringMap{},
|
||||
SharedGroups: utils.StringMap{},
|
||||
Categories: utils.StringMap{},
|
||||
},
|
||||
},
|
||||
&Action{
|
||||
@@ -846,10 +853,11 @@ func TestLoadActions(t *testing.T) {
|
||||
DestinationIds: utils.NewStringMap("NAT"),
|
||||
TimingIDs: utils.StringMap{},
|
||||
SharedGroups: utils.StringMap{},
|
||||
Categories: utils.StringMap{},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(as1[1], expected[1]) {
|
||||
if !reflect.DeepEqual(as1, expected) {
|
||||
t.Errorf("Error loading action1: %+v", as1[0].Balance)
|
||||
}
|
||||
as2 := csvr.actions["SHARED"]
|
||||
@@ -868,10 +876,11 @@ func TestLoadActions(t *testing.T) {
|
||||
Weight: 10,
|
||||
SharedGroups: utils.NewStringMap("SG1"),
|
||||
TimingIDs: utils.StringMap{},
|
||||
Categories: utils.StringMap{},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(as2[0], expected[0]) {
|
||||
if !reflect.DeepEqual(as2, expected) {
|
||||
t.Errorf("Error loading action: %+v", as2[0].Balance)
|
||||
}
|
||||
as3 := csvr.actions["DEFEE"]
|
||||
@@ -886,7 +895,9 @@ func TestLoadActions(t *testing.T) {
|
||||
Directions: utils.StringMap{},
|
||||
DestinationIds: utils.StringMap{},
|
||||
TimingIDs: utils.StringMap{},
|
||||
Categories: utils.StringMap{},
|
||||
SharedGroups: utils.StringMap{},
|
||||
Blocker: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -982,7 +993,7 @@ func TestLoadLCRs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadActionTimings(t *testing.T) {
|
||||
if len(csvr.actionPlans) != 6 {
|
||||
if len(csvr.actionPlans) != 7 {
|
||||
t.Error("Failed to load action timings: ", len(csvr.actionPlans))
|
||||
}
|
||||
atm := csvr.actionPlans["MORE_MINUTES"]
|
||||
@@ -1070,7 +1081,7 @@ func TestLoadActionTriggers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadAccountActions(t *testing.T) {
|
||||
if len(csvr.accountActions) != 11 {
|
||||
if len(csvr.accountActions) != 12 {
|
||||
t.Error("Failed to load account actions: ", len(csvr.accountActions))
|
||||
}
|
||||
aa := csvr.accountActions["vdf:minitsboy"]
|
||||
|
||||
@@ -173,12 +173,15 @@ func APItoModelAction(as *utils.TPActions) (result []TpAction) {
|
||||
Directions: a.Directions,
|
||||
Units: a.Units,
|
||||
ExpiryTime: a.ExpiryTime,
|
||||
Filter: a.Filter,
|
||||
TimingTags: a.TimingTags,
|
||||
DestinationTags: a.DestinationIds,
|
||||
RatingSubject: a.RatingSubject,
|
||||
Categories: a.Categories,
|
||||
SharedGroups: a.SharedGroups,
|
||||
BalanceWeight: a.BalanceWeight,
|
||||
BalanceBlocker: a.BalanceBlocker,
|
||||
BalanceDisabled: a.BalanceDisabled,
|
||||
ExtraParameters: a.ExtraParameters,
|
||||
Weight: a.Weight,
|
||||
})
|
||||
@@ -231,6 +234,7 @@ func APItoModelActionTrigger(ats *utils.TPActionTriggers) (result []TpActionTrig
|
||||
BalanceRatingSubject: at.BalanceRatingSubject,
|
||||
BalanceCategories: at.BalanceCategories,
|
||||
BalanceSharedGroups: at.BalanceSharedGroups,
|
||||
BalanceBlocker: at.BalanceBlocker,
|
||||
BalanceDisabled: at.BalanceDisabled,
|
||||
MinQueuedItems: at.MinQueuedItems,
|
||||
ActionsTag: at.ActionsId,
|
||||
|
||||
@@ -390,12 +390,15 @@ func (tps TpActions) GetActions() (map[string][]*utils.TPAction, error) {
|
||||
Directions: tpAc.Directions,
|
||||
Units: tpAc.Units,
|
||||
ExpiryTime: tpAc.ExpiryTime,
|
||||
Filter: tpAc.Filter,
|
||||
TimingTags: tpAc.TimingTags,
|
||||
DestinationIds: tpAc.DestinationTags,
|
||||
RatingSubject: tpAc.RatingSubject,
|
||||
Categories: tpAc.Categories,
|
||||
SharedGroups: tpAc.SharedGroups,
|
||||
BalanceWeight: tpAc.BalanceWeight,
|
||||
BalanceBlocker: tpAc.BalanceBlocker,
|
||||
BalanceDisabled: tpAc.BalanceDisabled,
|
||||
ExtraParameters: tpAc.ExtraParameters,
|
||||
Weight: tpAc.Weight,
|
||||
}
|
||||
@@ -437,6 +440,7 @@ func (tps TpActionTriggers) GetActionTriggers() (map[string][]*utils.TPActionTri
|
||||
BalanceRatingSubject: tpAt.BalanceRatingSubject,
|
||||
BalanceCategories: tpAt.BalanceCategories,
|
||||
BalanceSharedGroups: tpAt.BalanceSharedGroups,
|
||||
BalanceBlocker: tpAt.BalanceBlocker,
|
||||
BalanceDisabled: tpAt.BalanceDisabled,
|
||||
Weight: tpAt.Weight,
|
||||
ActionsId: tpAt.ActionsTag,
|
||||
|
||||
@@ -265,8 +265,8 @@ func TestTPActionsAsExportSlice(t *testing.T) {
|
||||
},
|
||||
}
|
||||
expectedSlc := [][]string{
|
||||
[]string{"TEST_ACTIONS", "*topup_reset", "", "", "*monetary", utils.OUT, "call", "*any", "special1", "GROUP1", "*never", "", "5", "10", "false", "10"},
|
||||
[]string{"TEST_ACTIONS", "*http_post", "http://localhost/¶m1=value1", "", "", "", "", "", "", "", "", "", "0", "0", "false", "20"},
|
||||
[]string{"TEST_ACTIONS", "*topup_reset", "", "", "", "*monetary", utils.OUT, "call", "*any", "special1", "GROUP1", "*never", "", "5", "10", "false", "false", "10"},
|
||||
[]string{"TEST_ACTIONS", "*http_post", "http://localhost/¶m1=value1", "", "", "", "", "", "", "", "", "", "", "0", "0", "false", "false", "20"},
|
||||
}
|
||||
|
||||
ms := APItoModelAction(tpActs)
|
||||
@@ -567,6 +567,7 @@ func TestTPActionPlanAsExportSlice(t *testing.T) {
|
||||
BalanceRatingSubject: "special1",
|
||||
BalanceCategories: "call",
|
||||
BalanceSharedGroups: "SHARED_1",
|
||||
BalanceBlocker: false,
|
||||
BalanceDisabled: false,
|
||||
MinQueuedItems: 0,
|
||||
ActionsId: "LOG_WARNING",
|
||||
@@ -588,6 +589,7 @@ func TestTPActionPlanAsExportSlice(t *testing.T) {
|
||||
BalanceRatingSubject: "special1",
|
||||
BalanceCategories: "call",
|
||||
BalanceSharedGroups: "SHARED_1",
|
||||
BalanceBlocker: false,
|
||||
BalanceDisabled: false,
|
||||
MinQueuedItems: 0,
|
||||
ActionsId: "LOG_WARNING",
|
||||
@@ -595,8 +597,8 @@ func TestTPActionPlanAsExportSlice(t *testing.T) {
|
||||
},
|
||||
}
|
||||
expectedSlc := [][]string{
|
||||
[]string{"STANDARD_TRIGGERS", "1", "*min_balance", "2", "false", "0", "b1", "*monetary", "*out", "call", "", "special1", "SHARED_1", "*never", "T1", "0", "false", "0", "LOG_WARNING", "10"},
|
||||
[]string{"STANDARD_TRIGGERS", "2", "*max_event_counter", "5", "false", "0", "b2", "*monetary", "*out", "call", "FS_USERS", "special1", "SHARED_1", "*never", "T1", "0", "false", "0", "LOG_WARNING", "10"},
|
||||
[]string{"STANDARD_TRIGGERS", "1", "*min_balance", "2", "false", "0", "b1", "*monetary", "*out", "call", "", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"},
|
||||
[]string{"STANDARD_TRIGGERS", "2", "*max_event_counter", "5", "false", "0", "b2", "*monetary", "*out", "call", "FS_USERS", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"},
|
||||
}
|
||||
ms := APItoModelActionTrigger(at)
|
||||
var slc [][]string
|
||||
|
||||
@@ -156,19 +156,21 @@ type TpAction struct {
|
||||
Tag string `index:"0" re:"\w+\s*"`
|
||||
Action string `index:"1" re:"\*\w+\s*"`
|
||||
ExtraParameters string `index:"2" re:"\S+\s*"`
|
||||
BalanceTag string `index:"3" re:"\w+\s*"`
|
||||
BalanceType string `index:"4" re:"\*\w+\s*"`
|
||||
Directions string `index:"5" re:""`
|
||||
Categories string `index:"6" re:""`
|
||||
DestinationTags string `index:"7" re:"\*any|\w+\s*"`
|
||||
RatingSubject string `index:"8" re:"\w+\s*"`
|
||||
SharedGroups string `index:"9" re:"[0-9A-Za-z_;]*"`
|
||||
ExpiryTime string `index:"10" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"`
|
||||
TimingTags string `index:"11" re:"[0-9A-Za-z_;]*|\*any"`
|
||||
Units float64 `index:"12" re:"\d+\s*"`
|
||||
BalanceWeight float64 `index:"13" re:"\d+\.?\d*\s*"`
|
||||
BalanceDisabled bool `index:"14" re:""`
|
||||
Weight float64 `index:"15" re:"\d+\.?\d*\s*"`
|
||||
Filter string `index:"3" re:"\S+\s*"`
|
||||
BalanceTag string `index:"4" re:"\w+\s*"`
|
||||
BalanceType string `index:"5" re:"\*\w+\s*"`
|
||||
Directions string `index:"6" re:""`
|
||||
Categories string `index:"7" re:""`
|
||||
DestinationTags string `index:"8" re:"\*any|\w+\s*"`
|
||||
RatingSubject string `index:"9" re:"\w+\s*"`
|
||||
SharedGroups string `index:"10" re:"[0-9A-Za-z_;]*"`
|
||||
ExpiryTime string `index:"11" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"`
|
||||
TimingTags string `index:"12" re:"[0-9A-Za-z_;]*|\*any"`
|
||||
Units float64 `index:"13" re:"\d+\s*"`
|
||||
BalanceWeight float64 `index:"14" re:"\d+\.?\d*\s*"`
|
||||
BalanceBlocker bool `index:"15" re:""`
|
||||
BalanceDisabled bool `index:"16" re:""`
|
||||
Weight float64 `index:"17" re:"\d+\.?\d*\s*"`
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
@@ -201,10 +203,11 @@ type TpActionTrigger struct {
|
||||
BalanceExpiryTime string `index:"13" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"`
|
||||
BalanceTimingTags string `index:"14" re:"[0-9A-Za-z_;]*|\*any"`
|
||||
BalanceWeight float64 `index:"15" re:"\d+\.?\d*"`
|
||||
BalanceDisabled bool `index:"16" re:""`
|
||||
MinQueuedItems int `index:"17" re:"\d+"`
|
||||
ActionsTag string `index:"18" re:"\w+"`
|
||||
Weight float64 `index:"19" re:"\d+\.?\d*"`
|
||||
BalanceBlocker bool `index:"16" re:""`
|
||||
BalanceDisabled bool `index:"17" re:""`
|
||||
MinQueuedItems int `index:"18" re:"\d+"`
|
||||
ActionsTag string `index:"19" re:"\w+"`
|
||||
Weight float64 `index:"20" re:"\d+\.?\d*"`
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ func TestDifferentUuid(t *testing.T) {
|
||||
|
||||
func TestStorageTask(t *testing.T) {
|
||||
// clean previous unused tasks
|
||||
for i := 0; i < 16; i++ {
|
||||
for i := 0; i < 18; i++ {
|
||||
ratingStorage.PopTask()
|
||||
}
|
||||
|
||||
|
||||
@@ -513,6 +513,7 @@ func (tpr *TpReader) LoadActions() (err error) {
|
||||
Weight: tpact.Weight,
|
||||
ExtraParameters: tpact.ExtraParameters,
|
||||
ExpirationString: tpact.ExpiryTime,
|
||||
Filter: tpact.Filter,
|
||||
Balance: &Balance{
|
||||
Id: tpact.BalanceId,
|
||||
Value: tpact.Units,
|
||||
@@ -523,6 +524,8 @@ func (tpr *TpReader) LoadActions() (err error) {
|
||||
DestinationIds: utils.ParseStringMap(tpact.DestinationIds),
|
||||
SharedGroups: utils.ParseStringMap(tpact.SharedGroups),
|
||||
TimingIDs: utils.ParseStringMap(tpact.TimingTags),
|
||||
Blocker: tpact.BalanceBlocker,
|
||||
Disabled: tpact.BalanceDisabled,
|
||||
},
|
||||
}
|
||||
// load action timings from tags
|
||||
@@ -640,6 +643,8 @@ func (tpr *TpReader) LoadActionTriggers() (err error) {
|
||||
BalanceRatingSubject: atr.BalanceRatingSubject,
|
||||
BalanceCategories: utils.ParseStringMap(atr.BalanceCategories),
|
||||
BalanceSharedGroups: utils.ParseStringMap(atr.BalanceSharedGroups),
|
||||
BalanceBlocker: atr.BalanceBlocker,
|
||||
BalanceDisabled: atr.BalanceDisabled,
|
||||
Weight: atr.Weight,
|
||||
ActionsId: atr.ActionsId,
|
||||
MinQueuedItems: atr.MinQueuedItems,
|
||||
@@ -788,9 +793,12 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error
|
||||
BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds),
|
||||
BalanceWeight: apiAtr.BalanceWeight,
|
||||
BalanceExpirationDate: expTime,
|
||||
BalanceTimingTags: utils.ParseStringMap(apiAtr.BalanceTimingTags),
|
||||
BalanceRatingSubject: apiAtr.BalanceRatingSubject,
|
||||
BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories),
|
||||
BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups),
|
||||
BalanceBlocker: apiAtr.BalanceBlocker,
|
||||
BalanceDisabled: apiAtr.BalanceDisabled,
|
||||
Weight: apiAtr.Weight,
|
||||
ActionsId: apiAtr.ActionsId,
|
||||
}
|
||||
@@ -830,14 +838,19 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error
|
||||
Weight: tpact.Weight,
|
||||
ExtraParameters: tpact.ExtraParameters,
|
||||
ExpirationString: tpact.ExpiryTime,
|
||||
Filter: tpact.Filter,
|
||||
Balance: &Balance{
|
||||
Id: tpact.BalanceId,
|
||||
Value: tpact.Units,
|
||||
Weight: tpact.BalanceWeight,
|
||||
RatingSubject: tpact.RatingSubject,
|
||||
Categories: utils.ParseStringMap(tpact.Categories),
|
||||
Directions: utils.ParseStringMap(tpact.Directions),
|
||||
DestinationIds: utils.ParseStringMap(tpact.DestinationIds),
|
||||
SharedGroups: utils.ParseStringMap(tpact.SharedGroups),
|
||||
TimingIDs: utils.ParseStringMap(tpact.TimingTags),
|
||||
Blocker: tpact.BalanceBlocker,
|
||||
Disabled: tpact.BalanceDisabled,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1055,14 +1068,19 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) {
|
||||
Weight: tpact.Weight,
|
||||
ExtraParameters: tpact.ExtraParameters,
|
||||
ExpirationString: tpact.ExpiryTime,
|
||||
Filter: tpact.Filter,
|
||||
Balance: &Balance{
|
||||
Id: tpact.BalanceId,
|
||||
Value: tpact.Units,
|
||||
Weight: tpact.BalanceWeight,
|
||||
RatingSubject: tpact.RatingSubject,
|
||||
Categories: utils.ParseStringMap(tpact.Categories),
|
||||
Directions: utils.ParseStringMap(tpact.Directions),
|
||||
DestinationIds: utils.ParseStringMap(tpact.DestinationIds),
|
||||
SharedGroups: utils.ParseStringMap(tpact.SharedGroups),
|
||||
TimingIDs: utils.ParseStringMap(tpact.TimingTags),
|
||||
Blocker: tpact.BalanceBlocker,
|
||||
Disabled: tpact.BalanceDisabled,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user