using time.Duration for rateincrements and groupinterval

This commit is contained in:
Radu Ioan Fericean
2013-08-07 13:29:27 +03:00
parent 0785874357
commit c209dbfb9f
14 changed files with 99 additions and 83 deletions

View File

@@ -40,7 +40,7 @@ func (self *ApierV1) SetTPRate(attrs utils.TPRate, reply *string) error {
rts := make([]*engine.Rate, len(attrs.RateSlots))
for idx, rtSlot := range attrs.RateSlots {
rts[idx] = &engine.Rate{attrs.RateId, rtSlot.ConnectFee, rtSlot.Rate, float64(rtSlot.RatedUnits),
float64(rtSlot.RateIncrements), float64(rtSlot.GroupInterval), rtSlot.RoundingMethod, rtSlot.RoundingDecimals, rtSlot.Weight}
rtSlot.RateIncrements, rtSlot.GroupInterval, rtSlot.RoundingMethod, rtSlot.RoundingDecimals, rtSlot.Weight}
}
if err := self.StorDb.SetTPRates(attrs.TPid, map[string][]*engine.Rate{attrs.RateId: rts}); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())

View File

@@ -42,8 +42,8 @@ CREATE TABLE `tp_rates` (
`connect_fee` decimal(5,4) NOT NULL,
`rate` decimal(5,4) NOT NULL,
`rated_units` int(11) NOT NULL,
`rate_increments` int(11) NOT NULL,
`group_interval` int(11) NOT NULL,
`rate_increments` varchar(24) NOT NULL,
`group_interval` varchar(24) NOT NULL,
`rounding_method` varchar(255) NOT NULL,
`rounding_decimals` tinyint(4) NOT NULL,
`weight` decimal(5,2) NOT NULL,

View File

@@ -1,7 +1,7 @@
#Tag,ConnectFee,Rate,RatedUnits,RateIncrements,GroupInterval,RoundingMethod,RoundingDecimals,Weight
LANDLINE_PEAK,0.02,0.02,60,60,0,*up,4,10
LANDLINE_PEAK,0.02,0.01,1,1,60,*up,4,10
MOBILE_PEAK,0.02,0.14,60,60,0,*up,4,10
LANDLINE_OFFPEAK,1,0,60,60,0,*up,4,10
MOBILE_OFFPEAK,0.02,0.1,60,60,0,*up,4,10
RT_FS_USERS,0,0,60,60,0,*up,0,10
LANDLINE_PEAK,0.02,0.02,60,60s,0,*up,4,10
LANDLINE_PEAK,0.02,0.01,1,1s,60s,*up,4,10
MOBILE_PEAK,0.02,0.14,60,60s,0,*up,4,10
LANDLINE_OFFPEAK,1,0,60,60s,0,*up,4,10
MOBILE_OFFPEAK,0.02,0.1,60,60s,0,*up,4,10
RT_FS_USERS,0,0,60,60s,0,*up,0,10
1 #Tag ConnectFee Rate RatedUnits RateIncrements GroupInterval RoundingMethod RoundingDecimals Weight
2 LANDLINE_PEAK 0.02 0.02 60 60 60s 0 *up 4 10
3 LANDLINE_PEAK 0.02 0.01 1 1 1s 60 60s *up 4 10
4 MOBILE_PEAK 0.02 0.14 60 60 60s 0 *up 4 10
5 LANDLINE_OFFPEAK 1 0 60 60 60s 0 *up 4 10
6 MOBILE_OFFPEAK 0.02 0.1 60 60 60s 0 *up 4 10
7 RT_FS_USERS 0 0 60 60 60s 0 *up 0 10

View File

@@ -188,7 +188,7 @@ Splits the received timespan into sub time spans according to the activation per
*/
func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*TimeSpan) {
if firstSpan == nil {
firstSpan = &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, CallDuration: cd.CallDuration.Seconds()}
firstSpan = &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, CallDuration: cd.CallDuration}
}
timespans = append(timespans, firstSpan)
// split on (free) minute buckets

File diff suppressed because one or more lines are too long

View File

@@ -45,9 +45,9 @@ type Interval struct {
}
type Price struct {
StartSecond float64
StartSecond time.Duration
Value float64
RateIncrements float64
RateIncrements time.Duration
}
func (p *Price) Equal(o *Price) bool {
@@ -193,19 +193,20 @@ func (i *Interval) Equal(o *Interval) bool {
i.EndTime == o.EndTime
}
func (i *Interval) GetCost(duration, startSecond float64) (cost float64) {
func (i *Interval) GetCost(duration, startSecond time.Duration) (cost float64) {
price := i.GetPrice(startSecond)
rateIncrements := i.GetRateIncrements(startSecond)
rateIncrements := i.GetRateIncrements(startSecond).Seconds()
d := float64(duration.Seconds())
if i.PricedUnits != 0 {
cost = math.Ceil(duration/rateIncrements) * rateIncrements * (price / i.PricedUnits)
cost = math.Ceil(d/rateIncrements) * rateIncrements * (price / i.PricedUnits)
} else {
cost = math.Ceil(duration/rateIncrements) * rateIncrements * price
cost = math.Ceil(d/rateIncrements) * rateIncrements * price
}
return utils.Round(cost, i.RoundingDecimals, i.RoundingMethod)
}
// Gets the price for a the provided start second
func (i *Interval) GetPrice(startSecond float64) float64 {
func (i *Interval) GetPrice(startSecond time.Duration) float64 {
i.Prices.Sort()
for index, price := range i.Prices {
if price.StartSecond <= startSecond && (index == len(i.Prices)-1 ||
@@ -216,7 +217,7 @@ func (i *Interval) GetPrice(startSecond float64) float64 {
return -1
}
func (i *Interval) GetRateIncrements(startSecond float64) float64 {
func (i *Interval) GetRateIncrements(startSecond time.Duration) time.Duration {
i.Prices.Sort()
for index, price := range i.Prices {
if price.StartSecond <= startSecond && (index == len(i.Prices)-1 ||

View File

@@ -45,11 +45,11 @@ WEEKENDS,*any,*any,*any,6;7,00:00:00
ONE_TIME_RUN,2012,,,,*asap
`
rates = `
R1,0,0.2,60,1,0,*middle,2,10
R2,0,0.1,60,1,0,*middle,2,10
R3,0,0.05,60,1,0,*middle,2,10
R4,1,1,1,1,0,*up,2,10
R5,0,0.5,1,1,0,*down,2,10
R1,0,0.2,60,1s,0,*middle,2,10
R2,0,0.1,60,1s,0,*middle,2,10
R3,0,0.05,60,1s,0,*middle,2,10
R4,1,1,1,1s,0,*up,2,10
R5,0,0.5,1,1s,0,*down,2,10
`
destinationRates = `
RT_STANDARD,GERMANY,R1

View File

@@ -29,6 +29,7 @@ import (
"regexp"
"strconv"
"strings"
"time"
)
type TPLoader interface {
@@ -46,11 +47,12 @@ type TPLoader interface {
}
type Rate struct {
Tag string
ConnectFee, Price, PricedUnits, RateIncrements, GroupInterval float64
RoundingMethod string
RoundingDecimals int
Weight float64
Tag string
ConnectFee, Price, PricedUnits float64
RateIncrements, GroupInterval time.Duration
RoundingMethod string
RoundingDecimals int
Weight float64
}
func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, groupInterval, roundingMethod, roundingDecimals, weight string) (r *Rate, err error) {
@@ -64,7 +66,7 @@ func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, groupInterval,
log.Printf("Error parsing price from: %v", price)
return
}
gi, err := strconv.ParseFloat(groupInterval, 64)
gi, err := time.ParseDuration(groupInterval)
if err != nil {
log.Printf("Error parsing group interval from: %v", price)
return
@@ -74,7 +76,7 @@ func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, groupInterval,
log.Printf("Error parsing priced units from: %v", pricedUnits)
return
}
ri, err := strconv.ParseFloat(rateIncrements, 64)
ri, err := time.ParseDuration(rateIncrements)
if err != nil {
log.Printf("Error parsing rates increments from: %v", rateIncrements)
return

View File

@@ -462,9 +462,9 @@ func (d *Destination) Restore(input string) error {
func (pg PriceGroups) Store() (result string, err error) {
for _, p := range pg {
result += strconv.FormatFloat(p.StartSecond, 'f', -1, 64) +
result += p.StartSecond.String() +
":" + strconv.FormatFloat(p.Value, 'f', -1, 64) +
":" + strconv.FormatFloat(p.RateIncrements, 'f', -1, 64) +
":" + p.RateIncrements.String() +
","
}
result = strings.TrimRight(result, ",")
@@ -478,7 +478,7 @@ func (pg *PriceGroups) Restore(input string) error {
if len(priceElements) != 3 {
continue
}
ss, err := strconv.ParseFloat(priceElements[0], 64)
ss, err := time.ParseDuration(priceElements[0])
if err != nil {
return err
}
@@ -486,7 +486,7 @@ func (pg *PriceGroups) Restore(input string) error {
if err != nil {
return err
}
ri, err := strconv.ParseFloat(priceElements[2], 64)
ri, err := time.ParseDuration(priceElements[2])
if err != nil {
return err
}

View File

@@ -43,7 +43,7 @@ func TestSimpleMarshallerApStoreRestore(t *testing.T) {
}
func TestSimpleMarshallerApRestoreFromString(t *testing.T) {
s := "2012-02-01T14:30:01Z|;1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0:0.2:1;60;1;\n"
s := "2012-02-01T14:30:01Z|;1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0:0.2:1s;60;1;\n"
ap := &ActivationPeriod{}
err := ap.Restore(s)
if err != nil || len(ap.Intervals) != 1 {
@@ -142,10 +142,10 @@ func TestIntervalStoreRestore(t *testing.T) {
}
func TestIntervalRestoreFromString(t *testing.T) {
s := ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0:0.2:1;60;0;"
s := ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0:0.2:1s;60;0;"
i := Interval{}
err := i.Restore(s)
if err != nil || !i.Prices.Equal(PriceGroups{&Price{0, 0.2, 1}}) {
if err != nil || !i.Prices.Equal(PriceGroups{&Price{0, 0.2, 1 * time.Second}}) {
t.Errorf("Error restoring inteval period from string %+v", i)
}
}

View File

@@ -23,6 +23,7 @@ import (
"encoding/json"
"fmt"
"github.com/cgrates/cgrates/utils"
"time"
)
type SQLStorage struct {
@@ -242,13 +243,14 @@ func (self *SQLStorage) GetTPRate(tpid, rtId string) (*utils.TPRate, error) {
for rows.Next() {
i++ //Keep here a reference so we know we got at least one prefix
var connectFee, rate, weight float64
var ratedUnits, rateIncrements, roundingDecimals, groupInterval int
var ratedUnits, roundingDecimals int
var rateIncrements, groupInterval time.Duration
var roundingMethod string
err = rows.Scan(&connectFee, &rate, &ratedUnits, &rateIncrements, &groupInterval, &roundingMethod, &roundingDecimals, &weight)
if err != nil {
return nil, err
}
rt.RateSlots = append(rt.RateSlots, utils.RateSlot{connectFee, rate, ratedUnits, rateIncrements, groupInterval,
rt.RateSlots = append(rt.RateSlots, utils.RateSlot{connectFee, rate, ratedUnits, rateIncrements * time.Second, groupInterval * time.Second,
roundingMethod, roundingDecimals, weight})
}
if i == 0 {
@@ -974,7 +976,8 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*Rate, error) {
defer rows.Close()
for rows.Next() {
var tag, roundingMethod string
var connect_fee, rate, priced_units, rate_increments, group_interval, weight float64
var connect_fee, rate, priced_units, weight float64
var rate_increments, group_interval time.Duration
var roundingDecimals int
if err := rows.Scan(&tag, &connect_fee, &rate, &priced_units, &rate_increments, &group_interval, &roundingMethod, &roundingDecimals, &weight); err != nil {
return nil, err

View File

@@ -20,7 +20,6 @@ package engine
import (
"fmt"
"math"
"time"
)
@@ -33,7 +32,7 @@ type TimeSpan struct {
ActivationPeriod *ActivationPeriod
Interval *Interval
MinuteInfo *MinuteInfo
CallDuration float64 // the call duration so far till TimeEnd
CallDuration time.Duration // the call duration so far till TimeEnd
}
// Holds the bonus minute information related to a specified timespan
@@ -60,9 +59,8 @@ func (ts *TimeSpan) getCost(cd *CallDescriptor) (cost float64) {
if ts.Interval == nil {
return 0
}
duration := ts.GetDuration().Seconds()
i := ts.Interval
cost = i.GetCost(duration, ts.GetGroupStart())
cost = i.GetCost(ts.GetDuration(), ts.GetGroupStart())
// if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
// userBalance.mux.RLock()
// if percentageDiscount, err := userBalance.getVolumeDiscount(cd.Destination, INBOUND); err == nil && percentageDiscount > 0 {
@@ -113,7 +111,7 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
for _, price := range i.Prices {
if ts.GetGroupStart() < price.StartSecond && ts.GetGroupEnd() >= price.StartSecond {
ts.SetInterval(i)
splitTime := ts.TimeStart.Add(time.Duration(price.StartSecond-ts.GetGroupStart()) * time.Second)
splitTime := ts.TimeStart.Add(price.StartSecond - ts.GetGroupStart())
nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd}
ts.TimeEnd = splitTime
nts.SetInterval(i)
@@ -215,14 +213,22 @@ func (ts *TimeSpan) SplitByMinuteBucket(mb *MinuteBucket) (newTs *TimeSpan) {
return
}
func (ts *TimeSpan) GetGroupStart() float64 {
return math.Max(0, ts.CallDuration-ts.GetDuration().Seconds())
func (ts *TimeSpan) GetGroupStart() time.Duration {
s := ts.CallDuration - ts.GetDuration()
if s < 0 {
s = 0
}
return s
}
func (ts *TimeSpan) GetGroupEnd() float64 {
func (ts *TimeSpan) GetGroupEnd() time.Duration {
return ts.CallDuration
}
func (ts *TimeSpan) SetNewCallDuration(nts *TimeSpan) {
ts.CallDuration = math.Max(0, ts.CallDuration-nts.GetDuration().Seconds())
d := ts.CallDuration - nts.GetDuration()
if d < 0 {
d = 0
}
ts.CallDuration = d
}

View File

@@ -192,7 +192,7 @@ func TestTimespanGetCost(t *testing.T) {
if ts1.getCost(cd) != 0 {
t.Error("No interval and still kicking")
}
ts1.Interval = &Interval{Prices: PriceGroups{&Price{0, 1.0, 1}}}
ts1.Interval = &Interval{Prices: PriceGroups{&Price{0, 1.0, 1 * time.Second}}}
if ts1.getCost(cd) != 600 {
t.Error("Expected 10 got ", ts1.getCost(cd))
}
@@ -203,9 +203,9 @@ func TestTimespanGetCost(t *testing.T) {
}
func TestSetInterval(t *testing.T) {
i1 := &Interval{Prices: PriceGroups{&Price{0, 1.0, 1}}}
i1 := &Interval{Prices: PriceGroups{&Price{0, 1.0, 1 * time.Second}}}
ts1 := TimeSpan{Interval: i1}
i2 := &Interval{Prices: PriceGroups{&Price{0, 2.0, 1}}}
i2 := &Interval{Prices: PriceGroups{&Price{0, 2.0, 1 * time.Second}}}
ts1.SetInterval(i2)
if ts1.Interval != i1 {
t.Error("Smaller price interval should win")
@@ -332,16 +332,16 @@ func TestTimespanSplitByMinuteBucketScarceExpiringDifferentScarceFirst(t *testin
func TestTimespanSplitGroupedRates(t *testing.T) {
i := &Interval{
EndTime: "17:59:00",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{900, 1, 1}},
Prices: PriceGroups{&Price{0, 2, 1 * time.Second}, &Price{900 * time.Second, 1, 1 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 18, 00, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 1800}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 1800 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 45, 00, 0, time.UTC)
if ts.TimeStart != t1 || ts.TimeEnd != splitTime {
t.Error("Incorrect first half", ts)
t.Error("Incorrect first half", ts.TimeStart, ts.TimeEnd)
}
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
@@ -349,8 +349,8 @@ func TestTimespanSplitGroupedRates(t *testing.T) {
if ts.Interval != i {
t.Error("Interval not attached correctly")
}
c1 := ts.Interval.GetCost(ts.GetDuration().Seconds(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration().Seconds(), nts.GetGroupStart())
c1 := ts.Interval.GetCost(ts.GetDuration(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration(), nts.GetGroupStart())
if c1 != 1800 || c2 != 900 {
t.Error("Wrong costs: ", c1, c2)
}
@@ -366,11 +366,11 @@ func TestTimespanSplitGroupedRates(t *testing.T) {
func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
i := &Interval{
EndTime: "17:59:00",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{30, 1, 60}},
Prices: PriceGroups{&Price{0, 2, 1 * time.Second}, &Price{30 * time.Second, 1, 60 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 31, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 60}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 60 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 30, 30, 0, time.UTC)
@@ -383,8 +383,8 @@ func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
if ts.Interval != i {
t.Error("Interval not attached correctly")
}
c1 := ts.Interval.GetCost(ts.GetDuration().Seconds(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration().Seconds(), nts.GetGroupStart())
c1 := ts.Interval.GetCost(ts.GetDuration(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration(), nts.GetGroupStart())
if c1 != 60 || c2 != 60 {
t.Error("Wrong costs: ", c1, c2)
}
@@ -400,7 +400,7 @@ func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
func TestTimespanSplitRightHourMarginBeforeGroup(t *testing.T) {
i := &Interval{
EndTime: "17:00:30",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{60, 1, 60}},
Prices: PriceGroups{&Price{0, 2, 1 * time.Second}, &Price{60 * time.Second, 1, 60 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 01, 0, 0, time.UTC)
@@ -433,11 +433,11 @@ func TestTimespanSplitRightHourMarginBeforeGroup(t *testing.T) {
func TestTimespanSplitGroupSecondSplit(t *testing.T) {
i := &Interval{
EndTime: "17:03:30",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{60, 1, 1}},
Prices: PriceGroups{&Price{0, 2, 1 * time.Second}, &Price{60 * time.Second, 1, 1 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 04, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 240}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 240 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 01, 00, 0, time.UTC)
@@ -477,11 +477,11 @@ func TestTimespanSplitGroupSecondSplit(t *testing.T) {
func TestTimespanSplitMultipleGroup(t *testing.T) {
i := &Interval{
EndTime: "17:05:00",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{60, 1, 1}, &Price{180, 1, 1}},
Prices: PriceGroups{&Price{0, 2, 1 * time.Second}, &Price{60 * time.Second, 1, 1 * time.Second}, &Price{180 * time.Second, 1, 1 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 04, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 240}
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 240 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 01, 00, 0, time.UTC)

View File

@@ -18,6 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"time"
)
// This file deals with tp_* data definition
type TPRate struct {
@@ -27,14 +31,14 @@ type TPRate struct {
}
type RateSlot struct {
ConnectFee float64 // ConnectFee applied once the call is answered
Rate float64 // Rate applied
RatedUnits int // Number of billing units this rate applies to
RateIncrements int // This rate will apply in increments of duration
GroupInterval int // Group position
RoundingMethod string // Use this method to round the cost
RoundingDecimals int // Round the cost number of decimals
Weight float64 // Rate's priority when dealing with grouped rates
ConnectFee float64 // ConnectFee applied once the call is answered
Rate float64 // Rate applied
RatedUnits int // Number of billing units this rate applies to
RateIncrements time.Duration // This rate will apply in increments of duration
GroupInterval time.Duration // Group position
RoundingMethod string // Use this method to round the cost
RoundingDecimals int // Round the cost number of decimals
Weight float64 // Rate's priority when dealing with grouped rates
}
type TPDestinationRate struct {