diff --git a/loaders/libloader.go b/loaders/libloader.go index 5376cb4be..eebdff810 100644 --- a/loaders/libloader.go +++ b/loaders/libloader.go @@ -48,11 +48,10 @@ func (ld LoaderData) GetRateIDs() ([]string, error) { if _, has := ld[utils.RateIDs]; !has { return nil, fmt.Errorf("cannot find RateIDs in <%+v>", ld) } - if rateIDs, canCast := ld[utils.RateIDs].([]string); !canCast { - return nil, fmt.Errorf("cannot cast <%+v> to []string", ld) - } else { - return rateIDs, nil + if rateIDs := ld[utils.RateIDs].(string); len(rateIDs) != 0 { + return strings.Split(rateIDs, utils.INFIELD_SEP), nil } + return []string{}, nil } // UpdateFromCSV will update LoaderData with data received from fileName, diff --git a/loaders/loader.go b/loaders/loader.go index b59f38587..20aa3f9f5 100644 --- a/loaders/loader.go +++ b/loaders/loader.go @@ -891,6 +891,7 @@ func (ldr *Loader) removeLoadedData(loaderType string, lds map[string][]LoaderDa ids = append(ids, tntID) if ldr.flagsTpls[loaderType].GetBool(utils.MetaPartial) { + if rateIDs, err := ldData[0].GetRateIDs(); err != nil { return err } else { diff --git a/loaders/loader_test.go b/loaders/loader_test.go index de8e10cac..8bbb6c8b9 100644 --- a/loaders/loader_test.go +++ b/loaders/loader_test.go @@ -1645,3 +1645,319 @@ cgrates.org,RP1,,,,,,,,,,RT_CHRISTMAS,,* * 24 12 *,30,false,0s,0.06,1m,1s } } + +func TestLoaderRemoveRateProfileRates(t *testing.T) { + data := engine.NewInternalDB(nil, nil, true, config.CgrConfig().DataDbCfg().Items) + ldr := &Loader{ + ldrID: "TestLoaderRemoveRateProfileRates", + bufLoaderData: make(map[string][]LoaderData), + flagsTpls: make(map[string]utils.FlagsWithParams), + dm: engine.NewDataManager(data, config.CgrConfig().CacheCfg(), nil), + timezone: "UTC", + } + ldr.dataTpls = map[string][]*config.FCTemplate{ + utils.MetaRateProfiles: []*config.FCTemplate{ + &config.FCTemplate{Tag: "TenantID", + Path: "Tenant", + Type: utils.META_COMPOSED, + Value: config.NewRSRParsersMustCompile("~*req.0", true, utils.INFIELD_SEP), + Mandatory: true}, + &config.FCTemplate{Tag: "ProfileID", + Path: "ID", + Type: utils.META_COMPOSED, + Value: config.NewRSRParsersMustCompile("~*req.1", true, utils.INFIELD_SEP), + Mandatory: true}, + &config.FCTemplate{Tag: "RateIDs", + Path: "RateIDs", + Type: utils.META_COMPOSED, + Value: config.NewRSRParsersMustCompile("~*req.2", true, utils.INFIELD_SEP)}, + }, + } + + rPfr := &engine.RateProfile{ + Tenant: "cgrates.org", + ID: "RP1", + FilterIDs: []string{"*string:~*req.Subject:1001"}, + Weight: 0, + ConnectFee: 0.1, + RoundingMethod: "*up", + RoundingDecimals: 4, + MinCost: 0.1, + MaxCost: 0.6, + MaxCostStrategy: "*free", + Rates: map[string]*engine.Rate{ + "RT_WEEK": &engine.Rate{ + ID: "RT_WEEK", + Weight: 0, + ActivationStart: "* * * * 1-5", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.12, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Minute), + }, + &engine.IntervalRate{ + IntervalStart: time.Duration(1 * time.Minute), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + "RT_WEEKEND": &engine.Rate{ + ID: "RT_WEEKEND", + Weight: 10, + ActivationStart: "* * * * 0,6", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + "RT_CHRISTMAS": &engine.Rate{ + ID: "RT_CHRISTMAS", + Weight: 30, + ActivationStart: "* * 24 12 *", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + }, + } + if err := ldr.dm.SetRateProfile(rPfr, true); err != nil { + t.Error(err) + } + rPfr2 := &engine.RateProfile{ + Tenant: "cgrates.org", + ID: "RP2", + FilterIDs: []string{"*string:~*req.Subject:1001"}, + Weight: 0, + ConnectFee: 0.1, + RoundingMethod: "*up", + RoundingDecimals: 4, + MinCost: 0.1, + MaxCost: 0.6, + MaxCostStrategy: "*free", + Rates: map[string]*engine.Rate{ + "RT_WEEK": &engine.Rate{ + ID: "RT_WEEK", + Weight: 0, + ActivationStart: "* * * * 1-5", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.12, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Minute), + }, + &engine.IntervalRate{ + IntervalStart: time.Duration(1 * time.Minute), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + "RT_WEEKEND": &engine.Rate{ + ID: "RT_WEEKEND", + Weight: 10, + ActivationStart: "* * * * 0,6", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + "RT_CHRISTMAS": &engine.Rate{ + ID: "RT_CHRISTMAS", + Weight: 30, + ActivationStart: "* * 24 12 *", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + }, + } + if err := ldr.dm.SetRateProfile(rPfr2, true); err != nil { + t.Error(err) + } + + ratePrfCnt1 := ` +#Tenant,ID,RateIDs +cgrates.org,RP1,RT_WEEKEND +` + ratePrfCnt2 := ` +#Tenant,ID,RateIDs +cgrates.org,RP2,RT_WEEKEND;RT_CHRISTMAS +cgrates.org,RP1, +` + rdr1 := ioutil.NopCloser(strings.NewReader(ratePrfCnt1)) + csvRdr1 := csv.NewReader(rdr1) + csvRdr1.Comment = '#' + ldr.rdrs = map[string]map[string]*openedCSVFile{ + utils.MetaRateProfiles: map[string]*openedCSVFile{ + utils.RateProfilesCsv: &openedCSVFile{fileName: utils.RateProfilesCsv, + rdr: rdr1, csvRdr: csvRdr1}}, + } + if flag, err := utils.FlagsWithParamsFromSlice([]string{utils.MetaPartial}); err != nil { + t.Error(err) + } else { + ldr.flagsTpls[utils.MetaRateProfiles] = flag + } + if err := ldr.removeContent(utils.MetaRateProfiles, utils.EmptyString); err != nil { + t.Error(err) + } + if len(ldr.bufLoaderData) != 0 { + t.Errorf("wrong buffer content: %+v", ldr.bufLoaderData) + } + + eRatePrf := &engine.RateProfile{ + Tenant: "cgrates.org", + ID: "RP1", + FilterIDs: []string{"*string:~*req.Subject:1001"}, + Weight: 0, + ConnectFee: 0.1, + RoundingMethod: "*up", + RoundingDecimals: 4, + MinCost: 0.1, + MaxCost: 0.6, + MaxCostStrategy: "*free", + Rates: map[string]*engine.Rate{ + "RT_WEEK": &engine.Rate{ + ID: "RT_WEEK", + Weight: 0, + ActivationStart: "* * * * 1-5", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.12, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Minute), + }, + &engine.IntervalRate{ + IntervalStart: time.Duration(1 * time.Minute), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + "RT_CHRISTMAS": &engine.Rate{ + ID: "RT_CHRISTMAS", + Weight: 30, + ActivationStart: "* * 24 12 *", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + }, + } + if rcv, err := ldr.dm.GetRateProfile("cgrates.org", "RP1", + true, false, utils.NonTransactional); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rcv, eRatePrf) { + t.Errorf("expecting: %+v,\n received: %+v", utils.ToJSON(eRatePrf), utils.ToJSON(rcv)) + } + + rdr2 := ioutil.NopCloser(strings.NewReader(ratePrfCnt2)) + csvRdr2 := csv.NewReader(rdr2) + csvRdr2.Comment = '#' + ldr.rdrs = map[string]map[string]*openedCSVFile{ + utils.MetaRateProfiles: map[string]*openedCSVFile{ + utils.RateProfilesCsv: &openedCSVFile{fileName: utils.RateProfilesCsv, + rdr: rdr2, csvRdr: csvRdr2}}, + } + if flag, err := utils.FlagsWithParamsFromSlice([]string{utils.MetaPartial}); err != nil { + t.Error(err) + } else { + ldr.flagsTpls[utils.MetaRateProfiles] = flag + } + if err := ldr.removeContent(utils.MetaRateProfiles, utils.EmptyString); err != nil { + t.Error(err) + } + if len(ldr.bufLoaderData) != 0 { + t.Errorf("wrong buffer content: %+v", ldr.bufLoaderData) + } + + eRatePrf2 := &engine.RateProfile{ + Tenant: "cgrates.org", + ID: "RP2", + FilterIDs: []string{"*string:~*req.Subject:1001"}, + Weight: 0, + ConnectFee: 0.1, + RoundingMethod: "*up", + RoundingDecimals: 4, + MinCost: 0.1, + MaxCost: 0.6, + MaxCostStrategy: "*free", + Rates: map[string]*engine.Rate{ + "RT_WEEK": &engine.Rate{ + ID: "RT_WEEK", + Weight: 0, + ActivationStart: "* * * * 1-5", + IntervalRates: []*engine.IntervalRate{ + &engine.IntervalRate{ + IntervalStart: time.Duration(0 * time.Second), + Value: 0.12, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Minute), + }, + &engine.IntervalRate{ + IntervalStart: time.Duration(1 * time.Minute), + Value: 0.06, + Unit: time.Duration(1 * time.Minute), + Increment: time.Duration(1 * time.Second), + }, + }, + }, + }, + } + if rcv, err := ldr.dm.GetRateProfile("cgrates.org", "RP2", + true, false, utils.NonTransactional); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rcv, eRatePrf2) { + t.Errorf("expecting: %+v,\n received: %+v", utils.ToJSON(eRatePrf2), utils.ToJSON(rcv)) + } + + eRatePrf3 := &engine.RateProfile{ + Tenant: "cgrates.org", + ID: "RP1", + FilterIDs: []string{"*string:~*req.Subject:1001"}, + Weight: 0, + ConnectFee: 0.1, + RoundingMethod: "*up", + RoundingDecimals: 4, + MinCost: 0.1, + MaxCost: 0.6, + MaxCostStrategy: "*free", + Rates: map[string]*engine.Rate{}, + } + if rcv, err := ldr.dm.GetRateProfile("cgrates.org", "RP1", + true, false, utils.NonTransactional); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rcv, eRatePrf3) { + t.Errorf("expecting: %+v,\n received: %+v", utils.ToJSON(eRatePrf3), utils.ToJSON(rcv)) + } +} diff --git a/utils/coreutils.go b/utils/coreutils.go index 9f87e92d8..cdf5a972e 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -209,8 +209,16 @@ func ParseTimeDetectLayout(tmStr string, timezone string) (time.Time, error) { return time.Now().AddDate(0, 0, 1), nil // add one day case tmStr == "*monthly": return time.Now().AddDate(0, 1, 0), nil // add one month + case tmStr == "*monthly_estimated": + initialMnt := time.Now().Month() + tAfter := time.Now().AddDate(0, 1, 0) + for tAfter.Month()-initialMnt > 1 { + tAfter = tAfter.AddDate(0, 0, -1) + } + return tAfter, nil case tmStr == "*yearly": return time.Now().AddDate(1, 0, 0), nil // add one year + case strings.HasPrefix(tmStr, "*month_end"): expDate := GetEndOfMonth(time.Now()) extraDur, err := getAddDuration(tmStr) diff --git a/utils/coreutils_test.go b/utils/coreutils_test.go index c4733016d..2db023681 100644 --- a/utils/coreutils_test.go +++ b/utils/coreutils_test.go @@ -407,6 +407,19 @@ func TestParseTimeDetectLayout(t *testing.T) { } else if expected.Sub(date).Seconds() > 1 { t.Errorf("received: %+v", date) } + + expected = time.Now().AddDate(0, 1, 0) + if date, err := ParseTimeDetectLayout("*monthly_estimated", ""); err != nil { + t.Error(err) + } else { + for date.Month()-expected.Month() > 1 { + expected = expected.AddDate(0, 0, -1) + } + if expected.Sub(date).Seconds() > 1 { + t.Errorf("received: %+v", date) + } + } + expected = time.Now().AddDate(0, 1, 0) if date, err := ParseTimeDetectLayout("*mo", ""); err != nil { t.Error(err) diff --git a/utils/dynamicdataprovider_test.go b/utils/dynamicdataprovider_test.go index c79f50a1d..bf3129964 100644 --- a/utils/dynamicdataprovider_test.go +++ b/utils/dynamicdataprovider_test.go @@ -18,7 +18,6 @@ along with this program. If not, see package utils import ( - "fmt" "strings" "testing" ) @@ -252,5 +251,4 @@ func TestDynamicDataProviderGetFullFieldPath(t *testing.T) { if newpath == nil { t.Errorf("Expected: %v,received %q", nil, newpath) } - fmt.Println(*newpath) }