better rounding in callcost

This commit is contained in:
Radu Ioan Fericean
2015-11-08 12:33:56 +02:00
parent e83c5a4839
commit 73685bb336
8 changed files with 53 additions and 26 deletions

View File

@@ -153,13 +153,14 @@ func (cc *CallCost) AsJSON() string {
return string(ccJson)
}
func (cc *CallCost) UpdateCost() {
func (cc *CallCost) updateCost() {
cost := 0.0
if cc.deductConnectFee { // add back the connectFee
cost += cc.GetConnectFee()
}
for _, ts := range cc.Timespans {
cost += ts.getCost()
ts.Cost = ts.calculateCost()
cost += ts.Cost
cost = utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals
}
cc.Cost = cost

View File

@@ -450,7 +450,8 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
// handle max cost
maxCost, strategy := ts.RateInterval.GetMaxCost()
cost += ts.getCost()
ts.Cost = ts.calculateCost()
cost += ts.Cost
cd.MaxCostSoFar += cost
//log.Print("Before: ", cost)
if strategy != "" && maxCost > 0 {
@@ -496,7 +497,7 @@ func (cd *CallDescriptor) getCost() (*CallCost, error) {
if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil {
cost += ts.RateInterval.Rating.ConnectFee
}
cost += ts.getCost()
cost += ts.calculateCost()
}
//startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.Category))
@@ -636,7 +637,7 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool)
utils.Logger.Err(fmt.Sprintf("<Rater> Error getting cost for account key <%s>: %s", cd.GetAccountKey(), err.Error()))
return nil, err
}
cc.UpdateCost()
cc.updateCost()
cc.Timespans.Compress()
//log.Printf("OUT CC: ", cc)
return

View File

@@ -576,10 +576,43 @@ func TestGetCostRoundingIssue(t *testing.T) {
cc, err := cd.GetCost()
expected := 0.17
if cc.Cost != expected || err != nil {
t.Log(utils.ToIJSON(cc))
t.Errorf("Expected %v was %+v", expected, cc)
}
}
func TestGetCostMaxDebitRoundingIssue(t *testing.T) {
ap, _ := ratingStorage.GetActionPlans("TOPUP10_AT")
for _, at := range ap {
at.Execute()
}
cd := &CallDescriptor{
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "dy",
Account: "dy",
Destination: "0723123113",
TimeStart: time.Date(2015, 10, 26, 13, 29, 27, 0, time.UTC),
TimeEnd: time.Date(2015, 10, 26, 13, 29, 51, 0, time.UTC),
MaxCostSoFar: 0,
}
acc, err := accountingStorage.GetAccount("cgrates.org:dy")
if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1 {
t.Errorf("Error getting account: %+v (%v)", utils.ToIJSON(acc), err)
}
cc, err := cd.MaxDebit()
expected := 0.39
if cc.Cost != expected || err != nil {
t.Log(utils.ToIJSON(cc))
t.Errorf("Expected %v was %+v", expected, cc)
}
acc, err = accountingStorage.GetAccount("cgrates.org:dy")
if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1-expected {
t.Errorf("Error getting account: %+v (%v)", utils.ToIJSON(acc), err)
}
}
func TestMaxSessionTimeWithMaxCostFree(t *testing.T) {
ap, _ := ratingStorage.GetActionPlans("TOPUP10_AT")
for _, at := range ap {

View File

@@ -197,6 +197,7 @@ CDRST2_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,5,CDRST_WARN_HTTP,10
vdf,minitsboy,MORE_MINUTES,STANDARD_TRIGGER,,
cgrates.org,12345,TOPUP10_AT,STANDARD_TRIGGERS,,
cgrates.org,123456,TOPUP10_AT,STANDARD_TRIGGERS,,
cgrates.org,dy,TOPUP10_AT,STANDARD_TRIGGERS,,
cgrates.org,remo,TOPUP10_AT,,,
vdf,empty0,TOPUP_SHARED0_AT,,,
vdf,empty10,TOPUP_SHARED10_AT,,,
@@ -1043,7 +1044,7 @@ func TestLoadActionTriggers(t *testing.T) {
}
func TestLoadAccountActions(t *testing.T) {
if len(csvr.accountActions) != 10 {
if len(csvr.accountActions) != 11 {
t.Error("Failed to load account actions: ", len(csvr.accountActions))
}
aa := csvr.accountActions["vdf:minitsboy"]

View File

@@ -318,7 +318,6 @@ func (i *RateInterval) GetCost(duration, startSecond time.Duration) float64 {
GetRateParameters(startSecond)
price /= rateUnit.Seconds()
d := duration.Seconds()
return d * price
}

View File

@@ -254,21 +254,15 @@ func (ts *TimeSpan) SetRateInterval(interval *RateInterval) {
// Returns the cost of the timespan according to the relevant cost interval.
// It also sets the Cost field of this timespan (used for refund on session
// manager debit loop where the cost cannot be recalculated)
func (ts *TimeSpan) getCost() float64 {
func (ts *TimeSpan) calculateCost() float64 {
if ts.Increments.Length() == 0 {
if ts.RateInterval == nil {
return 0
}
cost := ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart())
ts.Cost = utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod)
return ts.Cost
return ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart())
} else {
cost := ts.Increments.GetTotalCost()
if ts.RateInterval != nil && ts.RateInterval.Rating != nil {
return utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod)
} else {
return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
}
return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
}
}
@@ -291,8 +285,8 @@ func (ts *TimeSpan) createIncrementsSlice() {
// because ts cost is rounded
//incrementCost := rate / rateUnit.Seconds() * rateIncrement.Seconds()
nbIncrements := int(ts.GetDuration() / rateIncrement)
incrementCost := ts.getCost() / float64(nbIncrements)
incrementCost = utils.Round(incrementCost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals
incrementCost := ts.calculateCost() / float64(nbIncrements)
incrementCost = utils.Round(incrementCost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod)
for s := 0; s < nbIncrements; s++ {
inc := &Increment{
Duration: rateIncrement,

View File

@@ -214,7 +214,7 @@ 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}
if ts1.getCost() != 0 {
if ts1.calculateCost() != 0 {
t.Error("No interval and still kicking")
}
ts1.SetRateInterval(
@@ -223,12 +223,12 @@ func TestTimespanGetCost(t *testing.T) {
Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}},
},
)
if ts1.getCost() != 600 {
if ts1.calculateCost() != 600 {
t.Error("Expected 10 got ", ts1.Cost)
}
ts1.RateInterval = nil
ts1.SetRateInterval(&RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 60 * time.Second}}}})
if ts1.getCost() != 10 {
if ts1.calculateCost() != 10 {
t.Error("Expected 6000 got ", ts1.Cost)
}
}
@@ -239,8 +239,8 @@ func TestTimespanGetCostIntervals(t *testing.T) {
for i := 0; i < 11; i++ {
ts.Increments[i] = &Increment{Cost: 0.02}
}
if ts.getCost() != 0.22 {
t.Error("Error caclulating timespan cost: ", ts.getCost())
if ts.calculateCost() != 0.22 {
t.Error("Error caclulating timespan cost: ", ts.calculateCost())
}
}
@@ -742,7 +742,7 @@ func TestTimespanCreateIncrements(t *testing.T) {
if len(ts.Increments) != 3 {
t.Error("Error creating increment slice: ", len(ts.Increments))
}
if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.06667 {
if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.07 {
t.Error("Wrong second slice: ", ts.Increments[2].Cost)
}
}

View File

@@ -233,8 +233,6 @@ func (s *Session) SaveOperations() {
firstCC.Merge(cc)
//utils.Logger.Debug(fmt.Sprintf("AFTER MERGE: %s", utils.ToJSON(firstCC)))
}
// make sure we have rounded timespans final cost
firstCC.UpdateCost()
var reply string
err := s.sessionManager.CdrSrv().LogCallCost(&engine.CallCostLog{