diff --git a/apier/apier.go b/apier/apier.go index 2c6cea4cc..46d534dd4 100644 --- a/apier/apier.go +++ b/apier/apier.go @@ -238,8 +238,8 @@ func (self *Apier) AddAccount(attr *AttrAccount, reply *float64) error { } type AttrSetAccountAction struct { - TPid string - RateProfileId string + TPid string + AccountActionId string } // Process dependencies and load a specific rating profile from storDb into dataDb. @@ -249,8 +249,15 @@ func (self *Apier) SetAccountAction(attrs AttrSetAccountAction, reply *string) e } dbReader := rater.NewDbReader(self.StorDb, self.DataDb, attrs.TPid) - if err := dbReader.LoadAccountActionByTag(attrs.RateProfileId); err != nil { - return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + rater.AccLock.Guard(attrs.AccountActionId, func() (float64, error) { + if err := dbReader.LoadAccountActionsByTag(attrs.AccountActionId); err != nil { + return 0, fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } + return 0, nil + }) + if self.Sched != nil { + self.Sched.LoadActionTimings(self.DataDb) + self.Sched.Restart() } *reply = "OK" return nil diff --git a/docs/apicalls.rst b/docs/apicalls.rst index 1c6a95d32..7a93a2d76 100644 --- a/docs/apicalls.rst +++ b/docs/apicalls.rst @@ -331,10 +331,65 @@ AddAcount Example AddAccount(attr \*AttrAccount, reply \*float64) +Apier.SetAccountAction +++++++++++++++++++++++ + +Process dependencies and load a specific rating profile from storDb into dataDb. + +**Request**: + + Data: + :: + + type AttrSetAccountAction struct { + TPid string + AccountActionId string + } + + Mandatory parameters: ``[]string{"TPid", "AccountActionId"}`` + + *JSON sample*: + :: + + { + "id": 0, + "method": "Apier.SetAccountAction", + "params": [ + { + "AccountActionId": "ACC_SAMPLE_1", + "TPid": "TPID_SAMPLE_1" + } + ] + } + +**Reply**: + + Data: + :: + + string + + Possible answers: + ``OK`` - Success. + + *JSON sample*: + :: + + { + "error": null, + "id": 0, + "result": "OK" + } + +**Errors**: + + ``MANDATORY_IE_MISSING`` - Mandatory parameter missing from request. + + ``SERVER_ERROR`` - Server error occurred. + RatingProfiles ~~~~~~~~~~~~~~ - .. toctree:: :maxdepth: 2 diff --git a/rater/action.go b/rater/action.go index 5c602a0a8..62aa77f42 100644 --- a/rater/action.go +++ b/rater/action.go @@ -88,7 +88,7 @@ func logAction(ub *UserBalance, a *Action) (err error) { } func resetTriggersAction(ub *UserBalance, a *Action) (err error) { - ub.resetActionTriggers() + ub.resetActionTriggers(a) return } @@ -180,7 +180,7 @@ func genericReset(ub *UserBalance) { } ub.MinuteBuckets = make([]*MinuteBucket, 0) ub.UnitCounters = make([]*UnitsCounter, 0) - ub.resetActionTriggers() + ub.resetActionTriggers(nil) } // Structure to store actions according to weight diff --git a/rater/actions_test.go b/rater/actions_test.go index 227f68c34..58759e4b2 100644 --- a/rater/actions_test.go +++ b/rater/actions_test.go @@ -415,7 +415,7 @@ func TestActionResetTriggres(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil) @@ -424,13 +424,40 @@ func TestActionResetTriggres(t *testing.T) { } } +func TestActionResetTriggresExecutesThem(t *testing.T) { + ub := &UserBalance{ + Id: "TEST_UB", + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + } + resetTriggersAction(ub, nil) + if ub.ActionTriggers[0].Executed == true || ub.BalanceMap[CREDIT][0].Value == 12 { + t.Error("Reset triggers action failed!") + } +} + +func TestActionResetTriggresActionFilter(t *testing.T) { + ub := &UserBalance{ + Id: "TEST_UB", + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + } + resetTriggersAction(ub, &Action{BalanceId: SMS}) + if ub.ActionTriggers[0].Executed == false || ub.ActionTriggers[1].Executed == false { + t.Error("Reset triggers action failed!") + } +} + func TestActionSetPostpaid(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } setPostpaidAction(ub, nil) @@ -445,7 +472,7 @@ func TestActionSetPrepaid(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } setPrepaidAction(ub, nil) @@ -460,8 +487,8 @@ func TestActionResetPrepaid(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetPrepaidAction(ub, nil) if ub.Type != UB_TYPE_PREPAID || @@ -479,8 +506,8 @@ func TestActionResetPostpaid(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, - ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, + ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetPostpaidAction(ub, nil) if ub.Type != UB_TYPE_POSTPAID || @@ -498,7 +525,7 @@ func TestActionTopupResetCredit(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10} @@ -518,7 +545,7 @@ func TestActionTopupResetMinutes(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationId: "NAT"}} @@ -539,7 +566,7 @@ func TestActionTopupCredit(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10} @@ -559,7 +586,7 @@ func TestActionTopupMinutes(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: MINUTES, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationId: "NAT"}} @@ -580,7 +607,7 @@ func TestActionDebitCredit(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10} @@ -600,7 +627,7 @@ func TestActionDebitMinutes(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: MINUTES, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationId: "NAT"}} @@ -621,7 +648,7 @@ func TestActionResetAllCounters(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetCountersAction(ub, nil) @@ -648,7 +675,7 @@ func TestActionResetCounterMinutes(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: MINUTES} @@ -676,7 +703,7 @@ func TestActionResetCounterCREDIT(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}, &UnitsCounter{BalanceId: SMS, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: CREDIT, Direction: OUTBOUND} diff --git a/rater/loader_csv.go b/rater/loader_csv.go index 713084112..3d9a19270 100644 --- a/rater/loader_csv.go +++ b/rater/loader_csv.go @@ -394,17 +394,10 @@ func (csvr *CSVReader) LoadActions() (err error) { ExpirationDate: expDate, } } else { - price, percent := 0.0, 0.0 value, err := strconv.ParseFloat(record[8], 64) if err != nil { return errors.New(fmt.Sprintf("Could not parse action price: %v", err)) } - if record[7] == PERCENT { - percent = value - } - if record[7] == ABSOLUTE { - price = value - } minutesWeight, err := strconv.ParseFloat(record[9], 64) if err != nil { return errors.New(fmt.Sprintf("Could not parse action minutes weight: %v", err)) @@ -423,8 +416,8 @@ func (csvr *CSVReader) LoadActions() (err error) { MinuteBucket: &MinuteBucket{ Seconds: units, Weight: minutesWeight, - Price: price, - Percent: percent, + Price: value, + PriceType: record[7], DestinationId: record[6], ExpirationDate: expDate, }, diff --git a/rater/loader_db.go b/rater/loader_db.go index 95bc21fb2..add07a8b1 100644 --- a/rater/loader_db.go +++ b/rater/loader_db.go @@ -313,7 +313,7 @@ func (dbr *DbReader) LoadAccountActions() (err error) { tag := fmt.Sprintf("%s:%s:%s", aa.Direction, aa.Tenant, aa.Account) aTriggers, exists := dbr.actionsTriggers[aa.ActionTriggersTag] if aa.ActionTriggersTag != "" && !exists { - // only return error if there was something ther for the tag + // only return error if there was something there for the tag return errors.New(fmt.Sprintf("Could not get action triggers for tag %v", aa.ActionTriggersTag)) } ub := &UserBalance{ @@ -341,27 +341,84 @@ func (dbr *DbReader) LoadAccountActionsByTag(tag string) error { return err } accountAction := accountActions[0] - actionTimingsMap, err := dbr.storDb.GetTpActionTimings(dbr.tpid, accountAction.ActionTimingsTag) - if err != nil { - return err - } - actionTriggersMap, err := dbr.storDb.GetTpActionTriggers(dbr.tpid, accountAction.ActionTriggersTag) - if err != nil { - return err - } + id := fmt.Sprintf("%s:%s:%s", accountAction.Direction, accountAction.Tenant, accountAction.Account) - // collecting action ids - var actionsIds []string - for _, atms := range actionTimingsMap { - for _, atm := range atms { - actionsIds = append(actionsIds, atm.ActionsId) + var actionsIds []string // collects action ids + + // action timings + if accountAction.ActionTimingsTag != "" { + // get old userBalanceIds + var exitingUserBalanceIds []string + exitingActionTimings, err := dbr.dataDb.GetActionTimings(accountAction.ActionTimingsTag) + if err == nil && len(exitingActionTimings) > 0 { + // all action timings from a specific tag shuld have the same list of user balances from the first one + exitingUserBalanceIds = exitingActionTimings[0].UserBalanceIds + } + + actionTimingsMap, err := dbr.storDb.GetTpActionTimings(dbr.tpid, accountAction.ActionTimingsTag) + if err != nil { + return err + } + var actionTimings []*ActionTiming + for _, at := range actionTimingsMap[accountAction.ActionTimingsTag] { + _, exists := dbr.actions[at.ActionsId] + if !exists { + return errors.New(fmt.Sprintf("ActionTiming: Could not load the action for tag: %v", at.ActionsId)) + } + t, exists := dbr.timings[at.Tag] + if !exists { + return errors.New(fmt.Sprintf("ActionTiming: Could not load the timing for tag: %v", at.Tag)) + } + actTmg := &ActionTiming{ + Id: utils.GenUUID(), + Tag: at.Tag, + Weight: at.Weight, + Timing: &Interval{ + Months: t.Months, + MonthDays: t.MonthDays, + WeekDays: t.WeekDays, + StartTime: t.StartTime, + }, + ActionsId: at.ActionsId, + } + // collect action ids from timings + actionsIds = append(actionsIds, actTmg.ActionsId) + //add user balance id if no already in + found := false + for _, ubId := range exitingUserBalanceIds { + if ubId == id { + found = true + break + } + } + if !found { + at.UserBalanceIds = append(exitingUserBalanceIds, id) + } + actionTimings = append(actionTimings, actTmg) + } + + // write action timings + err = dbr.dataDb.SetActionTimings(accountAction.ActionTimingsTag, actionTimings) + if err != nil { + return err } } - for _, atrs := range actionTriggersMap { - for _, atr := range atrs { + + // action triggers + var actionTriggers ActionTriggerPriotityList + if accountAction.ActionTriggersTag != "" { + actionTriggersMap, err := dbr.storDb.GetTpActionTriggers(dbr.tpid, accountAction.ActionTriggersTag) + if err != nil { + return err + } + actionTriggers = actionTriggersMap[accountAction.ActionTriggersTag] + // collect action ids from triggers + for _, atr := range actionTriggers { actionsIds = append(actionsIds, atr.ActionsId) } } + + // actions var acts map[string][]*Action for _, actId := range actionsIds { actions, err := dbr.storDb.GetTpActions(dbr.tpid, actId) @@ -372,79 +429,19 @@ func (dbr *DbReader) LoadAccountActionsByTag(tag string) error { acts[id] = act } } - - // write action timings - for k, atms := range actionTimingsMap { - err = dbr.storDb.SetActionTimings(k, atms) - if err != nil { - return err - } - } // write actions for k, as := range acts { - err = dbr.storDb.SetActions(k, as) + err = dbr.dataDb.SetActions(k, as) if err != nil { return err } } - activationPeriods := make(map[string]*ActivationPeriod) - resultRatingProfile := &RatingProfile{Id: tag} - rpm, err := dbr.storDb.GetTpRatingProfiles(dbr.tpid, tag) + ub, err := dbr.dataDb.GetUserBalance(id) if err != nil { return err - } else if len(rpm) == 0 { - return fmt.Errorf("No RateProfile with id: %s", tag) - } - for _, ratingProfile := range rpm { - resultRatingProfile.FallbackKey = ratingProfile.FallbackKey // it will be the last fallback key - at := time.Unix(ratingProfile.activationTime, 0) - drtm, err := dbr.storDb.GetTpDestinationRateTimings(dbr.tpid, ratingProfile.destRatesTimingTag) - if err != nil { - return err - } else if len(drtm) == 0 { - return fmt.Errorf("No DestRateTimings profile with id: %s", ratingProfile.destRatesTimingTag) - } - for _, destrateTiming := range drtm { - tm, err := dbr.storDb.GetTpTimings(dbr.tpid, destrateTiming.TimingsTag) - if err != nil { - return err - } else if len(tm) == 0 { - return fmt.Errorf("No Timings profile with id: %s", destrateTiming.TimingsTag) - } - destrateTiming.timing = tm[destrateTiming.TimingsTag] - drm, err := dbr.storDb.GetTpDestinationRates(dbr.tpid, destrateTiming.DestinationRatesTag) - if err != nil { - return err - } else if len(drm) == 0 { - return fmt.Errorf("No Timings profile with id: %s", destrateTiming.DestinationRatesTag) - } - for _, drate := range drm[destrateTiming.DestinationRatesTag] { - rt, err := dbr.storDb.GetTpRates(dbr.tpid, drate.RateTag) - if err != nil { - return err - } else if len(rt) == 0 { - return fmt.Errorf("No Rates profile with id: %s", drate.RateTag) - } - drate.Rate = rt[drate.RateTag] - if _, exists := activationPeriods[destrateTiming.Tag]; !exists { - activationPeriods[destrateTiming.Tag] = &ActivationPeriod{} - } - activationPeriods[destrateTiming.Tag].AddIntervalIfNotPresent(destrateTiming.GetInterval(drate)) - dm, err := dbr.storDb.GetTpDestinations(dbr.tpid, drate.DestinationsTag) - if err != nil { - return err - } - for _, destination := range dm { - ap := activationPeriods[ratingProfile.destRatesTimingTag] - newAP := &ActivationPeriod{ActivationTime: at} - newAP.Intervals = append(newAP.Intervals, ap.Intervals...) - resultRatingProfile.AddActivationPeriodIfNotPresent(destination.Id, newAP) - dbr.dataDb.SetDestination(destination) - } - } - } } + ub.ActionTriggers = actionTriggers - return dbr.dataDb.SetRatingProfile(resultRatingProfile) + return dbr.dataDb.SetUserBalance(ub) } diff --git a/rater/minute_buckets.go b/rater/minute_buckets.go index 8ad1d3293..c525834f0 100644 --- a/rater/minute_buckets.go +++ b/rater/minute_buckets.go @@ -27,13 +27,18 @@ import ( type MinuteBucket struct { Seconds float64 Weight float64 - Price float64 - Percent float64 // percentage from standard price + Price float64 // percentage from standard price or absolute value depending on Type + PriceType string DestinationId string ExpirationDate time.Time precision int } +const ( + PERCENT = "PERCENT" + ABSOLUTE = "ABSOLUTE" +) + // Returns the available number of seconds for a specified credit func (mb *MinuteBucket) GetSecondsForCredit(credit float64) (seconds float64) { seconds = mb.Seconds @@ -49,7 +54,7 @@ func (mb *MinuteBucket) Clone() *MinuteBucket { Seconds: mb.Seconds, Weight: mb.Weight, Price: mb.Price, - Percent: mb.Percent, + PriceType: mb.PriceType, DestinationId: mb.DestinationId, } } @@ -59,7 +64,7 @@ func (mb *MinuteBucket) Equal(o *MinuteBucket) bool { return mb.DestinationId == o.DestinationId && mb.Weight == o.Weight && mb.Price == o.Price && - mb.Percent == o.Percent + mb.PriceType == o.PriceType } func (mb *MinuteBucket) IsExpired() bool { diff --git a/rater/minute_buckets_test.go b/rater/minute_buckets_test.go index 7ae16173d..128426d2f 100644 --- a/rater/minute_buckets_test.go +++ b/rater/minute_buckets_test.go @@ -57,16 +57,16 @@ func TestMinutBucketSortPrice(t *testing.T) { } func TestMinutBucketEqual(t *testing.T) { - mb1 := &MinuteBucket{Weight: 1, precision: 1, Price: 1, Percent: 1, DestinationId: ""} - mb2 := &MinuteBucket{Weight: 1, precision: 1, Price: 1, Percent: 1, DestinationId: ""} - mb3 := &MinuteBucket{Weight: 1, precision: 1, Price: 2, Percent: 1, DestinationId: ""} + mb1 := &MinuteBucket{Weight: 1, precision: 1, Price: 1, PriceType: ABSOLUTE, DestinationId: ""} + mb2 := &MinuteBucket{Weight: 1, precision: 1, Price: 1, PriceType: ABSOLUTE, DestinationId: ""} + mb3 := &MinuteBucket{Weight: 1, precision: 1, Price: 2, PriceType: ABSOLUTE, DestinationId: ""} if !mb1.Equal(mb2) || mb2.Equal(mb3) { t.Error("Equal failure!", mb1, mb2, mb3) } } func TestMinutBucketClone(t *testing.T) { - mb1 := &MinuteBucket{Seconds: 1, Weight: 2, Price: 3, Percent: 4, DestinationId: "5"} + mb1 := &MinuteBucket{Seconds: 1, Weight: 2, Price: 3, PriceType: ABSOLUTE, DestinationId: "5"} mb2 := mb1.Clone() if mb1 == mb2 || !reflect.DeepEqual(mb1, mb2) { t.Error("Cloning failure: ", mb1, mb2) diff --git a/rater/simple_serializer.go b/rater/simple_serializer.go index a9d46da05..46969f287 100644 --- a/rater/simple_serializer.go +++ b/rater/simple_serializer.go @@ -522,7 +522,7 @@ func (mb *MinuteBucket) Store() (result string, err error) { result += strconv.FormatFloat(mb.Seconds, 'f', -1, 64) + ";" result += strconv.FormatFloat(mb.Weight, 'f', -1, 64) + ";" result += strconv.FormatFloat(mb.Price, 'f', -1, 64) + ";" - result += strconv.FormatFloat(mb.Percent, 'f', -1, 64) + ";" + result += mb.PriceType + ";" result += mb.DestinationId return } @@ -533,7 +533,7 @@ func (mb *MinuteBucket) Restore(input string) error { mb.Seconds, _ = strconv.ParseFloat(elements[0], 64) mb.Weight, _ = strconv.ParseFloat(elements[1], 64) mb.Price, _ = strconv.ParseFloat(elements[2], 64) - mb.Percent, _ = strconv.ParseFloat(elements[3], 64) + mb.PriceType = elements[3] mb.DestinationId = elements[4] return nil } diff --git a/rater/storage_sql.go b/rater/storage_sql.go index 738371d4e..225e1a446 100644 --- a/rater/storage_sql.go +++ b/rater/storage_sql.go @@ -990,13 +990,7 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er ExpirationDate: expDate, } } else { - var percent, price float64 - if rate_type == PERCENT { - percent = rate - } - if rate_type == ABSOLUTE { - price = rate - } + var price float64 a = &Action{ Id: utils.GenUUID(), ActionType: action, @@ -1008,7 +1002,7 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er Seconds: units, Weight: minutes_weight, Price: price, - Percent: percent, + PriceType: rate_type, DestinationId: destinations_tag, ExpirationDate: expDate, }, diff --git a/rater/units_counter_test.go b/rater/units_counter_test.go index 059a2866b..0ea435fa4 100644 --- a/rater/units_counter_test.go +++ b/rater/units_counter_test.go @@ -28,10 +28,10 @@ func TestUnitsCounterStoreRestore(t *testing.T) { Direction: OUTBOUND, BalanceId: SMS, Units: 100, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, } r, err := uc.Store() - if err != nil || r != "OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET" { + if err != nil || r != "OUT/SMS/100/0;20;1;;NAT,0;10;10;ABSOLUTE;RET" { t.Errorf("Error serializing units counter: %v", string(r)) } o := &UnitsCounter{} @@ -46,7 +46,7 @@ func TestUnitsCounterAddMinuteBucket(t *testing.T) { Direction: OUTBOUND, BalanceId: SMS, Units: 100, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, } uc.addMinutes(20, "test") if len(uc.MinuteBuckets) != 2 { @@ -59,7 +59,7 @@ func TestUnitsCounterAddMinuteBucketExists(t *testing.T) { Direction: OUTBOUND, BalanceId: SMS, Units: 100, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, } uc.addMinutes(5, "0723") if len(uc.MinuteBuckets) != 2 || uc.MinuteBuckets[0].Seconds != 15 { diff --git a/rater/userbalance.go b/rater/userbalance.go index 1701a7419..53e0f6f35 100644 --- a/rater/userbalance.go +++ b/rater/userbalance.go @@ -38,9 +38,6 @@ const ( TRAFFIC = "INTERNET" TRAFFIC_TIME = "INTERNET_TIME" MINUTES = "MINUTES" - // Price types - PERCENT = "PERCENT" - ABSOLUTE = "ABSOLUTE" ) /* @@ -283,7 +280,7 @@ func (ub *UserBalance) debitBalance(balanceId string, amount float64, count bool } // Scans the action trigers and execute the actions for which trigger is met -func (ub *UserBalance) executeActionTriggers() { +func (ub *UserBalance) executeActionTriggers(a *Action) { ub.ActionTriggers.Sort() for _, at := range ub.ActionTriggers { if at.Executed { @@ -291,6 +288,13 @@ func (ub *UserBalance) executeActionTriggers() { // the next reset (see RESET_TRIGGERS action type) continue } + if a != nil && (at.BalanceId != a.BalanceId || + at.Direction != a.Direction || + (a.MinuteBucket != nil && + (at.ThresholdType != a.MinuteBucket.PriceType || + at.ThresholdValue != a.MinuteBucket.Price))) { + continue + } if strings.Contains(at.ThresholdType, "COUNTER") { for _, uc := range ub.UnitCounters { if uc.BalanceId == at.BalanceId { @@ -358,10 +362,19 @@ func (ub *UserBalance) executeActionTriggers() { } // Mark all action trigers as ready for execution -func (ub *UserBalance) resetActionTriggers() { +// If the action is not nil it acts like a filter +func (ub *UserBalance) resetActionTriggers(a *Action) { for _, at := range ub.ActionTriggers { + if a != nil && (at.BalanceId != a.BalanceId || + at.Direction != a.Direction || + (a.MinuteBucket != nil && + (at.ThresholdType != a.MinuteBucket.PriceType || + at.ThresholdValue != a.MinuteBucket.Price))) { + continue + } at.Executed = false } + ub.executeActionTriggers(a) } // Returns the unit counter that matches the specified action type @@ -396,7 +409,7 @@ func (ub *UserBalance) countUnits(a *Action) { } else { unitsCounter.Units += a.Units } - ub.executeActionTriggers() + ub.executeActionTriggers(nil) } // Create minute counters for all triggered actions that have actions operating on minute buckets diff --git a/rater/userbalance_test.go b/rater/userbalance_test.go index a291d5bb8..725cea2e7 100644 --- a/rater/userbalance_test.go +++ b/rater/userbalance_test.go @@ -100,7 +100,7 @@ func TestUserBalanceStoreRestore(t *testing.T) { Direction: OUTBOUND, BalanceId: SMS, Units: 100, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, } at := &ActionTrigger{ Id: "some_uuid", @@ -117,7 +117,7 @@ func TestUserBalanceStoreRestore(t *testing.T) { Id: "rif", Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, UnitCounters: []*UnitsCounter{uc, uc}, ActionTriggers: ActionTriggerPriotityList{at, at, at}, } @@ -361,7 +361,7 @@ func TestUserBalancedebitMinuteBucket(t *testing.T) { Id: "rif", Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{SMS: BalanceChain{&Balance{Value: 14}}, TRAFFIC: BalanceChain{&Balance{Value: 1204}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, } newMb := &MinuteBucket{Weight: 20, Price: 1, DestinationId: "NEW"} ub.debitMinuteBucket(newMb) @@ -376,7 +376,7 @@ func TestUserBalancedebitMinuteBucketExists(t *testing.T) { Id: "rif", Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 15, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 15, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, } newMb := &MinuteBucket{Seconds: -10, Weight: 20, Price: 1, DestinationId: "NAT"} ub.debitMinuteBucket(newMb) @@ -390,7 +390,7 @@ func TestUserBalanceAddMinuteNil(t *testing.T) { Id: "rif", Type: UB_TYPE_POSTPAID, BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, } ub.debitMinuteBucket(nil) if len(ub.MinuteBuckets) != 2 { @@ -422,7 +422,7 @@ func TestUserBalanceExecuteTriggeredActions(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: "MAX_COUNTER", ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(&Action{BalanceId: CREDIT, Units: 1}) @@ -435,7 +435,7 @@ func TestUserBalanceExecuteTriggeredActions(t *testing.T) { t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds) } // we can reset them - ub.resetActionTriggers() + ub.resetActionTriggers(nil) ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}) if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 120 || ub.MinuteBuckets[0].Seconds != 30 { t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds) @@ -447,7 +447,7 @@ func TestUserBalanceExecuteTriggeredActionsBalance(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: "MIN_COUNTER", ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(&Action{BalanceId: CREDIT, Units: 1})