mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-17 14:19:54 +05:00
rounding methods in csv loader and implemented triggered type
This commit is contained in:
@@ -29,6 +29,7 @@ type ActionTrigger struct {
|
||||
BalanceId string
|
||||
Direction string
|
||||
ThresholdValue float64
|
||||
ThresholdType string
|
||||
DestinationId string
|
||||
Weight float64
|
||||
ActionsId string
|
||||
|
||||
@@ -235,7 +235,7 @@ func (csvr *CSVReader) LoadRates() (err error) {
|
||||
continue
|
||||
}
|
||||
var r *Rate
|
||||
r, err = NewRate(record[0], record[1], record[2], record[3], record[4], record[5])
|
||||
r, err = NewRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -492,7 +492,7 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) {
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not parse action trigger value: %v", err))
|
||||
}
|
||||
weight, err := strconv.ParseFloat(record[6], 64)
|
||||
weight, err := strconv.ParseFloat(record[7], 64)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not parse action trigger weight: %v", err))
|
||||
}
|
||||
@@ -501,8 +501,9 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) {
|
||||
BalanceId: record[1],
|
||||
Direction: record[2],
|
||||
ThresholdValue: value,
|
||||
DestinationId: record[4],
|
||||
ActionsId: record[5],
|
||||
ThresholdType: record[4],
|
||||
DestinationId: record[5],
|
||||
ActionsId: record[6],
|
||||
Weight: weight,
|
||||
}
|
||||
csvr.actionsTriggers[tag] = append(csvr.actionsTriggers[tag], at)
|
||||
|
||||
@@ -44,11 +44,11 @@ WEEKENDS,*all,*all,*all,6;7,00:00:00
|
||||
ONE_TIME_RUN,2012,,,,*asap
|
||||
`
|
||||
rates = `
|
||||
R1,0,0.2,60,1,10
|
||||
R2,0,0.1,60,1,10
|
||||
R3,0,0.05,60,1,10
|
||||
R4,1,1,1,1,10
|
||||
R5,0,0.5,1,1,10
|
||||
R1,0,0.2,60,1,*middle,2,10
|
||||
R2,0,0.1,60,1,*middle,2,10
|
||||
R3,0,0.05,60,1,*middle,2,10
|
||||
R4,1,1,1,1,*up,2,10
|
||||
R5,0,0.5,1,1,*down,2,10
|
||||
`
|
||||
destinationRates = `
|
||||
RT_STANDARD,GERMANY,R1
|
||||
@@ -90,8 +90,8 @@ MINI,TOPUP,MINUTES,OUT,100,NAT,ABSOLUTE,0,10,10
|
||||
MORE_MINUTES,MINI,ONE_TIME_RUN,10
|
||||
`
|
||||
actionTriggers = `
|
||||
STANDARD_TRIGGER,MINUTES,OUT,10,GERMANY_O2,SOME_1,10
|
||||
STANDARD_TRIGGER,MINUTES,OUT,200,GERMANY,SOME_2,10
|
||||
STANDARD_TRIGGER,MINUTES,OUT,10,COUNTER,GERMANY_O2,SOME_1,10
|
||||
STANDARD_TRIGGER,MINUTES,OUT,200,BALANCE,GERMANY,SOME_2,10
|
||||
`
|
||||
accountActions = `
|
||||
vdf,minitsboy,OUT,MORE_MINUTES,STANDARD_TRIGGER
|
||||
|
||||
@@ -50,7 +50,7 @@ type Rate struct {
|
||||
Weight float64
|
||||
}
|
||||
|
||||
func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, weight string) (r *Rate, err error) {
|
||||
func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, roundingMethod, roundingDecimals, weight string) (r *Rate, err error) {
|
||||
cf, err := strconv.ParseFloat(connectFee, 64)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing connect fee from: %v", connectFee)
|
||||
@@ -76,13 +76,21 @@ func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, weight string)
|
||||
log.Printf("Error parsing rates increments from: %s", weight)
|
||||
return
|
||||
}
|
||||
rd, err := strconv.Atoi(roundingDecimals)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing rounding decimals: %s", roundingDecimals)
|
||||
return
|
||||
}
|
||||
|
||||
r = &Rate{
|
||||
Tag: tag,
|
||||
ConnectFee: cf,
|
||||
Price: p,
|
||||
PricedUnits: pu,
|
||||
RateIncrements: ri,
|
||||
Weight: wght,
|
||||
Tag: tag,
|
||||
ConnectFee: cf,
|
||||
Price: p,
|
||||
PricedUnits: pu,
|
||||
RateIncrements: ri,
|
||||
Weight: wght,
|
||||
RoundingMethod: roundingMethod,
|
||||
RoundingDecimals: rd,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -242,6 +242,7 @@ func (at *ActionTrigger) Store() (result string, err error) {
|
||||
result += at.DestinationId + ";"
|
||||
result += at.ActionsId + ";"
|
||||
result += strconv.FormatFloat(at.ThresholdValue, 'f', -1, 64) + ";"
|
||||
result += at.ThresholdType + ";"
|
||||
result += strconv.FormatFloat(at.Weight, 'f', -1, 64) + ";"
|
||||
result += strconv.FormatBool(at.Executed)
|
||||
return
|
||||
@@ -249,7 +250,7 @@ func (at *ActionTrigger) Store() (result string, err error) {
|
||||
|
||||
func (at *ActionTrigger) Restore(input string) error {
|
||||
elements := strings.Split(input, ";")
|
||||
if len(elements) != 8 {
|
||||
if len(elements) != 9 {
|
||||
return notEnoughElements
|
||||
}
|
||||
at.Id = elements[0]
|
||||
@@ -258,8 +259,9 @@ func (at *ActionTrigger) Restore(input string) error {
|
||||
at.DestinationId = elements[3]
|
||||
at.ActionsId = elements[4]
|
||||
at.ThresholdValue, _ = strconv.ParseFloat(elements[5], 64)
|
||||
at.Weight, _ = strconv.ParseFloat(elements[6], 64)
|
||||
at.Executed, _ = strconv.ParseBool(elements[7])
|
||||
at.ThresholdType = elements[6]
|
||||
at.Weight, _ = strconv.ParseFloat(elements[7], 64)
|
||||
at.Executed, _ = strconv.ParseBool(elements[8])
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -117,12 +117,13 @@ func TestActionTriggerStoreRestore(t *testing.T) {
|
||||
BalanceId: CREDIT,
|
||||
Direction: OUTBOUND,
|
||||
ThresholdValue: 100.0,
|
||||
ThresholdType: "MAX_COUNTER",
|
||||
DestinationId: "NAT",
|
||||
Weight: 10.0,
|
||||
ActionsId: "Commando",
|
||||
}
|
||||
r, err := at.Store()
|
||||
if err != nil || r != "some_uuid;MONETARY;OUT;NAT;Commando;100;10;false" {
|
||||
if err != nil || r != "some_uuid;MONETARY;OUT;NAT;Commando;100;MAX_COUNTER;10;false" {
|
||||
t.Errorf("Error serializing action trigger: %v", string(r))
|
||||
}
|
||||
o := &ActionTrigger{}
|
||||
|
||||
@@ -977,8 +977,8 @@ func (self *SQLStorage) GetTpActionTriggers(tpid, tag string) (map[string][]*Act
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var threshold, weight float64
|
||||
var tpid, tag, balances_tag, direction, destinations_tag, actions_tag string
|
||||
if err := rows.Scan(&id, &tpid, &tag, &balances_tag, &direction, &threshold, &destinations_tag, &actions_tag, &weight); err != nil {
|
||||
var tpid, tag, balances_tag, direction, destinations_tag, actions_tag, thresholdType string
|
||||
if err := rows.Scan(&id, &tpid, &tag, &balances_tag, &direction, &threshold, &thresholdType, &destinations_tag, &actions_tag, &weight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -987,6 +987,7 @@ func (self *SQLStorage) GetTpActionTriggers(tpid, tag string) (map[string][]*Act
|
||||
BalanceId: balances_tag,
|
||||
Direction: direction,
|
||||
ThresholdValue: threshold,
|
||||
ThresholdType: thresholdType,
|
||||
DestinationId: destinations_tag,
|
||||
ActionsId: actions_tag,
|
||||
Weight: weight,
|
||||
|
||||
@@ -21,6 +21,7 @@ package rater
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -261,19 +262,65 @@ func (ub *UserBalance) executeActionTriggers() {
|
||||
// the next reset (see RESET_TRIGGERS action type)
|
||||
continue
|
||||
}
|
||||
for _, uc := range ub.UnitCounters {
|
||||
if uc.BalanceId == at.BalanceId {
|
||||
if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safty
|
||||
for _, mb := range uc.MinuteBuckets {
|
||||
if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
if strings.Contains(at.ThresholdType, "COUNTER") {
|
||||
for _, uc := range ub.UnitCounters {
|
||||
if uc.BalanceId == at.BalanceId {
|
||||
if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safety
|
||||
for _, mb := range uc.MinuteBuckets {
|
||||
if strings.Contains(at.ThresholdType, "MAX") {
|
||||
if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
}
|
||||
} else { //MIN
|
||||
if mb.DestinationId == at.DestinationId && mb.Seconds <= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(at.ThresholdType, "MAX") {
|
||||
if uc.Units >= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
}
|
||||
} else { //MIN
|
||||
if uc.Units <= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // BALANCE
|
||||
for _, b := range ub.BalanceMap[at.BalanceId] {
|
||||
if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safety
|
||||
for _, mb := range ub.MinuteBuckets {
|
||||
if strings.Contains(at.ThresholdType, "MAX") {
|
||||
if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
}
|
||||
} else { //MIN
|
||||
if mb.DestinationId == at.DestinationId && mb.Seconds <= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if uc.Units >= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
if strings.Contains(at.ThresholdType, "MAX") {
|
||||
if b.Value >= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
}
|
||||
} else { //MIN
|
||||
if b.Value <= at.ThresholdValue {
|
||||
// run the actions
|
||||
at.Execute(ub)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,38 +417,51 @@ func TestUserBalanceAddMinutBucketEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestUserBalanceExecuteTriggeredActions(t *testing.T) {
|
||||
ub := &UserBalance{
|
||||
Id: "TEST_UB",
|
||||
BalanceMap: map[string]float64{CREDIT + OUTBOUND: 100},
|
||||
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"}},
|
||||
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS"}},
|
||||
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: "MAX_COUNTER", ActionsId: "TEST_ACTIONS"}},
|
||||
}
|
||||
ub.countUnits(&Action{BalanceId: CREDIT, Units: 1})
|
||||
if ub.BalanceMap[CREDIT+OUTBOUND] != 110 || ub.MinuteBuckets[0].Seconds != 20 {
|
||||
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND], ub.MinuteBuckets[0].Seconds)
|
||||
if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 {
|
||||
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds)
|
||||
}
|
||||
// are set to executed
|
||||
ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1})
|
||||
if ub.BalanceMap[CREDIT+OUTBOUND] != 110 || ub.MinuteBuckets[0].Seconds != 20 {
|
||||
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND], ub.MinuteBuckets[0].Seconds)
|
||||
if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 {
|
||||
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds)
|
||||
}
|
||||
// we can reset them
|
||||
ub.resetActionTriggers()
|
||||
ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1})
|
||||
if ub.BalanceMap[CREDIT+OUTBOUND] != 120 || ub.MinuteBuckets[0].Seconds != 30 {
|
||||
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND], ub.MinuteBuckets[0].Seconds)
|
||||
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)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
func TestUserBalanceExecuteTriggeredActionsBalance(t *testing.T) {
|
||||
ub := &UserBalance{
|
||||
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"}},
|
||||
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: "MIN_COUNTER", ActionsId: "TEST_ACTIONS"}},
|
||||
}
|
||||
ub.countUnits(&Action{BalanceId: CREDIT, Units: 1})
|
||||
if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 {
|
||||
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserBalanceExecuteTriggeredActionsOrder(t *testing.T) {
|
||||
ub := &UserBalance{
|
||||
Id: "TEST_UB_OREDER",
|
||||
BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}},
|
||||
UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}},
|
||||
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS_ORDER"}},
|
||||
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ThresholdType: "MAX_COUNTER", ActionsId: "TEST_ACTIONS_ORDER"}},
|
||||
}
|
||||
ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1})
|
||||
if len(ub.BalanceMap[CREDIT+OUTBOUND]) != 1 || ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 10 {
|
||||
|
||||
Reference in New Issue
Block a user