diff --git a/data/tariffplans/testtp/Actions.csv b/data/tariffplans/testtp/Actions.csv index 045fc0666..49f7f0e15 100644 --- a/data/tariffplans/testtp/Actions.csv +++ b/data/tariffplans/testtp/Actions.csv @@ -4,4 +4,5 @@ BONUS_1,*topup,,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,false,10 LOG_BALANCE,*log,,,,,,,,,,,,,,false,false,10 CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,,false,false,10 CDRST_LOG,*log,,,,,,,,,,,,,,false,false,10 -TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 \ No newline at end of file +TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 +EXP,*topup,,,,*voice,*out,,,,,*monthly,*any,300,10,false,false,10 diff --git a/engine/action_plan.go b/engine/action_plan.go index b9c40d519..4c3b2078e 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -303,7 +303,10 @@ func (at *ActionTiming) Execute() (err error) { continue } } - if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && parseErr == nil && !expDate.IsZero() { + if a.Balance == nil { + a.Balance = &BalanceFilter{} + } + if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate } @@ -333,7 +336,7 @@ func (at *ActionTiming) Execute() (err error) { } if len(at.accountIDs) == 0 { // action timing executing without accounts for _, a := range aac { - if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && + if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.EmptyExpirationDate()) && parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate diff --git a/engine/action_trigger.go b/engine/action_trigger.go index dc3a5423c..8e1dac8fd 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -80,10 +80,11 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro if a.Balance == nil { a.Balance = &BalanceFilter{} } - if a.ExpirationString != "" { + if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} - *a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) + *a.Balance.ExpirationDate = expDate } + actionFunction, exists := getActionFunc(a.ActionType) if !exists { utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) diff --git a/engine/actions_test.go b/engine/actions_test.go index 416fbc9ac..4cbe95279 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -2055,6 +2055,29 @@ func TestActionCSVFilter(t *testing.T) { } } +func TestActionExpirationTime(t *testing.T) { + if _, err := accountingStorage.GetAccount("cgrates.org:expo"); err != nil { + t.Errorf("account to be removed not found: %v", err) + } + a, err := ratingStorage.GetActions("EXP", false) + if err != nil || a == nil { + t.Error("Error getting actions: ", err) + } + + at := &ActionTiming{ + accountIDs: utils.StringMap{"cgrates.org:expo": true}, + actions: a, + } + for rep := 0; rep < 5; rep++ { + at.Execute() + afterUb, err := accountingStorage.GetAccount("cgrates.org:expo") + if err != nil || + len(afterUb.BalanceMap[utils.VOICE]) != rep+1 { + t.Error("error topuping expiration balance: ", utils.ToIJSON(afterUb)) + } + } +} + /**************** Benchmarks ********************************/ func BenchmarkUUID(b *testing.B) { diff --git a/engine/balance_filter.go b/engine/balance_filter.go index dc832cf0b..f9e408d39 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -274,7 +274,7 @@ func (bp *BalanceFilter) GetFactor() ValueFactor { return *bp.Factor } -func (bp *BalanceFilter) HasExpirationDate() bool { +func (bp *BalanceFilter) EmptyExpirationDate() bool { if bp.ExpirationDate == nil { return true } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 0e0fdc49e..e2bdd5be5 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -175,6 +175,7 @@ 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 FILTER,*topup,,"{""*and"":[{""Value"":{""*lt"":0}},{""Id"":{""*eq"":""*default""}}]}",bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 +EXP,*topup,,,,*voice,*out,,,,,*monthly,*any,300,10,false,false,10 ` actionPlans = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 @@ -186,6 +187,7 @@ TOPUP_SHARED10_AT,SE10,*asap,10 TOPUP_EMPTY_AT,EE0,*asap,10 POST_AT,NEG,*asap,10 BLOCK_AT,BLOCK,*asap,10 +EXP_AT,EXP,*asap,10 ` actionTriggers = ` @@ -213,6 +215,7 @@ vdf,emptyY,TOPUP_EMPTY_AT,,, vdf,post,POST_AT,,, cgrates.org,alodis,TOPUP_EMPTY_AT,,true,true cgrates.org,block,BLOCK_AT,,false,false +cgrates.org,expo,EXP_AT,,false,false ` derivedCharges = ` @@ -815,7 +818,7 @@ func TestLoadRatingProfiles(t *testing.T) { } func TestLoadActions(t *testing.T) { - if len(csvr.actions) != 11 { + if len(csvr.actions) != 12 { t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] @@ -1001,7 +1004,7 @@ func TestLoadLCRs(t *testing.T) { } func TestLoadActionTimings(t *testing.T) { - if len(csvr.actionPlans) != 7 { + if len(csvr.actionPlans) != 8 { t.Error("Failed to load action timings: ", len(csvr.actionPlans)) } atm := csvr.actionPlans["MORE_MINUTES"] @@ -1096,7 +1099,7 @@ func TestLoadActionTriggers(t *testing.T) { } func TestLoadAccountActions(t *testing.T) { - if len(csvr.accountActions) != 12 { + if len(csvr.accountActions) != 13 { t.Error("Failed to load account actions: ", len(csvr.accountActions)) } aa := csvr.accountActions["vdf:minitsboy"] diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 4338a4521..82ac4d14f 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -1128,15 +1128,17 @@ func (tpr *TpReader) LoadAccountActions() (err error) { } ub.InitCounters() tpr.accountActions[aa.KeyId()] = ub - actionPlan, exists := tpr.actionPlans[aa.ActionPlanId] - if !exists { - log.Printf("could not get action plan for tag %v", aa.ActionPlanId) - // must not continue here + if aa.ActionPlanId != "" { + actionPlan, exists := tpr.actionPlans[aa.ActionPlanId] + if !exists { + log.Printf("could not get action plan for tag %v", aa.ActionPlanId) + // must not continue here + } + if actionPlan.AccountIDs == nil { + actionPlan.AccountIDs = make(utils.StringMap) + } + actionPlan.AccountIDs[aa.KeyId()] = true } - if actionPlan.AccountIDs == nil { - actionPlan.AccountIDs = make(utils.StringMap) - } - actionPlan.AccountIDs[aa.KeyId()] = true } return nil }