diff --git a/engine/account.go b/engine/account.go index 03224b12d..99875285b 100644 --- a/engine/account.go +++ b/engine/account.go @@ -569,9 +569,9 @@ func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, cou } // Scans the action trigers and execute the actions for which trigger is met -func (ub *Account) ExecuteActionTriggers(a *Action) { - ub.ActionTriggers.Sort() - for _, at := range ub.ActionTriggers { +func (acc *Account) ExecuteActionTriggers(a *Action) { + acc.ActionTriggers.Sort() + for _, at := range acc.ActionTriggers { // sanity check if !strings.Contains(at.ThresholdType, "counter") && !strings.Contains(at.ThresholdType, "balance") { continue @@ -585,45 +585,45 @@ func (ub *Account) ExecuteActionTriggers(a *Action) { continue } if strings.Contains(at.ThresholdType, "counter") { - for _, uc := range ub.UnitCounters { + for _, uc := range acc.UnitCounters { if uc.BalanceType == at.BalanceType && strings.Contains(at.ThresholdType, uc.CounterType[1:]) { - for _, mb := range uc.Balances { + for _, b := range uc.Balances { if strings.HasPrefix(at.ThresholdType, "*max") { - if mb.MatchActionTrigger(at) && mb.GetValue() >= at.ThresholdValue { - at.Execute(ub, nil) + if b.MatchActionTrigger(at) && b.GetValue() >= at.ThresholdValue { + at.Execute(acc, nil) } } else { //MIN - if mb.MatchActionTrigger(at) && mb.GetValue() <= at.ThresholdValue { - at.Execute(ub, nil) + if b.MatchActionTrigger(at) && b.GetValue() <= at.ThresholdValue { + at.Execute(acc, nil) } } } } } } else { // BALANCE - for _, b := range ub.BalanceMap[at.BalanceType] { + for _, b := range acc.BalanceMap[at.BalanceType] { if !b.dirty && at.ThresholdType != utils.TRIGGER_BALANCE_EXPIRED { // do not check clean balances continue } switch at.ThresholdType { case utils.TRIGGER_MAX_BALANCE: if b.MatchActionTrigger(at) && b.GetValue() >= at.ThresholdValue { - at.Execute(ub, nil) + at.Execute(acc, nil) } case utils.TRIGGER_MIN_BALANCE: if b.MatchActionTrigger(at) && b.GetValue() <= at.ThresholdValue { - at.Execute(ub, nil) + at.Execute(acc, nil) } case utils.TRIGGER_BALANCE_EXPIRED: if b.MatchActionTrigger(at) && b.IsExpired() { - at.Execute(ub, nil) + at.Execute(acc, nil) } } } } } - ub.CleanExpiredBalances() + acc.CleanExpiredBalances() } // Mark all action trigers as ready for execution @@ -656,6 +656,7 @@ func (acc *Account) countUnits(amount float64, kind string, cc *CallCost, b *Bal // Create counters for all triggered actions func (acc *Account) InitCounters() { + oldUcs := acc.UnitCounters acc.UnitCounters = nil ucTempMap := make(map[string]*UnitCounter) for _, at := range acc.ActionTriggers { @@ -682,6 +683,14 @@ func (acc *Account) InitCounters() { uc.Balances = append(uc.Balances, b) } } + // copy old counter values + for _, uc := range acc.UnitCounters { + for _, oldUc := range oldUcs { + if uc.CopyCounterValues(oldUc) { + break + } + } + } } func (acc *Account) CleanExpiredBalances() { diff --git a/engine/action.go b/engine/action.go index 0134cbc3c..00e23bcb5 100644 --- a/engine/action.go +++ b/engine/action.go @@ -553,14 +553,14 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac utils.Logger.Err(fmt.Sprintf("Could not remove account Id: %s: %v", accID, err)) return err } - // clean the account id from all action plans - allAPs, err := ratingStorage.GetAllActionPlans() - if err != nil && err != utils.ErrNotFound { - utils.Logger.Err(fmt.Sprintf("Could not get action plans: %s: %v", accID, err)) - return err - } - var dirtyAps []string - _, err = Guardian.Guard(func() (interface{}, error) { + _, err := Guardian.Guard(func() (interface{}, error) { + // clean the account id from all action plans + allAPs, err := ratingStorage.GetAllActionPlans() + if err != nil && err != utils.ErrNotFound { + utils.Logger.Err(fmt.Sprintf("Could not get action plans: %s: %v", accID, err)) + return 0, err + } + var dirtyAps []string for key, ap := range allAPs { if _, exists := ap.AccountIDs[accID]; !exists { // save action plan @@ -580,7 +580,6 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac if err != nil { return err } - // TODO: no scheduler reload? return nil } diff --git a/engine/units_counter.go b/engine/units_counter.go index a25602504..77aa21f31 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -27,6 +27,23 @@ type UnitCounter struct { Balances BalanceChain // first balance is the general one (no destination) } +// Returns true if the counters were of the same type +// Copies the value from old balances +func (uc *UnitCounter) CopyCounterValues(oldUc *UnitCounter) bool { + if uc.BalanceType+uc.CounterType != oldUc.BalanceType+oldUc.CounterType { // type check + return false + } + for _, b := range uc.Balances { + for _, oldB := range oldUc.Balances { + if b.Equal(oldB) { + b.Value = oldB.Value + break + } + } + } + return true +} + type UnitCounters []*UnitCounter func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *Balance) { diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index f136c23b7..aeee72cdf 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -246,6 +246,92 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { } } +func TestUnitCountersKeepValuesAfterInit(t *testing.T) { + a := &Account{ + ActionTriggers: ActionTriggers{ + &ActionTrigger{ + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + BalanceType: utils.MONETARY, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + BalanceType: utils.MONETARY, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 20, + }, + &ActionTrigger{ + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + BalanceType: utils.VOICE, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceDestinationIds: utils.NewStringMap("NAT"), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR22", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + BalanceType: utils.VOICE, + BalanceDestinationIds: utils.NewStringMap("RET"), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + BalanceType: utils.VOICE, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + BalanceType: utils.SMS, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + BalanceType: utils.SMS, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 10, + }, + }, + } + a.InitCounters() + a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) + + if len(a.UnitCounters) != 4 || + len(a.UnitCounters[1].Balances) != 2 || + a.UnitCounters[1].Balances[0].Value != 10 || + a.UnitCounters[1].Balances[1].Value != 10 { + for _, uc := range a.UnitCounters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Balances { + t.Logf("B: %+v", b) + } + } + t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) + } + a.InitCounters() + + if len(a.UnitCounters) != 4 || + len(a.UnitCounters[1].Balances) != 2 || + a.UnitCounters[1].Balances[0].Value != 10 || + a.UnitCounters[1].Balances[1].Value != 10 { + for _, uc := range a.UnitCounters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Balances { + t.Logf("B: %+v", b) + } + } + t.Errorf("Error keeping counter values after init: %v", len(a.UnitCounters)) + } +} + func TestUnitCountersResetCounterById(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{