diff --git a/engine/callcost.go b/engine/callcost.go index e786318a7..98984333c 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -162,13 +162,19 @@ func (cc *CallCost) AsJSON() string { return utils.ToJSON(cc) } +// public function to update final (merged) callcost +func (cc *CallCost) UpdateCost() { + cc.deductConnectFee = true + cc.updateCost() +} + func (cc *CallCost) updateCost() { cost := 0.0 if cc.deductConnectFee { // add back the connectFee cost += cc.GetConnectFee() } for _, ts := range cc.Timespans { - ts.Cost = ts.calculateCost() + ts.Cost = ts.CalculateCost() cost += ts.Cost cost = utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals } diff --git a/engine/calldesc.go b/engine/calldesc.go index 051d98940..2ead17ea5 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -458,7 +458,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) { // handle max cost maxCost, strategy := ts.RateInterval.GetMaxCost() - ts.Cost = ts.calculateCost() + ts.Cost = ts.CalculateCost() cost += ts.Cost cd.MaxCostSoFar += cost //log.Print("Before: ", cost) @@ -517,7 +517,7 @@ func (cd *CallDescriptor) getCost() (*CallCost, error) { if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil { cost += ts.RateInterval.Rating.ConnectFee } - cost += ts.calculateCost() + cost += ts.CalculateCost() } //startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.Category)) diff --git a/engine/cdrs.go b/engine/cdrs.go index b522b829c..f4a5b0714 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -108,6 +108,7 @@ func (self *CdrServer) ProcessExternalCdr(eCDR *ExternalCDR) error { // RPC method, used to log callcosts to db func (self *CdrServer) LogCallCost(ccl *CallCostLog) error { + ccl.CallCost.UpdateCost() // make sure the total cost reflect the increments ccl.CallCost.UpdateRatedUsage() // make sure rated usage is updated if ccl.CheckDuplicate { _, err := self.guard.Guard(func() (interface{}, error) { @@ -177,6 +178,7 @@ func (self *CdrServer) processCdr(cdr *CDR) (err error) { } if self.cgrCfg.CDRSStoreCdrs { // Store RawCDRs, this we do sync so we can reply with the status if cdr.CostDetails != nil { + cdr.CostDetails.UpdateCost() cdr.CostDetails.UpdateRatedUsage() } if err := self.cdrDb.SetCDR(cdr, false); err != nil { // Only original CDR stored in primary table, no derived @@ -236,6 +238,7 @@ func (self *CdrServer) rateStoreStatsReplicate(cdr *CDR, sendToStats bool) error if self.cgrCfg.CDRSStoreCdrs { // Store CDRs // Store RatedCDR if cdr.CostDetails != nil { + cdr.CostDetails.UpdateCost() cdr.CostDetails.UpdateRatedUsage() } if err := self.cdrDb.SetCDR(cdr, true); err != nil { diff --git a/engine/timespans.go b/engine/timespans.go index b89739552..b7f530d59 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -280,7 +280,7 @@ func (incs Increments) GetTotalCost() float64 { for _, increment := range incs { cost += increment.GetCost() } - return cost + return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) } func (incs Increments) Length() (length int) { @@ -321,15 +321,14 @@ 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) calculateCost() float64 { +func (ts *TimeSpan) CalculateCost() float64 { if ts.Increments.Length() == 0 { if ts.RateInterval == nil { return 0 } return ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart()) } else { - cost := ts.Increments.GetTotalCost() - return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) + return ts.Increments.GetTotalCost() } } @@ -352,7 +351,7 @@ func (ts *TimeSpan) createIncrementsSlice() { // because ts cost is rounded //incrementCost := rate / rateUnit.Seconds() * rateIncrement.Seconds() nbIncrements := int(ts.GetDuration() / rateIncrement) - incrementCost := ts.calculateCost() / float64(nbIncrements) + 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{ diff --git a/engine/timespans_test.go b/engine/timespans_test.go index 47b64315d..225cba090 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -214,7 +214,7 @@ func TestTSTimespanGetCost(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.calculateCost() != 0 { + if ts1.CalculateCost() != 0 { t.Error("No interval and still kicking") } ts1.SetRateInterval( @@ -223,12 +223,12 @@ func TestTSTimespanGetCost(t *testing.T) { Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}}, }, ) - if ts1.calculateCost() != 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.calculateCost() != 10 { + if ts1.CalculateCost() != 10 { t.Error("Expected 6000 got ", ts1.Cost) } } @@ -239,8 +239,8 @@ func TestTSTimespanGetCostIntervals(t *testing.T) { for i := 0; i < 11; i++ { ts.Increments[i] = &Increment{Cost: 0.02} } - if ts.calculateCost() != 0.22 { - t.Error("Error caclulating timespan cost: ", ts.calculateCost()) + if ts.CalculateCost() != 0.22 { + t.Error("Error caclulating timespan cost: ", ts.CalculateCost()) } } diff --git a/sessionmanager/session.go b/sessionmanager/session.go index 061ea84ca..91fb952d2 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -180,6 +180,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error { lastCC.Timespans = lastCC.Timespans[:i] } else { ts.SplitByIncrement(lastRefundedIncrementIndex) + ts.Cost = ts.CalculateCost() } break // do not go to other timespans } else { @@ -214,6 +215,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error { } //utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements))) lastCC.Cost -= refundIncrements.GetTotalCost() + lastCC.UpdateRatedUsage() lastCC.Timespans.Compress() return nil } diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 18a86a9b5..d097b91e5 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -132,6 +132,7 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { lastCC.Timespans = lastCC.Timespans[:i] } else { ts.SplitByIncrement(lastRefundedIncrementIndex) + ts.Cost = ts.CalculateCost() } break // do not go to other timespans } else { @@ -166,6 +167,7 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { } //utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements))) lastCC.Cost -= refundIncrements.GetTotalCost() + lastCC.UpdateRatedUsage() lastCC.Timespans.Compress() return nil }