mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-21 23:28:44 +05:00
improved csv loading
This commit is contained in:
@@ -39,7 +39,10 @@ func (self *ApierV1) SetTPDestinationRate(attrs utils.TPDestinationRate, reply *
|
||||
}
|
||||
drs := make([]*engine.DestinationRate, len(attrs.DestinationRates))
|
||||
for idx, dr := range attrs.DestinationRates {
|
||||
drs[idx] = &engine.DestinationRate{attrs.DestinationRateId, dr.DestinationId, dr.RateId, nil}
|
||||
drs[idx] = &engine.DestinationRate{
|
||||
Tag: attrs.DestinationRateId,
|
||||
DestinationsTag: dr.DestinationId,
|
||||
RateTag: dr.RateId}
|
||||
}
|
||||
if err := self.StorDb.SetTPDestinationRates(attrs.TPid, map[string][]*engine.DestinationRate{attrs.DestinationRateId: drs}); err != nil {
|
||||
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
|
||||
|
||||
@@ -42,7 +42,7 @@ func (self *ApierV1) SetTPDestRateTiming(attrs utils.TPDestRateTiming, reply *st
|
||||
drts[idx] = &engine.DestinationRateTiming{Tag: attrs.DestRateTimingId,
|
||||
DestinationRatesTag: drt.DestRatesId,
|
||||
Weight: drt.Weight,
|
||||
TimingsTag: drt.TimingId,
|
||||
TimingTag: drt.TimingId,
|
||||
}
|
||||
}
|
||||
if err := self.StorDb.SetTPDestRateTimings(attrs.TPid, map[string][]*engine.DestinationRateTiming{attrs.DestRateTimingId: drts}); err != nil {
|
||||
|
||||
@@ -30,19 +30,19 @@ import (
|
||||
)
|
||||
|
||||
type CSVReader struct {
|
||||
sep rune
|
||||
storage DataStorage
|
||||
readerFunc func(string, rune, int) (*csv.Reader, *os.File, error)
|
||||
actions map[string][]*Action
|
||||
actionsTimings map[string][]*ActionTiming
|
||||
actionsTriggers map[string][]*ActionTrigger
|
||||
accountActions []*UserBalance
|
||||
destinations []*Destination
|
||||
timings map[string]*Timing
|
||||
rates map[string]*LoadRate
|
||||
destinationRates map[string][]*DestinationRate
|
||||
ratingPlans map[string]*RatingPlan
|
||||
ratingProfiles map[string]*RatingProfile
|
||||
sep rune
|
||||
storage DataStorage
|
||||
readerFunc func(string, rune, int) (*csv.Reader, *os.File, error)
|
||||
actions map[string][]*Action
|
||||
actionsTimings map[string][]*ActionTiming
|
||||
actionsTriggers map[string][]*ActionTrigger
|
||||
accountActions []*UserBalance
|
||||
destinations []*Destination
|
||||
timings map[string]*Timing
|
||||
rates map[string][]*LoadRate
|
||||
destinationRates map[string][]*DestinationRate
|
||||
destinationRateTimings map[string][]*DestinationRateTiming
|
||||
ratingProfiles map[string]*RatingProfile
|
||||
// file names
|
||||
destinationsFn, ratesFn, destinationratesFn, timingsFn, destinationratetimingsFn, ratingprofilesFn,
|
||||
actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string
|
||||
@@ -55,10 +55,10 @@ func NewFileCSVReader(storage DataStorage, sep rune, destinationsFn, timingsFn,
|
||||
c.actions = make(map[string][]*Action)
|
||||
c.actionsTimings = make(map[string][]*ActionTiming)
|
||||
c.actionsTriggers = make(map[string][]*ActionTrigger)
|
||||
c.rates = make(map[string]*LoadRate)
|
||||
c.rates = make(map[string][]*LoadRate)
|
||||
c.destinationRates = make(map[string][]*DestinationRate)
|
||||
c.timings = make(map[string]*Timing)
|
||||
c.ratingPlans = make(map[string]*RatingPlan)
|
||||
c.destinationRateTimings = make(map[string][]*DestinationRateTiming)
|
||||
c.ratingProfiles = make(map[string]*RatingProfile)
|
||||
c.readerFunc = openFileCSVReader
|
||||
c.destinationsFn, c.timingsFn, c.ratesFn, c.destinationratesFn, c.destinationratetimingsFn, c.ratingprofilesFn,
|
||||
@@ -228,7 +228,14 @@ func (csvr *CSVReader) LoadRates() (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
csvr.rates[tag] = r
|
||||
// same tag only to create rate groups
|
||||
existingRates, exists := csvr.rates[tag]
|
||||
if exists {
|
||||
if err := existingRates[len(existingRates)-1].ValidNextGroup(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
csvr.rates[tag] = append(csvr.rates[tag], r)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -249,11 +256,20 @@ func (csvr *CSVReader) LoadDestinationRates() (err error) {
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not get rates for tag %v", record[2]))
|
||||
}
|
||||
//ToDo: Not checking presence of destinations?
|
||||
destinationExists := false
|
||||
for _, d := range csvr.destinations {
|
||||
if d.Id == record[1] {
|
||||
destinationExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !destinationExists {
|
||||
return errors.New(fmt.Sprintf("Could not get destination for tag %v", record[1]))
|
||||
}
|
||||
dr := &DestinationRate{
|
||||
Tag: tag,
|
||||
DestinationsTag: record[1],
|
||||
Rate: r,
|
||||
rates: r,
|
||||
}
|
||||
|
||||
csvr.destinationRates[tag] = append(csvr.destinationRates[tag], dr)
|
||||
@@ -277,18 +293,12 @@ func (csvr *CSVReader) LoadDestinationRateTimings() (err error) {
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not get timing for tag %v", record[2]))
|
||||
}
|
||||
|
||||
rt := NewDestinationRateTiming(record[1], t, record[3])
|
||||
drs, exists := csvr.destinationRates[record[1]]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not find destination rate for tag %v", record[1]))
|
||||
}
|
||||
for _, dr := range drs {
|
||||
if _, exists := csvr.ratingPlans[tag]; !exists {
|
||||
csvr.ratingPlans[tag] = &RatingPlan{}
|
||||
}
|
||||
csvr.ratingPlans[tag].AddRateInterval(rt.GetRateInterval(dr))
|
||||
}
|
||||
drt := NewDestinationRateTiming(drs, t, record[3])
|
||||
csvr.destinationRateTimings[tag] = append(csvr.destinationRateTimings[tag], drt)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -315,19 +325,22 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) {
|
||||
rp = &RatingProfile{Id: key}
|
||||
csvr.ratingProfiles[key] = rp
|
||||
}
|
||||
for _, d := range csvr.destinations {
|
||||
ap, exists := csvr.ratingPlans[record[5]]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not load ratinTiming for tag: %v", record[5]))
|
||||
}
|
||||
newAP := &RatingPlan{ActivationTime: at}
|
||||
//copy(newAP.Intervals, ap.Intervals)
|
||||
newAP.RateIntervals = append(newAP.RateIntervals, ap.RateIntervals...)
|
||||
rp.AddRatingPlanIfNotPresent(d.Id, newAP)
|
||||
if fallbacksubject != "" {
|
||||
rp.FallbackKey = fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject)
|
||||
drts, exists := csvr.destinationRateTimings[record[5]]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not load destination rate timings for tag: %v", record[5]))
|
||||
}
|
||||
|
||||
for _, drt := range drts {
|
||||
plan := &RatingPlan{ActivationTime: at}
|
||||
for _, dr := range drt.destinationRates {
|
||||
plan.AddRateInterval(drt.GetRateInterval(dr))
|
||||
rp.AddRatingPlanIfNotPresent(dr.DestinationsTag, plan)
|
||||
}
|
||||
}
|
||||
|
||||
if fallbacksubject != "" {
|
||||
rp.FallbackKey = fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ func TestLoadRates(t *testing.T) {
|
||||
if len(csvr.rates) != 5 {
|
||||
t.Error("Failed to load rates: ", csvr.rates)
|
||||
}
|
||||
rate := csvr.rates["R1"]
|
||||
rate := csvr.rates["R1"][0]
|
||||
if !reflect.DeepEqual(rate, &LoadRate{
|
||||
Tag: "R1",
|
||||
ConnectFee: 0,
|
||||
@@ -221,9 +221,9 @@ func TestLoadRates(t *testing.T) {
|
||||
RoundingDecimals: 2,
|
||||
Weight: 10,
|
||||
}) {
|
||||
t.Error("Error loading rate: ", csvr.rates)
|
||||
t.Error("Error loading rate: ", csvr.rates["R1"][0])
|
||||
}
|
||||
rate = csvr.rates["R2"]
|
||||
rate = csvr.rates["R2"][0]
|
||||
if !reflect.DeepEqual(rate, &LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
@@ -237,7 +237,7 @@ func TestLoadRates(t *testing.T) {
|
||||
}) {
|
||||
t.Error("Error loading rate: ", csvr.rates)
|
||||
}
|
||||
rate = csvr.rates["R3"]
|
||||
rate = csvr.rates["R3"][0]
|
||||
if !reflect.DeepEqual(rate, &LoadRate{
|
||||
Tag: "R3",
|
||||
ConnectFee: 0,
|
||||
@@ -251,7 +251,7 @@ func TestLoadRates(t *testing.T) {
|
||||
}) {
|
||||
t.Error("Error loading rate: ", csvr.rates)
|
||||
}
|
||||
rate = csvr.rates["R4"]
|
||||
rate = csvr.rates["R4"][0]
|
||||
if !reflect.DeepEqual(rate, &LoadRate{
|
||||
Tag: "R4",
|
||||
ConnectFee: 1,
|
||||
@@ -265,7 +265,7 @@ func TestLoadRates(t *testing.T) {
|
||||
}) {
|
||||
t.Error("Error loading rate: ", csvr.rates)
|
||||
}
|
||||
rate = csvr.rates["R5"]
|
||||
rate = csvr.rates["R5"][0]
|
||||
if !reflect.DeepEqual(rate, &LoadRate{
|
||||
Tag: "R5",
|
||||
ConnectFee: 0,
|
||||
@@ -284,24 +284,24 @@ func TestLoadRates(t *testing.T) {
|
||||
|
||||
func TestLoadDestinationRates(t *testing.T) {
|
||||
if len(csvr.destinationRates) != 5 {
|
||||
t.Error("Failed to load rates: ", csvr.rates)
|
||||
t.Error("Failed to load destinationrates: ", csvr.destinationRates)
|
||||
}
|
||||
drs := csvr.destinationRates["RT_STANDARD"]
|
||||
if !reflect.DeepEqual(drs, []*DestinationRate{
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY",
|
||||
Rate: csvr.rates["R1"],
|
||||
rates: csvr.rates["R1"],
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY_O2",
|
||||
Rate: csvr.rates["R2"],
|
||||
rates: csvr.rates["R2"],
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY_PREMIUM",
|
||||
Rate: csvr.rates["R2"],
|
||||
rates: csvr.rates["R2"],
|
||||
},
|
||||
}) {
|
||||
t.Error("Error loading destination rate: ", drs)
|
||||
@@ -311,7 +311,7 @@ func TestLoadDestinationRates(t *testing.T) {
|
||||
&DestinationRate{
|
||||
Tag: "RT_DEFAULT",
|
||||
DestinationsTag: "ALL",
|
||||
Rate: csvr.rates["R2"],
|
||||
rates: csvr.rates["R2"],
|
||||
},
|
||||
}) {
|
||||
t.Error("Error loading destination rate: ", drs)
|
||||
@@ -321,12 +321,12 @@ func TestLoadDestinationRates(t *testing.T) {
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY",
|
||||
Rate: csvr.rates["R2"],
|
||||
rates: csvr.rates["R2"],
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY_O2",
|
||||
Rate: csvr.rates["R3"],
|
||||
rates: csvr.rates["R3"],
|
||||
},
|
||||
}) {
|
||||
t.Error("Error loading destination rate: ", drs)
|
||||
@@ -336,7 +336,7 @@ func TestLoadDestinationRates(t *testing.T) {
|
||||
&DestinationRate{
|
||||
Tag: "P1",
|
||||
DestinationsTag: "NAT",
|
||||
Rate: csvr.rates["R4"],
|
||||
rates: csvr.rates["R4"],
|
||||
},
|
||||
}) {
|
||||
t.Error("Error loading destination rate: ", drs)
|
||||
@@ -346,7 +346,7 @@ func TestLoadDestinationRates(t *testing.T) {
|
||||
&DestinationRate{
|
||||
Tag: "P2",
|
||||
DestinationsTag: "NAT",
|
||||
Rate: csvr.rates["R5"],
|
||||
rates: csvr.rates["R5"],
|
||||
},
|
||||
}) {
|
||||
t.Error("Error loading destination rate: ", drs)
|
||||
@@ -354,228 +354,171 @@ func TestLoadDestinationRates(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadDestinationRateTimings(t *testing.T) {
|
||||
if len(csvr.ratingPlans) != 4 {
|
||||
t.Error("Failed to load rate timings: ", csvr.ratingPlans)
|
||||
if len(csvr.destinationRateTimings) != 4 {
|
||||
t.Error("Failed to load rate timings: ", csvr.destinationRateTimings)
|
||||
}
|
||||
rplan := csvr.ratingPlans["STANDARD"]
|
||||
expected := &RatingPlan{
|
||||
ActivationTime: time.Time{},
|
||||
RateIntervals: RateIntervalList{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.2,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
rplan := csvr.destinationRateTimings["STANDARD"]
|
||||
expected := []*DestinationRateTiming{
|
||||
&DestinationRateTiming{
|
||||
destinationRates: []*DestinationRate{
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R1",
|
||||
ConnectFee: 0,
|
||||
Price: 0.2,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY_O2",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
Price: 0.1,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY_PREMIUM",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
Price: 0.1,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "18:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.05,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
Weight: 10,
|
||||
timing: &Timing{
|
||||
Id: "WORKDAYS_00",
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00"},
|
||||
},
|
||||
&DestinationRateTiming{
|
||||
destinationRates: []*DestinationRate{
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
Price: 0.1,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY_O2",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R3",
|
||||
ConnectFee: 0,
|
||||
Price: 0.05,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{6, 0},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.05,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
Weight: 10,
|
||||
timing: &Timing{
|
||||
Id: "WORKDAYS_18",
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "18:00:00",
|
||||
},
|
||||
},
|
||||
&DestinationRateTiming{
|
||||
destinationRates: []*DestinationRate{
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
Price: 0.1,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY_O2",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R3",
|
||||
ConnectFee: 0,
|
||||
Price: 0.05,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Weight: 10,
|
||||
timing: &Timing{
|
||||
Id: "WEEKENDS",
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{6, 0},
|
||||
StartTime: "00:00:00",
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rplan, expected) {
|
||||
t.Errorf("Error loading rating plan: %#v", csvr.ratingPlans["STANDARD"])
|
||||
}
|
||||
rplan = csvr.ratingPlans["PREMIUM"]
|
||||
expected = &RatingPlan{
|
||||
ActivationTime: time.Time{},
|
||||
RateIntervals: RateIntervalList{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{6, 0},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.05,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rplan, expected) {
|
||||
t.Errorf("Error loading rating plan: %#v", csvr.ratingPlans["PREMIUM"])
|
||||
}
|
||||
rplan = csvr.ratingPlans["DEFAULT"]
|
||||
expected = &RatingPlan{
|
||||
ActivationTime: time.Time{},
|
||||
RateIntervals: RateIntervalList{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rplan, expected) {
|
||||
t.Errorf("Error loading rating plan: %#v", csvr.ratingPlans["DEFAULT"])
|
||||
}
|
||||
rplan = csvr.ratingPlans["EVENING"]
|
||||
expected = &RatingPlan{
|
||||
ActivationTime: time.Time{},
|
||||
RateIntervals: RateIntervalList{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 1,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Second,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_UP,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "18:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.5,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Second,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_DOWN,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{6, 0},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.5,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Second,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_DOWN,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rplan, expected) {
|
||||
t.Errorf("Error loading rating plan: %#v", csvr.ratingPlans["EVENING"])
|
||||
t.Errorf("Error loading destination rate timing: %#v", rplan)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,22 +529,13 @@ func TestLoadRatingProfiles(t *testing.T) {
|
||||
rp := csvr.ratingProfiles["*out:CUSTOMER_1:0:rif:from:tm"]
|
||||
expected := &RatingProfile{}
|
||||
if reflect.DeepEqual(rp, expected) {
|
||||
t.Error("Error loading rating profile: ", csvr.ratingProfiles["*out:CUSTOMER_1:0:rif:from:tm"])
|
||||
t.Errorf("Error loading rating profile: %#v", rp)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CUSTOMER_1,0,*out,rif:from:tm,2012-01-01T00:00:00Z,PREMIUM,danb
|
||||
CUSTOMER_1,0,*out,rif:from:tm,2012-02-28T00:00:00Z,STANDARD,danb
|
||||
CUSTOMER_2,0,*out,danb:87.139.12.167,2012-01-01T00:00:00Z,STANDARD,danb
|
||||
CUSTOMER_1,0,*out,danb,2012-01-01T00:00:00Z,PREMIUM,
|
||||
vdf,0,*out,rif,2012-01-01T00:00:00Z,EVENING,
|
||||
vdf,0,*out,rif,2012-02-28T00:00:00Z,EVENING,
|
||||
vdf,0,*out,minu,2012-01-01T00:00:00Z,EVENING,
|
||||
vdf,0,*out,*any,2012-02-28T00:00:00Z,EVENING,
|
||||
vdf,0,*out,one,2012-02-28T00:00:00Z,STANDARD,
|
||||
vdf,0,*out,inf,2012-02-28T00:00:00Z,STANDARD,inf
|
||||
vdf,0,*out,fall,2012-02-28T00:00:00Z,PREMIUM,rif
|
||||
*/
|
||||
|
||||
func TestLoadActions(t *testing.T) {
|
||||
|
||||
@@ -35,7 +35,7 @@ type DbReader struct {
|
||||
accountActions []*UserBalance
|
||||
destinations []*Destination
|
||||
timings map[string]*Timing
|
||||
rates map[string]*LoadRate
|
||||
rates map[string][]*LoadRate
|
||||
destinationRates map[string][]*DestinationRate
|
||||
activationPeriods map[string]*RatingPlan
|
||||
ratingProfiles map[string]*RatingProfile
|
||||
@@ -141,11 +141,11 @@ func (dbr *DbReader) LoadDestinationRates() (err error) {
|
||||
}
|
||||
for _, drs := range dbr.destinationRates {
|
||||
for _, dr := range drs {
|
||||
rate, exists := dbr.rates[dr.RateTag]
|
||||
rates, exists := dbr.rates[dr.RateTag]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not find rate for tag %v", dr.RateTag))
|
||||
}
|
||||
dr.Rate = rate
|
||||
dr.rates = rates
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -157,9 +157,9 @@ func (dbr *DbReader) LoadDestinationRateTimings() error {
|
||||
return err
|
||||
}
|
||||
for _, rt := range rts {
|
||||
t, exists := dbr.timings[rt.TimingsTag]
|
||||
t, exists := dbr.timings[rt.TimingTag]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not get timing for tag %v", rt.TimingsTag))
|
||||
return errors.New(fmt.Sprintf("Could not get timing for tag %v", rt.TimingTag))
|
||||
}
|
||||
rt.timing = t
|
||||
drs, exists := dbr.destinationRates[rt.DestinationRatesTag]
|
||||
@@ -223,12 +223,12 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
|
||||
}
|
||||
for _, destrateTiming := range drtm {
|
||||
Logger.Debug(fmt.Sprintf("Destination rate timing: %v", rpm))
|
||||
tm, err := dbr.storDb.GetTpTimings(dbr.tpid, destrateTiming.TimingsTag)
|
||||
tm, err := dbr.storDb.GetTpTimings(dbr.tpid, destrateTiming.TimingTag)
|
||||
Logger.Debug(fmt.Sprintf("Timing: %v", rpm))
|
||||
if err != nil || len(tm) == 0 {
|
||||
return fmt.Errorf("No Timings profile with id %s: %v", destrateTiming.TimingsTag, err)
|
||||
return fmt.Errorf("No Timings profile with id %s: %v", destrateTiming.TimingTag, err)
|
||||
}
|
||||
destrateTiming.timing = tm[destrateTiming.TimingsTag]
|
||||
destrateTiming.timing = tm[destrateTiming.TimingTag]
|
||||
drm, err := dbr.storDb.GetTpDestinationRates(dbr.tpid, destrateTiming.DestinationRatesTag)
|
||||
if err != nil || len(drm) == 0 {
|
||||
return fmt.Errorf("No Timings profile with id %s: %v", destrateTiming.DestinationRatesTag, err)
|
||||
@@ -240,7 +240,7 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
|
||||
return fmt.Errorf("No Rates profile with id %s: %v", drate.RateTag, err)
|
||||
}
|
||||
Logger.Debug(fmt.Sprintf("Rate: %v", rpm))
|
||||
drate.Rate = rt[drate.RateTag]
|
||||
drate.rates = rt[drate.RateTag]
|
||||
if _, exists := activationPeriods[destrateTiming.Tag]; !exists {
|
||||
activationPeriods[destrateTiming.Tag] = &RatingPlan{}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
@@ -106,11 +107,24 @@ func NewLoadRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterv
|
||||
return
|
||||
}
|
||||
|
||||
func (present *LoadRate) ValidNextGroup(next *LoadRate) error {
|
||||
if next.GroupIntervalStart <= present.GroupIntervalStart {
|
||||
return errors.New(fmt.Sprintf("Next rate group interval start must be heigher than the last one: %#v", next))
|
||||
}
|
||||
if math.Mod(next.GroupIntervalStart.Seconds(), present.RateIncrement.Seconds()) != 0 {
|
||||
return errors.New(fmt.Sprintf("GroupIntervalStart of %#v must be a multiple of RateIncrement of %#v", next, present))
|
||||
}
|
||||
if present.RoundingMethod != next.RoundingMethod || present.RoundingDecimals != next.RoundingDecimals {
|
||||
return errors.New(fmt.Sprintf("Rounding stuff must be equal for sam rate tag: %#v, %#v", present, next))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DestinationRate struct {
|
||||
Tag string
|
||||
DestinationsTag string
|
||||
RateTag string
|
||||
Rate *LoadRate
|
||||
RateTag string // intermediary used when loading from db
|
||||
rates []*LoadRate
|
||||
}
|
||||
|
||||
type Timing struct {
|
||||
@@ -135,43 +149,46 @@ func NewTiming(timingInfo ...string) (rt *Timing) {
|
||||
|
||||
type DestinationRateTiming struct {
|
||||
Tag string
|
||||
DestinationRatesTag string
|
||||
DestinationRatesTag string // intermediary used when loading from db
|
||||
destinationRates []*DestinationRate
|
||||
Weight float64
|
||||
TimingsTag string // intermediary used when loading from db
|
||||
TimingTag string // intermediary used when loading from db
|
||||
timing *Timing
|
||||
}
|
||||
|
||||
func NewDestinationRateTiming(destinationRatesTag string, timing *Timing, weight string) (rt *DestinationRateTiming) {
|
||||
func NewDestinationRateTiming(destinationRates []*DestinationRate, timing *Timing, weight string) (drt *DestinationRateTiming) {
|
||||
w, err := strconv.ParseFloat(weight, 64)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing weight unit from: %v", weight)
|
||||
return
|
||||
}
|
||||
rt = &DestinationRateTiming{
|
||||
DestinationRatesTag: destinationRatesTag,
|
||||
Weight: w,
|
||||
timing: timing,
|
||||
drt = &DestinationRateTiming{
|
||||
destinationRates: destinationRates,
|
||||
timing: timing,
|
||||
Weight: w,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rt *DestinationRateTiming) GetRateInterval(dr *DestinationRate) (i *RateInterval) {
|
||||
func (drt *DestinationRateTiming) GetRateInterval(dr *DestinationRate) (i *RateInterval) {
|
||||
i = &RateInterval{
|
||||
Years: rt.timing.Years,
|
||||
Months: rt.timing.Months,
|
||||
MonthDays: rt.timing.MonthDays,
|
||||
WeekDays: rt.timing.WeekDays,
|
||||
StartTime: rt.timing.StartTime,
|
||||
Weight: rt.Weight,
|
||||
ConnectFee: dr.Rate.ConnectFee,
|
||||
RoundingMethod: dr.Rate.RoundingMethod,
|
||||
RoundingDecimals: dr.Rate.RoundingDecimals,
|
||||
Rates: RateGroups{&Rate{
|
||||
GroupIntervalStart: dr.Rate.GroupIntervalStart,
|
||||
Value: dr.Rate.Price,
|
||||
RateIncrement: dr.Rate.RateIncrement,
|
||||
RateUnit: dr.Rate.RateUnit,
|
||||
}},
|
||||
Years: drt.timing.Years,
|
||||
Months: drt.timing.Months,
|
||||
MonthDays: drt.timing.MonthDays,
|
||||
WeekDays: drt.timing.WeekDays,
|
||||
StartTime: drt.timing.StartTime,
|
||||
Weight: drt.Weight,
|
||||
ConnectFee: dr.rates[0].ConnectFee,
|
||||
RoundingMethod: dr.rates[0].RoundingMethod,
|
||||
RoundingDecimals: dr.rates[0].RoundingDecimals,
|
||||
}
|
||||
for _, rl := range dr.rates {
|
||||
i.Rates = append(i.Rates, &Rate{
|
||||
GroupIntervalStart: rl.GroupIntervalStart,
|
||||
Value: rl.Price,
|
||||
RateIncrement: rl.RateIncrement,
|
||||
RateUnit: rl.RateUnit,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -40,18 +40,17 @@ type xCachedRatingPlans struct {
|
||||
/*
|
||||
Adds one ore more intervals to the internal interval list only if it is not allready in the list.
|
||||
*/
|
||||
func (ap *RatingPlan) AddRateInterval(is ...*RateInterval) {
|
||||
for _, i := range is {
|
||||
func (ap *RatingPlan) AddRateInterval(ris ...*RateInterval) {
|
||||
for _, ri := range ris {
|
||||
found := false
|
||||
for _, ei := range ap.RateIntervals {
|
||||
if i.Equal(ei) {
|
||||
(&ei.Rates).AddRate(i.Rates...)
|
||||
for _, eri := range ap.RateIntervals {
|
||||
if ri.Equal(eri) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
ap.RateIntervals = append(ap.RateIntervals, i)
|
||||
ap.RateIntervals = append(ap.RateIntervals, ri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,8 @@ func TestFallbackDirect(t *testing.T) {
|
||||
func TestFallbackMultiple(t *testing.T) {
|
||||
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "fall", Destination: "0723045"}
|
||||
cd.LoadRatingPlans()
|
||||
if len(cd.RatingPlans) != 1 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
|
||||
if len(cd.RatingPlans) != 2 {
|
||||
t.Errorf("Error restoring rating plans: %#v", cd.RatingPlans)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +161,8 @@ func TestApAddRateIntervalGroups(t *testing.T) {
|
||||
if len(ap.RateIntervals) != 1 {
|
||||
t.Error("Wronfully appended interval ;)")
|
||||
}
|
||||
if len(ap.RateIntervals[0].Rates) != 2 {
|
||||
t.Error("Group prices not formed: ", ap.RateIntervals[0].Rates)
|
||||
if len(ap.RateIntervals[0].Rates) != 1 {
|
||||
t.Errorf("Group prices not formed: %#v", ap.RateIntervals[0].Rates[0])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,20 +31,21 @@ type RatingProfile struct {
|
||||
}
|
||||
|
||||
// Adds an activation period that applyes to current rating profile if not already present.
|
||||
func (rp *RatingProfile) AddRatingPlanIfNotPresent(destInfo string, aps ...*RatingPlan) {
|
||||
func (rp *RatingProfile) AddRatingPlanIfNotPresent(destInfo string, plans ...*RatingPlan) {
|
||||
if rp.DestinationMap == nil {
|
||||
rp.DestinationMap = make(map[string][]*RatingPlan, 1)
|
||||
}
|
||||
for _, ap := range aps {
|
||||
for _, plan := range plans {
|
||||
found := false
|
||||
for _, eap := range rp.DestinationMap[destInfo] {
|
||||
if ap.Equal(eap) {
|
||||
for _, existingPlan := range rp.DestinationMap[destInfo] {
|
||||
if plan.Equal(existingPlan) {
|
||||
existingPlan.AddRateInterval(plan.RateIntervals...)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
rp.DestinationMap[destInfo] = append(rp.DestinationMap[destInfo], ap)
|
||||
rp.DestinationMap[destInfo] = append(rp.DestinationMap[destInfo], plan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ type LoadStorage interface {
|
||||
// loader functions
|
||||
GetTpDestinations(string, string) ([]*Destination, error)
|
||||
GetTpTimings(string, string) (map[string]*Timing, error)
|
||||
GetTpRates(string, string) (map[string]*LoadRate, error)
|
||||
GetTpRates(string, string) (map[string][]*LoadRate, error)
|
||||
GetTpDestinationRates(string, string) (map[string][]*DestinationRate, error)
|
||||
GetTpDestinationRateTimings(string, string) ([]*DestinationRateTiming, error)
|
||||
GetTpRatingProfiles(string, string) (map[string]*RatingProfile, error)
|
||||
|
||||
@@ -363,7 +363,7 @@ func (self *SQLStorage) SetTPDestRateTimings(tpid string, drts map[string][]*Des
|
||||
qry += ","
|
||||
}
|
||||
qry += fmt.Sprintf("('%s','%s','%s','%s',%f)",
|
||||
tpid, drtId, drt.DestinationRatesTag, drt.TimingsTag, drt.Weight)
|
||||
tpid, drtId, drt.DestinationRatesTag, drt.TimingTag, drt.Weight)
|
||||
i++
|
||||
}
|
||||
}
|
||||
@@ -921,8 +921,8 @@ func (self *SQLStorage) GetTpDestinations(tpid, tag string) ([]*Destination, err
|
||||
return dests, nil
|
||||
}
|
||||
|
||||
func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*LoadRate, error) {
|
||||
rts := make(map[string]*LoadRate)
|
||||
func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string][]*LoadRate, error) {
|
||||
rts := make(map[string][]*LoadRate)
|
||||
q := fmt.Sprintf("SELECT tag, connect_fee, rate, rate_unit, rate_increment, group_interval_start, rounding_method, rounding_decimals, weight FROM %s WHERE tpid='%s' ", utils.TBL_TP_RATES, tpid)
|
||||
if tag != "" {
|
||||
q += fmt.Sprintf(" AND tag='%s'", tag)
|
||||
@@ -951,7 +951,14 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*LoadRate, erro
|
||||
RoundingDecimals: roundingDecimals,
|
||||
Weight: weight,
|
||||
}
|
||||
rts[tag] = r
|
||||
// same tag only to create rate groups
|
||||
existingRates, exists := rts[tag]
|
||||
if exists {
|
||||
if err := existingRates[len(existingRates)-1].ValidNextGroup(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
rts[tag] = append(rts[tag], r)
|
||||
}
|
||||
return rts, nil
|
||||
}
|
||||
@@ -1027,7 +1034,7 @@ func (self *SQLStorage) GetTpDestinationRateTimings(tpid, tag string) ([]*Destin
|
||||
Tag: tag,
|
||||
DestinationRatesTag: destination_rates_tag,
|
||||
Weight: weight,
|
||||
TimingsTag: timings_tag,
|
||||
TimingTag: timings_tag,
|
||||
}
|
||||
rts = append(rts, rt)
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ func (self *TPCSVImporter) importDestRateTimings(fn string) error {
|
||||
drt := &DestinationRateTiming{Tag: record[0],
|
||||
DestinationRatesTag: record[1],
|
||||
Weight: weight,
|
||||
TimingsTag: record[2],
|
||||
TimingTag: record[2],
|
||||
}
|
||||
if err := self.StorDb.SetTPDestRateTimings(self.TPid, map[string][]*DestinationRateTiming{drt.Tag: []*DestinationRateTiming{drt}}); err != nil {
|
||||
if self.Verbose {
|
||||
|
||||
@@ -287,6 +287,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
|
||||
if paid {
|
||||
continue
|
||||
}
|
||||
// TODO: Split if some increments were processed by minutes
|
||||
// debit monetary
|
||||
for _, b := range usefulMoneyBalances {
|
||||
if b.Value == 0 {
|
||||
|
||||
Reference in New Issue
Block a user