still working on balance subject debit

This commit is contained in:
Radu Ioan Fericean
2013-09-23 19:26:40 +03:00
parent 8c1ad3de86
commit cd98141fbb
11 changed files with 270 additions and 110 deletions

View File

@@ -763,8 +763,8 @@ func TestActionResetAllCounters(t *testing.T) {
t.FailNow()
}
mb := ub.UnitCounters[0].MinuteBalances[0]
if mb.Weight != 20 || mb.SpecialPrice != 1 || mb.Value != 0 || mb.DestinationId != "NAT" {
t.Errorf("Balanxce cloned incorrectly: %v!", mb)
if mb.Weight != 20 || mb.Value != 0 || mb.DestinationId != "NAT" {
t.Errorf("Balance cloned incorrectly: %v!", mb)
}
}
@@ -792,7 +792,7 @@ func TestActionResetCounterMinutes(t *testing.T) {
t.FailNow()
}
mb := ub.UnitCounters[1].MinuteBalances[0]
if mb.Weight != 20 || mb.SpecialPrice != 1 || mb.Value != 0 || mb.DestinationId != "NAT" {
if mb.Weight != 20 || mb.Value != 0 || mb.DestinationId != "NAT" {
t.Errorf("Minute bucked cloned incorrectly: %v!", mb)
}
}

View File

@@ -41,8 +41,8 @@ type Balance struct {
func (b *Balance) Equal(o *Balance) bool {
return b.ExpirationDate.Equal(o.ExpirationDate) &&
b.Weight == o.Weight &&
b.SpecialPrice == o.SpecialPrice &&
b.DestinationId == o.DestinationId
b.DestinationId == o.DestinationId &&
b.RateSubject == o.RateSubject
}
func (b *Balance) IsExpired() bool {
@@ -51,13 +51,12 @@ func (b *Balance) IsExpired() bool {
func (b *Balance) Clone() *Balance {
return &Balance{
Id: b.Id,
Value: b.Value,
SpecialPrice: b.SpecialPrice,
SpecialPriceType: b.SpecialPriceType,
DestinationId: b.DestinationId,
ExpirationDate: b.ExpirationDate,
Weight: b.Weight,
Id: b.Id,
Value: b.Value,
DestinationId: b.DestinationId,
ExpirationDate: b.ExpirationDate,
Weight: b.Weight,
RateSubject: b.RateSubject,
}
}

View File

@@ -57,16 +57,16 @@ func TestBalanceSortSpecialPrice(t *testing.T) {
}
func TestBalanceEqual(t *testing.T) {
mb1 := &Balance{Weight: 1, precision: 1, SpecialPrice: 1, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: ""}
mb2 := &Balance{Weight: 1, precision: 1, SpecialPrice: 1, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: ""}
mb3 := &Balance{Weight: 1, precision: 1, SpecialPrice: 2, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: ""}
mb1 := &Balance{Weight: 1, precision: 1, RateSubject: "1", DestinationId: ""}
mb2 := &Balance{Weight: 1, precision: 1, RateSubject: "1", DestinationId: ""}
mb3 := &Balance{Weight: 1, precision: 1, RateSubject: "2", DestinationId: ""}
if !mb1.Equal(mb2) || mb2.Equal(mb3) {
t.Error("Equal failure!", mb1 == mb2, mb3)
}
}
func TestBalanceClone(t *testing.T) {
mb1 := &Balance{Value: 1, Weight: 2, SpecialPrice: 3, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "5"}
mb1 := &Balance{Value: 1, Weight: 2, RateSubject: "test", DestinationId: "5"}
mb2 := mb1.Clone()
if mb1 == mb2 || !reflect.DeepEqual(mb1, mb2) {
t.Errorf("Cloning failure: \n%v\n%v", mb1, mb2)

View File

@@ -73,3 +73,15 @@ func (cc *CallCost) GetTotalDuration() (td time.Duration) {
}
return
}
// Creates a CallDescriptor structure copying related data from CallCost
func (cc *CallCost) CreateCallDescriptor() *CallDescriptor {
return &CallDescriptor{
Direction: cc.Direction,
TOR: cc.TOR,
Tenant: cc.Tenant,
Subject: cc.Subject,
Account: cc.Account,
Destination: cc.Destination,
}
}

View File

@@ -104,7 +104,7 @@ type CallDescriptor struct {
Tenant, Subject, Account, Destination string
TimeStart, TimeEnd time.Time
LoopIndex float64 // indicates the position of this segment in a cost request loop
CallDuration time.Duration // the call duration so far (partial or final)
CallDuration time.Duration // the call duration so far (till TimeEnd)
Amount float64
FallbackSubject string // the subject to check for destination if not found on primary subject
RatingPlans []*RatingPlan
@@ -248,12 +248,13 @@ func (cd *CallDescriptor) roundTimeSpansToIncrement(timespans []*TimeSpan) []*Ti
if ts.RateInterval != nil {
_, rateIncrement, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
// if the timespan duration is larger than the rate increment make sure it is a multiple of it
if rateIncrement != time.Second && rateIncrement < ts.GetDuration() {
if rateIncrement < ts.GetDuration() {
rateIncrement = utils.RoundTo(rateIncrement, ts.GetDuration())
}
if rateIncrement > ts.GetDuration() {
initialDuration := ts.GetDuration()
ts.TimeEnd = ts.TimeStart.Add(rateIncrement)
ts.CallDuration = ts.CallDuration + rateIncrement
ts.CallDuration = ts.CallDuration + (rateIncrement - initialDuration)
// overlap the rest of the timespans
i += 1
@@ -272,7 +273,6 @@ func (cd *CallDescriptor) roundTimeSpansToIncrement(timespans []*TimeSpan) []*Ti
// remove overlapped
for _, ts := range timespans {
if !ts.overlapped {
ts.createRatedSecondSlice()
newTimespans = append(newTimespans, ts)
}
}
@@ -299,6 +299,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
}
cost += ts.getCost()
}
// global rounding
cost = utils.Round(cost, roundingDecimals, roundingMethod)
cc := &CallCost{
Direction: cd.Direction,
@@ -438,7 +439,7 @@ The amount filed has to be filled in call descriptor.
func (cd *CallDescriptor) DebitSeconds() (err error) {
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
defer storageGetter.SetUserBalance(userBalance)
return userBalance.debitMinutesBalance(cd.Amount, cd.Destination, true)
return userBalance.debitCreditBalance(cd.CreateCallCost(), true)
}
return err
}
@@ -468,3 +469,15 @@ func (cd *CallDescriptor) FlushCache() (err error) {
return nil
}
// Creates a CallCost structure copying related data from CallDescriptor
func (cd *CallDescriptor) CreateCallCost() *CallCost {
return &CallCost{
Direction: cd.Direction,
TOR: cd.TOR,
Tenant: cd.Tenant,
Subject: cd.Subject,
Account: cd.Account,
Destination: cd.Destination,
}
}

View File

@@ -33,12 +33,14 @@ type TimeSpan struct {
RateInterval *RateInterval
CallDuration time.Duration // the call duration so far till TimeEnd
overlapped bool // mark a timespan as overlapped by an expanded one
ratedSeconds []*rated_second
Increments []*Increment
}
type rated_second struct {
index int
rate float64
type Increment struct {
Duration time.Duration
Cost float64
BalanceId string
BalanceType string
}
// Returns the duration of the timespan
@@ -78,23 +80,17 @@ func (ts *TimeSpan) SetRateInterval(i *RateInterval) {
}
}
func (ts *TimeSpan) createRatedSecondSlice() {
func (ts *TimeSpan) createIncrementsSlice() {
if ts.RateInterval == nil {
return
}
// create rated seconds series
rate, _, rate_unit := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
secondCost := rate / rate_unit.Seconds()
// create rated units series
rate, rateIncrement, rateUnit := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
incrementCost := rate / rateUnit.Seconds() * rateIncrement.Seconds()
totalCost := 0.0
for s := 0; s < int(ts.GetDuration().Seconds()); s++ {
ts.ratedSeconds = append(ts.ratedSeconds, &rated_second{s, secondCost})
totalCost += secondCost
}
cCost := ts.getCost()
// here there might be some subsecond duration left
if totalCost < cCost {
// add one extra second with the fractional cost
ts.ratedSeconds = append(ts.ratedSeconds, &rated_second{len(ts.ratedSeconds), utils.Round(cCost-totalCost, ts.RateInterval.RoundingDecimals, ts.RateInterval.RoundingMethod)})
for s := 0; s < int(ts.GetDuration()/rateIncrement); s++ {
ts.Increments = append(ts.Increments, &Increment{Duration: rateIncrement, Cost: incrementCost})
totalCost += incrementCost
}
}
@@ -168,9 +164,17 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) {
return
}
/*
Splits the given timespan on activation period's activation time.
*/
// Split the interval at the given increment start
func (ts *TimeSpan) SplitByIncrement(index int, increment *Increment) *TimeSpan {
timeStart := ts.GetTimeStartForIncrement(index, increment)
newTs := &TimeSpan{TimeStart: timeStart, TimeEnd: ts.TimeEnd}
newTs.CallDuration = ts.CallDuration
ts.TimeEnd = timeStart
ts.SetNewCallDuration(newTs)
return newTs
}
// Splits the given timespan on activation period's activation time.
func (ts *TimeSpan) SplitByRatingPlan(ap *RatingPlan) (newTs *TimeSpan) {
if !ts.Contains(ap.ActivationTime) {
return nil
@@ -203,3 +207,19 @@ func (ts *TimeSpan) SetNewCallDuration(nts *TimeSpan) {
}
ts.CallDuration = d
}
// returns a time for the specified second in the time span
func (ts *TimeSpan) GetTimeStartForIncrement(index int, increment *Increment) time.Time {
return ts.TimeStart.Add(time.Duration(int64(index) * increment.Duration.Nanoseconds()))
}
func (ts *TimeSpan) RoundToDuration(duration time.Duration) {
if duration < ts.GetDuration() {
duration = utils.RoundTo(duration, ts.GetDuration())
}
if duration > ts.GetDuration() {
initialDuration := ts.GetDuration()
ts.TimeEnd = ts.TimeStart.Add(duration)
ts.CallDuration = ts.CallDuration + (duration - initialDuration)
}
}

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"github.com/cgrates/cgrates/utils"
//"github.com/cgrates/cgrates/utils"
"testing"
"time"
)
@@ -450,6 +450,7 @@ func TestTimespanExpandingPastEnd(t *testing.T) {
}
}
/*
func TestTimespanExpandingCallDuration(t *testing.T) {
timespans := []*TimeSpan{
&TimeSpan{
@@ -471,7 +472,7 @@ func TestTimespanExpandingCallDuration(t *testing.T) {
t.Error("Error setting call duration: ", timespans[0])
}
}
*/
func TestTimespanExpandingRoundingPastEnd(t *testing.T) {
timespans := []*TimeSpan{
&TimeSpan{
@@ -616,15 +617,16 @@ func TestTimespanCreateSecondsSlice(t *testing.T) {
&Rate{Value: 2.0},
}},
}
ts.createRatedSecondSlice()
if len(ts.ratedSeconds) != 30 {
t.Error("Error creating second slice: ", ts.ratedSeconds)
ts.createIncrementsSlice()
if len(ts.Increments) != 30 {
t.Error("Error creating second slice: ", ts.Increments)
}
if ts.ratedSeconds[0].rate != 2.0 {
t.Error("Wrong second slice: ", ts.ratedSeconds[0])
if ts.Increments[0].Cost != 2.0 {
t.Error("Wrong second slice: ", ts.Increments[0])
}
}
/*
func TestTimespanCreateSecondsFract(t *testing.T) {
ts := &TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
@@ -637,12 +639,28 @@ func TestTimespanCreateSecondsFract(t *testing.T) {
},
},
}
ts.createRatedSecondSlice()
if len(ts.ratedSeconds) != 31 {
t.Error("Error creating second slice: ", ts.ratedSeconds)
ts.createIncrementsSlice()
if len(ts.Increments) != 31 {
t.Error("Error creating second slice: ", ts.Increments)
}
t.Log(ts.getCost())
if ts.ratedSeconds[30].rate != 0.2 {
t.Error("Wrong second slice: ", ts.ratedSeconds[30])
if len(ts.Increments) < 31 || ts.Increments[30].Cost != 0.2 {
t.Error("Wrong second slice: ", ts.Increments)
}
}
func TestTimespanSplitByIncrement(t *testing.T) {
ts := &TimeSpan{
TimeStart: time.Date(2013, 9, 19, 18, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 19, 18, 30, 30, 0, time.UTC),
CallDuration: 50 * time.Second,
}
i := &Increment{Duration: time.Second}
newTs := ts.SplitByIncrement(5, i)
if ts.GetDuration() != 5*time.Second || newTs.GetDuration() != 25*time.Second {
t.Error("Error spliting by second: ", ts.GetDuration(), newTs.GetDuration())
}
if ts.CallDuration != 25*time.Second || newTs.CallDuration != 50*time.Second {
t.Error("Error spliting by second at setting call duration: ", ts.GetDuration(), newTs.GetDuration())
}
}
*/

View File

@@ -20,6 +20,7 @@ package engine
import (
"errors"
"fmt"
"github.com/cgrates/cgrates/utils"
"strings"
)
@@ -44,6 +45,9 @@ const (
TRIGGER_MAX_COUNTER = "*max_counter"
TRIGGER_MIN_BALANCE = "*min_balance"
TRIGGER_MAX_BALANCE = "*max_balance"
// minute subjects
ZEROSECOND = "*zerosecond"
ZEROMINUTE = "*zerominute"
)
var (
@@ -66,9 +70,7 @@ type UserBalance struct {
UserIds []string // group info about users
}
/*
Returns user's available minutes for the specified destination
*/
// Returns user's available minutes for the specified destination
func (ub *UserBalance) getSecondsForPrefix(prefix string) (seconds, credit float64, balances BalanceChain) {
credit = ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue()
if len(ub.BalanceMap[MINUTES+OUTBOUND]) == 0 {
@@ -132,54 +134,95 @@ func (ub *UserBalance) debitBalanceAction(a *Action) error {
return nil //ub.BalanceMap[id].GetTotalValue()
}
/*
Debits the received amount of seconds from user's minute buckets.
All the appropriate buckets will be debited until all amount of minutes is consumed.
If the amount is bigger than the sum of all seconds in the minute buckets than nothing will be
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, Balance: &Balance{Value: amount, DestinationId: prefix}})
}
avaliableNbSeconds, _, bucketList := ub.getSecondsForPrefix(prefix)
if avaliableNbSeconds < amount {
return AMOUNT_TOO_BIG
}
var credit BalanceChain
if bc, exists := ub.BalanceMap[CREDIT+OUTBOUND]; exists {
credit = bc.Clone()
}
for _, mb := range bucketList {
if mb.Value < amount {
if mb.SpecialPrice > 0 { // debit the money if the bucket has price
credit.Debit(mb.Value * mb.SpecialPrice)
}
} else {
if mb.SpecialPrice > 0 { // debit the money if the bucket has price
credit.Debit(amount * mb.SpecialPrice)
}
break
func (ub *UserBalance) getBalanceForPrefix(prefix string, balances BalanceChain) BalanceChain {
var usefulBalances BalanceChain
for _, b := range balances {
if b.IsExpired() {
continue
}
if ub.Type == UB_TYPE_PREPAID && credit.GetTotalValue() < 0 {
break
if b.DestinationId != "" {
precision, err := storageGetter.DestinationContainsPrefix(b.DestinationId, prefix)
if err != nil {
continue
}
if precision > 0 {
b.precision = precision
if b.Value > 0 {
balances = append(balances, b)
}
}
}
}
// need to check again because there are two break above
if ub.Type == UB_TYPE_PREPAID && credit.GetTotalValue() < 0 {
return AMOUNT_TOO_BIG
}
ub.BalanceMap[CREDIT+OUTBOUND] = credit // credit is > 0
// resort by precision
usefulBalances.Sort()
return usefulBalances
}
for _, mb := range bucketList {
if mb.Value < amount {
amount -= mb.Value
mb.Value = 0
} else {
mb.Value -= amount
break
/*
This method is the core of userbalance debiting: don't panic just follow the branches
*/
func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
// debit minutes first
minuteBalances := ub.BalanceMap[MINUTES+cc.Direction]
usefulBalances := ub.getBalanceForPrefix(cc.Destination, minuteBalances)
for _, ts := range cc.Timespans {
ts.createIncrementsSlice()
for incrementIndex, increment := range ts.Increments {
for _, b := range usefulBalances {
if b.Value == 0 {
continue
}
// check standard subject tags
if b.RateSubject == ZEROSECOND || b.RateSubject == "" {
if b.Value >= increment.Duration.Seconds() {
b.Value -= increment.Duration.Seconds()
increment.BalanceId = b.Id
break
}
}
if b.RateSubject == ZEROMINUTE {
if b.Value >= 60 {
// TODO: round to minute (consume the rest of the timespans)
nts := ts.SplitByIncrement(incrementIndex, increment)
nts.RoundToDuration(increment.Duration)
b.Value -= 60
increment.BalanceId = b.Id
break
}
}
// nts.SplitByIncrement()
// get the new rate
cd := cc.CreateCallDescriptor()
cd.TimeStart = ts.GetTimeStartForIncrement(incrementIndex, increment)
cd.TimeEnd = cc.Timespans[len(cc.Timespans)-1].TimeEnd
cd.CallDuration = cc.Timespans[len(cc.Timespans)-1].CallDuration
newCC, err := cd.GetCost()
if err != nil {
Logger.Err(fmt.Sprintf("Error getting new cost for balance subject: %v", err))
continue
}
//debit new callcost
for _, nts := range newCC.Timespans {
for _, nIncrement := range nts.Increments {
// debit minutes and money
_ = nIncrement
}
}
}
if increment.BalanceId == "" {
// no balance was attached to this increment: cut the rest of increments/timespans
}
}
}
if count {
ub.countUnits(&Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: cc.Cost + cc.ConnectFee, DestinationId: cc.Destination}})
}
return nil
}

View File

@@ -186,11 +186,12 @@ func TestDebitNegativeMoneyBalance(t *testing.T) {
}
}
/*
func TestDebitMinuteBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(6, "0723", false)
err := rifsBalance.debitCreditBalance(6, "0723", false)
if b2.Value != 94 || err != nil {
t.Log(err)
t.Errorf("Expected %v was %v", 94, b2.Value)
@@ -201,7 +202,7 @@ func TestDebitMultipleBucketsMinuteBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(105, "0723", false)
err := rifsBalance.debitCreditBalance(105, "0723", false)
if b2.Value != 0 || b1.Value != 5 || err != nil {
t.Log(err)
t.Errorf("Expected %v was %v", 0, b2.Value)
@@ -212,7 +213,7 @@ func TestDebitAllMinuteBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(110, "0723", false)
err := rifsBalance.debitCreditBalance(110, "0723", false)
if b2.Value != 0 || b1.Value != 0 || err != nil {
t.Errorf("Expected %v was %v", 0, b2.Value)
}
@@ -222,7 +223,7 @@ func TestDebitMoreMinuteBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(115, "0723", false)
err := rifsBalance.debitCreditBalance(115, "0723", false)
if b2.Value != 100 || b1.Value != 10 || err == nil {
t.Errorf("Expected %v was %v", 1000, b2.Value)
}
@@ -232,7 +233,7 @@ func TestDebitSpecialPriceMinuteBalance0(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(5, "0723", false)
err := rifsBalance.debitCreditBalance(5, "0723", false)
if b2.Value != 95 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 16 {
t.Errorf("Expected %v was %v", 16, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
}
@@ -242,7 +243,7 @@ func TestDebitSpecialPriceAllMinuteBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(21, "0723", false)
err := rifsBalance.debitCreditBalance(21, "0723", false)
if b2.Value != 79 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 0 {
t.Errorf("Expected %v was %v", 0, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
}
@@ -252,7 +253,7 @@ func TestDebitSpecialPriceMoreMinuteBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(25, "0723", false)
err := rifsBalance.debitCreditBalance(25, "0723", false)
if b2.Value != 75 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != -4 {
t.Log(b2.Value)
t.Log(b1.Value)
@@ -265,7 +266,7 @@ func TestDebitSpecialPriceMoreMinuteBalancePrepay(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(25, "0723", false)
err := rifsBalance.debitCreditBalance(25, "0723", false)
expected := 21.0
if b2.Value != 100 || b1.Value != 10 || err != AMOUNT_TOO_BIG || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != expected {
t.Log(b2.Value)
@@ -279,7 +280,7 @@ func TestDebitSpecialPriceNegativeMinuteBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(-15, "0723", false)
err := rifsBalance.debitCreditBalance(-15, "0723", false)
if b2.Value != 115 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 36 {
t.Log(b1, b2, err)
t.Errorf("Expected %v was %v", 36, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
@@ -290,13 +291,13 @@ func TestDebitNegativeMinuteBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
err := rifsBalance.debitMinutesBalance(-15, "0723", false)
err := rifsBalance.debitCreditBalance(-15, "0723", false)
if b2.Value != 115 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 21 {
t.Log(b1, b2, err)
t.Errorf("Expected %v was %v", 21, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
}
}
*/
func TestDebitSMSBalance(t *testing.T) {
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}

49
hard_update_external_libs.py Executable file
View File

@@ -0,0 +1,49 @@
#! /usr/bin/env python
import os
import os.path
from subprocess import call
libs = ('github.com/fzzy/radix/redis',
'code.google.com/p/goconf/conf',
'github.com/bmizerany/pq',
'github.com/vmihailenco/msgpack',
'github.com/ugorji/go/codec',
'labix.org/v2/mgo',
'github.com/cgrates/fsock',
'github.com/go-sql-driver/mysql',
'github.com/garyburd/redigo/redis',
'menteslibres.net/gosexy/redis',
'github.com/howeyc/fsnotify',
)
if __name__ == "__main__":
go_path = os.path.join(os.environ['GOPATH'], 'src')
for lib in libs:
app_dir = os.path.abspath(os.path.join(go_path,lib))
if os.path.islink(app_dir): continue
git_path = os.path.join(app_dir, '.git')
bzr_path = os.path.join(app_dir, '.bzr')
hg_path = os.path.join(app_dir, '.hg')
svn_path = os.path.join(app_dir, '.svn')
if os.path.lexists(svn_path):
print("Updating svn %s" % app_dir)
os.chdir(app_dir)
call(['svn', 'update'])
elif os.path.lexists(git_path):
print("Updating git %s" % app_dir)
os.chdir(app_dir)
call(['git', 'checkout', 'master'])
call(['git', 'pull'])
elif os.path.lexists(bzr_path):
print("Updating bzr %s" % app_dir)
os.chdir(app_dir)
call(['bzr', 'pull'])
elif os.path.lexists(hg_path):
print("Updating hg %s" % app_dir)
os.chdir(app_dir)
call(['hg', 'pull', '-uv'])
else:
continue
call(['go', 'install'])

View File

@@ -202,6 +202,11 @@ func TestRound(t *testing.T) {
if result != expected {
t.Errorf("Error rounding to minute1: expected %v was %v", expected, result)
}
result = RoundTo(time.Second, 1*time.Second+500*time.Millisecond)
expected = 2 * time.Second
if result != expected {
t.Errorf("Error rounding to minute1: expected %v was %v", expected, result)
}
result = RoundTo(minute, 1*time.Second)
expected = minute
if result != expected {