diff --git a/engine/rateprofile.go b/engine/rateprofile.go index 082cc9c90..a357413b0 100644 --- a/engine/rateprofile.go +++ b/engine/rateprofile.go @@ -236,3 +236,7 @@ func CostForIntervals(rtIvls []*RateSInterval) (cost *utils.Decimal) { } return } + +// CompressIntervals will compress intervals which equal +func CompressIntervals(rtIvls []*RateSInterval) { +} diff --git a/rates/librates.go b/rates/librates.go index e133d46c2..1e726d19d 100644 --- a/rates/librates.go +++ b/rates/librates.go @@ -178,8 +178,7 @@ func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Dura } // computeRateSIntervals will give out the cost projection for the given orderedRates and usage -func computeRateSIntervals(rts []*orderedRate, usage time.Duration) (rtIvls []*engine.RateSInterval, err error) { - var rtUsageSIdx time.Duration // rtUsageSIdx for one rate +func computeRateSIntervals(rts []*orderedRate, usageStart, usage time.Duration) (rtIvls []*engine.RateSInterval, err error) { for i, rt := range rts { isLastRt := i == len(rts)-1 var rtUsageEIdx time.Duration @@ -189,7 +188,7 @@ func computeRateSIntervals(rts []*orderedRate, usage time.Duration) (rtIvls []*e rtUsageEIdx = usage } var rIcmts []*engine.RateSIncrement - iRtUsageSIdx := rtUsageSIdx + iRtUsageSIdx := usageStart iRtUsageEIdx := rtUsageEIdx for j, iRt := range rt.IntervalRates { if iRtUsageSIdx >= rtUsageEIdx { // charged enough for interval @@ -201,9 +200,10 @@ func computeRateSIntervals(rts []*orderedRate, usage time.Duration) (rtIvls []*e rt.UID(), iRtUsageSIdx) } isLastIRt := j == len(rt.IntervalRates)-1 - if iRt.IntervalStart > iRtUsageSIdx || - (!isLastIRt && rt.IntervalRates[j+1].IntervalStart <= iRtUsageSIdx) { - break // the rates should be already ordered, break here + if iRt.IntervalStart > iRtUsageSIdx { + break + } else if !isLastIRt && rt.IntervalRates[j+1].IntervalStart <= iRtUsageSIdx { + continue // the rates should be already ordered, break here } if !isLastIRt { iRtUsageEIdx = rt.IntervalRates[j+1].IntervalStart @@ -236,13 +236,13 @@ func computeRateSIntervals(rts []*orderedRate, usage time.Duration) (rtIvls []*e } rtIvls = append(rtIvls, &engine.RateSInterval{ - UsageStart: rtUsageSIdx, + UsageStart: usageStart, Increments: rIcmts, CompressFactor: 1}) if iRtUsageSIdx >= usage { // charged enough for the usage break } - rtUsageSIdx = rtUsageEIdx // continue for the next interval + usageStart = rtUsageEIdx // continue for the next interval } return } diff --git a/rates/librates_test.go b/rates/librates_test.go index 4979c9306..5d2f66318 100644 --- a/rates/librates_test.go +++ b/rates/librates_test.go @@ -1669,9 +1669,63 @@ func TestComputeRateSIntervals(t *testing.T) { CompressFactor: 1, }, } - if rtIvls, err := computeRateSIntervals(rts, time.Duration(130*time.Second)); err != nil { + if rtIvls, err := computeRateSIntervals(rts, + time.Duration(0), time.Duration(130*time.Second)); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRtIvls, rtIvls) { t.Errorf("expecting: %+v, received: %+v", eRtIvls, rtIvls) } + + rts = []*orderedRate{ + { + time.Duration(0), + rt0, + }, + { + time.Duration(90 * time.Second), + rt1, + }, + } + + eRtIvls = []*engine.RateSInterval{ + { + UsageStart: time.Duration(time.Minute), + Increments: []*engine.RateSIncrement{ + { + UsageStart: time.Duration(time.Minute), + Usage: time.Duration(30 * time.Second), + Rate: rt0, + IntervalRateIndex: 1, + CompressFactor: 30, + }, + }, + CompressFactor: 1, + }, + { + UsageStart: time.Duration(90 * time.Second), + Increments: []*engine.RateSIncrement{ + { + UsageStart: time.Duration(90 * time.Second), + Usage: time.Duration(30 * time.Second), + Rate: rt1, + IntervalRateIndex: 0, + CompressFactor: 30, + }, + { + UsageStart: time.Duration(2 * time.Minute), + Usage: time.Duration(10 * time.Second), + Rate: rt1, + IntervalRateIndex: 1, + CompressFactor: 10, + }, + }, + CompressFactor: 1, + }, + } + if rtIvls, err := computeRateSIntervals(rts, + time.Duration(time.Minute), time.Duration(130*time.Second)); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eRtIvls, rtIvls) { + t.Errorf("expecting: %+v, received: %+v", utils.ToIJSON(eRtIvls), utils.ToIJSON(rtIvls)) + } } diff --git a/rates/rates.go b/rates/rates.go index 5539b5698..43a8fd899 100644 --- a/rates/rates.go +++ b/rates/rates.go @@ -133,30 +133,43 @@ func (rS *RateS) matchingRateProfileForEvent(tnt string, args *ArgsCostForEvent, func (rS *RateS) rateProfileCostForEvent(rtPfl *engine.RateProfile, args *ArgsCostForEvent) (rts []*engine.RateSInterval, err error) { var rtIDs utils.StringSet if rtIDs, err = engine.MatchingItemIDsForEvent( - args.CGRevent.Event, + args.CGREventWithOpts.CGREvent.Event, rS.cfg.RateSCfg().RateStringIndexedFields, rS.cfg.RateSCfg().RatePrefixIndexedFields, + rS.cfg.RateSCfg().RateSuffixIndexedFields, rS.dm, utils.CacheRateFilterIndexes, - utils.ConcatenatedKey(args.CGRevent.Tenant, rtPfl.ID), + utils.ConcatenatedKey(args.CGREventWithOpts.CGREvent.Tenant, rtPfl.ID), rS.cfg.RateSCfg().RateIndexedSelects, rS.cfg.RateSCfg().RateNestedFields, ); err != nil { return } aRates := make([]*engine.Rate, len(rtIDs)) - evNm := utils.MapStorage{utils.MetaReq: cgrEv.Event} + evNm := utils.MapStorage{utils.MetaReq: args.CGREventWithOpts.CGREvent.Event} for rtID := range rtIDs { rt := rtPfl.Rates[rtID] // pick the rate directly from map based on matched ID var pass bool - if pass, err = rS.filterS.Pass(cgrEv.Tenant, rt.FilterIDs, evNm); err != nil { + if pass, err = rS.filterS.Pass(args.CGREventWithOpts.CGREvent.Tenant, rt.FilterIDs, evNm); err != nil { return } else if !pass { continue } aRates = append(aRates, rt) } - ordRts := orderRatesOnIntervals(aRates) + var sTime time.Time + if sTime, err = args.StartTime(rS.cfg.GeneralCfg().DefaultTimezone); err != nil { + return + } + var usage time.Duration + if usage, err = args.Usage(); err != nil { + return + } + var ordRts []*orderedRate + if ordRts, err = orderRatesOnIntervals(aRates, sTime, usage, true, 1000000); err != nil { + return + } + return } */ @@ -191,6 +204,19 @@ func (args *ArgsCostForEvent) StartTime(tmz string) (sTime time.Time, err error) return time.Now(), nil } +// usage returns the event time used to check active rate profiles +func (args *ArgsCostForEvent) Usage() (usage time.Duration, err error) { + if uIface, has := args.Opts[utils.OptsRatesUsage]; has { + return utils.IfaceAsDuration(uIface) + } + if usage, err = args.CGREvent.FieldAsDuration(utils.Usage); err != nil { + if err != utils.ErrNotFound { + return + } + } + return time.Duration(time.Minute), nil +} + // V1CostForEvent will be called to calculate the cost for an event func (rS *RateS) V1CostForEvent(args *ArgsCostForEvent, cC *utils.ChargedCost) (err error) { return diff --git a/utils/consts.go b/utils/consts.go index 1d61b268c..29575cc47 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -2343,7 +2343,7 @@ var ( ) // CGROptionsSet the possible cgr options -var CGROptionsSet = NewStringSet([]string{OptsRatesStartTime, OptsSessionTTL, OptsSessionTTLMaxDelay, +var CGROptionsSet = NewStringSet([]string{OptsRatesStartTime, OptsRatesUsage, OptsSessionTTL, OptsSessionTTLMaxDelay, OptsSessionTTLLastUsed, OptsSessionTTLLastUsage, OptsSessionTTLUsage, OptsDebitInterval, OptsStirATest, OptsStirPayloadMaxDuration, OptsStirIdentity, OptsStirOriginatorTn, OptsStirOriginatorURI, OptsStirDestinationTn, OptsStirDestinationURI, OptsStirPublicKeyPath, OptsStirPrivateKeyPath, @@ -2372,6 +2372,7 @@ const ( OptsRoutesLimit = "*routes_limit" OptsRoutesOffset = "*routes_offset" OptsRatesStartTime = "*ratesStartTime" + OptsRatesUsage = "*ratesUsage" OptsSessionTTL = "*sessionTTL" OptsSessionTTLMaxDelay = "*sessionTTLMaxDelay" OptsSessionTTLLastUsed = "*sessionTTLLastUsed"