From 19f30a418dcb674ae121f912f48e09813680b518 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 7 Dec 2020 15:49:28 +0200 Subject: [PATCH] SessionS - properly charge terminate without initiate event --- packages/debian/changelog | 3 +- sessions/sessions.go | 77 ++++++++++++++++++++---------------- sessions/sessions_it_test.go | 4 +- 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/packages/debian/changelog b/packages/debian/changelog index 1d3aa5aa0..fc174c860 100644 --- a/packages/debian/changelog +++ b/packages/debian/changelog @@ -56,7 +56,8 @@ cgrates (0.10.1) UNRELEASED; urgency=medium * [SessionS] Use correctly SessionTTLUsage when calculate end usage in case of terminate session from ttl mechanism * [SessionS] Add SessionTLLLastUsage as option for an extra debit in case of ttl mechanism * [Templates] Added new dataconverter: *string2hex - + * [SessionS] Properly charge terminate without initiate event + -- DanB Wed, 5 May 2020 15:22:59 +0200 cgrates (0.10.0) UNRELEASED; urgency=medium diff --git a/sessions/sessions.go b/sessions/sessions.go index ea3394e42..6b7f3790d 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -1420,17 +1420,17 @@ func (sS *SessionS) updateSession(s *Session, updtEv engine.MapEvent, isMsg, for } var maxUsageSet bool // so we know if we have set the 0 on purpose for i, sr := range s.SRuns { + reqType := sr.Event.GetStringIgnoreErrors(utils.RequestType) + if reqType == utils.META_NONE { + continue + } var rplyMaxUsage time.Duration - if !authReqs.HasField( - sr.Event.GetStringIgnoreErrors(utils.RequestType)) { + if reqType != utils.META_PREPAID || s.debitStop != nil { rplyMaxUsage = reqMaxUsage } else if rplyMaxUsage, err = sS.debitSession(s, i, reqMaxUsage, updtEv.GetDurationPtrIgnoreErrors(utils.LastUsed)); err != nil { return } - if rplyMaxUsage > reqMaxUsage { - rplyMaxUsage = reqMaxUsage - } if !maxUsageSet || rplyMaxUsage < maxUsage { maxUsage = rplyMaxUsage maxUsageSet = true @@ -1472,35 +1472,37 @@ func (sS *SessionS) endSession(s *Session, tUsage, lastUsage *time.Duration, sUsage = sr.TotalUsage } if sr.EventCost != nil { - if notCharged := sUsage - sr.EventCost.GetUsage(); notCharged > 0 { // we did not charge enough, make a manual debit here - if sr.CD.LoopIndex > 0 { - sr.CD.TimeStart = sr.CD.TimeEnd + if !isMsg { // in case of one time charge there is no need of corrections + if notCharged := sUsage - sr.EventCost.GetUsage(); notCharged > 0 { // we did not charge enough, make a manual debit here + if sr.CD.LoopIndex > 0 { + sr.CD.TimeStart = sr.CD.TimeEnd + } + sr.CD.TimeEnd = sr.CD.TimeStart.Add(notCharged) + sr.CD.DurationIndex += notCharged + cc := new(engine.CallCost) + if err = sS.connMgr.Call(sS.cgrCfg.SessionSCfg().RALsConns, nil, utils.ResponderDebit, + &engine.CallDescriptorWithArgDispatcher{ + CallDescriptor: sr.CD, + ArgDispatcher: s.ArgDispatcher}, cc); err == nil { + sr.EventCost.Merge( + engine.NewEventCostFromCallCost(cc, s.CGRID, + sr.Event.GetStringIgnoreErrors(utils.RunID))) + } + } else if notCharged < 0 { // charged too much, try refund + if err = sS.refundSession(s, sRunIdx, -notCharged); err != nil { + utils.Logger.Warning( + fmt.Sprintf( + "<%s> failed refunding session: <%s>, srIdx: <%d>, error: <%s>", + utils.SessionS, s.CGRID, sRunIdx, err.Error())) + } } - sr.CD.TimeEnd = sr.CD.TimeStart.Add(notCharged) - sr.CD.DurationIndex += notCharged - cc := new(engine.CallCost) - if err = sS.connMgr.Call(sS.cgrCfg.SessionSCfg().RALsConns, nil, utils.ResponderDebit, - &engine.CallDescriptorWithArgDispatcher{ - CallDescriptor: sr.CD, - ArgDispatcher: s.ArgDispatcher}, cc); err == nil { - sr.EventCost.Merge( - engine.NewEventCostFromCallCost(cc, s.CGRID, - sr.Event.GetStringIgnoreErrors(utils.RunID))) - } - } else if notCharged < 0 { // charged too much, try refund - if err = sS.refundSession(s, sRunIdx, -notCharged); err != nil { + if err := sS.roundCost(s, sRunIdx); err != nil { // will round the cost and refund the extra increment utils.Logger.Warning( - fmt.Sprintf( - "<%s> failed refunding session: <%s>, srIdx: <%d>, error: <%s>", + fmt.Sprintf("<%s> failed rounding session cost for <%s>, srIdx: <%d>, error: <%s>", utils.SessionS, s.CGRID, sRunIdx, err.Error())) } - } - if err := sS.roundCost(s, sRunIdx); err != nil { // will round the cost and refund the extra increment - utils.Logger.Warning( - fmt.Sprintf("<%s> failed rounding session cost for <%s>, srIdx: <%d>, error: <%s>", - utils.SessionS, s.CGRID, sRunIdx, err.Error())) - } + } if sS.cgrCfg.SessionSCfg().StoreSCosts { if err := sS.storeSCost(s, sRunIdx); err != nil { utils.Logger.Warning( @@ -1508,7 +1510,6 @@ func (sS *SessionS) endSession(s *Session, tUsage, lastUsage *time.Duration, utils.SessionS, s.CGRID, sRunIdx, err.Error())) } } - // set cost fields sr.Event[utils.Cost] = sr.EventCost.GetCost() sr.Event[utils.CostDetails] = utils.ToJSON(sr.EventCost) // avoid map[string]interface{} when decoding @@ -2538,6 +2539,7 @@ func (sS *SessionS) BiRPCv1TerminateSession(clnt rpcclient.ClientConnector, } var s *Session fib := utils.Fib() + var isMsg bool // one time charging, do not perform indexing and sTerminator for i := 0; i < sS.cgrCfg.SessionSCfg().TerminateAttempts; i++ { if s = sS.getRelocateSession(cgrID, ev.GetStringIgnoreErrors(utils.InitialOriginID), @@ -2549,21 +2551,26 @@ func (sS *SessionS) BiRPCv1TerminateSession(clnt rpcclient.ClientConnector, time.Sleep(time.Duration(fib()) * time.Millisecond) continue } - + isMsg = true if s, err = sS.initSession(args.CGREvent.Tenant, ev, sS.biJClntID(clnt), ev.GetStringIgnoreErrors(utils.OriginID), dbtItvl, - args.ArgDispatcher, false, args.ForceDuration); err != nil { + args.ArgDispatcher, isMsg, args.ForceDuration); err != nil { + return utils.NewErrRALs(err) + } + if _, err = sS.updateSession(s, ev, isMsg, args.ForceDuration); err != nil { return err } - + break + } + if !isMsg { + s.UpdateSRuns(ev, sS.cgrCfg.SessionSCfg().AlterableFields) } - s.UpdateSRuns(ev, sS.cgrCfg.SessionSCfg().AlterableFields) if err = sS.terminateSession(s, ev.GetDurationPtrIgnoreErrors(utils.Usage), ev.GetDurationPtrIgnoreErrors(utils.LastUsed), ev.GetTimePtrIgnoreErrors(utils.AnswerTime, utils.EmptyString), - false); err != nil { + isMsg); err != nil { return utils.NewErrRALs(err) } } diff --git a/sessions/sessions_it_test.go b/sessions/sessions_it_test.go index d7998e630..c262ca0cc 100644 --- a/sessions/sessions_it_test.go +++ b/sessions/sessions_it_test.go @@ -227,7 +227,7 @@ func testSessionsItUpdateUnexist(t *testing.T) { utils.SetupTime: time.Date(2016, time.January, 5, 18, 30, 49, 0, time.UTC), utils.AnswerTime: time.Date(2016, time.January, 5, 18, 31, 05, 0, time.UTC), utils.Usage: usage, - utils.CGRDebitInterval: "10s", + utils.CGRDebitInterval: "0s", }, }, } @@ -242,7 +242,7 @@ func testSessionsItUpdateUnexist(t *testing.T) { time.Sleep(10 * time.Millisecond) - eAcntVal = 8.582900 + eAcntVal = 8.599600 if err := sItRPC.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {