func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error { end := lastCC.Timespans[len(lastCC.Timespans)-1].TimeEnd refundDuration := end.Sub(hangupTime) //engine.Logger.Debug(fmt.Sprintf("HANGUPTIME: %s REFUNDDURATION: %s", hangupTime.String(), refundDuration.String())) var refundIncrements engine.Increments for i := len(lastCC.Timespans) - 1; i >= 0; i-- { ts := lastCC.Timespans[i] tsDuration := ts.GetDuration() if refundDuration <= tsDuration { lastRefundedIncrementIndex := -1 for j := len(ts.Increments) - 1; j >= 0; j-- { increment := ts.Increments[j] if increment.Duration <= refundDuration { refundIncrements = append(refundIncrements, increment) refundDuration -= increment.Duration lastRefundedIncrementIndex = j } else { break //increment duration is larger, cannot refund increment } } if lastRefundedIncrementIndex == 0 { lastCC.Timespans[i] = nil lastCC.Timespans = lastCC.Timespans[:i] } else { ts.SplitByIncrement(lastRefundedIncrementIndex) } break // do not go to other timespans } else { refundIncrements = append(refundIncrements, ts.Increments...) // remove the timespan entirely lastCC.Timespans[i] = nil lastCC.Timespans = lastCC.Timespans[:i] // continue to the next timespan with what is left to refund refundDuration -= tsDuration } } // show only what was actualy refunded (stopped in timespan) // engine.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration)) if len(refundIncrements) > 0 { cd := &engine.CallDescriptor{ Direction: lastCC.Direction, Tenant: lastCC.Tenant, Category: lastCC.Category, Subject: lastCC.Subject, Account: lastCC.Account, Destination: lastCC.Destination, Increments: refundIncrements, } var response float64 err := s.sessionManager.Rater().RefundIncrements(cd, &response) if err != nil { return err } } //engine.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements))) lastCC.Cost -= refundIncrements.GetTotalCost() lastCC.Timespans.Compress() return nil }
func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) { go sm.processCdr(ev.AsStoredCdr()) s := sm.GetSession(ev.GetUUID()) if s == nil { // Not handled by us return } else { sm.RemoveSession(s.uuid) // Unreference it early so we avoid concurrency } defer s.Close(ev) // Stop loop and save the costs deducted so far to database attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT), Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)} var dcs utils.DerivedChargers if err := sm.rater.GetDerivedChargers(attrsDC, &dcs); err != nil { engine.Logger.Err(fmt.Sprintf("<SessionManager> OnHangup: could not get derived charging for event %s: %s", ev.GetUUID(), err.Error())) return } dcs, _ = dcs.AppendDefaultRun() for _, dc := range dcs { if ev.GetReqType(dc.ReqTypeField) != utils.PREPAID { continue } sr := s.GetSessionRun(dc.RunId) if sr == nil { continue // Did not save a sessionRun for this dc } if len(sr.callCosts) == 0 { continue // why would we have 0 callcosts } lastCC := sr.callCosts[len(sr.callCosts)-1] lastCC.Timespans.Decompress() // put credit back startTime, err := ev.GetAnswerTime(dc.AnswerTimeField) if err != nil { engine.Logger.Crit("Error parsing prepaid call start time from event") return } duration, err := ev.GetDuration(dc.UsageField) if err != nil { engine.Logger.Crit(fmt.Sprintf("Error parsing call duration from event %s", err.Error())) return } hangupTime := startTime.Add(duration) end := lastCC.Timespans[len(lastCC.Timespans)-1].TimeEnd refundDuration := end.Sub(hangupTime) var refundIncrements engine.Increments for i := len(lastCC.Timespans) - 1; i >= 0; i-- { ts := lastCC.Timespans[i] tsDuration := ts.GetDuration() if refundDuration <= tsDuration { lastRefundedIncrementIndex := 0 for j := len(ts.Increments) - 1; j >= 0; j-- { increment := ts.Increments[j] if increment.Duration <= refundDuration { refundIncrements = append(refundIncrements, increment) refundDuration -= increment.Duration lastRefundedIncrementIndex = j } } ts.SplitByIncrement(lastRefundedIncrementIndex) break // do not go to other timespans } else { refundIncrements = append(refundIncrements, ts.Increments...) // remove the timespan entirely lastCC.Timespans[i] = nil lastCC.Timespans = lastCC.Timespans[:i] // continue to the next timespan with what is left to refund refundDuration -= tsDuration } } // show only what was actualy refunded (stopped in timespan) // engine.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration)) if len(refundIncrements) > 0 { cd := &engine.CallDescriptor{ Direction: lastCC.Direction, Tenant: lastCC.Tenant, Category: lastCC.Category, Subject: lastCC.Subject, Account: lastCC.Account, Destination: lastCC.Destination, Increments: refundIncrements, } var response float64 err := sm.rater.RefundIncrements(*cd, &response) if err != nil { engine.Logger.Err(fmt.Sprintf("Debit cents failed: %v", err)) } } cost := refundIncrements.GetTotalCost() lastCC.Cost -= cost lastCC.Timespans.Compress() } }
// Attempts to refund a duration, error on failure func (self *SMGSession) refund(refundDuration time.Duration) error { initialRefundDuration := refundDuration lastCC := self.callCosts[len(self.callCosts)-1] lastCC.Timespans.Decompress() var refundIncrements engine.Increments for i := len(lastCC.Timespans) - 1; i >= 0; i-- { ts := lastCC.Timespans[i] tsDuration := ts.GetDuration() if refundDuration <= tsDuration { lastRefundedIncrementIndex := -1 for j := len(ts.Increments) - 1; j >= 0; j-- { increment := ts.Increments[j] if increment.Duration <= refundDuration { refundIncrements = append(refundIncrements, increment) refundDuration -= increment.Duration lastRefundedIncrementIndex = j } else { break //increment duration is larger, cannot refund increment } } if lastRefundedIncrementIndex == 0 { lastCC.Timespans[i] = nil lastCC.Timespans = lastCC.Timespans[:i] } else { ts.SplitByIncrement(lastRefundedIncrementIndex) ts.Cost = ts.CalculateCost() } break // do not go to other timespans } else { refundIncrements = append(refundIncrements, ts.Increments...) // remove the timespan entirely lastCC.Timespans[i] = nil lastCC.Timespans = lastCC.Timespans[:i] // continue to the next timespan with what is left to refund refundDuration -= tsDuration } } // show only what was actualy refunded (stopped in timespan) // utils.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration)) if len(refundIncrements) > 0 { cd := &engine.CallDescriptor{ Direction: lastCC.Direction, Tenant: lastCC.Tenant, Category: lastCC.Category, Subject: lastCC.Subject, Account: lastCC.Account, Destination: lastCC.Destination, TOR: lastCC.TOR, Increments: refundIncrements, } cd.Increments.Compress() utils.Logger.Info(fmt.Sprintf("Refunding duration %v with cd: %s", initialRefundDuration, utils.ToJSON(cd))) var response float64 err := self.rater.RefundIncrements(cd, &response) if err != nil { return err } } //utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements))) lastCC.Cost -= refundIncrements.GetTotalCost() lastCC.UpdateRatedUsage() lastCC.Timespans.Compress() return nil }
func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error { end := lastCC.Timespans[len(lastCC.Timespans)-1].TimeEnd refundDuration := end.Sub(hangupTime) var refundIncrements engine.Increments for i := len(lastCC.Timespans) - 1; i >= 0; i-- { ts := lastCC.Timespans[i] tsDuration := ts.GetDuration() if refundDuration <= tsDuration { lastRefundedIncrementIndex := -1 for j := len(ts.Increments) - 1; j >= 0; j-- { increment := ts.Increments[j] if increment.Duration <= refundDuration { refundIncrements = append(refundIncrements, increment) refundDuration -= increment.Duration lastRefundedIncrementIndex = j } else { break //increment duration is larger, cannot refund increment } } if lastRefundedIncrementIndex == 0 { lastCC.Timespans[i] = nil lastCC.Timespans = lastCC.Timespans[:i] } else { ts.SplitByIncrement(lastRefundedIncrementIndex) ts.Cost = ts.CalculateCost() } break // do not go to other timespans } else { refundIncrements = append(refundIncrements, ts.Increments...) // remove the timespan entirely lastCC.Timespans[i] = nil lastCC.Timespans = lastCC.Timespans[:i] // continue to the next timespan with what is left to refund refundDuration -= tsDuration } } // show only what was actualy refunded (stopped in timespan) // utils.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration)) if len(refundIncrements) > 0 { cd := &engine.CallDescriptor{ CgrID: s.eventStart.GetCgrId(s.sessionManager.Timezone()), Direction: lastCC.Direction, Tenant: lastCC.Tenant, Category: lastCC.Category, Subject: lastCC.Subject, Account: lastCC.Account, Destination: lastCC.Destination, TOR: lastCC.TOR, Increments: refundIncrements, } cd.Increments.Compress() //utils.Logger.Info(fmt.Sprintf("Refunding duration %v with cd: %+v", refundDuration, cd)) var response float64 err := s.sessionManager.Rater().Call("Responder.RefundIncrements", cd, &response) if err != nil { return err } } lastCC.Cost -= refundIncrements.GetTotalCost() lastCC.UpdateRatedUsage() lastCC.Timespans.Compress() return nil }