mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
still working on balance subject debit
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
49
hard_update_external_libs.py
Executable 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'])
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user