value formula passing test

This commit is contained in:
Radu Ioan Fericean
2016-04-22 15:07:05 +03:00
parent 206d33242f
commit 768cdcc43a
17 changed files with 240 additions and 98 deletions

View File

@@ -426,7 +426,7 @@ func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *st
Uuid: attr.BalanceUuid,
ID: attr.BalanceId,
Type: utils.StringPointer(attr.BalanceType),
Value: &engine.ValueFormula{Static: attr.Value},
Value: &utils.ValueFormula{Static: attr.Value},
ExpirationDate: expTime,
RatingSubject: attr.RatingSubject,
Weight: attr.Weight,
@@ -522,7 +522,7 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error {
},
}
if attr.Value != nil {
a.Balance.Value = &engine.ValueFormula{Static: *attr.Value}
a.Balance.Value = &utils.ValueFormula{Static: *attr.Value}
}
if attr.Directions != nil {
a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions))
@@ -582,7 +582,7 @@ func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error {
},
}
if attr.Value != nil {
a.Balance.Value = &engine.ValueFormula{Static: *attr.Value}
a.Balance.Value = &utils.ValueFormula{Static: *attr.Value}
}
if attr.Directions != nil {
a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions))

View File

@@ -504,9 +504,9 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error
}
storeActions := make(engine.Actions, len(attrs.Actions))
for idx, apiAct := range attrs.Actions {
var vf *engine.ValueFormula
var vf *utils.ValueFormula
if apiAct.Units != "" {
if x, err := engine.ParseBalanceFilterValue(apiAct.Units); err == nil {
if x, err := utils.ParseBalanceFilterValue(apiAct.Units); err == nil {
vf = x
} else {
return err

View File

@@ -482,7 +482,7 @@ func (mig MigratorRC8) migrateActions() error {
bf.Type = utils.StringPointer(oldAc.BalanceType)
}
if oldAc.Balance.Value != 0 {
bf.Value = &engine.ValueFormula{Static: oldAc.Balance.Value}
bf.Value = &utils.ValueFormula{Static: oldAc.Balance.Value}
}
if oldAc.Balance.RatingSubject != "" {
bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject)

View File

@@ -364,7 +364,7 @@ func (mig MigratorRC8) migrateActionsInt() error {
bf.Type = utils.StringPointer(oldAc.BalanceType)
}
if oldAc.Balance.Value != 0 {
bf.Value = &engine.ValueFormula{Static: oldAc.Balance.Value}
bf.Value = &utils.ValueFormula{Static: oldAc.Balance.Value}
}
if oldAc.Balance.RatingSubject != "" {
bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject)

View File

@@ -170,6 +170,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error {
return errors.New("nil action")
}
bClone := a.Balance.CreateBalance()
//log.Print("Bclone: ", utils.ToJSON(a.Balance))
if bClone == nil {
return errors.New("nil balance")
}

View File

@@ -856,7 +856,7 @@ func TestAccountdebitBalanceExists(t *testing.T) {
BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14}}, utils.DATA: Balances{&Balance{Value: 1024}}, utils.VOICE: Balances{&Balance{Value: 15, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}},
}
newMb := &BalanceFilter{
Value: &ValueFormula{Static: -10},
Value: &utils.ValueFormula{Static: -10},
Type: utils.StringPointer(utils.VOICE),
Weight: utils.Float64Pointer(20),
DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}),
@@ -883,19 +883,19 @@ func TestAccountAddMinuteNil(t *testing.T) {
func TestAccountAddMinutBucketEmpty(t *testing.T) {
mb1 := &BalanceFilter{
Value: &ValueFormula{Static: -10},
Value: &utils.ValueFormula{Static: -10},
Type: utils.StringPointer(utils.VOICE),
DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}),
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
}
mb2 := &BalanceFilter{
Value: &ValueFormula{Static: -10},
Value: &utils.ValueFormula{Static: -10},
Type: utils.StringPointer(utils.VOICE),
DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}),
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
}
mb3 := &BalanceFilter{
Value: &ValueFormula{Static: -10},
Value: &utils.ValueFormula{Static: -10},
Type: utils.StringPointer(utils.VOICE),
DestinationIDs: utils.StringMapPointer(utils.StringMap{"OTHER": true}),
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),

View File

@@ -291,7 +291,6 @@ func (at *ActionTiming) Execute() (err error) {
transactionFailed := false
removeAccountActionFound := false
for _, a := range aac {
//log.Print("A: ", utils.ToJSON(a))
// check action filter
if len(a.Filter) > 0 {
matched, err := acc.matchActionFilter(a.Filter)

View File

@@ -413,7 +413,7 @@ func TestActionPlanLogFunction(t *testing.T) {
ActionType: "*log",
Balance: &BalanceFilter{
Type: utils.StringPointer("test"),
Value: &ValueFormula{Static: 1.1},
Value: &utils.ValueFormula{Static: 1.1},
},
}
at := &ActionTiming{
@@ -430,7 +430,7 @@ func TestActionPlanFunctionNotAvailable(t *testing.T) {
ActionType: "VALID_FUNCTION_TYPE",
Balance: &BalanceFilter{
Type: utils.StringPointer("test"),
Value: &ValueFormula{Static: 1.1},
Value: &utils.ValueFormula{Static: 1.1},
},
}
at := &ActionTiming{
@@ -659,7 +659,7 @@ func TestActionTriggerMatchAll(t *testing.T) {
Type: utils.StringPointer(utils.MONETARY),
RatingSubject: utils.StringPointer("test1"),
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
Value: &ValueFormula{Static: 2},
Value: &utils.ValueFormula{Static: 2},
Weight: utils.Float64Pointer(1.0),
DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")),
SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")),
@@ -669,7 +669,7 @@ func TestActionTriggerMatchAll(t *testing.T) {
Type: utils.StringPointer(utils.MONETARY),
RatingSubject: utils.StringPointer("test1"),
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
Value: &ValueFormula{Static: 2},
Value: &utils.ValueFormula{Static: 2},
Weight: utils.Float64Pointer(1.0),
DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")),
SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")),
@@ -799,7 +799,7 @@ func TestActionTopupResetCredit(t *testing.T) {
UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}},
ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}},
}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
topupResetAction(ub, nil, a, nil)
if ub.AllowNegative ||
ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 ||
@@ -818,7 +818,7 @@ func TestActionTopupValueFactor(t *testing.T) {
a := &Action{
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.MONETARY),
Value: &ValueFormula{Static: 10},
Value: &utils.ValueFormula{Static: 10},
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
},
ExtraParameters: `{"*monetary":2.0}`,
@@ -839,7 +839,7 @@ func TestActionTopupResetCreditId(t *testing.T) {
},
},
}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("TEST_B"), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("TEST_B"), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
topupResetAction(ub, nil, a, nil)
if ub.AllowNegative ||
ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 ||
@@ -858,7 +858,7 @@ func TestActionTopupResetCreditNoId(t *testing.T) {
},
},
}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
topupResetAction(ub, nil, a, nil)
if ub.AllowNegative ||
ub.BalanceMap[utils.MONETARY].GetTotalValue() != 20 ||
@@ -876,7 +876,7 @@ func TestActionTopupResetMinutes(t *testing.T) {
UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}},
ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}},
}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &utils.ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
topupResetAction(ub, nil, a, nil)
if ub.AllowNegative ||
ub.BalanceMap[utils.VOICE].GetTotalValue() != 5 ||
@@ -895,7 +895,7 @@ func TestActionTopupCredit(t *testing.T) {
UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}},
ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}},
}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
topupAction(ub, nil, a, nil)
if ub.AllowNegative ||
ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 ||
@@ -913,7 +913,7 @@ func TestActionTopupMinutes(t *testing.T) {
UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}},
ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}},
}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &utils.ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
topupAction(ub, nil, a, nil)
if ub.AllowNegative ||
ub.BalanceMap[utils.VOICE].GetTotalValue() != 15 ||
@@ -932,7 +932,7 @@ func TestActionDebitCredit(t *testing.T) {
UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}},
ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}},
}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
debitAction(ub, nil, a, nil)
if ub.AllowNegative ||
ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 ||
@@ -950,7 +950,7 @@ func TestActionDebitMinutes(t *testing.T) {
UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}},
ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}},
}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &utils.ValueFormula{Static: 5}, Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}
debitAction(ub, nil, a, nil)
if ub.AllowNegative ||
ub.BalanceMap[utils.VOICE][0].GetValue() != 5 ||
@@ -1119,7 +1119,7 @@ func TestActionPlanLogging(t *testing.T) {
}
func TestActionMakeNegative(t *testing.T) {
a := &Action{Balance: &BalanceFilter{Value: &ValueFormula{Static: 10}}}
a := &Action{Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 10}}}
genericMakeNegative(a)
if a.Balance.GetValue() > 0 {
t.Error("Failed to make negative: ", a)
@@ -1153,7 +1153,7 @@ func TestTopupAction(t *testing.T) {
initialUb, _ := accountingStorage.GetAccount("vdf:minu")
a := &Action{
ActionType: TOPUP,
Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)},
Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)},
}
at := &ActionTiming{
@@ -1174,7 +1174,7 @@ func TestTopupActionLoaded(t *testing.T) {
initialUb, _ := accountingStorage.GetAccount("vdf:minitsboy")
a := &Action{
ActionType: TOPUP,
Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)},
Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)},
}
at := &ActionTiming{
@@ -1201,7 +1201,7 @@ func TestActionCdrlogEmpty(t *testing.T) {
err := cdrLogAction(acnt, nil, cdrlog, Actions{
&Action{
ActionType: DEBIT,
Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
},
})
if err != nil {
@@ -1223,11 +1223,11 @@ func TestActionCdrlogWithParams(t *testing.T) {
err := cdrLogAction(acnt, nil, cdrlog, Actions{
&Action{
ActionType: DEBIT,
Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
},
&Action{
ActionType: DEBIT_RESET,
Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
},
})
if err != nil {
@@ -1250,11 +1250,11 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) {
err := cdrLogAction(acnt, nil, cdrlog, Actions{
&Action{
ActionType: DEBIT,
Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
},
&Action{
ActionType: DEBIT_RESET,
Balance: &BalanceFilter{Value: &ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)},
},
})
if err != nil {
@@ -1335,11 +1335,11 @@ func TestActionTransactionFuncType(t *testing.T) {
actions: []*Action{
&Action{
ActionType: TOPUP,
Balance: &BalanceFilter{Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY)},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY)},
},
&Action{
ActionType: "VALID_FUNCTION_TYPE",
Balance: &BalanceFilter{Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer("test")},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, Type: utils.StringPointer("test")},
},
},
}
@@ -1371,7 +1371,7 @@ func TestActionTransactionBalanceType(t *testing.T) {
actions: []*Action{
&Action{
ActionType: TOPUP,
Balance: &BalanceFilter{Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY)},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY)},
},
&Action{
ActionType: TOPUP,
@@ -1407,7 +1407,7 @@ func TestActionTransactionBalanceNotType(t *testing.T) {
actions: []*Action{
&Action{
ActionType: TOPUP,
Balance: &BalanceFilter{Value: &ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.VOICE)},
Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.VOICE)},
},
&Action{
ActionType: TOPUP,
@@ -1445,14 +1445,14 @@ func TestActionWithExpireWithoutExpire(t *testing.T) {
ActionType: TOPUP,
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.VOICE),
Value: &ValueFormula{Static: 15},
Value: &utils.ValueFormula{Static: 15},
},
},
&Action{
ActionType: TOPUP,
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.VOICE),
Value: &ValueFormula{Static: 30},
Value: &utils.ValueFormula{Static: 30},
ExpirationDate: utils.TimePointer(time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC)),
},
},
@@ -1672,7 +1672,7 @@ func TestActionConditionalTopup(t *testing.T) {
Filter: `{"Type":"*monetary","Value":1,"Weight":10}`,
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.MONETARY),
Value: &ValueFormula{Static: 11},
Value: &utils.ValueFormula{Static: 11},
Weight: utils.Float64Pointer(30),
},
}
@@ -1736,7 +1736,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) {
Filter: `{"Type":"*monetary","Value":2,"Weight":10}`,
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.MONETARY),
Value: &ValueFormula{Static: 11},
Value: &utils.ValueFormula{Static: 11},
Weight: utils.Float64Pointer(30),
},
}
@@ -1800,7 +1800,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) {
Filter: `{"Type":"*voice","Value":{"*gte":100}}`,
Balance: &BalanceFilter{
Type: utils.StringPointer(utils.MONETARY),
Value: &ValueFormula{Static: 11},
Value: &utils.ValueFormula{Static: 11},
Weight: utils.Float64Pointer(10),
},
}
@@ -2021,7 +2021,7 @@ func TestActionSetBalance(t *testing.T) {
Balance: &BalanceFilter{
ID: utils.StringPointer("m2"),
Type: utils.StringPointer(utils.MONETARY),
Value: &ValueFormula{Static: 11},
Value: &utils.ValueFormula{Static: 11},
Weight: utils.Float64Pointer(10),
},
}
@@ -2122,7 +2122,7 @@ func TestActionCdrlogBalanceValue(t *testing.T) {
Balance: &BalanceFilter{
ID: utils.StringPointer("*default"),
Uuid: utils.StringPointer("25a02c82-f09f-4c6e-bacf-8ed4b076475a"),
Value: &ValueFormula{Static: 1.1},
Value: &utils.ValueFormula{Static: 1.1},
Type: utils.StringPointer(utils.MONETARY),
},
},
@@ -2132,7 +2132,7 @@ func TestActionCdrlogBalanceValue(t *testing.T) {
Balance: &BalanceFilter{
ID: utils.StringPointer("*default"),
Uuid: utils.StringPointer("25a02c82-f09f-4c6e-bacf-8ed4b076475a"),
Value: &ValueFormula{Static: 2.1},
Value: &utils.ValueFormula{Static: 2.1},
Type: utils.StringPointer(utils.MONETARY),
},
},
@@ -2222,6 +2222,22 @@ func TestCgrRpcAction(t *testing.T) {
}
}
func TestValueFormulaDebit(t *testing.T) {
if _, err := accountingStorage.GetAccount("cgrates.org:vf"); err != nil {
t.Errorf("account to be removed not found: %v", err)
}
at := &ActionTiming{
accountIDs: utils.StringMap{"cgrates.org:vf": true},
ActionsID: "VF",
}
at.Execute()
afterUb, err := accountingStorage.GetAccount("cgrates.org:vf")
if err != nil || afterUb.BalanceMap[utils.MONETARY].GetTotalValue() != -0.333334 {
t.Error("error debiting account: ", err, utils.ToIJSON(afterUb))
}
}
/**************** Benchmarks ********************************/
func BenchmarkUUID(b *testing.B) {

View File

@@ -1,10 +1,7 @@
package engine
import (
"encoding/json"
"errors"
"reflect"
"strconv"
"time"
"github.com/cgrates/cgrates/utils"
@@ -14,7 +11,7 @@ type BalanceFilter struct {
Uuid *string
ID *string
Type *string
Value *ValueFormula
Value *utils.ValueFormula
Directions *utils.StringMap
ExpirationDate *time.Time
Weight *float64
@@ -61,7 +58,7 @@ func (bf *BalanceFilter) Clone() *BalanceFilter {
*result.ID = *bf.ID
}
if bf.Value != nil {
result.Value = new(ValueFormula)
result.Value = new(utils.ValueFormula)
*result.Value = *bf.Value
}
if bf.RatingSubject != nil {
@@ -180,7 +177,7 @@ func (bp *BalanceFilter) GetValue() float64 {
return bp.Value.Static
}
// calculate using formula
formula, exists := valueFormulas[bp.Value.Method]
formula, exists := utils.ValueFormulas[bp.Value.Method]
if !exists {
return 0.0
}
@@ -189,7 +186,7 @@ func (bp *BalanceFilter) GetValue() float64 {
func (bp *BalanceFilter) SetValue(v float64) {
if bp.Value == nil {
bp.Value = new(ValueFormula)
bp.Value = new(utils.ValueFormula)
}
bp.Value.Static = v
}
@@ -334,37 +331,3 @@ func (bf *BalanceFilter) ModifyBalance(b *Balance) {
}
b.SetDirty() // Mark the balance as dirty since we have modified and it should be checked by action triggers
}
//for computing a dynamic value for Value field
type ValueFormula struct {
Method string
Params map[string]interface{}
Static float64
}
func ParseBalanceFilterValue(val string) (*ValueFormula, error) {
u, err := strconv.ParseFloat(val, 64)
if err == nil {
return &ValueFormula{Static: u}, err
}
var vf ValueFormula
err = json.Unmarshal([]byte(val), &vf)
if err == nil {
return &vf, err
}
return nil, errors.New("Invalid value: " + val)
}
type valueFormula func(map[string]interface{}) float64
const (
PERIODIC = "*periodic"
)
var valueFormulas = map[string]valueFormula{
PERIODIC: periodicFormula,
}
func periodicFormula(params map[string]interface{}) float64 {
return 0.0
}

View File

@@ -41,12 +41,12 @@ func init() {
func populateDB() {
ats := []*Action{
&Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}}},
&Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: &ValueFormula{Static: 10}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}},
&Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}}},
&Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: &utils.ValueFormula{Static: 10}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}},
}
ats1 := []*Action{
&Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &ValueFormula{Static: 10}}, Weight: 10},
&Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}}, Weight: 10},
&Action{ActionType: "*reset_account", Weight: 20},
}

View File

@@ -180,6 +180,7 @@ BLOCK_EMPTY,*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
NOEXP,*topup,,,,*voice,*out,,,,,*unlimited,*any,50,10,false,false,10
VF,*debit,,,,*monetary,*out,,,,,*unlimited,*any,"{""Method"":""*periodic"",""Params"":{""Units"":10, ""Interval"":""month"", ""Increment"":""day""}}",10,false,false,10
`
actionPlans = `
MORE_MINUTES,MINI,ONE_TIME_RUN,10
@@ -223,6 +224,7 @@ cgrates.org,block,BLOCK_AT,,false,false
cgrates.org,block_empty,BLOCK_EMPTY_AT,,false,false
cgrates.org,expo,EXP_AT,,false,false
cgrates.org,expnoexp,,,false,false
cgrates.org,vf,,,false,false
`
derivedCharges = `
@@ -825,7 +827,7 @@ func TestLoadRatingProfiles(t *testing.T) {
}
func TestLoadActions(t *testing.T) {
if len(csvr.actions) != 14 {
if len(csvr.actions) != 15 {
t.Error("Failed to load actions: ", len(csvr.actions))
}
as1 := csvr.actions["MINI"]
@@ -840,7 +842,7 @@ func TestLoadActions(t *testing.T) {
Type: utils.StringPointer(utils.MONETARY),
Uuid: as1[0].Balance.Uuid,
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
Value: &ValueFormula{Static: 10},
Value: &utils.ValueFormula{Static: 10},
Weight: utils.Float64Pointer(10),
DestinationIDs: nil,
TimingIDs: nil,
@@ -860,7 +862,7 @@ func TestLoadActions(t *testing.T) {
Type: utils.StringPointer(utils.VOICE),
Uuid: as1[1].Balance.Uuid,
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
Value: &ValueFormula{Static: 100},
Value: &utils.ValueFormula{Static: 100},
Weight: utils.Float64Pointer(10),
RatingSubject: utils.StringPointer("test"),
DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")),
@@ -887,7 +889,7 @@ func TestLoadActions(t *testing.T) {
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
DestinationIDs: nil,
Uuid: as2[0].Balance.Uuid,
Value: &ValueFormula{Static: 100},
Value: &utils.ValueFormula{Static: 100},
Weight: utils.Float64Pointer(10),
SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")),
TimingIDs: nil,
@@ -1106,7 +1108,7 @@ func TestLoadActionTriggers(t *testing.T) {
}
func TestLoadAccountActions(t *testing.T) {
if len(csvr.accountActions) != 15 {
if len(csvr.accountActions) != 16 {
t.Error("Failed to load account actions: ", len(csvr.accountActions))
}
aa := csvr.accountActions["vdf:minitsboy"]

View File

@@ -274,7 +274,7 @@ func TestDifferentUuid(t *testing.T) {
func TestStorageTask(t *testing.T) {
// clean previous unused tasks
for i := 0; i < 19; i++ {
for i := 0; i < 20; i++ {
ratingStorage.PopTask()
}

View File

@@ -531,7 +531,7 @@ func (tpr *TpReader) LoadActions() (err error) {
}
if tpact.Units != "" && tpact.Units != utils.ANY {
vf, err := ParseBalanceFilterValue(tpact.Units)
vf, err := utils.ParseBalanceFilterValue(tpact.Units)
if err != nil {
return err
}
@@ -1007,7 +1007,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error
}
if tpact.Units != "" && tpact.Units != utils.ANY {
vf, err := ParseBalanceFilterValue(tpact.Units)
vf, err := utils.ParseBalanceFilterValue(tpact.Units)
if err != nil {
return err
}
@@ -1355,7 +1355,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) {
}
if tpact.Units != "" && tpact.Units != utils.ANY {
vf, err := ParseBalanceFilterValue(tpact.Units)
vf, err := utils.ParseBalanceFilterValue(tpact.Units)
if err != nil {
return err
}

View File

@@ -277,3 +277,13 @@ func (wd WeekDays) Serialize(sep string) string {
}
return wdStr
}
func DaysInMonth(year int, month time.Month) float64 {
return float64(time.Date(year, month, 1, 0, 0, 0, 0, time.UTC).AddDate(0, 1, -1).Day())
}
func DaysInYear(year int) float64 {
first := time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC)
last := first.AddDate(1, 0, 0)
return float64(last.Sub(first).Hours() / 24)
}

View File

@@ -161,3 +161,30 @@ func TestDateseriesMonthsIsCompleteYes(t *testing.T) {
t.Error("Error months IsComplete: ", months)
}
}
func TestDateseriesDaysInMonth(t *testing.T) {
if n := DaysInMonth(2016, 4); n != 30 {
t.Error("error calculating days: ", n)
}
if n := DaysInMonth(2016, 2); n != 29 {
t.Error("error calculating days: ", n)
}
if n := DaysInMonth(2016, 1); n != 31 {
t.Error("error calculating days: ", n)
}
if n := DaysInMonth(2016, 12); n != 31 {
t.Error("error calculating days: ", n)
}
if n := DaysInMonth(2015, 2); n != 28 {
t.Error("error calculating days: ", n)
}
}
func TestDateseriesDaysInYear(t *testing.T) {
if n := DaysInYear(2016); n != 366 {
t.Error("error calculating days: ", n)
}
if n := DaysInYear(2015); n != 365 {
t.Error("error calculating days: ", n)
}
}

85
utils/value_formula.go Normal file
View File

@@ -0,0 +1,85 @@
package utils
import (
"encoding/json"
"errors"
"log"
"strconv"
"time"
)
//for computing a dynamic value for Value field
type ValueFormula struct {
Method string
Params map[string]interface{}
Static float64
}
func ParseBalanceFilterValue(val string) (*ValueFormula, error) {
u, err := strconv.ParseFloat(val, 64)
if err == nil {
return &ValueFormula{Static: u}, err
}
var vf ValueFormula
if err := json.Unmarshal([]byte(val), &vf); err == nil {
return &vf, err
}
return nil, errors.New("Invalid value: " + val)
}
type valueFormula func(map[string]interface{}) float64
const (
PERIODIC = "*periodic"
)
var ValueFormulas = map[string]valueFormula{
PERIODIC: periodicFormula,
}
func periodicFormula(params map[string]interface{}) float64 {
// check parameters
unitsInterface, unitsFound := params["Units"]
intervalInterface, intervalFound := params["Interval"]
incrementInterface, incrementFound := params["Increment"]
if !unitsFound || !intervalFound || !incrementFound {
return 0.0
}
units, ok := unitsInterface.(float64)
if !ok {
log.Print("units")
return 0.0
}
var interval string
switch intr := intervalInterface.(type) {
case string:
interval = intr
case []byte:
interval = string(intr)
default:
return 0.0
}
var increment string
switch incr := incrementInterface.(type) {
case string:
increment = incr
case []byte:
increment = string(incr)
default:
return 0.0
}
now := time.Now()
if increment == "day" {
if interval == "week" {
return units / 7
}
if interval == "month" {
return units / DaysInMonth(now.Year(), now.Month())
}
if interval == "year" {
return units / DaysInYear(now.Year())
}
}
return 0.0
}

View File

@@ -0,0 +1,39 @@
package utils
import (
"encoding/json"
"testing"
"time"
)
func TestValueFormulaDayWeek(t *testing.T) {
params := make(map[string]interface{})
if err := json.Unmarshal([]byte(`{"Units":10, "Interval":"week", "Increment":"day"}`), &params); err != nil {
t.Error("error unmarshalling params: ", err)
}
if x := periodicFormula(params); x != 10/7.0 {
t.Error("error caclulating value using formula: ", x)
}
}
func TestValueFormulaDayMonth(t *testing.T) {
params := make(map[string]interface{})
if err := json.Unmarshal([]byte(`{"Units":10, "Interval":"month", "Increment":"day"}`), &params); err != nil {
t.Error("error unmarshalling params: ", err)
}
now := time.Now()
if x := periodicFormula(params); x != 10/DaysInMonth(now.Year(), now.Month()) {
t.Error("error caclulating value using formula: ", x)
}
}
func TestValueFormulaDayYear(t *testing.T) {
params := make(map[string]interface{})
if err := json.Unmarshal([]byte(`{"Units":10, "Interval":"year", "Increment":"day"}`), &params); err != nil {
t.Error("error unmarshalling params: ", err)
}
now := time.Now()
if x := periodicFormula(params); x != 10/DaysInYear(now.Year()) {
t.Error("error caclulating value using formula: ", x)
}
}