diff --git a/engine/callcost.go b/engine/callcost.go index ea61a1dd1..0cb79c919 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -161,3 +161,13 @@ func (cc *CallCost) ToDataCost() (*DataCost, error) { } return dc, nil } + +func (cc *CallCost) GetLongestRounding() (roundingDecimals int, roundingMethod string) { + for _, ts := range cc.Timespans { + if ts.RateInterval.Rating.RoundingDecimals > roundingDecimals { + roundingDecimals = ts.RateInterval.Rating.RoundingDecimals + roundingMethod = ts.RateInterval.Rating.RoundingMethod + } + } + return +} diff --git a/engine/callcost_test.go b/engine/callcost_test.go index 365e79b14..5a8cdf3dd 100644 --- a/engine/callcost_test.go +++ b/engine/callcost_test.go @@ -200,7 +200,7 @@ func TestCallCostToDataCost(t *testing.T) { t.Error("Error convertiong to data cost: ", err) } js, _ := json.Marshal(dc) - expected := `{"Direction":"*out","Category":"data","Tenant":"cgrates.org","Subject":"rif","Account":"","Destination":"*any","TOR":"*data","Cost":65,"DataSpans":[{"DataStart":0,"DataEnd":60,"Cost":60,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":1000000000},{"GroupIntervalStart":60000000000,"Value":1,"RateIncrement":1000000000,"RateUnit":1000000000}],"RoundingMethod":"*up","RoundingDecimals":4},"Weight":10},"DataIndex":60,"Increments":[],"MatchedSubject":"","MatchedPrefix":"","MatchedDestId":""},{"DataStart":60,"DataEnd":65,"Cost":5,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":1000000000},{"GroupIntervalStart":60000000000,"Value":1,"RateIncrement":1000000000,"RateUnit":1000000000}],"RoundingMethod":"*up","RoundingDecimals":4},"Weight":10},"DataIndex":65,"Increments":[],"MatchedSubject":"*out:cgrates.org:data:rif","MatchedPrefix":"*any","MatchedDestId":"*any"}]}` + expected := `{"Direction":"*out","Category":"data","Tenant":"cgrates.org","Subject":"rif","Account":"","Destination":"*any","TOR":"*data","Cost":65,"DataSpans":[{"DataStart":0,"DataEnd":60,"Cost":60,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"RoundingMethod":"*middle","RoundingDecimals":4,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":1000000000},{"GroupIntervalStart":60000000000,"Value":1,"RateIncrement":1000000000,"RateUnit":1000000000}]},"Weight":10},"DataIndex":60,"Increments":[],"MatchedSubject":"","MatchedPrefix":"","MatchedDestId":""},{"DataStart":60,"DataEnd":65,"Cost":5,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"RoundingMethod":"*middle","RoundingDecimals":4,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":1000000000},{"GroupIntervalStart":60000000000,"Value":1,"RateIncrement":1000000000,"RateUnit":1000000000}]},"Weight":10},"DataIndex":65,"Increments":[],"MatchedSubject":"*out:cgrates.org:data:rif","MatchedPrefix":"*any","MatchedDestId":"*any"}]}` if string(js) != expected { t.Error("Error coverting to data cost: ", string(js)) } diff --git a/engine/calldesc.go b/engine/calldesc.go index ae011236d..8196b0950 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -431,9 +431,6 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) { } cost += ts.getCost() } - // global rounding - //TODO: use the longest rounding/method from ts - cost = utils.Round(cost, globalRoundingDecimals, globalRoundingMethod) //startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.Category)) cc := &CallCost{ Direction: cd.Direction, @@ -447,6 +444,9 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) { deductConnectFee: cd.LoopIndex == 0, TOR: cd.TOR, } + // global rounding + roundingDecimals, roundingMethod := cc.GetLongestRounding() + cc.Cost = utils.Round(cc.Cost, roundingDecimals, roundingMethod) //Logger.Info(fmt.Sprintf(" Get Cost: %s => %v", cd.GetKey(), cc)) cc.Timespans.Compress() return cc, err diff --git a/engine/loader_csv.go b/engine/loader_csv.go index 96e503197..41e4be6c0 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -385,7 +385,7 @@ func (csvr *CSVReader) LoadRates() (err error) { for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() { tag := record[0] var r *utils.TPRate - r, err = NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7]) + r, err = NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5]) if err != nil { return err } @@ -420,6 +420,11 @@ func (csvr *CSVReader) LoadDestinationRates() (err error) { if !exists { return fmt.Errorf("Could not get rates for tag %v", record[2]) } + roundingDecimals, err := strconv.Atoi(record[4]) + if err != nil { + log.Printf("Error parsing rounding decimals: %s", record[4]) + return err + } destinationExists := false for _, d := range csvr.destinations { if d.Id == record[1] { @@ -427,7 +432,7 @@ func (csvr *CSVReader) LoadDestinationRates() (err error) { break } } - var err error + if !destinationExists && csvr.dataStorage != nil { if destinationExists, err = csvr.dataStorage.HasData(DESTINATION_PREFIX, record[1]); err != nil { return err @@ -440,8 +445,10 @@ func (csvr *CSVReader) LoadDestinationRates() (err error) { DestinationRateId: tag, DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{ - DestinationId: record[1], - Rate: r, + DestinationId: record[1], + Rate: r, + RoundingMethod: record[3], + RoundingDecimals: roundingDecimals, }, }, } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 327c8329b..b4dfc9f7a 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -56,37 +56,37 @@ ALWAYS,*any,*any,*any,*any,00:00:00 ASAP,*any,*any,*any,*any,*asap ` rates = ` -R1,0,0.2,60,1,0,*middle,2 -R2,0,0.1,60s,1s,0,*middle,2 -R3,0,0.05,60s,1s,0,*middle,2 -R4,1,1,1s,1s,0,*up,2 -R5,0,0.5,1s,1s,0,*down,2 -LANDLINE_OFFPEAK,0,1,1,60,0,*up,4 -LANDLINE_OFFPEAK,0,1,1,1,60,*up,4 -GBP_71,0.000000,5.55555,1s,1s,0s,*up,4 -GBP_72,0.000000,7.77777,1s,1s,0s,*up,4 -GBP_70,0.000000,1,1,1,0,*up,4 -RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s,*up,8 -RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8 -R_URG,0,0,1,1,0,*down,2 +R1,0,0.2,60,1,0 +R2,0,0.1,60s,1s,0 +R3,0,0.05,60s,1s,0 +R4,1,1,1s,1s,0 +R5,0,0.5,1s,1s,0 +LANDLINE_OFFPEAK,0,1,1,60,0 +LANDLINE_OFFPEAK,0,1,1,1,60 +GBP_71,0.000000,5.55555,1s,1s,0s +GBP_72,0.000000,7.77777,1s,1s,0s +GBP_70,0.000000,1,1,1,0 +RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s +RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s +R_URG,0,0,1,1,0 ` destinationRates = ` -RT_STANDARD,GERMANY,R1 -RT_STANDARD,GERMANY_O2,R2 -RT_STANDARD,GERMANY_PREMIUM,R2 -RT_DEFAULT,ALL,R2 -RT_STD_WEEKEND,GERMANY,R2 -RT_STD_WEEKEND,GERMANY_O2,R3 -P1,NAT,R4 -P2,NAT,R5 -T1,NAT,LANDLINE_OFFPEAK -T2,GERMANY,GBP_72 -T2,GERMANY_O2,GBP_70 -T2,GERMANY_PREMIUM,GBP_71 -DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG -DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5 -DATA_RATE,*any,LANDLINE_OFFPEAK -RT_URG,URG,R_URG +RT_STANDARD,GERMANY,R1,*middle,4 +RT_STANDARD,GERMANY_O2,R2,*middle,4 +RT_STANDARD,GERMANY_PREMIUM,R2,*middle,4 +RT_DEFAULT,ALL,R2,*middle,4 +RT_STD_WEEKEND,GERMANY,R2,*middle,4 +RT_STD_WEEKEND,GERMANY_O2,R3,*middle,4 +P1,NAT,R4,*middle,4 +P2,NAT,R5,*middle,4 +T1,NAT,LANDLINE_OFFPEAK,*middle,4 +T2,GERMANY,GBP_72,*middle,4 +T2,GERMANY_O2,GBP_70,*middle,4 +T2,GERMANY_PREMIUM,GBP_71,*middle,4 +DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG,*middle,4 +DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5,*middle,4 +DATA_RATE,*any,LANDLINE_OFFPEAK,*middle,4 +RT_URG,URG,R_URG,*middle,4 ` ratingPlans = ` STANDARD,RT_STANDARD,WORKDAYS_00,10 @@ -309,7 +309,7 @@ func TestLoadRates(t *testing.T) { t.Error("Failed to load rates: ", csvr.rates) } rate := csvr.rates["R1"].RateSlots[0] - expctRs, err := utils.NewRateSlot(0, 0.2, "60", "1", "0", utils.ROUNDING_MIDDLE, 2) + expctRs, err := utils.NewRateSlot(0, 0.2, "60", "1", "0") if err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || @@ -319,7 +319,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate, expctRs) } rate = csvr.rates["R2"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(0, 0.1, "60s", "1s", "0", utils.ROUNDING_MIDDLE, 2); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.1, "60s", "1s", "0"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -328,7 +328,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["R3"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(0, 0.05, "60s", "1s", "0", utils.ROUNDING_MIDDLE, 2); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.05, "60s", "1s", "0"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -337,7 +337,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["R4"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(1, 1.0, "1s", "1s", "0", utils.ROUNDING_UP, 2); err != nil { + if expctRs, err = utils.NewRateSlot(1, 1.0, "1s", "1s", "0"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -346,7 +346,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["R5"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(0, 0.5, "1s", "1s", "0", utils.ROUNDING_DOWN, 2); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.5, "1s", "1s", "0"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -355,7 +355,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["LANDLINE_OFFPEAK"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(0, 1, "1", "60", "0", utils.ROUNDING_UP, 4); err != nil { + if expctRs, err = utils.NewRateSlot(0, 1, "1", "60", "0"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -364,7 +364,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["LANDLINE_OFFPEAK"].RateSlots[1] - if expctRs, err = utils.NewRateSlot(0, 1, "1", "1", "60", utils.ROUNDING_UP, 4); err != nil { + if expctRs, err = utils.NewRateSlot(0, 1, "1", "1", "60"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -384,16 +384,22 @@ func TestLoadDestinationRates(t *testing.T) { DestinationRateId: "RT_STANDARD", DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{ - DestinationId: "GERMANY", - Rate: csvr.rates["R1"], + DestinationId: "GERMANY", + Rate: csvr.rates["R1"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, &utils.DestinationRate{ - DestinationId: "GERMANY_O2", - Rate: csvr.rates["R2"], + DestinationId: "GERMANY_O2", + Rate: csvr.rates["R2"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, &utils.DestinationRate{ - DestinationId: "GERMANY_PREMIUM", - Rate: csvr.rates["R2"], + DestinationId: "GERMANY_PREMIUM", + Rate: csvr.rates["R2"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, }, } @@ -405,24 +411,30 @@ func TestLoadDestinationRates(t *testing.T) { DestinationRateId: "RT_DEFAULT", DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{ - DestinationId: "ALL", - Rate: csvr.rates["R2"], + DestinationId: "ALL", + Rate: csvr.rates["R2"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, }, }) { - t.Errorf("Error loading destination rate: %+v", drs) + t.Errorf("Error loading destination rate: %+v", drs.DestinationRates[0]) } drs = csvr.destinationRates["RT_STD_WEEKEND"] if !reflect.DeepEqual(drs, &utils.TPDestinationRate{ DestinationRateId: "RT_STD_WEEKEND", DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{ - DestinationId: "GERMANY", - Rate: csvr.rates["R2"], + DestinationId: "GERMANY", + Rate: csvr.rates["R2"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, &utils.DestinationRate{ - DestinationId: "GERMANY_O2", - Rate: csvr.rates["R3"], + DestinationId: "GERMANY_O2", + Rate: csvr.rates["R3"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, }, }) { @@ -433,8 +445,10 @@ func TestLoadDestinationRates(t *testing.T) { DestinationRateId: "P1", DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{ - DestinationId: "NAT", - Rate: csvr.rates["R4"], + DestinationId: "NAT", + Rate: csvr.rates["R4"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, }, }) { @@ -445,8 +459,10 @@ func TestLoadDestinationRates(t *testing.T) { DestinationRateId: "P2", DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{ - DestinationId: "NAT", - Rate: csvr.rates["R5"], + DestinationId: "NAT", + Rate: csvr.rates["R5"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, }, }) { @@ -457,8 +473,10 @@ func TestLoadDestinationRates(t *testing.T) { DestinationRateId: "T1", DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{ - DestinationId: "NAT", - Rate: csvr.rates["LANDLINE_OFFPEAK"], + DestinationId: "NAT", + Rate: csvr.rates["LANDLINE_OFFPEAK"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, }, }) { @@ -469,16 +487,22 @@ func TestLoadDestinationRates(t *testing.T) { DestinationRateId: "T2", DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{ - DestinationId: "GERMANY", - Rate: csvr.rates["GBP_72"], + DestinationId: "GERMANY", + Rate: csvr.rates["GBP_72"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, &utils.DestinationRate{ - DestinationId: "GERMANY_O2", - Rate: csvr.rates["GBP_70"], + DestinationId: "GERMANY_O2", + Rate: csvr.rates["GBP_70"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, &utils.DestinationRate{ - DestinationId: "GERMANY_PREMIUM", - Rate: csvr.rates["GBP_71"], + DestinationId: "GERMANY_PREMIUM", + Rate: csvr.rates["GBP_71"], + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, }, }) { @@ -524,7 +548,7 @@ func TestLoadRatingPlans(t *testing.T) { }, }, Ratings: map[string]*RIRate{ - "d54545c1": &RIRate{ + "822a5aef": &RIRate{ ConnectFee: 0, Rates: []*Rate{ &Rate{ @@ -535,9 +559,9 @@ func TestLoadRatingPlans(t *testing.T) { }, }, RoundingMethod: utils.ROUNDING_MIDDLE, - RoundingDecimals: 2, + RoundingDecimals: 4, }, - "4bb00b9c": &RIRate{ + "4a25c533": &RIRate{ ConnectFee: 0, Rates: []*Rate{ &Rate{ @@ -548,9 +572,9 @@ func TestLoadRatingPlans(t *testing.T) { }, }, RoundingMethod: utils.ROUNDING_MIDDLE, - RoundingDecimals: 2, + RoundingDecimals: 4, }, - "e06c337f": &RIRate{ + "b05c5f6b": &RIRate{ ConnectFee: 0, Rates: []*Rate{ &Rate{ @@ -561,9 +585,9 @@ func TestLoadRatingPlans(t *testing.T) { }, }, RoundingMethod: utils.ROUNDING_MIDDLE, - RoundingDecimals: 2, + RoundingDecimals: 4, }, - "2efe78aa": &RIRate{ + "9f49ef8e": &RIRate{ ConnectFee: 0, Rates: []*Rate{ &Rate{ @@ -573,56 +597,56 @@ func TestLoadRatingPlans(t *testing.T) { RateUnit: time.Second, }, }, - RoundingMethod: utils.ROUNDING_DOWN, - RoundingDecimals: 2, + RoundingMethod: utils.ROUNDING_MIDDLE, + RoundingDecimals: 4, }, }, DestinationRates: map[string]RPRateList{ "GERMANY": []*RPRate{ &RPRate{ Timing: "14ae6e41", - Rating: "d54545c1", + Rating: "822a5aef", Weight: 10, }, &RPRate{ Timing: "9a6f8e32", - Rating: "4bb00b9c", + Rating: "4a25c533", Weight: 10, }, &RPRate{ Timing: "7181e535", - Rating: "4bb00b9c", + Rating: "4a25c533", Weight: 10, }, }, "GERMANY_O2": []*RPRate{ &RPRate{ Timing: "14ae6e41", - Rating: "4bb00b9c", + Rating: "4a25c533", Weight: 10, }, &RPRate{ Timing: "9a6f8e32", - Rating: "e06c337f", + Rating: "b05c5f6b", Weight: 10, }, &RPRate{ Timing: "7181e535", - Rating: "e06c337f", + Rating: "b05c5f6b", Weight: 10, }, }, "GERMANY_PREMIUM": []*RPRate{ &RPRate{ Timing: "14ae6e41", - Rating: "4bb00b9c", + Rating: "4a25c533", Weight: 10, }, }, "URG": []*RPRate{ &RPRate{ Timing: "96c78ff5", - Rating: "2efe78aa", + Rating: "9f49ef8e", Weight: 20, }, }, diff --git a/engine/loader_helpers.go b/engine/loader_helpers.go index 0be1eef8f..56d5b5b6c 100644 --- a/engine/loader_helpers.go +++ b/engine/loader_helpers.go @@ -52,7 +52,7 @@ type TPLoader interface { WriteToDatabase(bool, bool) error } -func NewLoadRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterval, roundingMethod, roundingDecimals string) (r *utils.TPRate, err error) { +func NewLoadRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterval string) (r *utils.TPRate, err error) { cf, err := strconv.ParseFloat(connectFee, 64) if err != nil { log.Printf("Error parsing connect fee from: %v", connectFee) @@ -63,12 +63,7 @@ func NewLoadRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterv log.Printf("Error parsing price from: %v", price) return } - rd, err := strconv.Atoi(roundingDecimals) - if err != nil { - log.Printf("Error parsing rounding decimals: %s", roundingDecimals) - return - } - rs, err := utils.NewRateSlot(cf, p, ratedUnits, rateIncrements, groupInterval, roundingMethod, rd) + rs, err := utils.NewRateSlot(cf, p, ratedUnits, rateIncrements, groupInterval) if err != nil { return nil, err } @@ -86,9 +81,6 @@ func ValidNextGroup(present, next *utils.RateSlot) error { if math.Mod(next.GroupIntervalStartDuration().Seconds(), present.RateIncrementDuration().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 } @@ -128,8 +120,8 @@ func GetRateInterval(rpl *utils.TPRatingPlanBinding, dr *utils.DestinationRate) Weight: rpl.Weight, Rating: &RIRate{ ConnectFee: dr.Rate.RateSlots[0].ConnectFee, - RoundingMethod: dr.Rate.RateSlots[0].RoundingMethod, - RoundingDecimals: dr.Rate.RateSlots[0].RoundingDecimals, + RoundingMethod: dr.RoundingMethod, + RoundingDecimals: dr.RoundingDecimals, }, } for _, rl := range dr.Rate.RateSlots { @@ -189,7 +181,7 @@ var FileValidators = map[string]*FileLineRegexValidator{ regexp.MustCompile(`(?:\w+\s*,\s*){1}(?:\*any\s*,\s*|(?:\d{1,4};?)+\s*,\s*|\s*,\s*){4}(?:\d{2}:\d{2}:\d{2}|\*asap){1}$`), "Tag([0-9A-Za-z_]),Years([0-9;]|*any|),Months([0-9;]|*any|),MonthDays([0-9;]|*any|),WeekDays([0-9;]|*any|),Time([0-9:]|*asap)"}, utils.RATES_CSV: &FileLineRegexValidator{utils.RATES_NRCOLS, - regexp.MustCompile(`(?:\w+\s*,\s*){1}(?:\d+\.?\d*,){2}(?:\d+s*,){3}(?:\*\w+,){1}(?:\d+\.?\d*,?){1}$`), + regexp.MustCompile(`(?:\w+\s*,\s*){1}(?:\d+\.?\d*,){2}(?:\d+s*,?){3}$`), "Tag([0-9A-Za-z_]),ConnectFee([0-9.]),Rate([0-9.]),RateUnit([0-9.]),RateIncrementStart([0-9.])"}, utils.DESTINATION_RATES_CSV: &FileLineRegexValidator{utils.DESTINATION_RATES_NRCOLS, regexp.MustCompile(`^(?:\w+\s*),(?:\w+\s*),(?:\w+\s*)$`), diff --git a/engine/loader_helpers_test.go b/engine/loader_helpers_test.go index ea5db6a7f..a3ded234b 100644 --- a/engine/loader_helpers_test.go +++ b/engine/loader_helpers_test.go @@ -20,11 +20,12 @@ package engine import ( "bufio" - "github.com/cgrates/cgrates/utils" "io" "reflect" "strings" "testing" + + "github.com/cgrates/cgrates/utils" ) var timingsSample = `#Tag,Years,Months,MonthDays,WeekDays,Time @@ -39,7 +40,7 @@ DUMMY,INVALID;DATA GERMANY_MOBILE,+4915 ` var ratesSample = `#Tag,DestinationRatesTag,TimingTag,Weight -RT_1CENT,0,1,1s,1s,0s,*up,2 +RT_1CENT,0,1,1s,1s,0s DUMMY,INVALID;DATA ` @@ -410,7 +411,7 @@ func TestTPCSVFileParser(t *testing.T) { if err != nil { t.Error(err) } - if !reflect.DeepEqual(record, []string{"RT_1CENT", "0", "1", "1s", "1s", "0s", "*up", "2"}) { + if !reflect.DeepEqual(record, []string{"RT_1CENT", "0", "1", "1s", "1s", "0s"}) { t.Error("Unexpected record extracted", record) } case 3: diff --git a/engine/rateinterval.go b/engine/rateinterval.go index b8bc8bd68..535fb73f6 100644 --- a/engine/rateinterval.go +++ b/engine/rateinterval.go @@ -54,9 +54,9 @@ func (rit *RITiming) Stringify() string { // Separate structure used for rating plan size optimization type RIRate struct { ConnectFee float64 - Rates RateGroups // GroupRateInterval (start time): Rate - RoundingMethod string //ROUNDING_UP, ROUNDING_DOWN, ROUNDING_MIDDLE + RoundingMethod string RoundingDecimals int + Rates RateGroups // GroupRateInterval (start time): Rate } func (rir *RIRate) Stringify() string { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 44e1c00b8..67536a5b1 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -198,8 +198,7 @@ func (self *SQLStorage) SetTPRates(tpid string, rts map[string][]*utils.RateSlot buffer.WriteRune(',') } buffer.WriteString(fmt.Sprintf("('%s', '%s', %f, %f, '%s', '%s','%s','%s', %d)", - tpid, rtId, rt.ConnectFee, rt.Rate, rt.RateUnit, rt.RateIncrement, rt.GroupIntervalStart, - rt.RoundingMethod, rt.RoundingDecimals)) + tpid, rtId, rt.ConnectFee, rt.Rate, rt.RateUnit, rt.RateIncrement, rt.GroupIntervalStart)) i++ } } @@ -900,13 +899,12 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*utils.TPRate, } defer rows.Close() for rows.Next() { - var tag, rate_unit, rate_increment, group_interval_start, roundingMethod string + var tag, rate_unit, rate_increment, group_interval_start string var connect_fee, rate float64 - var roundingDecimals int - if err := rows.Scan(&tag, &connect_fee, &rate, &rate_unit, &rate_increment, &group_interval_start, &roundingMethod, &roundingDecimals); err != nil { + if err := rows.Scan(&tag, &connect_fee, &rate, &rate_unit, &rate_increment, &group_interval_start); err != nil { return nil, err } - rs, err := utils.NewRateSlot(connect_fee, rate, rate_unit, rate_increment, group_interval_start, roundingMethod, roundingDecimals) + rs, err := utils.NewRateSlot(connect_fee, rate, rate_unit, rate_increment, group_interval_start) if err != nil { return nil, err } diff --git a/engine/timespans_test.go b/engine/timespans_test.go index 8dfd85d94..976a6b02c 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -729,7 +729,7 @@ func TestTimespanCreateIncrements(t *testing.T) { if len(ts.Increments) != 3 { t.Error("Error creating increment slice: ", len(ts.Increments)) } - if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.0667 { + if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.0666666667 { t.Error("Wrong second slice: ", ts.Increments[2].Cost) } } diff --git a/engine/tpimporter_csv.go b/engine/tpimporter_csv.go index 93019fbaf..7adacbb11 100644 --- a/engine/tpimporter_csv.go +++ b/engine/tpimporter_csv.go @@ -146,7 +146,7 @@ func (self *TPCSVImporter) importRates(fn string) error { } continue } - rt, err := NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7]) + rt, err := NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5]) if err != nil { return err } diff --git a/general_tests/ddazmbl1_test.go b/general_tests/ddazmbl1_test.go index 7f94f6b8f..5c4d67e8b 100644 --- a/general_tests/ddazmbl1_test.go +++ b/general_tests/ddazmbl1_test.go @@ -42,10 +42,10 @@ func TestLoadCsvTp(t *testing.T) { ASAP,*any,*any,*any,*any,*asap` destinations := `DST_UK_Mobile_BIG5,447596 DST_UK_Mobile_BIG5,447956` - rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s,*up,8 -RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8` - destinationRates := `DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG -DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5` + rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s +RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s` + destinationRates := `DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG,*up,8 +DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5,*up,8` ratingPlans := `RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10 RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` ratingProfiles := `*out,cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_UK, diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index c4465a1a4..9088d204a 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -42,10 +42,10 @@ func TestLoadCsvTp2(t *testing.T) { ASAP,*any,*any,*any,*any,*asap` destinations := `DST_UK_Mobile_BIG5,447596 DST_UK_Mobile_BIG5,447956` - rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s,*up,8 -RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8` - destinationRates := `DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG -DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5` + rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s +RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s` + destinationRates := `DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG,*up,8 +DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5,*up,8` ratingPlans := `RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10 RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` ratingProfiles := `*out,cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_UK, diff --git a/general_tests/ddazmbl3_test.go b/general_tests/ddazmbl3_test.go index 284c2ab9c..15963b650 100644 --- a/general_tests/ddazmbl3_test.go +++ b/general_tests/ddazmbl3_test.go @@ -42,10 +42,10 @@ func TestLoadCsvTp3(t *testing.T) { ASAP,*any,*any,*any,*any,*asap` destinations := `DST_UK_Mobile_BIG5,447596 DST_UK_Mobile_BIG5,447956` - rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s,*up,8 -RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8` - destinationRates := `DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG -DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5` + rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s +RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s` + destinationRates := `DR_UK_Mobile_BIG5_PKG,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5_PKG,*up,8 +DR_UK_Mobile_BIG5,DST_UK_Mobile_BIG5,RT_UK_Mobile_BIG5,*up,8` ratingPlans := `RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10 RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` ratingProfiles := `*out,cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_UK, diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 44992cefa..eb8d11ac2 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -40,9 +40,9 @@ type TPRate struct { } // Needed so we make sure we always use SetDurations() on a newly created value -func NewRateSlot(connectFee, rate float64, rateUnit, rateIncrement, grpInterval, rndMethod string, rndDecimals int) (*RateSlot, error) { +func NewRateSlot(connectFee, rate float64, rateUnit, rateIncrement, grpInterval string) (*RateSlot, error) { rs := &RateSlot{ConnectFee: connectFee, Rate: rate, RateUnit: rateUnit, RateIncrement: rateIncrement, - GroupIntervalStart: grpInterval, RoundingMethod: rndMethod, RoundingDecimals: rndDecimals} + GroupIntervalStart: grpInterval} if err := rs.SetDurations(); err != nil { return nil, err } @@ -55,8 +55,6 @@ type RateSlot struct { RateUnit string // Number of billing units this rate applies to RateIncrement string // This rate will apply in increments of duration GroupIntervalStart string // Group position - RoundingMethod string // Use this method to round the cost - RoundingDecimals int // Round the cost number of decimals rateUnitDur time.Duration rateIncrementDur time.Duration groupIntervalStartDur time.Duration @@ -93,9 +91,11 @@ type TPDestinationRate struct { } type DestinationRate struct { - DestinationId string // The destination identity - RateId string // The rate identity - Rate *TPRate + DestinationId string // The destination identity + RateId string // The rate identity + Rate *TPRate + RoundingMethod string + RoundingDecimals int } type ApierTPTiming struct { diff --git a/utils/consts.go b/utils/consts.go index ec9503162..1ecc0d5cc 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -51,8 +51,8 @@ const ( DERIVED_CHARGERS_CSV = "DerivedChargers.csv" TIMINGS_NRCOLS = 6 DESTINATIONS_NRCOLS = 2 - RATES_NRCOLS = 8 - DESTINATION_RATES_NRCOLS = 3 + RATES_NRCOLS = 6 + DESTINATION_RATES_NRCOLS = 5 DESTRATE_TIMINGS_NRCOLS = 4 RATE_PROFILES_NRCOLS = 7 SHARED_GROUPS_NRCOLS = 4