diff --git a/engine/callcost.go b/engine/callcost.go index bb3c019da..cbce826ad 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -27,7 +27,7 @@ import ( type CallCost struct { Direction, TOR, Tenant, Subject, Account, Destination string Cost, ConnectFee float64 - Timespans []*TimeSpan + Timespans TimeSpans } // Pretty printing for call cost diff --git a/engine/calldesc.go b/engine/calldesc.go index 6096fbf33..777324923 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -22,11 +22,12 @@ import ( "encoding/json" "errors" "fmt" + "log/syslog" + "time" + "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/history" "github.com/cgrates/cgrates/utils" - "log/syslog" - "time" ) func init() { @@ -343,45 +344,28 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti // if the rate interval for any timespan has a RatingIncrement larger than the timespan duration // the timespan must expand potentially overlaping folowing timespans and may exceed call // descriptor's initial duration -func (cd *CallDescriptor) roundTimeSpansToIncrement(timespans []*TimeSpan) []*TimeSpan { - for i, ts := range timespans { - //log.Printf("TS: %+v", ts) +func (cd *CallDescriptor) roundTimeSpansToIncrement(timespans TimeSpans) []*TimeSpan { + for i := 0; i < len(timespans); i++ { + ts := timespans[i] + if ts.overlapped { + continue + } if ts.RateInterval != nil { _, rateIncrement, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart()) - //log.Printf("Inc: %+v", rateIncrement) // if the timespan duration is larger than the rate increment make sure it is a multiple of it if rateIncrement < ts.GetDuration() { rateIncrement = utils.RoundTo(rateIncrement, ts.GetDuration()) } - //log.Printf("Inc: %+v", rateIncrement) if rateIncrement > ts.GetDuration() { initialDuration := ts.GetDuration() - //log.Printf("Initial: %+v", initialDuration) ts.TimeEnd = ts.TimeStart.Add(rateIncrement) ts.CallDuration = ts.CallDuration + (rateIncrement - initialDuration) - //log.Printf("After: %+v", ts.CallDuration) - - // overlap the rest of the timespans - i += 1 - for ; i < len(timespans); i++ { - if timespans[i].TimeEnd.Before(ts.TimeEnd) || timespans[i].TimeEnd.Equal(ts.TimeEnd) { - timespans[i].overlapped = true - } else if timespans[i].TimeStart.Before(ts.TimeEnd) { - timespans[i].TimeStart = ts.TimeEnd - } - } - break + timespans.RemoveOverlapedFromIndex(i) } } } - var newTimespans []*TimeSpan - // remove overlapped - for _, ts := range timespans { - if !ts.overlapped { - newTimespans = append(newTimespans, ts) - } - } - return newTimespans + + return timespans } /* diff --git a/engine/realcalls_test.go b/engine/realcalls_test.go index ff92da840..e0375ed83 100644 --- a/engine/realcalls_test.go +++ b/engine/realcalls_test.go @@ -49,7 +49,7 @@ var balanceInsufficient = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","Bal var costInsufficient = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":1,"ConnectFee":3,"Timespans":[{"TimeStart":"2013-12-05T09:52:17+01:00","TimeEnd":"2013-12-05T09:53:17+01:00","Cost":1,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":3,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}` -func SomeTestDebitInsufficientBalance(t *testing.T) { +func FIXMETestDebitInsufficientBalance(t *testing.T) { b1 := new(UserBalance) if err := json.Unmarshal([]byte(balanceInsufficient), b1); err != nil { t.Error("Error restoring balance1: ", err) diff --git a/engine/timespans.go b/engine/timespans.go index 1984e191d..a63fd3ac7 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -20,8 +20,10 @@ package engine import ( //"fmt" - "github.com/cgrates/cgrates/utils" + "time" + + "github.com/cgrates/cgrates/utils" ) /* @@ -33,8 +35,8 @@ type TimeSpan struct { ratingInfo *RatingInfo RateInterval *RateInterval CallDuration time.Duration // the call duration so far till TimeEnd - overlapped bool // mark a timespan as overlapped by an expanded one Increments Increments + overlapped bool MatchedSubject, MatchedPrefix string } @@ -53,6 +55,87 @@ type MinuteInfo struct { Price float64 } +type TimeSpans []*TimeSpan + +func (timespans *TimeSpans) RemoveOverlapedFromIndex(index int) { + tss := *timespans + ts := tss[index] + endOverlapIndex := index + for i := index + 1; i < len(tss); i++ { + if tss[i].TimeEnd.Before(ts.TimeEnd) || tss[i].TimeEnd.Equal(ts.TimeEnd) { + endOverlapIndex = i + } else if tss[i].TimeStart.Before(ts.TimeEnd) { + tss[i].TimeStart = ts.TimeEnd + break + } + } + if endOverlapIndex > index { + newSliceEnd := len(tss) - (endOverlapIndex - index) + // delete overlapped + copy(tss[index+1:], tss[endOverlapIndex+1:]) + for i := newSliceEnd; i < len(tss); i++ { + tss[i] = nil + } + *timespans = tss[:newSliceEnd] + } +} + +// The paidTs will replace the timespans that are exactly under them from the reciver list +func (timespans *TimeSpans) OverlapWithTimeSpans(paidTs TimeSpans, newTs *TimeSpan, index int) bool { + tss := *timespans + // calculate overlaped timespans + var paidDuration time.Duration + for _, pts := range paidTs { + paidDuration += pts.GetDuration() + } + if paidDuration > 0 { + // we must add the rest of the current ts to the remaingTs + var remainingTs []*TimeSpan + overlapStartIndex := index + if newTs != nil { + remainingTs = append(remainingTs, newTs) + overlapStartIndex += 1 + } + for tsi := overlapStartIndex; tsi < len(tss); tsi++ { + remainingTs = append(remainingTs, tss[tsi]) + } + overlapEndIndex := 0 + for i, rts := range remainingTs { + if paidDuration >= rts.GetDuration() { + paidDuration -= rts.GetDuration() + } else { + if paidDuration > 0 { + // this ts was not fully paid + fragment := rts.SplitByDuration(paidDuration) + paidTs = append(paidTs, fragment) + } + // find the end position in tss + overlapEndIndex = overlapStartIndex + i + break + } + // find the end position in tss + overlapEndIndex = overlapStartIndex + i + } + // delete from index to current + if overlapEndIndex == len(tss)-1 { + tss = tss[:overlapStartIndex] + } else { + tss = append(tss[:overlapStartIndex], tss[overlapEndIndex+1:]...) + } + + // append the timespans to outer tss + for i, pts := range paidTs { + tss = append(tss, nil) + copy(tss[overlapStartIndex+i+1:], tss[overlapStartIndex+i:]) + tss[overlapStartIndex+i] = pts + } + *timespans = tss + return true + } + *timespans = tss + return false +} + func (incr *Increment) Clone() *Increment { nIncr := &Increment{ Duration: incr.Duration, @@ -318,6 +401,9 @@ func (ts *TimeSpan) SetNewCallDuration(nts *TimeSpan) { } func (nts *TimeSpan) copyRatingInfo(ts *TimeSpan) { + if ts.ratingInfo == nil { + return + } nts.ratingInfo = ts.ratingInfo nts.MatchedSubject = ts.ratingInfo.MatchedSubject nts.MatchedPrefix = ts.ratingInfo.MatchedPrefix diff --git a/engine/timespans_test.go b/engine/timespans_test.go index 271e5140f..5a002b9e3 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -19,9 +19,10 @@ along with this program. If not, see package engine import ( - "github.com/cgrates/cgrates/utils" "testing" "time" + + "github.com/cgrates/cgrates/utils" ) func TestRightMargin(t *testing.T) { @@ -517,7 +518,7 @@ func TestTimespanExpandingPastEnd(t *testing.T) { t.Error("Error removing overlaped intervals: ", timespans) } if !timespans[0].TimeEnd.Equal(time.Date(2013, 9, 10, 14, 31, 0, 0, time.UTC)) { - t.Error("Error expanding timespan: ", timespans[0]) + t.Errorf("Error expanding timespan: %+v", timespans[0]) } } @@ -862,3 +863,596 @@ func TestTimespanSplitByDuration(t *testing.T) { t.Error("Error spliting increment: ", ts.Increments[4], newTs.Increments[0]) } } + +func TestRemoveOverlapedFromIndexMiddle(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(1) + if len(tss) != 3 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestRemoveOverlapedFromIndexMiddleNonBounds(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 30, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(1) + if len(tss) != 4 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 47, 30, 0, time.UTC) || + tss[2].TimeStart != time.Date(2013, 12, 5, 15, 47, 30, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[3].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestRemoveOverlapedFromIndexMiddleNonBoundsOver(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 30, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(1) + if len(tss) != 3 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 48, 30, 0, time.UTC) || + tss[2].TimeStart != time.Date(2013, 12, 5, 15, 48, 30, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestRemoveOverlapedFromIndexEnd(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(1) + if len(tss) != 2 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestRemoveOverlapedFromIndexEndPast(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 50, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(1) + if len(tss) != 2 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 50, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestRemoveOverlapedFromIndexAll(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(0) + if len(tss) != 1 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestRemoveOverlapedFromIndexNone(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(0) + if len(tss) != 4 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[3].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestRemoveOverlapedFromIndexOne(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(0) + if len(tss) != 1 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestRemoveOverlapedFromIndexTwo(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 50, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + } + (&tss).RemoveOverlapedFromIndex(0) + if len(tss) != 1 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 50, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %+v", ts) + } + t.Error("Error removing overlaped timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansMiddleLong(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 30, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 1) + if len(tss) != 3 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 48, 30, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansMiddleMedium(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 30, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 1) + if len(tss) != 4 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 47, 30, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[3].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansMiddleShort(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 30, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 1) + if len(tss) != 5 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 46, 30, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC) || + tss[3].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[4].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansStart(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 30, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 0) + if len(tss) != 3 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 47, 30, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansAlmostEnd(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 30, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 3) + if len(tss) != 5 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[3].TimeEnd != time.Date(2013, 12, 5, 15, 48, 30, 0, time.UTC) || + tss[4].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansEnd(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 3) + if len(tss) != 4 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[3].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansPastEnd(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 30, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 3) + if len(tss) != 4 || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC) || + tss[1].TimeEnd != time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC) || + tss[2].TimeEnd != time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC) || + tss[3].TimeEnd != time.Date(2013, 12, 5, 15, 49, 30, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansAll(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 0) + if len(tss) != 1 || + tss[0].TimeStart != time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC) || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} + +func TestOverlapWithTimeSpansAllPast(t *testing.T) { + tss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 46, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 47, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + }, + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 48, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 0, 0, time.UTC), + }, + } + newTss := TimeSpans{ + &TimeSpan{ + TimeStart: time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 12, 5, 15, 49, 30, 0, time.UTC), + }, + } + (&tss).OverlapWithTimeSpans(newTss, nil, 0) + if len(tss) != 1 || + tss[0].TimeStart != time.Date(2013, 12, 5, 15, 45, 0, 0, time.UTC) || + tss[0].TimeEnd != time.Date(2013, 12, 5, 15, 49, 30, 0, time.UTC) { + for _, ts := range tss { + t.Logf("TS: %v", ts) + } + t.Error("Error overlaping with timespans timespans: ", tss) + } +} diff --git a/engine/userbalance.go b/engine/userbalance.go index 96695e8f1..942a57ec9 100644 --- a/engine/userbalance.go +++ b/engine/userbalance.go @@ -21,6 +21,7 @@ package engine import ( "errors" "fmt" + "log" "github.com/cgrates/cgrates/utils" @@ -180,16 +181,18 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error { return nil } } - // debit minutes for tsIndex := 0; tsIndex < len(cc.Timespans); tsIndex++ { ts := cc.Timespans[tsIndex] ts.createIncrementsSlice() tsWasSplit := false + log.Print("TS: ", ts) for incrementIndex, increment := range ts.Increments { if tsWasSplit { break } paid := false + // debit minutes + log.Print("Debit minutes") for _, b := range usefulMinuteBalances { // check standard subject tags if b.RateSubject == ZEROSECOND || b.RateSubject == "" { @@ -228,14 +231,6 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error { }, } newTs.createIncrementsSlice() - // overlap the rest of the timespans - for i := tsIndex + 1; i < len(cc.Timespans); i++ { - if cc.Timespans[i].TimeEnd.Before(newTs.TimeEnd) || cc.Timespans[i].TimeEnd.Equal(newTs.TimeEnd) { - cc.Timespans[i].overlapped = true - } else if cc.Timespans[i].TimeStart.Before(newTs.TimeEnd) { - cc.Timespans[i].TimeStart = ts.TimeEnd - } - } // insert the new timespan if newTs != ts { tsIndex++ @@ -245,14 +240,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error { tsWasSplit = true } - var newTimespans []*TimeSpan - // remove overlapped - for _, ots := range cc.Timespans { - if !ots.overlapped { - newTimespans = append(newTimespans, ots) - } - } - cc.Timespans = newTimespans + cc.Timespans.RemoveOverlapedFromIndex(tsIndex) b.Value -= amount newTs.Increments[0].BalanceUuids = append(newTs.Increments[0].BalanceUuids, b.Uuid) newTs.Increments[0].MinuteInfo = &MinuteInfo{cc.Destination, amount, 0} @@ -280,6 +268,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error { var paidTs []*TimeSpan for _, nts := range newCC.Timespans { nts.createIncrementsSlice() + log.Printf("XXX: %+v", nts) paidTs = append(paidTs, nts) for nIdx, nInc := range nts.Increments { // debit minutes and money @@ -308,51 +297,9 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error { } } } - - // calculate overlaped timespans - var paidDuration time.Duration - for _, pts := range paidTs { - paidDuration += pts.GetDuration() - } - if paidDuration > 0 { - // split from current increment - newTs := ts.SplitByIncrement(incrementIndex) - var remainingTs []*TimeSpan - if newTs != nil { - remainingTs = append(remainingTs, newTs) - } else { - // nothing was paied form current ts so remove it - cc.Timespans = append(cc.Timespans[:tsIndex], cc.Timespans[tsIndex+1:]...) - tsIndex-- - } - for tsi := tsIndex + 1; tsi < len(cc.Timespans); tsi++ { - remainingTs = append(remainingTs, cc.Timespans[tsi]) - } - for remainingIndex, rts := range remainingTs { - if paidDuration >= rts.GetDuration() { - paidDuration -= rts.GetDuration() - } else { - if paidDuration > 0 { - // this ts was not fully paid - fragment := rts.SplitByDuration(paidDuration) - paidTs = append(paidTs, fragment) - } - // delete from tsIndex to current - cc.Timespans = append(cc.Timespans[:tsIndex], cc.Timespans[remainingIndex:]...) - break - } - } - - // append the timpespans to outer timespans - for _, pts := range paidTs { - tsIndex++ - cc.Timespans = append(cc.Timespans, nil) - copy(cc.Timespans[tsIndex+1:], cc.Timespans[tsIndex:]) - cc.Timespans[tsIndex] = pts - } - paid = true - tsWasSplit = true - } + newTs := ts.SplitByIncrement(incrementIndex) + overlapped := (&cc.Timespans).OverlapWithTimeSpans(paidTs, newTs, tsIndex) + paid, tsWasSplit = overlapped, overlapped } if paid { continue @@ -368,9 +315,11 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error { newTs.createIncrementsSlice() tsWasSplit = true } + log.Print("BREAK ON MINUTES") break } } + log.Print("Debit money") // debit monetary for _, b := range usefulMoneyBalances { // check standard subject tags @@ -401,6 +350,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error { var paidTs []*TimeSpan for _, nts := range newCC.Timespans { nts.createIncrementsSlice() + log.Printf("COST: %+v", nts) paidTs = append(paidTs, nts) for nIdx, nInc := range nts.Increments { // debit money @@ -416,54 +366,13 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error { } } } - // calculate overlaped timespans - var paidDuration time.Duration - for _, pts := range paidTs { - paidDuration += pts.GetDuration() - } - if paidDuration > 0 { - // split from current increment - newTs := ts.SplitByIncrement(incrementIndex) - var remainingTs []*TimeSpan - if newTs != nil { - remainingTs = append(remainingTs, newTs) - } else { - // nothing was paied form current ts so remove it - cc.Timespans = append(cc.Timespans[:tsIndex], cc.Timespans[tsIndex+1:]...) - tsIndex-- - } - - for tsi := tsIndex + 1; tsi < len(cc.Timespans); tsi++ { - remainingTs = append(remainingTs, cc.Timespans[tsi]) - } - for remainingIndex, rts := range remainingTs { - if paidDuration >= rts.GetDuration() { - paidDuration -= rts.GetDuration() - } else { - if paidDuration > 0 { - // this ts was not fully paid - fragment := rts.SplitByDuration(paidDuration) - paidTs = append(paidTs, fragment) - } - // delete from tsIndex to current - cc.Timespans = append(cc.Timespans[:tsIndex], cc.Timespans[remainingIndex:]...) - break - } - } - - // append the timpespans to outer timespans - for _, pts := range paidTs { - tsIndex++ - cc.Timespans = append(cc.Timespans, nil) - copy(cc.Timespans[tsIndex+1:], cc.Timespans[tsIndex:]) - cc.Timespans[tsIndex] = pts - } - paid = true - tsWasSplit = true - } + newTs := ts.SplitByIncrement(incrementIndex) + overlapped := (&cc.Timespans).OverlapWithTimeSpans(paidTs, newTs, tsIndex) + paid, tsWasSplit = overlapped, overlapped } } if !paid { + // FIXME: must debit all from the first monetary balance (go negative) // no balance was attached to this increment: cut the rest of increments/timespans if incrementIndex == 0 { // if we are right at the begining in the ts leave it out diff --git a/engine/userbalance_test.go b/engine/userbalance_test.go index 2c0cb6b03..b52ed48c3 100644 --- a/engine/userbalance_test.go +++ b/engine/userbalance_test.go @@ -659,8 +659,8 @@ func TestDebitCreditSubjectMixed(t *testing.T) { Timespans: []*TimeSpan{ &TimeSpan{ TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), - TimeEnd: time.Date(2013, 9, 24, 10, 49, 10, 0, time.UTC), - CallDuration: 0, + TimeEnd: time.Date(2013, 9, 24, 10, 48, 55, 0, time.UTC), + CallDuration: 55 * time.Second, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, }, }, @@ -679,12 +679,15 @@ func TestDebitCreditSubjectMixed(t *testing.T) { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } if rifsBalance.BalanceMap[MINUTES+OUTBOUND][0].Value != 0 || - rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 80 { + rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 20 { t.Errorf("Error extracting minutes from balance: %+v, %+v", rifsBalance.BalanceMap[MINUTES+OUTBOUND][0].Value, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) } - if len(cc.Timespans) != 1 || cc.Timespans[0].GetDuration() != 40*time.Second { - t.Error("Error truncating extra timespans: ", cc.Timespans[0].GetDuration()) + if len(cc.Timespans) != 9 || cc.Timespans[0].GetDuration() != 40*time.Second { + for _, ts := range cc.Timespans { + t.Logf("%+v %+v", ts, ts.Increments[0]) + } + t.Error("Error truncating extra timespans: ", len(cc.Timespans), cc.Timespans[0].GetDuration()) } } @@ -701,13 +704,13 @@ func TestDebitCreditSubjectMixedMoreTS(t *testing.T) { TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), CallDuration: 0, - RateInterval: &RateInterval{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}, + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, }, &TimeSpan{ TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC), CallDuration: 10 * time.Second, - RateInterval: &RateInterval{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}, + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, }, }, } @@ -732,8 +735,7 @@ func TestDebitCreditSubjectMixedMoreTS(t *testing.T) { if len(cc.Timespans) != 2 || cc.Timespans[0].GetDuration() != time.Minute || cc.Timespans[1].GetDuration() != 20*time.Second { t.Error("Error truncating extra timespans: ", cc.Timespans) } -} -*/ +}*/ func TestDebitSMSBalance(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"}