Implement balance counters for number of minutes with a special discount per group of destinations

This commit is contained in:
Radu Ioan Fericean
2013-07-14 20:29:40 +03:00
parent 460b2470f1
commit 45fd41a1a2
13 changed files with 167 additions and 149 deletions

View File

@@ -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}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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], ";"),
},
}
}

View File

@@ -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
}
/*

View File

@@ -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)

View File

@@ -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

View File

@@ -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, ";"),
},
}
}

View File

@@ -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

View File

@@ -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
}
}
}
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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")