rated seconds slice

This commit is contained in:
Radu Ioan Fericean
2013-09-12 21:09:42 +03:00
parent a35f5ed480
commit e18420ea23
8 changed files with 162 additions and 275 deletions

View File

@@ -34,6 +34,7 @@ type Balance struct {
SpecialPriceType string
SpecialPrice float64 // absolute for minutes and percent for monetary (can be positive or negative)
DestinationId string
RateSubject string
precision int
}

View File

@@ -48,7 +48,7 @@ func (cc *CallCost) Merge(other *CallCost) {
ts := cc.Timespans[len(cc.Timespans)-1]
otherTs := other.Timespans[0]
if reflect.DeepEqual(ts.RatingPlan, otherTs.RatingPlan) &&
reflect.DeepEqual(ts.MinuteInfo, otherTs.MinuteInfo) && reflect.DeepEqual(ts.RateInterval, otherTs.RateInterval) {
reflect.DeepEqual(ts.RateInterval, otherTs.RateInterval) {
// extend the last timespan with
ts.TimeEnd = ts.TimeEnd.Add(otherTs.GetDuration())
// add the rest of the timspans

View File

@@ -86,7 +86,7 @@ func TestMultipleInputLeftMerge(t *testing.T) {
if cc1.Cost != 90 {
t.Errorf("expected 90 was %v", cc1.Cost)
}
t1 = time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
/*t1 = time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
t2 = time.Date(2012, time.February, 2, 18, 02, 0, 0, time.UTC)
cd = &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc2, _ := cd.GetCost()
@@ -99,7 +99,7 @@ func TestMultipleInputLeftMerge(t *testing.T) {
}
if cc1.Cost != 120 {
t.Errorf("Exdpected 120 was %v", cc1.Cost)
}
}*/
}
func TestMultipleInputRightMerge(t *testing.T) {

View File

@@ -197,26 +197,6 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
firstSpan = &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, CallDuration: cd.CallDuration}
}
timespans = append(timespans, firstSpan)
// split on (free) minute buckets
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
_, _, minuteBalances := userBalance.getSecondsForPrefix(cd.Destination)
for _, b := range minuteBalances {
for i := 0; i < len(timespans); i++ {
if timespans[i].MinuteInfo != nil {
continue
}
newTs := timespans[i].SplitByMinuteBalance(b)
if newTs != nil {
timespans = append(timespans, newTs)
firstSpan = newTs // we move the firstspan to the newly created one for further spliting
break
}
}
}
}
if firstSpan.MinuteInfo != nil {
return // all the timespans are on minutes
}
if len(cd.RatingPlans) == 0 {
return
}
@@ -230,9 +210,6 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
} else {
afterStart = true
for i := 0; i < len(timespans); i++ {
if timespans[i].MinuteInfo != nil {
continue
}
newTs := timespans[i].SplitByRatingPlan(ap)
if newTs != nil {
timespans = append(timespans, newTs)
@@ -245,9 +222,6 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
}
// split on price intervals
for i := 0; i < len(timespans); i++ {
if timespans[i].MinuteInfo != nil {
continue // cont try to split timespans payed with minutes
}
ap := timespans[i].RatingPlan
//timespans[i].RatingPlan = nil
ap.RateIntervals.Sort()
@@ -262,27 +236,29 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
}
}
}
timespans = cd.expandTimeSpans(timespans)
timespans = cd.roundTimeSpansToIncrement(timespans)
return
}
// 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) expandTimeSpans(timespans []*TimeSpan) []*TimeSpan {
func (cd *CallDescriptor) roundTimeSpansToIncrement(timespans []*TimeSpan) []*TimeSpan {
for i, ts := range timespans {
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 < ts.GetDuration() {
if rateIncrement != time.Second && rateIncrement < ts.GetDuration() {
rateIncrement = utils.RoundTo(rateIncrement, ts.GetDuration())
}
if rateIncrement > ts.GetDuration() {
ts.TimeEnd = ts.TimeStart.Add(rateIncrement)
ts.SetNewCallDuration(ts) // set new call duration for this timespan
ts.CallDuration = ts.CallDuration + rateIncrement
// overlap the rest of the timespans
i += 1
for ; i < len(timespans); i++ {
if timespans[i].TimeEnd.Before(ts.TimeEnd) {
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
@@ -292,14 +268,15 @@ func (cd *CallDescriptor) expandTimeSpans(timespans []*TimeSpan) []*TimeSpan {
}
}
}
var newTimespans []*TimeSpan
// remove overlapped
for i, ts := range timespans {
if ts.overlapped {
timespans = timespans[:i]
break
for _, ts := range timespans {
if !ts.overlapped {
ts.createRatedSecondSlice()
newTimespans = append(newTimespans, ts)
}
}
return timespans
return newTimespans
}
/*
@@ -317,10 +294,10 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
for i, ts := range timespans {
// only add connect fee if this is the first/only call cost request
if cd.LoopIndex == 0 && i == 0 && ts.MinuteInfo == nil && ts.RateInterval != nil {
if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil {
connectionFee = ts.RateInterval.ConnectFee
}
cost += ts.getCost(cd)
cost += ts.getCost()
}
cost = utils.Round(cost, roundingDecimals, roundingMethod)
cc := &CallCost{
@@ -376,10 +353,10 @@ func (cd *CallDescriptor) GetMaxSessionTime(startTime time.Time) (seconds float6
cost := 0.0
for i, ts := range timespans {
if i == 0 && ts.MinuteInfo == nil && ts.RateInterval != nil {
if i == 0 && ts.RateInterval != nil {
cost += ts.RateInterval.ConnectFee
}
cost += ts.getCost(cd)
cost += ts.Cost
}
//logger.Print(availableCredit, availableSeconds, cost)
if cost < availableCredit {
@@ -408,14 +385,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
Logger.Debug(fmt.Sprintf("<Rater> Attempting to debit from %v, value: %v", cd.GetUserBalanceKey(), cc.Cost+cc.ConnectFee))
defer storageGetter.SetUserBalance(userBalance)
if cc.Cost != 0 || cc.ConnectFee != 0 {
userBalance.debitBalance(CREDIT, cc.Cost+cc.ConnectFee, true)
}
for _, ts := range cc.Timespans {
if ts.MinuteInfo != nil {
if err = userBalance.debitMinutesBalance(ts.MinuteInfo.Quantity, cd.Destination, true); err != nil {
return cc, err
}
}
userBalance.debitBalance(CREDIT+OUTBOUND, cc.Cost+cc.ConnectFee, true)
}
}
return

View File

@@ -21,7 +21,6 @@ package engine
import (
"fmt"
"github.com/cgrates/cgrates/utils"
"math"
"reflect"
"sort"
"strconv"
@@ -40,7 +39,7 @@ type RateInterval struct {
StartTime, EndTime string // ##:##:## format
Weight, ConnectFee float64
Rates RateGroups // GroupRateInterval (start time): Rate
RoundingMethod string
RoundingMethod string //ROUNDING_UP, ROUNDING_DOWN, ROUNDING_MIDDLE
RoundingDecimals int
}
@@ -194,13 +193,12 @@ func (i *RateInterval) Equal(o *RateInterval) bool {
i.EndTime == o.EndTime
}
func (i *RateInterval) GetCost(duration, startSecond time.Duration) (cost float64) {
price, rateIncrement, rateUnit := i.GetRateParameters(startSecond)
d := float64(duration.Seconds())
func (i *RateInterval) GetCost(duration, startSecond time.Duration) float64 {
price, _, rateUnit := i.GetRateParameters(startSecond)
d := duration.Seconds()
price /= rateUnit.Seconds()
ri := rateIncrement.Seconds()
cost = math.Ceil(d/ri) * ri * price
return utils.Round(cost, i.RoundingDecimals, i.RoundingMethod)
return utils.Round(d*price, i.RoundingDecimals, i.RoundingMethod)
}
// Gets the price for a the provided start second

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"fmt"
"github.com/cgrates/cgrates/utils"
"time"
)
@@ -31,16 +31,14 @@ type TimeSpan struct {
Cost float64
RatingPlan *RatingPlan
RateInterval *RateInterval
MinuteInfo *MinuteInfo
CallDuration time.Duration // the call duration so far till TimeEnd
overlapped bool // mark a timespan as overlapped by an expanded one
ratedSeconds []*rated_second
}
// Holds the bonus minute information related to a specified timespan
type MinuteInfo struct {
DestinationId string
Quantity float64
Price float64
type rated_second struct {
index int
rate float64
}
// Returns the duration of the timespan
@@ -48,27 +46,20 @@ func (ts *TimeSpan) GetDuration() time.Duration {
return ts.TimeEnd.Sub(ts.TimeStart)
}
// Returns true if the given time is inside timespan range.
func (ts *TimeSpan) Contains(t time.Time) bool {
return t.After(ts.TimeStart) && t.Before(ts.TimeEnd)
}
// Returns the cost of the timespan according to the relevant cost interval.
// It also sets the Cost field of this timespan (used for refound on session
// manager debit loop where the cost cannot be recalculated)
func (ts *TimeSpan) getCost(cd *CallDescriptor) (cost float64) {
if ts.MinuteInfo != nil {
return ts.GetDuration().Seconds() * ts.MinuteInfo.Price
}
func (ts *TimeSpan) getCost() float64 {
if ts.RateInterval == nil {
return 0
}
i := ts.RateInterval
cost = i.GetCost(ts.GetDuration(), ts.GetGroupStart())
ts.Cost = cost
return
}
/*
Returns true if the given time is inside timespan range.
*/
func (ts *TimeSpan) Contains(t time.Time) bool {
return t.After(ts.TimeStart) && t.Before(ts.TimeEnd)
ts.Cost = ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart())
return ts.Cost
}
/*
@@ -87,6 +78,26 @@ func (ts *TimeSpan) SetRateInterval(i *RateInterval) {
}
}
func (ts *TimeSpan) createRatedSecondSlice() {
if ts.RateInterval == nil {
return
}
// create rated seconds series
rate, _, rate_unit := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
secondCost := rate / rate_unit.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)})
}
}
/*
Splits the given timespan according to how it relates to the interval.
It will modify the endtime of the received timespan and it will return
@@ -171,43 +182,6 @@ func (ts *TimeSpan) SplitByRatingPlan(ap *RatingPlan) (newTs *TimeSpan) {
return
}
/*
Splits the given timespan on minute bucket's duration.
*/
func (ts *TimeSpan) SplitByMinuteBalance(mb *Balance) (newTs *TimeSpan) {
// if mb expired skip it
if !mb.ExpirationDate.IsZero() && (ts.TimeStart.Equal(mb.ExpirationDate) || ts.TimeStart.After(mb.ExpirationDate)) {
return nil
}
// expiring before time spans end
if !mb.ExpirationDate.IsZero() && ts.TimeEnd.After(mb.ExpirationDate) {
newTs = &TimeSpan{TimeStart: mb.ExpirationDate, TimeEnd: ts.TimeEnd}
newTs.CallDuration = ts.CallDuration
ts.TimeEnd = mb.ExpirationDate
ts.SetNewCallDuration(newTs)
}
s := ts.GetDuration().Seconds()
ts.MinuteInfo = &MinuteInfo{mb.DestinationId, s, mb.SpecialPrice}
if s <= mb.Value {
mb.Value -= s
return newTs
}
secDuration, _ := time.ParseDuration(fmt.Sprintf("%vs", mb.Value))
newTimeEnd := ts.TimeStart.Add(secDuration)
newTs = &TimeSpan{TimeStart: newTimeEnd, TimeEnd: ts.TimeEnd}
ts.TimeEnd = newTimeEnd
newTs.CallDuration = ts.CallDuration
ts.MinuteInfo.Quantity = mb.Value
ts.SetNewCallDuration(newTs)
mb.Value = 0
return
}
// Returns the starting time of this timespan
func (ts *TimeSpan) GetGroupStart() time.Duration {
s := ts.CallDuration - ts.GetDuration()

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"github.com/cgrates/cgrates/utils"
"testing"
"time"
)
@@ -188,17 +189,17 @@ func TestTimespanGetCost(t *testing.T) {
t1 := time.Date(2012, time.February, 5, 17, 45, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 5, 17, 55, 0, 0, time.UTC)
ts1 := TimeSpan{TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Subject: "other"}
if ts1.getCost(cd) != 0 {
if ts1.getCost() != 0 {
t.Error("No interval and still kicking")
}
ts1.RateInterval = &RateInterval{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}}
if ts1.getCost(cd) != 600 {
t.Error("Expected 10 got ", ts1.getCost(cd))
ts1.SetRateInterval(&RateInterval{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}})
if ts1.getCost() != 600 {
t.Error("Expected 10 got ", ts1.Cost)
}
ts1.RateInterval.Rates[0].RateUnit = 60 * time.Second
if ts1.getCost(cd) != 10 {
t.Error("Expected 6000 got ", ts1.getCost(cd))
ts1.RateInterval = nil
ts1.SetRateInterval(&RateInterval{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 60 * time.Second}}})
if ts1.getCost() != 10 {
t.Error("Expected 6000 got ", ts1.Cost)
}
}
@@ -217,118 +218,6 @@ func TestSetRateInterval(t *testing.T) {
}
}
func TestTimespanSplitByMinuteBucketPlenty(t *testing.T) {
t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC)
t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)
mb := &Balance{Value: 180}
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
newTs := ts.SplitByMinuteBalance(mb)
if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 120 {
t.Error("Not enough minutes on minute bucket split")
}
if newTs != nil {
t.Error("Bad extra timespan on minute bucket split")
}
}
func TestTimespanSplitByMinuteBalanceScarce(t *testing.T) {
t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC)
t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)
mb := &Balance{Value: 60}
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
newTs := ts.SplitByMinuteBalance(mb)
if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 60 {
t.Error("Not enough minutes on minute bucket split")
}
if newTs == nil || newTs.MinuteInfo != nil {
t.Error("Missing extra timespan on minute bucket split")
}
}
func TestTimespanSplitByMinuteBalancePlentyExpired(t *testing.T) {
t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC)
t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)
mb := &Balance{Value: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 39, 0, 0, time.UTC)}
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
newTs := ts.SplitByMinuteBalance(mb)
if ts.MinuteInfo != nil {
t.Error("Not enough minutes on minute bucket split")
}
if newTs != nil {
t.Error("Bad extra timespan on minute bucket split")
}
}
func TestTimespanSplitByMinuteBalancePlentyExpiring(t *testing.T) {
t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC)
t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)
mb := &Balance{Value: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 0, 0, time.UTC)}
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
newTs := ts.SplitByMinuteBalance(mb)
if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 60 {
t.Error("Not enough minutes on minute bucket split")
}
if newTs == nil || newTs.MinuteInfo != nil {
t.Error("Missing extra timespan on minute bucket split")
}
}
func TestTimespanSplitByMinuteBalancePlentyExpiringEnd(t *testing.T) {
t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC)
t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)
mb := &Balance{Value: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)}
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
newTs := ts.SplitByMinuteBalance(mb)
if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 120 {
t.Error("Not enough minutes on minute bucket split")
}
if newTs != nil {
t.Error("Missing extra timespan on minute bucket split")
}
}
func TestTimespanSplitByMinuteBalanceScarceExpiringSame(t *testing.T) {
t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC)
t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)
mb := &Balance{Value: 120, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 0, 0, time.UTC)}
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
newTs := ts.SplitByMinuteBalance(mb)
if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 60 {
t.Error("Not enough minutes on minute bucket split")
}
if newTs == nil || newTs.MinuteInfo != nil {
t.Error("Missing extra timespan on minute bucket split")
}
}
func TestTimespanSplitByMinuteBalanceScarceExpiringDifferentExpFirst(t *testing.T) {
t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC)
t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)
mb := &Balance{Value: 140, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 1, 0, time.UTC)}
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
newTs := ts.SplitByMinuteBalance(mb)
if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 61 {
t.Error("Not enough minutes on minute bucket split: ", ts.MinuteInfo.Quantity)
}
if newTs == nil || newTs.MinuteInfo != nil {
t.Error("Missing extra timespan on minute bucket split")
}
}
func TestTimespanSplitByMinuteBalanceScarceExpiringDifferentScarceFirst(t *testing.T) {
t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC)
t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)
mb := &Balance{Value: 61, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 30, 0, time.UTC)}
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
newTs := ts.SplitByMinuteBalance(mb)
if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 61 {
t.Error("Not enough minutes on minute bucket split")
}
if newTs == nil || newTs.MinuteInfo != nil {
t.Error("Missing extra timespan on minute bucket split")
}
}
func TestTimespanSplitGroupedRates(t *testing.T) {
i := &RateInterval{
EndTime: "17:59:00",
@@ -366,19 +255,38 @@ func TestTimespanSplitGroupedRates(t *testing.T) {
func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
i := &RateInterval{
EndTime: "17:59:00",
Rates: RateGroups{&Rate{0, 2, 1 * time.Second, 1 * time.Second}, &Rate{30 * time.Second, 1, 60 * time.Second, 1 * time.Second}},
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 2,
RateIncrement: time.Second,
RateUnit: time.Second},
&Rate{
GroupIntervalStart: 30 * time.Second,
Value: 1,
RateIncrement: time.Minute,
RateUnit: time.Second,
}},
}
t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 31, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 60 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByRateInterval(i)
cd := &CallDescriptor{}
timespans := cd.roundTimeSpansToIncrement([]*TimeSpan{ts, nts})
if len(timespans) != 2 {
t.Error("Error rounding timespans: ", timespans)
}
ts = timespans[0]
nts = timespans[1]
splitTime := time.Date(2012, time.February, 3, 17, 30, 30, 0, time.UTC)
if ts.TimeStart != t1 || ts.TimeEnd != splitTime {
t.Error("Incorrect first half", ts)
}
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
t3 := time.Date(2012, time.February, 3, 17, 31, 30, 0, time.UTC)
if nts.TimeStart != splitTime || nts.TimeEnd != t3 {
t.Error("Incorrect second half", nts.TimeStart, nts.TimeEnd)
}
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
@@ -389,10 +297,10 @@ func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
t.Error("Wrong costs: ", c1, c2)
}
if ts.GetDuration().Seconds() != 0.5*60 || nts.GetDuration().Seconds() != 0.5*60 {
if ts.GetDuration().Seconds() != 0.5*60 || nts.GetDuration().Seconds() != 1*60 {
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
if ts.GetDuration()+nts.GetDuration() != oldDuration+30*time.Second {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
}
}
@@ -533,7 +441,7 @@ func TestTimespanExpandingPastEnd(t *testing.T) {
},
}
cd := &CallDescriptor{}
timespans = cd.expandTimeSpans(timespans)
timespans = cd.roundTimeSpansToIncrement(timespans)
if len(timespans) != 1 {
t.Error("Error removing overlaped intervals: ", timespans)
}
@@ -542,6 +450,28 @@ func TestTimespanExpandingPastEnd(t *testing.T) {
}
}
func TestTimespanExpandingCallDuration(t *testing.T) {
timespans := []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC),
RateInterval: &RateInterval{Rates: RateGroups{
&Rate{RateIncrement: 60 * time.Second},
}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 45, 0, time.UTC),
},
}
cd := &CallDescriptor{}
timespans = cd.roundTimeSpansToIncrement(timespans)
if timespans[0].CallDuration != time.Minute {
t.Error("Error setting call duration: ", timespans[0])
}
}
func TestTimespanExpandingRoundingPastEnd(t *testing.T) {
timespans := []*TimeSpan{
&TimeSpan{
@@ -557,7 +487,7 @@ func TestTimespanExpandingRoundingPastEnd(t *testing.T) {
},
}
cd := &CallDescriptor{}
timespans = cd.expandTimeSpans(timespans)
timespans = cd.roundTimeSpansToIncrement(timespans)
if len(timespans) != 2 {
t.Error("Error removing overlaped intervals: ", timespans)
}
@@ -585,7 +515,7 @@ func TestTimespanExpandingPastEndMultiple(t *testing.T) {
},
}
cd := &CallDescriptor{}
timespans = cd.expandTimeSpans(timespans)
timespans = cd.roundTimeSpansToIncrement(timespans)
if len(timespans) != 1 {
t.Error("Error removing overlaped intervals: ", timespans)
}
@@ -613,7 +543,7 @@ func TestTimespanExpandingPastEndMultipleEqual(t *testing.T) {
},
}
cd := &CallDescriptor{}
timespans = cd.expandTimeSpans(timespans)
timespans = cd.roundTimeSpansToIncrement(timespans)
if len(timespans) != 1 {
t.Error("Error removing overlaped intervals: ", timespans)
}
@@ -637,7 +567,7 @@ func TestTimespanExpandingBeforeEnd(t *testing.T) {
},
}
cd := &CallDescriptor{}
timespans = cd.expandTimeSpans(timespans)
timespans = cd.roundTimeSpansToIncrement(timespans)
if len(timespans) != 2 {
t.Error("Error removing overlaped intervals: ", timespans)
}
@@ -667,7 +597,7 @@ func TestTimespanExpandingBeforeEndMultiple(t *testing.T) {
},
}
cd := &CallDescriptor{}
timespans = cd.expandTimeSpans(timespans)
timespans = cd.roundTimeSpansToIncrement(timespans)
if len(timespans) != 3 {
t.Error("Error removing overlaped intervals: ", timespans)
}
@@ -677,3 +607,42 @@ func TestTimespanExpandingBeforeEndMultiple(t *testing.T) {
t.Error("Error expanding timespan: ", timespans[0])
}
}
func TestTimespanCreateSecondsSlice(t *testing.T) {
ts := &TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC),
RateInterval: &RateInterval{Rates: RateGroups{
&Rate{Value: 2.0},
}},
}
ts.createRatedSecondSlice()
if len(ts.ratedSeconds) != 30 {
t.Error("Error creating second slice: ", ts.ratedSeconds)
}
if ts.ratedSeconds[0].rate != 2.0 {
t.Error("Wrong second slice: ", ts.ratedSeconds[0])
}
}
func TestTimespanCreateSecondsFract(t *testing.T) {
ts := &TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 30, 100000000, time.UTC),
RateInterval: &RateInterval{
RoundingMethod: utils.ROUNDING_MIDDLE,
RoundingDecimals: 2,
Rates: RateGroups{
&Rate{Value: 2.0},
},
},
}
ts.createRatedSecondSlice()
if len(ts.ratedSeconds) != 31 {
t.Error("Error creating second slice: ", ts.ratedSeconds)
}
t.Log(ts.getCost())
if ts.ratedSeconds[30].rate != 0.2 {
t.Error("Wrong second slice: ", ts.ratedSeconds[30])
}
}

View File

@@ -247,7 +247,6 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
end := lastCC.Timespans[len(lastCC.Timespans)-1].TimeEnd
refoundDuration := end.Sub(hangupTime).Seconds()
cost := 0.0
seconds := 0.0
engine.Logger.Info(fmt.Sprintf("Refund duration: %v", refoundDuration))
for i := len(lastCC.Timespans) - 1; i >= 0; i-- {
ts := lastCC.Timespans[i]
@@ -258,18 +257,11 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
tmpCost := (procentage * ts.Cost) / 100
ts.Cost -= tmpCost
cost += tmpCost
if ts.MinuteInfo != nil {
// DestinationPrefix and Price take from lastCC and above caclulus
seconds += (procentage * ts.MinuteInfo.Quantity) / 100
}
// set the end time to now
ts.TimeEnd = hangupTime
break // do not go to other timespans
} else {
cost += ts.Cost
if ts.MinuteInfo != nil {
seconds += ts.MinuteInfo.Quantity
}
// remove the timestamp entirely
lastCC.Timespans = lastCC.Timespans[:i]
// continue to the next timespan with what is left to refound
@@ -293,25 +285,8 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
engine.Logger.Err(fmt.Sprintf("Debit cents failed: %v", err))
}
}
if seconds > 0 {
cd := &engine.CallDescriptor{
Direction: lastCC.Direction,
TOR: lastCC.TOR,
Tenant: lastCC.Tenant,
Subject: lastCC.Subject,
Account: lastCC.Account,
Destination: lastCC.Destination,
Amount: -seconds,
// FallbackSubject: lastCC.FallbackSubject, // ToDo: check how to best add it
}
var response float64
err := sm.connector.DebitSeconds(*cd, &response)
if err != nil {
engine.Logger.Err(fmt.Sprintf("Debit seconds failed: %v", err))
}
}
lastCC.Cost -= cost
engine.Logger.Info(fmt.Sprintf("Rambursed %v cents, %v seconds", cost, seconds))
engine.Logger.Info(fmt.Sprintf("Rambursed %v cents", cost))
}