From 45fd41a1a2526f77e915558b2e80f8bc5e38e8f2 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Sun, 14 Jul 2013 20:29:40 +0300 Subject: [PATCH] Implement balance counters for number of minutes with a special discount per group of destinations --- rater/actions_test.go | 38 +++++------ rater/calldesc.go | 2 +- rater/calldesc_test.go | 8 +-- rater/loader_csv.go | 10 +-- rater/minute_buckets.go | 31 +++++---- rater/minute_buckets_test.go | 8 +-- rater/simple_serializer.go | 4 +- rater/storage_sql.go | 27 ++++---- rater/timespans.go | 12 ++-- rater/units_counter.go | 18 +++--- rater/units_counter_test.go | 6 +- rater/userbalance.go | 32 ++++++---- rater/userbalance_test.go | 120 +++++++++++++++++------------------ 13 files changed, 167 insertions(+), 149 deletions(-) diff --git a/rater/actions_test.go b/rater/actions_test.go index ed091d19e..e8b77a3cd 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]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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) @@ -430,7 +430,7 @@ func TestActionSetPostpaid(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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 +445,7 @@ func TestActionSetPrepaid(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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,7 +460,7 @@ func TestActionResetPrepaid(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetPrepaidAction(ub, nil) @@ -479,7 +479,7 @@ func TestActionResetPostpaid(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetPostpaidAction(ub, nil) @@ -498,7 +498,7 @@ func TestActionTopupResetCredit(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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,10 +518,10 @@ func TestActionTopupResetMinutes(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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"}} + a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationIds: []string{"NAT"}}} topupResetAction(ub, a) if ub.Type != UB_TYPE_PREPAID || ub.MinuteBuckets[0].Seconds != 5 || @@ -539,7 +539,7 @@ func TestActionTopupCredit(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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, Units: 10} @@ -559,10 +559,10 @@ func TestActionTopupMinutes(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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"}} + a := &Action{BalanceId: MINUTES, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationIds: []string{"NAT"}}} topupAction(ub, a) if ub.Type != UB_TYPE_PREPAID || ub.MinuteBuckets[0].Seconds != 15 || @@ -580,7 +580,7 @@ func TestActionDebitCredit(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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, Units: 10} @@ -600,10 +600,10 @@ func TestActionDebitMinutes(t *testing.T) { Type: UB_TYPE_PREPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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"}} + a := &Action{BalanceId: MINUTES, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationIds: []string{"NAT"}}} debitAction(ub, a) if ub.Type != UB_TYPE_PREPAID || ub.MinuteBuckets[0].Seconds != 5 || @@ -621,7 +621,7 @@ func TestActionResetAllCounters(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetCountersAction(ub, nil) @@ -637,7 +637,7 @@ func TestActionResetAllCounters(t *testing.T) { t.FailNow() } mb := ub.UnitCounters[0].MinuteBuckets[0] - if mb.Weight != 20 || mb.Price != 1 || mb.Seconds != 10 || mb.DestinationId != "NAT" { + if mb.Weight != 20 || mb.Price != 1 || mb.Seconds != 10 || mb.DestinationIds[0] != "NAT" { t.Errorf("Minute bucked cloned incorrectly: %v!", mb) } } @@ -648,7 +648,7 @@ func TestActionResetCounterMinutes(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: MINUTES} @@ -665,7 +665,7 @@ func TestActionResetCounterMinutes(t *testing.T) { t.FailNow() } mb := ub.UnitCounters[1].MinuteBuckets[0] - if mb.Weight != 20 || mb.Price != 1 || mb.Seconds != 10 || mb.DestinationId != "NAT" { + if mb.Weight != 20 || mb.Price != 1 || mb.Seconds != 10 || mb.DestinationIds[0] != "NAT" { t.Errorf("Minute bucked cloned incorrectly: %v!", mb) } } @@ -676,7 +676,7 @@ func TestActionResetCounterCREDIT(t *testing.T) { Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{CREDIT: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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/calldesc.go b/rater/calldesc.go index 57ad9fd97..1077f24b7 100644 --- a/rater/calldesc.go +++ b/rater/calldesc.go @@ -447,7 +447,7 @@ func (cd *CallDescriptor) AddRecievedCallSeconds() (err error) { if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { a := &Action{ Direction: INBOUND, - MinuteBucket: &MinuteBucket{Seconds: cd.Amount, DestinationId: cd.Destination}, + MinuteBucket: &MinuteBucket{Seconds: cd.Amount, DestinationIds: []string{cd.Destination}}, } userBalance.countUnits(a) return nil diff --git a/rater/calldesc_test.go b/rater/calldesc_test.go index 985b5f426..d5e43606b 100644 --- a/rater/calldesc_test.go +++ b/rater/calldesc_test.go @@ -36,16 +36,16 @@ func populateDB() { CREDIT: 0, }, MinuteBuckets: []*MinuteBucket{ - &MinuteBucket{Seconds: 200, DestinationId: "NAT", Weight: 10}, - &MinuteBucket{Seconds: 100, DestinationId: "RET", Weight: 20}, + &MinuteBucket{Seconds: 200, DestinationIds: []string{"NAT"}, Weight: 10}, + &MinuteBucket{Seconds: 100, DestinationIds: []string{"RET"}, Weight: 20}, }, } broker := &UserBalance{ Id: "OUT:vdf:broker", Type: UB_TYPE_PREPAID, MinuteBuckets: []*MinuteBucket{ - &MinuteBucket{Seconds: 20, DestinationId: "NAT", Weight: 10, Price: 1}, - &MinuteBucket{Seconds: 100, DestinationId: "RET", Weight: 20}, + &MinuteBucket{Seconds: 20, DestinationIds: []string{"NAT"}, Weight: 10, Price: 1}, + &MinuteBucket{Seconds: 100, DestinationIds: []string{"RET"}, Weight: 20}, }, } if storageGetter != nil { diff --git a/rater/loader_csv.go b/rater/loader_csv.go index d6547c702..b23abb422 100644 --- a/rater/loader_csv.go +++ b/rater/loader_csv.go @@ -414,11 +414,11 @@ func (csvr *CSVReader) LoadActions() (err error) { Direction: record[3], Weight: weight, MinuteBucket: &MinuteBucket{ - Seconds: units, - Weight: minutesWeight, - Price: price, - Percent: percent, - DestinationId: record[5], + Seconds: units, + Weight: minutesWeight, + Price: price, + Percent: percent, + DestinationIds: strings.Split(record[5], ";"), }, } } diff --git a/rater/minute_buckets.go b/rater/minute_buckets.go index 61bae4bcb..d5338a50e 100644 --- a/rater/minute_buckets.go +++ b/rater/minute_buckets.go @@ -20,16 +20,19 @@ package rater import ( "math" + "reflect" "sort" + "time" ) type MinuteBucket struct { - Seconds float64 - Weight float64 - Price float64 - Percent float64 // percentage from standard price - DestinationId string - precision int + Seconds float64 + Weight float64 + Price float64 + Percent float64 // percentage from standard price + DestinationIds []string + ExpirationTime time.Time + precision int } // Returns the available number of seconds for a specified credit @@ -44,20 +47,22 @@ func (mb *MinuteBucket) GetSecondsForCredit(credit float64) (seconds float64) { // Creates a similar minute func (mb *MinuteBucket) Clone() *MinuteBucket { return &MinuteBucket{ - Seconds: mb.Seconds, - Weight: mb.Weight, - Price: mb.Price, - Percent: mb.Percent, - DestinationId: mb.DestinationId, + Seconds: mb.Seconds, + Weight: mb.Weight, + Price: mb.Price, + Percent: mb.Percent, + DestinationIds: mb.DestinationIds, + ExpirationTime: mb.ExpirationTime, } } // Equal method func (mb *MinuteBucket) Equal(o *MinuteBucket) bool { - return mb.DestinationId == o.DestinationId && + return reflect.DeepEqual(mb.DestinationIds, o.DestinationIds) && mb.Weight == o.Weight && mb.Price == o.Price && - mb.Percent == o.Percent + mb.Percent == o.Percent && + mb.ExpirationTime == o.ExpirationTime } /* diff --git a/rater/minute_buckets_test.go b/rater/minute_buckets_test.go index 7ae16173d..a525b3dd5 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, Percent: 1, DestinationIds: []string{}} + mb2 := &MinuteBucket{Weight: 1, precision: 1, Price: 1, Percent: 1, DestinationIds: []string{}} + mb3 := &MinuteBucket{Weight: 1, precision: 1, Price: 2, Percent: 1, DestinationIds: []string{}} 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, Percent: 4, DestinationIds: []string{"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 bec2c9077..d5287df07 100644 --- a/rater/simple_serializer.go +++ b/rater/simple_serializer.go @@ -464,7 +464,7 @@ func (mb *MinuteBucket) Store() (result string, err error) { 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.DestinationId + result += strings.Join(mb.DestinationIds, "^") return } @@ -475,7 +475,7 @@ func (mb *MinuteBucket) Restore(input string) error { mb.Weight, _ = strconv.ParseFloat(elements[1], 64) mb.Price, _ = strconv.ParseFloat(elements[2], 64) mb.Percent, _ = strconv.ParseFloat(elements[3], 64) - mb.DestinationId = elements[4] + mb.DestinationIds = strings.Split(elements[4], "^") return nil } return notEnoughElements diff --git a/rater/storage_sql.go b/rater/storage_sql.go index ff4264a0f..1123f1791 100644 --- a/rater/storage_sql.go +++ b/rater/storage_sql.go @@ -23,6 +23,7 @@ import ( "encoding/json" "fmt" "github.com/cgrates/cgrates/utils" + "strings" ) type SQLStorage struct { @@ -211,8 +212,8 @@ func (self *SQLStorage) ExistsTPRate(tpid, rtId string) (bool, error) { func (self *SQLStorage) SetTPRate(rt *utils.TPRate) error { for _, rtSlot := range rt.RateSlots { if _, err := self.Db.Exec(fmt.Sprintf("INSERT INTO %s (tpid, tag, connect_fee, rate, rated_units, rate_increments, rounding_method, rounding_decimals, weight) VALUES ('%s', '%s', %f, %f, %d, %d,'%s', %d, %f)", - utils.TBL_TP_RATES, rt.TPid, rt.RateId, rtSlot.ConnectFee, rtSlot.Rate, rtSlot.RatedUnits, rtSlot.RateIncrements, - rtSlot.RoundingMethod, rtSlot.RoundingDecimals, rtSlot.Weight)); err != nil { + utils.TBL_TP_RATES, rt.TPid, rt.RateId, rtSlot.ConnectFee, rtSlot.Rate, rtSlot.RatedUnits, rtSlot.RateIncrements, + rtSlot.RoundingMethod, rtSlot.RoundingDecimals, rtSlot.Weight)); err != nil { return err } } @@ -426,13 +427,13 @@ func (self *SQLStorage) ExistsTPRateProfile(tpid, rpId string) (bool, error) { func (self *SQLStorage) SetTPRateProfile(rp *utils.TPRateProfile) error { var qry string if len(rp.RatingActivations) == 0 { // Possibility to only set fallback rate subject - qry = fmt.Sprintf("INSERT INTO %s (tpid,tag,tenant,tor,direction,subject,activation_time,destrates_timing_tag,rates_fallback_subject) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', 0,'','%s')", + qry = fmt.Sprintf("INSERT INTO %s (tpid,tag,tenant,tor,direction,subject,activation_time,destrates_timing_tag,rates_fallback_subject) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', 0,'','%s')", utils.TBL_TP_RATE_PROFILES, rp.TPid, rp.RateProfileId, rp.Tenant, rp.TOR, rp.Direction, rp.Subject, rp.RatesFallbackSubject) } else { qry = fmt.Sprintf("INSERT INTO %s (tpid,tag,tenant,tor,direction,subject,activation_time,destrates_timing_tag,rates_fallback_subject) VALUES ", utils.TBL_TP_RATE_PROFILES) // Using multiple values in query to spare some network processing time for idx, rpa := range rp.RatingActivations { - if idx!=0 { //Consecutive values after the first will be prefixed with "," as separator + if idx != 0 { //Consecutive values after the first will be prefixed with "," as separator qry += "," } qry += fmt.Sprintf("('%s', '%s', '%s', '%s', '%s', '%s', %d,'%s','%s')", rp.TPid, rp.RateProfileId, rp.Tenant, rp.TOR, rp.Direction, rp.Subject, rpa.ActivationTime, rpa.DestRateTimingId, rp.RatesFallbackSubject) @@ -441,7 +442,7 @@ func (self *SQLStorage) SetTPRateProfile(rp *utils.TPRateProfile) error { if _, err := self.Db.Exec(qry); err != nil { return err } - return nil + return nil } func (self *SQLStorage) GetTPRateProfile(tpid, rpId string) (*utils.TPRateProfile, error) { @@ -472,7 +473,7 @@ func (self *SQLStorage) GetTPRateProfile(tpid, rpId string) (*utils.TPRateProfil if i == 0 { return nil, nil } - return rp, nil + return rp, nil } func (self *SQLStorage) GetTPRateProfileIds(filters *utils.AttrTPRateProfileIds) ([]string, error) { @@ -812,8 +813,8 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er for rows.Next() { var id int var units, rate, minutes_weight, weight float64 - var tpid, tag, action, balance_tag, direction, destinations_tag, rate_type string - if err := rows.Scan(&id, &tpid, &tag, &action, &balance_tag, &direction, &units, &destinations_tag, &rate_type, &rate, &minutes_weight, &weight); err != nil { + var tpid, tag, action, balance_tag, direction, destinations_tags, rate_type string + if err := rows.Scan(&id, &tpid, &tag, &action, &balance_tag, &direction, &units, &destinations_tags, &rate_type, &rate, &minutes_weight, &weight); err != nil { return nil, err } var a *Action @@ -839,11 +840,11 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er Direction: direction, Weight: weight, MinuteBucket: &MinuteBucket{ - Seconds: units, - Weight: minutes_weight, - Price: price, - Percent: percent, - DestinationId: destinations_tag, + Seconds: units, + Weight: minutes_weight, + Price: price, + Percent: percent, + DestinationIds: strings.Split(destinations_tags, ";"), }, } } diff --git a/rater/timespans.go b/rater/timespans.go index 5ecee0e56..ecad9ac89 100644 --- a/rater/timespans.go +++ b/rater/timespans.go @@ -36,9 +36,9 @@ type TimeSpan struct { // Holds the bonus minute information related to a specified timespan type MinuteInfo struct { - DestinationId string - Quantity float64 - Price float64 + DestinationIds []string + Quantity float64 + Price float64 } /* @@ -159,8 +159,12 @@ func (ts *TimeSpan) SplitByActivationPeriod(ap *ActivationPeriod) (newTs *TimeSp Splits the given timespan on minute bucket's duration. */ func (ts *TimeSpan) SplitByMinuteBucket(mb *MinuteBucket) (newTs *TimeSpan) { + // if mb expired skip it + if ts.TimeStart.Equal(mb.ExpirationTime) || ts.TimeStart.After(mb.ExpirationTime) { + return nil + } s := ts.GetDuration().Seconds() - ts.MinuteInfo = &MinuteInfo{mb.DestinationId, s, mb.Price} + ts.MinuteInfo = &MinuteInfo{mb.DestinationIds, s, mb.Price} if s <= mb.Seconds { mb.Seconds -= s return nil diff --git a/rater/units_counter.go b/rater/units_counter.go index ae59202bf..a5915a565 100644 --- a/rater/units_counter.go +++ b/rater/units_counter.go @@ -50,14 +50,16 @@ func (uc *UnitsCounter) initMinuteBuckets(ats []*ActionTrigger) { // is the same or ads the minutye bucket to the list if none matches. func (uc *UnitsCounter) addMinutes(amount float64, prefix string) { for _, mb := range uc.MinuteBuckets { - d, err := GetDestination(mb.DestinationId) - if err != nil { - Logger.Err(fmt.Sprintf("Minutes counter: unknown destination: %s", mb.DestinationId)) - continue - } - if _, ok := d.containsPrefix(prefix); ok { - mb.Seconds += amount - break + for _, dest := range mb.DestinationIds { + d, err := GetDestination(dest) + if err != nil { + Logger.Err(fmt.Sprintf("Minutes counter: unknown destination: %s", dest)) + continue + } + if _, ok := d.containsPrefix(prefix); ok { + mb.Seconds += amount + break + } } } } diff --git a/rater/units_counter_test.go b/rater/units_counter_test.go index 059a2866b..4a48216c0 100644 --- a/rater/units_counter_test.go +++ b/rater/units_counter_test.go @@ -28,7 +28,7 @@ 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, } r, err := uc.Store() if err != nil || r != "OUT/SMS/100/0;20;1;0;NAT,0;10;10;0;RET" { @@ -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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"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 332d8360a..ae2736b83 100644 --- a/rater/userbalance.go +++ b/rater/userbalance.go @@ -71,14 +71,16 @@ func (ub *UserBalance) getSecondsForPrefix(prefix string) (seconds, credit float return } for _, mb := range ub.MinuteBuckets { - d, err := GetDestination(mb.DestinationId) - if err != nil { - continue - } - if precision, ok := d.containsPrefix(prefix); ok { - mb.precision = precision - if mb.Seconds > 0 { - bucketList = append(bucketList, mb) + for _, dest := range mb.DestinationIds { + d, err := GetDestination(dest) + if err != nil { + continue + } + if precision, ok := d.containsPrefix(prefix); ok { + mb.precision = precision + if mb.Seconds > 0 { + bucketList = append(bucketList, mb) + } } } } @@ -121,7 +123,7 @@ debited and an error will be returned. */ func (ub *UserBalance) debitMinutesBalance(amount float64, prefix string, count bool) error { if count { - ub.countUnits(&Action{BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Seconds: amount, DestinationId: prefix}}) + ub.countUnits(&Action{BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Seconds: amount, DestinationIds: []string{prefix}}}) } avaliableNbSeconds, _, bucketList := ub.getSecondsForPrefix(prefix) if avaliableNbSeconds < amount { @@ -187,9 +189,11 @@ func (ub *UserBalance) executeActionTriggers() { 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) + for _, dest := range mb.DestinationIds { + if dest == at.DestinationId && mb.Seconds >= at.ThresholdValue { + // run the actions + at.Execute(ub) + } } } } else { @@ -238,7 +242,9 @@ func (ub *UserBalance) countUnits(a *Action) { ub.UnitCounters = append(ub.UnitCounters, unitsCounter) } if a.BalanceId == MINUTES && a.MinuteBucket != nil { - unitsCounter.addMinutes(a.MinuteBucket.Seconds, a.MinuteBucket.DestinationId) + for _, dest := range a.MinuteBucket.DestinationIds { + unitsCounter.addMinutes(a.MinuteBucket.Seconds, dest) + } } else { unitsCounter.Units += a.Units } diff --git a/rater/userbalance_test.go b/rater/userbalance_test.go index cbfe1a25b..102f6938f 100644 --- a/rater/userbalance_test.go +++ b/rater/userbalance_test.go @@ -36,7 +36,7 @@ func init() { func populateTestActionsForTriggers() { ats := []*Action{ &Action{ActionType: "TOPUP", BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}, - &Action{ActionType: "TOPUP", BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Weight: 20, Price: 1, Seconds: 10, DestinationId: "NAT"}}, + &Action{ActionType: "TOPUP", BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Weight: 20, Price: 1, Seconds: 10, DestinationIds: []string{"NAT"}}}, } storageGetter.SetActions("TEST_ACTIONS", ats) ats1 := []*Action{ @@ -51,7 +51,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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, } at := &ActionTrigger{ Id: "some_uuid", @@ -66,7 +66,7 @@ func TestUserBalanceStoreRestore(t *testing.T) { Id: "rif", Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{SMS + OUTBOUND: 14, TRAFFIC + OUTBOUND: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, UnitCounters: []*UnitsCounter{uc, uc}, ActionTriggers: ActionTriggerPriotityList{at, at, at}, } @@ -83,8 +83,8 @@ func TestUserBalanceStoreRestore(t *testing.T) { } func TestUserBalanceStorageStoreRestore(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} storageGetter.SetUserBalance(rifsBalance) ub1, err := storageGetter.GetUserBalance("other") @@ -94,8 +94,8 @@ func TestUserBalanceStorageStoreRestore(t *testing.T) { } func TestGetSecondsForPrefix(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, DestinationIds: []string{"RET"}} ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 200}} seconds, credit, bucketList := ub1.getSecondsForPrefix("0723") expected := 110.0 @@ -105,8 +105,8 @@ func TestGetSecondsForPrefix(t *testing.T) { } func TestGetPricedSeconds(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Price: 10, Weight: 10, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Price: 1, Weight: 20, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Price: 10, Weight: 10, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Price: 1, Weight: 20, DestinationIds: []string{"RET"}} ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} seconds, credit, bucketList := ub1.getSecondsForPrefix("0723") @@ -117,8 +117,8 @@ func TestGetPricedSeconds(t *testing.T) { } func TestUserBalanceStorageStore(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} storageGetter.SetUserBalance(rifsBalance) result, err := storageGetter.GetUserBalance(rifsBalance.Id) @@ -132,8 +132,8 @@ func TestUserBalanceStorageStore(t *testing.T) { } func TestDebitMoneyBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} result := rifsBalance.debitBalance(CREDIT, 6, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND] != 15 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND] { @@ -142,8 +142,8 @@ func TestDebitMoneyBalance(t *testing.T) { } func TestDebitAllMoneyBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} rifsBalance.debitBalance(CREDIT, 21, false) result := rifsBalance.debitBalance(CREDIT, 0, false) @@ -153,8 +153,8 @@ func TestDebitAllMoneyBalance(t *testing.T) { } func TestDebitMoreMoneyBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} result := rifsBalance.debitBalance(CREDIT, 22, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND] != -1 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND] { @@ -163,8 +163,8 @@ func TestDebitMoreMoneyBalance(t *testing.T) { } func TestDebitNegativeMoneyBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} result := rifsBalance.debitBalance(CREDIT, -15, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND] != 36 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND] { @@ -173,8 +173,8 @@ func TestDebitNegativeMoneyBalance(t *testing.T) { } func TestDebitMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(6, "0723", false) if b2.Seconds != 94 || err != nil { @@ -184,8 +184,8 @@ func TestDebitMinuteBalance(t *testing.T) { } func TestDebitMultipleBucketsMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(105, "0723", false) if b2.Seconds != 0 || b1.Seconds != 5 || err != nil { @@ -195,8 +195,8 @@ func TestDebitMultipleBucketsMinuteBalance(t *testing.T) { } func TestDebitAllMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(110, "0723", false) if b2.Seconds != 0 || b1.Seconds != 0 || err != nil { @@ -205,8 +205,8 @@ func TestDebitAllMinuteBalance(t *testing.T) { } func TestDebitMoreMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(115, "0723", false) if b2.Seconds != 100 || b1.Seconds != 10 || err == nil { @@ -215,8 +215,8 @@ func TestDebitMoreMinuteBalance(t *testing.T) { } func TestDebitPriceMinuteBalance0(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(5, "0723", false) if b2.Seconds != 95 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND] != 16 { @@ -225,8 +225,8 @@ func TestDebitPriceMinuteBalance0(t *testing.T) { } func TestDebitPriceAllMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(21, "0723", false) if b2.Seconds != 79 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND] != 0 { @@ -235,8 +235,8 @@ func TestDebitPriceAllMinuteBalance(t *testing.T) { } func TestDebitPriceMoreMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(25, "0723", false) if b2.Seconds != 100 || b1.Seconds != 10 || err == nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND] != 21 { @@ -246,8 +246,8 @@ func TestDebitPriceMoreMinuteBalance(t *testing.T) { } func TestDebitPriceNegativeMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(-15, "0723", false) if b2.Seconds != 115 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND] != 36 { @@ -257,8 +257,8 @@ func TestDebitPriceNegativeMinuteBalance(t *testing.T) { } func TestDebitNegativeMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} err := rifsBalance.debitMinutesBalance(-15, "0723", false) if b2.Seconds != 115 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND] != 21 { @@ -268,8 +268,8 @@ func TestDebitNegativeMinuteBalance(t *testing.T) { } func TestDebitSMSBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21, SMS + OUTBOUND: 100}} result := rifsBalance.debitBalance(SMS, 12, false) if rifsBalance.BalanceMap[SMS+OUTBOUND] != 88 || result != rifsBalance.BalanceMap[SMS+OUTBOUND] { @@ -278,8 +278,8 @@ func TestDebitSMSBalance(t *testing.T) { } func TestDebitAllSMSBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21, SMS + OUTBOUND: 100}} result := rifsBalance.debitBalance(SMS, 100, false) if rifsBalance.BalanceMap[SMS+OUTBOUND] != 0 || result != rifsBalance.BalanceMap[SMS+OUTBOUND] { @@ -288,8 +288,8 @@ func TestDebitAllSMSBalance(t *testing.T) { } func TestDebitMoreSMSBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21, SMS + OUTBOUND: 100}} result := rifsBalance.debitBalance(SMS, 110, false) if rifsBalance.BalanceMap[SMS+OUTBOUND] != -10 || result != rifsBalance.BalanceMap[SMS+OUTBOUND] { @@ -298,8 +298,8 @@ func TestDebitMoreSMSBalance(t *testing.T) { } func TestDebitNegativeSMSBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21, SMS + OUTBOUND: 100}} result := rifsBalance.debitBalance(SMS, -15, false) if rifsBalance.BalanceMap[SMS+OUTBOUND] != 115 || result != rifsBalance.BalanceMap[SMS+OUTBOUND] { @@ -312,9 +312,9 @@ func TestUserBalancedebitMinuteBucket(t *testing.T) { Id: "rif", Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{SMS: 14, TRAFFIC: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, } - newMb := &MinuteBucket{Weight: 20, Price: 1, DestinationId: "NEW"} + newMb := &MinuteBucket{Weight: 20, Price: 1, DestinationIds: []string{"NEW"}} ub.debitMinuteBucket(newMb) if len(ub.MinuteBuckets) != 3 || ub.MinuteBuckets[2] != newMb { t.Error("Error adding minute bucket!", len(ub.MinuteBuckets), ub.MinuteBuckets) @@ -327,9 +327,9 @@ func TestUserBalancedebitMinuteBucketExists(t *testing.T) { Id: "rif", Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{SMS + OUTBOUND: 14, TRAFFIC + OUTBOUND: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, } - newMb := &MinuteBucket{Seconds: -10, Weight: 20, Price: 1, DestinationId: "NAT"} + newMb := &MinuteBucket{Seconds: -10, Weight: 20, Price: 1, DestinationIds: []string{"NAT"}} ub.debitMinuteBucket(newMb) if len(ub.MinuteBuckets) != 2 || ub.MinuteBuckets[0].Seconds != 25 { t.Error("Error adding minute bucket!") @@ -341,7 +341,7 @@ func TestUserBalanceAddMinuteNil(t *testing.T) { Id: "rif", Type: UB_TYPE_POSTPAID, BalanceMap: map[string]float64{SMS + OUTBOUND: 14, TRAFFIC + OUTBOUND: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, } ub.debitMinuteBucket(nil) if len(ub.MinuteBuckets) != 2 { @@ -350,9 +350,9 @@ func TestUserBalanceAddMinuteNil(t *testing.T) { } func TestUserBalanceAddMinutBucketEmpty(t *testing.T) { - mb1 := &MinuteBucket{Seconds: -10, DestinationId: "NAT"} - mb2 := &MinuteBucket{Seconds: -10, DestinationId: "NAT"} - mb3 := &MinuteBucket{Seconds: -10, DestinationId: "OTHER"} + mb1 := &MinuteBucket{Seconds: -10, DestinationIds: []string{"NAT"}} + mb2 := &MinuteBucket{Seconds: -10, DestinationIds: []string{"NAT"}} + mb3 := &MinuteBucket{Seconds: -10, DestinationIds: []string{"OTHER"}} ub := &UserBalance{} ub.debitMinuteBucket(mb1) if len(ub.MinuteBuckets) != 1 { @@ -374,7 +374,7 @@ func TestUserBalanceExecuteTriggeredActions(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]float64{CREDIT + OUTBOUND: 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, DestinationIds: []string{"NAT"}}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationIds: []string{"RET"}}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(&Action{BalanceId: CREDIT, Units: 1}) @@ -455,8 +455,8 @@ func TestUserBalanceUnitCountingOutboundInbound(t *testing.T) { func BenchmarkGetSecondForPrefix(b *testing.B) { b.StopTimer() - b1 := &MinuteBucket{Seconds: 10, Price: 10, Weight: 10, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Price: 1, Weight: 20, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Price: 10, Weight: 10, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Price: 1, Weight: 20, DestinationIds: []string{"RET"}} ub1 := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} b.StartTimer() @@ -466,8 +466,8 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { } func BenchmarkUserBalanceStorageStoreRestore(b *testing.B) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationIds: []string{"RET"}} rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} for i := 0; i < b.N; i++ { storageGetter.SetUserBalance(rifsBalance) @@ -476,8 +476,8 @@ func BenchmarkUserBalanceStorageStoreRestore(b *testing.B) { } func BenchmarkGetSecondsForPrefix(b *testing.B) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, DestinationId: "RET"} + b1 := &MinuteBucket{Seconds: 10, Weight: 10, DestinationIds: []string{"NAT"}} + b2 := &MinuteBucket{Seconds: 100, Weight: 20, DestinationIds: []string{"RET"}} ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT + OUTBOUND: 21}} for i := 0; i < b.N; i++ { ub1.getSecondsForPrefix("0723")