diff --git a/apier/v1/routes_with_rates_it_test.go b/apier/v1/routes_with_rates_it_test.go index f94acc903..590c7607b 100644 --- a/apier/v1/routes_with_rates_it_test.go +++ b/apier/v1/routes_with_rates_it_test.go @@ -39,7 +39,8 @@ var ( testV1RouteSWithRateSRpcConn, testV1RouteSWithRateSFromFolder, testV1RouteSWithRateSGetRoutes, - testV1RouteSWithRateSAccountWithRateProfile, //need to be discussed + testV1RouteSWithRateSAccountWithRateProfile, + testV1RouteSWithRateSWithEmptyRateProfileIDs, testV1RouteSWithRateSStopEngine, } ) @@ -380,6 +381,86 @@ func testV1RouteSWithRateSAccountWithRateProfile(t *testing.T) { } +func testV1RouteSWithRateSWithEmptyRateProfileIDs(t *testing.T) { + routePrf = &RouteWithCache{ + RouteProfile: &engine.RouteProfile{ + Tenant: "cgrates.org", + ID: "RouteWithEmptyRatePRofileIDs", + FilterIDs: []string{"*string:~*req.EventName:testV1RouteSWithRateSWithEmptyRateProfileIDs"}, + Sorting: utils.MetaLC, + Routes: []*engine.Route{ + { + ID: "Route1", + RateProfileIDs: []string{"RT_ANY2CNT_SEC"}, + Weight: 20, + }, + { + ID: "RouteWithEmptyRP", + RateProfileIDs: []string{}, // we send empty RateProfileIDs and expected to match RT_DEFAULT + Weight: 10, + }, + }, + Weight: 100, + }, + } + + var result string + if err := routeSv1Rpc.Call(utils.APIerSv1SetRouteProfile, routePrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + + ev := &engine.ArgsGetRoutes{ + CGREventWithOpts: &utils.CGREventWithOpts{ + CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testV1RouteSWithRateSGetRoutes", + Event: map[string]interface{}{ + utils.Account: "1003", + utils.Subject: "1003", + utils.Destination: "1002", + utils.SetupTime: time.Date(2017, 12, 1, 14, 25, 0, 0, time.UTC), + utils.Usage: "1m20s", + "EventName": "testV1RouteSWithRateSWithEmptyRateProfileIDs", + }, + }, + }, + } + + expRoutes := engine.SortedRoutes{ + ProfileID: "RouteWithEmptyRatePRofileIDs", + Sorting: utils.MetaLC, + Count: 2, + SortedRoutes: []*engine.SortedRoute{ + { + RouteID: "RouteWithEmptyRP", + SortingData: map[string]interface{}{ + utils.Cost: 0.1333333333333334, + utils.RateProfileMatched: "RT_DEFAULT", + utils.Weight: 10.0, + }, + }, + { + RouteID: "Route1", + SortingData: map[string]interface{}{ + utils.Cost: 1.6, + utils.RateProfileMatched: "RT_ANY2CNT_SEC", + utils.Weight: 20.0, + }, + }, + }, + } + var routesReply engine.SortedRoutes + if err := routeSv1Rpc.Call(utils.RouteSv1GetRoutes, + ev, &routesReply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expRoutes, routesReply) { + t.Errorf("Expecting: %s, \n received: %s", + utils.ToJSON(expRoutes), utils.ToJSON(routesReply)) + } + +} func testV1RouteSWithRateSStopEngine(t *testing.T) { if err := engine.KillEngine(100); err != nil { t.Error(err) diff --git a/data/tariffplans/routes_with_rates/RateProfiles.csv b/data/tariffplans/routes_with_rates/RateProfiles.csv index b7fbdc23c..58bcb977d 100644 --- a/data/tariffplans/routes_with_rates/RateProfiles.csv +++ b/data/tariffplans/routes_with_rates/RateProfiles.csv @@ -4,4 +4,5 @@ cgrates.org,RT_RETAIL1,,,0,0,*up,4,0,0,*free,RT_ALWAYS,,"* * * * *",0,false,0s,0 cgrates.org,RT_RETAIL1,,,,,,,,,,RT_ALWAYS,,"* * * * *",0,false,1m,0.2,1m,10s cgrates.org,RT_ANY2CNT_SEC,,,0,0,*up,4,0,0,*free,RT_ALWAYS,,"* * * * *",0,false,0s,0.02,1s,1s cgrates.org,RT_ANY1CNT_SEC,,,0,0,*up,4,0,0,*free,RT_ALWAYS,,"* * * * *",0,false,0s,0.01,1s,1s +cgrates.org,RT_DEFAULT,,,20,0,*up,4,0,0,*free,RT_ALWAYS,,"* * * * *",0,false,0s,0.10,1m,10s diff --git a/engine/route_highestcost.go b/engine/route_highestcost.go index e156e7409..96553fb22 100755 --- a/engine/route_highestcost.go +++ b/engine/route_highestcost.go @@ -41,11 +41,11 @@ func (hcs *HightCostSorter) SortRoutes(prflID string, routes []*Route, Sorting: hcs.sorting, SortedRoutes: make([]*SortedRoute, 0)} for _, route := range routes { - if len(route.RatingPlanIDs) == 0 && len(route.AccountIDs) == 0 && len(route.RateProfileIDs) == 0 { + if len(route.RatingPlanIDs) == 0 && len(route.AccountIDs) == 0 && len(hcs.rS.cgrcfg.RouteSCfg().RateSConns) == 0 { utils.Logger.Warning( - fmt.Sprintf("<%s> supplier: <%s> - empty RatingPlanIDs or AccountIDs or RateProfileIDs", + fmt.Sprintf("<%s> supplier: <%s> - empty RatingPlanIDs or AccountIDs or no connection with rateS", utils.RouteS, route.ID)) - return nil, utils.NewErrMandatoryIeMissing("RatingPlanIDs or AccountIDs or RateProfileIDs") + return nil, utils.NewErrMandatoryIeMissing("RatingPlanIDs or AccountIDs or connection with rateS") } if srtSpl, pass, err := hcs.rS.populateSortingData(ev, route, extraOpts); err != nil { return nil, err diff --git a/engine/route_leastcost.go b/engine/route_leastcost.go index 971089a04..a0c47b9bc 100644 --- a/engine/route_leastcost.go +++ b/engine/route_leastcost.go @@ -41,11 +41,11 @@ func (lcs *LeastCostSorter) SortRoutes(prflID string, routes []*Route, Sorting: lcs.sorting, SortedRoutes: make([]*SortedRoute, 0)} for _, s := range routes { - if len(s.RatingPlanIDs) == 0 && len(s.AccountIDs) == 0 && len(s.RateProfileIDs) == 0 { + if len(s.RatingPlanIDs) == 0 && len(s.AccountIDs) == 0 && len(lcs.rS.cgrcfg.RouteSCfg().RateSConns) == 0 { utils.Logger.Warning( - fmt.Sprintf("<%s> supplier: <%s> - empty RatingPlanIDs or AccountIDs or RateProfileIDs", + fmt.Sprintf("<%s> supplier: <%s> - empty RatingPlanIDs or AccountIDs or no connection with RateS", utils.RouteS, s.ID)) - return nil, utils.NewErrMandatoryIeMissing("RatingPlanIDs or AccountIDs or RateProfileIDs") + return nil, utils.NewErrMandatoryIeMissing("RatingPlanIDs or AccountIDs or connection with RateS") } if srtSpl, pass, err := lcs.rS.populateSortingData(ev, s, extraOpts); err != nil { return nil, err diff --git a/engine/routes.go b/engine/routes.go index 4d3d62f0e..f876ffc34 100644 --- a/engine/routes.go +++ b/engine/routes.go @@ -262,8 +262,8 @@ func (rpS *RouteService) costForEvent(ev *utils.CGREvent, } if usage > accountMaxUsage { // remain usage needs to be covered by rating plans - if len(rpIDs) == 0 && len(rtPrfIDs) == 0 { - return nil, fmt.Errorf("no rating plans or rate profiles defined for remaining usage") + if len(rpIDs) == 0 && len(rpS.cgrcfg.RouteSCfg().RateSConns) == 0 { + return nil, fmt.Errorf("no rating plans or no connection to RateS defined for remaining usage") } // update the setup time and the usage sTime = sTime.Add(accountMaxUsage) @@ -420,7 +420,7 @@ func (rpS *RouteService) populateSortingData(ev *utils.CGREvent, route *Route, RouteParameters: route.RouteParameters, } //calculate costData if we have fields - if len(route.AccountIDs) != 0 || len(route.RatingPlanIDs) != 0 || len(route.RateProfileIDs) != 0 { + if len(route.AccountIDs) != 0 || len(route.RatingPlanIDs) != 0 || len(rpS.cgrcfg.RouteSCfg().RateSConns) != 0 { costData, err := rpS.costForEvent(ev, route.AccountIDs, route.RatingPlanIDs, route.RateProfileIDs) if err != nil { if extraOpts.ignoreErrors {