mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Added *sessionChargeable session option. Fixes #1702
This commit is contained in:
committed by
Dan Christian Bogos
parent
a2688b9536
commit
d2e04360bd
@@ -81,6 +81,7 @@ type Session struct {
|
||||
|
||||
debitStop chan struct{}
|
||||
sTerminator *sTerminator // automatic timeout for the session
|
||||
chargeable bool
|
||||
}
|
||||
|
||||
// Lock exported function from sync.RWMutex
|
||||
|
||||
@@ -425,6 +425,11 @@ func (sS *SessionS) debitSession(s *Session, sRunIdx int, dur time.Duration,
|
||||
return
|
||||
}
|
||||
sr := s.SRuns[sRunIdx]
|
||||
if !s.chargeable {
|
||||
sS.pause(sr, dur)
|
||||
sr.TotalUsage += sr.LastUsage
|
||||
return dur, nil
|
||||
}
|
||||
rDur := sr.debitReserve(dur, lastUsed) // debit out of reserve, rDur is still to be debited
|
||||
if rDur == time.Duration(0) {
|
||||
return dur, nil // complete debit out of reserve
|
||||
@@ -498,6 +503,27 @@ func (sS *SessionS) debitSession(s *Session, sRunIdx int, dur time.Duration,
|
||||
return
|
||||
}
|
||||
|
||||
func (sS *SessionS) pause(sr *SRun, dur time.Duration) {
|
||||
if sr.CD.LoopIndex > 0 {
|
||||
sr.CD.TimeStart = sr.CD.TimeEnd
|
||||
}
|
||||
sr.CD.TimeEnd = sr.CD.TimeStart.Add(dur)
|
||||
sr.CD.DurationIndex += dur
|
||||
ec := engine.NewFreeEventCost(sr.CD.CgrID, sr.CD.RunID, sr.CD.Account, sr.CD.TimeStart, dur)
|
||||
sr.LastUsage = dur
|
||||
sr.CD.LoopIndex++
|
||||
if sr.EventCost == nil { // is the first increment
|
||||
// when we start the call with debit interval 0
|
||||
// but later we update this value with one greater than 0
|
||||
sr.EventCost = ec
|
||||
} else { // we already debited something
|
||||
// copy the old AccountSummary as in Merge the old one is overwriten by the new one
|
||||
ec.AccountSummary = sr.EventCost.AccountSummary
|
||||
// similar to the debit merge the event costs
|
||||
sr.EventCost.Merge(ec)
|
||||
}
|
||||
}
|
||||
|
||||
// debitLoopSession will periodically debit sessions, ie: automatic prepaid
|
||||
// threadSafe since it will run into it's own goroutine
|
||||
func (sS *SessionS) debitLoopSession(s *Session, sRunIdx int,
|
||||
@@ -1121,6 +1147,7 @@ func (sS *SessionS) newSession(cgrEv *utils.CGREvent, resID, clntConnID string,
|
||||
ClientConnID: clntConnID,
|
||||
DebitInterval: dbtItval,
|
||||
}
|
||||
s.chargeable = s.OptsStart.GetBoolOrDefault(utils.OptsChargeable, true)
|
||||
if !isMsg && sS.isIndexed(s, false) { // check if already exists
|
||||
return nil, utils.ErrExists
|
||||
}
|
||||
@@ -1367,7 +1394,7 @@ func (sS *SessionS) initSessionDebitLoops(s *Session) {
|
||||
return
|
||||
}
|
||||
for i, sr := range s.SRuns {
|
||||
if s.DebitInterval != 0 &&
|
||||
if s.DebitInterval > 0 &&
|
||||
sr.Event.GetStringIgnoreErrors(utils.RequestType) == utils.MetaPrepaid {
|
||||
if s.debitStop == nil { // init the debitStop only for the first sRun with DebitInterval and RequestType MetaPrepaid
|
||||
s.debitStop = make(chan struct{})
|
||||
@@ -1450,6 +1477,7 @@ func (sS *SessionS) updateSession(s *Session, updtEv, opts engine.MapEvent, isMs
|
||||
s.updateSRuns(updtEv, sS.cgrCfg.SessionSCfg().AlterableFields)
|
||||
sS.setSTerminator(s, opts) // reset the terminator
|
||||
}
|
||||
s.chargeable = opts.GetBoolOrDefault(utils.OptsChargeable, true)
|
||||
//init has no updtEv
|
||||
if updtEv == nil {
|
||||
updtEv = engine.MapEvent(s.EventStart.Clone())
|
||||
@@ -1517,20 +1545,24 @@ func (sS *SessionS) endSession(s *Session, tUsage, lastUsage *time.Duration,
|
||||
if sr.EventCost != nil {
|
||||
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.CallDescriptorWithOpts{
|
||||
CallDescriptor: sr.CD,
|
||||
Opts: s.OptsStart,
|
||||
}, cc); err == nil {
|
||||
sr.EventCost.Merge(
|
||||
engine.NewEventCostFromCallCost(cc, s.CGRID,
|
||||
sr.Event.GetStringIgnoreErrors(utils.RunID)))
|
||||
if !s.chargeable {
|
||||
sS.pause(sr, notCharged)
|
||||
} else {
|
||||
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.CallDescriptorWithOpts{
|
||||
CallDescriptor: sr.CD,
|
||||
Opts: s.OptsStart,
|
||||
}, 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 {
|
||||
@@ -2585,7 +2617,7 @@ func (sS *SessionS) BiRPCv1TerminateSession(clnt rpcclient.ClientConnector,
|
||||
dbtItvl, isMsg, args.ForceDuration); err != nil {
|
||||
return utils.NewErrRALs(err)
|
||||
}
|
||||
if _, err = sS.updateSession(s, ev, args.Opts, isMsg); err != nil {
|
||||
if _, err = sS.updateSession(s, ev, opts, isMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
@@ -2593,6 +2625,9 @@ func (sS *SessionS) BiRPCv1TerminateSession(clnt rpcclient.ClientConnector,
|
||||
if !isMsg {
|
||||
s.UpdateSRuns(ev, sS.cgrCfg.SessionSCfg().AlterableFields)
|
||||
}
|
||||
s.Lock()
|
||||
s.chargeable = opts.GetBoolOrDefault(utils.OptsChargeable, true)
|
||||
s.Unlock()
|
||||
if err = sS.terminateSession(s,
|
||||
ev.GetDurationPtrIgnoreErrors(utils.Usage),
|
||||
ev.GetDurationPtrIgnoreErrors(utils.LastUsed),
|
||||
@@ -3396,6 +3431,10 @@ func (sS *SessionS) BiRPCv1ProcessEvent(clnt rpcclient.ClientConnector,
|
||||
dbtItvl, false, ralsOpts.Has(utils.MetaFD)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
s.Lock()
|
||||
s.chargeable = opts.GetBoolOrDefault(utils.OptsChargeable, true)
|
||||
s.Unlock()
|
||||
}
|
||||
if err = sS.terminateSession(s,
|
||||
ev.GetDurationPtrIgnoreErrors(utils.Usage),
|
||||
@@ -3637,7 +3676,6 @@ func (sS *SessionS) BiRPCv1DeactivateSessions(clnt rpcclient.ClientConnector,
|
||||
}
|
||||
|
||||
func (sS *SessionS) processCDR(cgrEv *utils.CGREvent, flags []string, rply *string, clnb bool) (err error) {
|
||||
|
||||
ev := engine.MapEvent(cgrEv.Event)
|
||||
cgrID := GetSetCGRID(ev)
|
||||
s := sS.getRelocateSession(cgrID,
|
||||
|
||||
@@ -308,6 +308,7 @@ func testForceSTerminatorManualTermination(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
@@ -348,6 +349,7 @@ func testForceSTerminatorPostCDRs(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
expected := "INTERNALLY_DISCONNECTED"
|
||||
@@ -384,6 +386,7 @@ func testForceSTerminatorReleaseSession(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
expected := "MANDATORY_IE_MISSING: [connIDs]"
|
||||
@@ -432,6 +435,7 @@ func testForceSTerminatorClientCall(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
expected := "MANDATORY_IE_MISSING: [connIDs]"
|
||||
@@ -464,6 +468,7 @@ func testDebitSession(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
//RunIdx cannot be higher than the length of sessions runs
|
||||
@@ -551,6 +556,7 @@ func testDebitSessionResponderMaxDebit(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
if maxDur, err := sessions.debitSession(ss, 0, 5*time.Second,
|
||||
@@ -614,6 +620,7 @@ func testDebitSessionResponderMaxDebitError(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
if maxDur, err := sessions.debitSession(ss, 0, 5*time.Minute,
|
||||
@@ -668,6 +675,7 @@ func testInitSessionDebitLoops(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
sessions.initSessionDebitLoops(ss)
|
||||
@@ -713,6 +721,7 @@ func testDebitLoopSessionErrorDebiting(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
// session already closed
|
||||
@@ -800,6 +809,7 @@ func testDebitLoopSession(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
go func() {
|
||||
if _, err := sessions.debitLoopSession(ss, 0, time.Second); err != nil {
|
||||
@@ -860,6 +870,7 @@ func testDebitLoopSessionFrcDiscLowerDbtInterval(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
go func() {
|
||||
if _, err := sessions.debitLoopSession(ss, 0, time.Second); err != nil {
|
||||
@@ -913,6 +924,7 @@ func testDebitLoopSessionLowBalance(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
sessions.cgrCfg.SessionSCfg().MinDurLowBalance = 10 * time.Second
|
||||
@@ -970,6 +982,7 @@ func testDebitLoopSessionWarningSessions(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
// will disconnect faster, MinDurLowBalance higher than the debit interval
|
||||
@@ -1031,6 +1044,7 @@ func testDebitLoopSessionDisconnectSession(t *testing.T) {
|
||||
NextAutoDebit: utils.TimePointer(time.Date(2020, time.April, 18, 23, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
// will disconnect faster
|
||||
@@ -1085,6 +1099,7 @@ func testStoreSCost(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
if err := sessions.storeSCost(ss, 0); err != nil {
|
||||
@@ -1132,6 +1147,7 @@ func testRefundSession(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
expectedErr := "no event cost"
|
||||
@@ -1266,6 +1282,7 @@ func testRoundCost(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
//mocking an error API Call
|
||||
@@ -1288,6 +1305,7 @@ func testDisconnectSession(t *testing.T) {
|
||||
TotalUsage: time.Minute,
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
sTestMock := &testMockClientConn{}
|
||||
@@ -1410,6 +1428,7 @@ func testNewSession(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
if rcv, err := sessions.newSession(cgrEv, "resourceID", "clientConnID",
|
||||
time.Second, false, false); err != nil {
|
||||
@@ -2080,6 +2099,7 @@ func testEndSession(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
|
||||
activationTime := time.Date(2020, 21, 07, 10, 0, 0, 0, time.UTC)
|
||||
@@ -2196,6 +2216,7 @@ func testBiRPCv1GetActivePassiveSessions(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
chargeable: true,
|
||||
}
|
||||
sr2[utils.ToR] = utils.MetaSMS
|
||||
sr2[utils.Subject] = "subject2"
|
||||
|
||||
Reference in New Issue
Block a user