max cost and strategy at rate level

This commit is contained in:
Radu Ioan Fericean
2015-03-23 11:39:27 +02:00
parent 44fa456eb5
commit 581648a04f
26 changed files with 144 additions and 68 deletions

View File

@@ -45,6 +45,8 @@ CREATE TABLE `tp_rates` (
`tpid` varchar(64) NOT NULL,
`tag` varchar(64) NOT NULL,
`connect_fee` decimal(7,4) NOT NULL,
`max_cost` decimal(7,4) NOT NULL,
`max_cost_strategy` varchar(16) NOT NULL,
`rate` decimal(7,4) NOT NULL,
`rate_unit` varchar(16) NOT NULL,
`rate_increment` varchar(16) NOT NULL,

View File

@@ -41,6 +41,8 @@ CREATE TABLE tp_rates (
tpid VARCHAR(64) NOT NULL,
tag VARCHAR(64) NOT NULL,
connect_fee NUMERIC(7,4) NOT NULL,
max_cost NUMERIC(7,4) NOT NULL,
max_cost_strategy VARCHAR(16) NOT NULL,
rate NUMERIC(7,4) NOT NULL,
rate_unit VARCHAR(16) NOT NULL,
rate_increment VARCHAR(16) NOT NULL,

View File

@@ -1,2 +1,2 @@
#Tag,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart
RT_1CENT,0,1,1s,1s,0s
RT_1CENT,0,0,,1,1s,1s,0s
1 #Tag #Tag,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart ConnectFee Rate RateUnit RateIncrement GroupIntervalStart
2 RT_1CENT RT_1CENT,0,0,,1,1s,1s,0s 0 1 1s 1s 0s

View File

@@ -1,4 +1,4 @@
#Tag,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart
RT_1CENT,0,1,1s,1s,0s
RT_DATA_2c,0,0.002,10,10,0
RT_SMS_5c,0,0.005,1,1,0
RT_1CENT,0,0,,1,1s,1s,0s
RT_DATA_2c,0,0,,0.002,10,10,0
RT_SMS_5c,0,0,,0.005,1,1,0
1 #Tag #Tag,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart ConnectFee Rate RateUnit RateIncrement GroupIntervalStart
2 RT_1CENT RT_1CENT,0,0,,1,1s,1s,0s 0 1 1s 1s 0s
3 RT_DATA_2c RT_DATA_2c,0,0,,0.002,10,10,0 0 0.002 10 10 0
4 RT_SMS_5c RT_SMS_5c,0,0,,0.005,1,1,0 0 0.005 1 1 0

View File

@@ -1,8 +1,8 @@
#Tag,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart
RT_10CNT,0.2,0.1,60s,60s,0s
RT_10CNT,0,0.05,60s,1s,60s
RT_20CNT,0.4,0.2,60s,60s,0s
RT_20CNT,0,0.1,60s,1s,60s
RT_40CNT,0.8,0.4,60s,30s,0s
RT_40CNT,0,0.2,60s,10s,60s
RT_1CNT,0,0.01,60s,60s,0s
RT_10CNT,0.2,0,,0.1,60s,60s,0s
RT_10CNT,0,0,,0.05,60s,1s,60s
RT_20CNT,0,0,.4,0.2,60s,60s,0s
RT_20CNT,0,0,,0.1,60s,1s,60s
RT_40CNT,0,0,.8,0.4,60s,30s,0s
RT_40CNT,0,0,,0.2,60s,10s,60s
RT_1CNT,0,0,,0.01,60s,60s,0s
1 #Tag #Tag,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart ConnectFee Rate RateUnit RateIncrement GroupIntervalStart
2 RT_10CNT RT_10CNT,0.2,0,,0.1,60s,60s,0s 0.2 0.1 60s 60s 0s
3 RT_10CNT RT_10CNT,0,0,,0.05,60s,1s,60s 0 0.05 60s 1s 60s
4 RT_20CNT RT_20CNT,0,0,.4,0.2,60s,60s,0s 0.4 0.2 60s 60s 0s
5 RT_20CNT RT_20CNT,0,0,,0.1,60s,1s,60s 0 0.1 60s 1s 60s
6 RT_40CNT RT_40CNT,0,0,.8,0.4,60s,30s,0s 0.8 0.4 60s 30s 0s
7 RT_40CNT RT_40CNT,0,0,,0.2,60s,10s,60s 0 0.2 60s 10s 60s
8 RT_1CNT RT_1CNT,0,0,,0.01,60s,60s,0s 0 0.01 60s 60s 0s

View File

@@ -218,6 +218,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
var leftCC *CallCost
var initialLength int
cc = cd.CreateCallCost()
generalBalanceChecker := true
for generalBalanceChecker {
generalBalanceChecker = false
@@ -231,7 +232,8 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
for _, balance := range usefulUnitBalances {
//log.Printf("Unit balance: %+v", balance)
// log.Printf("CD BEFORE UNIT: %+v", cd)
partCC, _ := balance.DebitUnits(cd, count, balance.account, usefulMoneyBalances)
partCC, _ := balance.DebitUnits(cd, balance.account, usefulMoneyBalances, count, dryRun)
//log.Printf("CD AFTER UNIT: %+v", cd)
if partCC != nil {
//log.Printf("partCC: %+v", partCC.Timespans[0])
@@ -253,6 +255,11 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
}
unitBalanceChecker = true
generalBalanceChecker = true
// check for max cost disconnect
if dryRun && cc.maxCostDisconect {
// only return if we are in dry run (max call duration)
return
}
}
}
}
@@ -265,7 +272,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
for _, balance := range usefulMoneyBalances {
//log.Printf("Money balance: %+v", balance)
//log.Printf("CD BEFORE MONEY: %+v", cd)
partCC, _ := balance.DebitMoney(cd, count, balance.account)
partCC, _ := balance.DebitMoney(cd, balance.account, count, dryRun)
//log.Printf("CD AFTER MONEY: %+v", cd)
//log.Printf("partCC: %+v", partCC)
if partCC != nil {
@@ -287,6 +294,10 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
}
moneyBalanceChecker = true
generalBalanceChecker = true
if dryRun && cc.maxCostDisconect {
// only return if we are in dry run (max call duration)
return
}
}
}
}

View File

@@ -250,7 +250,7 @@ func (b *Balance) SubstractAmount(amount float64) {
b.dirty = true
}
func (b *Balance) DebitUnits(cd *CallDescriptor, count bool, ub *Account, moneyBalances BalanceChain) (cc *CallCost, err error) {
func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances BalanceChain, count bool, dryRun bool) (cc *CallCost, err error) {
if !b.IsActiveAt(cd.TimeStart) || b.Value <= 0 {
return
}
@@ -324,11 +324,32 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, count bool, ub *Account, moneyB
ts.createIncrementsSlice()
}
//log.Printf("TS: %+v", ts)
maxCost, strategy := ts.RateInterval.GetMaxCost()
if strategy == utils.MAX_COST_DISCONNECT && cd.MaxCostSoFar >= maxCost {
// cat the entire current timespan
cc.maxCostDisconect = true
if dryRun {
cc.Timespans = cc.Timespans[:tsIndex]
return cc, nil
}
}
for incIndex, inc := range ts.Increments {
// debit minutes and money
seconds := inc.Duration.Seconds()
cost := inc.Cost
//log.Printf("INC: %+v", inc)
inc.paid = false
if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost {
cost, inc.Cost = 0.0, 0.0
inc.BalanceInfo.MoneyBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.Id
inc.paid = true
if count {
ub.countUnits(&Action{BalanceType: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: cost, DestinationId: cc.Destination}})
}
// go to nextincrement
continue
}
var moneyBal *Balance
for _, mb := range moneyBalances {
if mb.Value >= cost {
@@ -344,6 +365,7 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, count bool, ub *Account, moneyB
if cost != 0 {
inc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid
moneyBal.SubstractAmount(cost)
cd.MaxCostSoFar += cost
}
inc.paid = true
if count {
@@ -373,7 +395,7 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, count bool, ub *Account, moneyB
return
}
func (b *Balance) DebitMoney(cd *CallDescriptor, count bool, ub *Account) (cc *CallCost, err error) {
func (b *Balance) DebitMoney(cd *CallDescriptor, ub *Account, count bool, dryRun bool) (cc *CallCost, err error) {
if !b.IsActiveAt(cd.TimeStart) || b.Value <= 0 {
return
}
@@ -392,12 +414,35 @@ func (b *Balance) DebitMoney(cd *CallDescriptor, count bool, ub *Account) (cc *C
ts.createIncrementsSlice()
}
//log.Printf("TS: %+v", ts)
maxCost, strategy := ts.RateInterval.GetMaxCost()
if strategy == utils.MAX_COST_DISCONNECT && cd.MaxCostSoFar >= maxCost {
// cat the entire current timespan
cc.maxCostDisconect = true
if dryRun {
cc.Timespans = cc.Timespans[:tsIndex]
return cc, nil
}
}
for incIndex, inc := range ts.Increments {
// check standard subject tags
//log.Printf("INC: %+v", inc)
amount := inc.Cost
inc.paid = false
if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost {
amount, inc.Cost = 0.0, 0.0
inc.BalanceInfo.MoneyBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.Id
inc.paid = true
if count {
ub.countUnits(&Action{BalanceType: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}})
}
// go to nextincrement
continue
}
if b.Value >= amount {
b.SubstractAmount(amount)
cd.MaxCostSoFar += amount
inc.BalanceInfo.MoneyBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.Id
inc.paid = true

View File

@@ -29,6 +29,7 @@ type CallCost struct {
Cost float64
Timespans TimeSpans
deductConnectFee bool
maxCostDisconect bool
}
// Merges the received timespan if they are similar (same activation period, same interval, same minute info.

View File

@@ -110,7 +110,6 @@ type CallDescriptor struct {
// session limits
MaxRate float64
MaxRateUnit time.Duration
MaxCost float64
MaxCostSoFar float64
account *Account
test_callcost *CallCost // testing purpose only!
@@ -485,12 +484,6 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura
}
for _, incr := range ts.Increments {
totalCost += incr.Cost
if cd.MaxCost > 0 {
// limit availableCredit
if cd.MaxCostSoFar+totalCost > cd.MaxCost {
return utils.MinDuration(initialDuration, totalDuration), nil
}
}
if defaultBalance.Value < 0 && incr.BalanceInfo.MoneyBalanceUuid == defaultBalance.Uuid {
// this increment was payed with debt
// TODO: improve this check
@@ -660,7 +653,6 @@ func (cd *CallDescriptor) Clone() *CallDescriptor {
DurationIndex: cd.DurationIndex,
MaxRate: cd.MaxRate,
MaxRateUnit: cd.MaxRateUnit,
MaxCost: cd.MaxCost,
MaxCostSoFar: cd.MaxCostSoFar,
FallbackSubject: cd.FallbackSubject,
//RatingInfos: cd.RatingInfos,

View File

@@ -355,6 +355,8 @@ func TestMaxSessionTimeWithMaxRate(t *testing.T) {
}
}
/*
func TestMaxSessionTimeWithMaxCost(t *testing.T) {
ap, _ := accountingStorage.GetActionTimings("TOPUP10_AT")
for _, at := range ap {
@@ -369,7 +371,6 @@ func TestMaxSessionTimeWithMaxCost(t *testing.T) {
Destination: "447956",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 1, 0, 0, time.UTC),
MaxCost: 0.5,
MaxCostSoFar: 0,
}
result, err := cd.GetMaxSessionDuration()
@@ -378,6 +379,7 @@ func TestMaxSessionTimeWithMaxCost(t *testing.T) {
t.Errorf("Expected %v was %v", expected, result)
}
}
*/
func TestMaxSessionTimeWithAccountAlias(t *testing.T) {
cd := &CallDescriptor{

View File

@@ -398,7 +398,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])
r, err = NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7])
if err != nil {
return err
}

View File

@@ -55,19 +55,19 @@ ALWAYS,*any,*any,*any,*any,00:00:00
ASAP,*any,*any,*any,*any,*asap
`
rates = `
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
R1,0,0,,0.2,60,1,0
R2,0,0,,0.1,60s,1s,0
R3,0,0,,0.05,60s,1s,0
R4,1,0,,1,1s,1s,0
R5,0,0,,0.5,1s,1s,0
LANDLINE_OFFPEAK,0,0,,1,1,60,0
LANDLINE_OFFPEAK,0,0,,1,1,1,60
GBP_71,0.000000,0,,5.55555,1s,1s,0s
GBP_72,0.000000,0,,7.77777,1s,1s,0s
GBP_70,0.000000,0,,1,1,1,0
RT_UK_Mobile_BIG5_PKG,0.01,0,,0,20s,20s,0s
RT_UK_Mobile_BIG5,0.01,0,,0.10,1s,1s,0s
R_URG,0,0,,0,1,1,0
`
destinationRates = `
RT_STANDARD,GERMANY,R1,*middle,4
@@ -323,7 +323,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")
expctRs, err := utils.NewRateSlot(0, 0, "", 0.2, "60", "1", "0")
if err != nil {
t.Error("Error loading rate: ", rate, err.Error())
} else if !reflect.DeepEqual(rate, expctRs) ||
@@ -333,7 +333,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"); err != nil {
if expctRs, err = utils.NewRateSlot(0, 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() ||
@@ -342,7 +342,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"); err != nil {
if expctRs, err = utils.NewRateSlot(0, 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() ||
@@ -351,7 +351,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"); err != nil {
if expctRs, err = utils.NewRateSlot(1, 0, "", 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() ||
@@ -360,7 +360,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"); err != nil {
if expctRs, err = utils.NewRateSlot(0, 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() ||
@@ -369,7 +369,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"); err != nil {
if expctRs, err = utils.NewRateSlot(0, 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() ||
@@ -378,7 +378,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"); err != nil {
if expctRs, err = utils.NewRateSlot(0, 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() ||

View File

@@ -95,18 +95,24 @@ type TPLoader interface {
WriteToDatabase(bool, bool) error
}
func NewLoadRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterval string) (r *utils.TPRate, err error) {
func NewLoadRate(tag, connectFee, maxCost, maxCostStrategy, 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)
return
}
mc, err := strconv.ParseFloat(maxCost, 64)
if err != nil {
log.Printf("Error parsing max cost from: %v", maxCost)
return
}
p, err := strconv.ParseFloat(price, 64)
if err != nil {
log.Printf("Error parsing price from: %v", price)
return
}
rs, err := utils.NewRateSlot(cf, p, ratedUnits, rateIncrements, groupInterval)
rs, err := utils.NewRateSlot(cf, mc, maxCostStrategy, p, ratedUnits, rateIncrements, groupInterval)
if err != nil {
return nil, err
}
@@ -365,7 +371,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|<empty>),Months([0-9;]|*any|<empty>),MonthDays([0-9;]|*any|<empty>),WeekDays([0-9;]|*any|<empty>),Time([0-9:]|*asap)"},
utils.RATES_CSV: &FileLineRegexValidator{utils.RATES_NRCOLS,
regexp.MustCompile(`(?:\w+\s*),(?:\d+\.*\d*s*),(?:\d+\.*\d*s*),(?:\d+\.*\d*(ns|us|µs|ms|s|m|h)*\s*),(?:\d+\.*\d*(ns|us|µs|ms|s|m|h)*\s*),(?:\d+\.*\d*(ns|us|µs|ms|s|m|h)*\s*)$`),
regexp.MustCompile(`(?:\w+\s*),(?:\d+\.*\d*s*),(?:\d+\.*\d*s*),(?:\*free|\*diconnect)?,(?:\d+\.*\d*s*),(?:\d+\.*\d*(ns|us|µs|ms|s|m|h)*\s*),(?:\d+\.*\d*(ns|us|µs|ms|s|m|h)*\s*),(?:\d+\.*\d*(ns|us|µs|ms|s|m|h)*\s*)$`),
"Tag([0-9A-Za-z_]),ConnectFee([0-9.]),Rate([0-9.]),RateUnit([0-9.]ns|us|µs|ms|s|m|h),RateIncrementStart([0-9.]ns|us|µs|ms|s|m|h),GroupIntervalStart([0-9.]ns|us|µs|ms|s|m|h)"},
utils.DESTINATION_RATES_CSV: &FileLineRegexValidator{utils.DESTINATION_RATES_NRCOLS,
regexp.MustCompile(`^(?:\w+\s*),(?:\w+\s*|\*any),(?:\w+\s*),(?:\*up|\*down|\*middle),(?:\d+)$`),

View File

@@ -40,9 +40,9 @@ DUMMY,INVALID;DATA
GERMANY_MOBILE,+4915
`
var ratesSample = `#Tag,DestinationRatesTag,TimingTag,Weight
RT_1CENT,0,1,1s,1s,0s
RT_1CENT,0,0,,1,1s,1s,0s
DUMMY,INVALID;DATA
RT_DATA_2c,0,0.002,10,10,0
RT_DATA_2c,0,0,,0.002,10,10,0
`
var destRatesSample = `#Tag,DestinationsTag,RatesTag
@@ -415,7 +415,7 @@ func TestTPCSVFileParser(t *testing.T) {
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(record, []string{"RT_1CENT", "0", "1", "1s", "1s", "0s"}) {
if !reflect.DeepEqual(record, []string{"RT_1CENT", "0", "0", "", "1", "1s", "1s", "0s"}) {
t.Error("Unexpected record extracted", record)
}
case 3:

View File

@@ -54,6 +54,8 @@ type TpRate struct {
Tpid string
Tag string
ConnectFee float64
MaxCost float64
MaxCostStrategy string
Rate float64
RateUnit string
RateIncrement string

View File

@@ -181,6 +181,8 @@ type RIRate struct {
ConnectFee float64
RoundingMethod string
RoundingDecimals int
MaxCost float64
MaxCostStrategy string
Rates RateGroups // GroupRateInterval (start time): Rate
}
@@ -341,6 +343,13 @@ func (i *RateInterval) GetRateParameters(startSecond time.Duration) (rate float6
return -1, -1, -1
}
func (ri *RateInterval) GetMaxCost() (float64, string) {
if ri.Rating == nil {
return 0.0, ""
}
return ri.Rating.MaxCost, ri.Rating.MaxCostStrategy
}
// Structure to store intervals according to weight
type RateIntervalList []*RateInterval

View File

@@ -1073,7 +1073,7 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*utils.TPRate,
}
for _, tr := range tpRates {
rs, err := utils.NewRateSlot(tr.ConnectFee, tr.Rate, tr.RateUnit, tr.RateIncrement, tr.GroupIntervalStart)
rs, err := utils.NewRateSlot(tr.ConnectFee, tr.MaxCost, tr.MaxCostStrategy, tr.Rate, tr.RateUnit, tr.RateIncrement, tr.GroupIntervalStart)
if err != nil {
return nil, err
}

View File

@@ -157,7 +157,7 @@ func (self *TPCSVImporter) importRates(fn string) error {
}
continue
}
newRt, err := NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5])
newRt, err := NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7])
if err != nil {
return err
}

View File

@@ -40,9 +40,9 @@ ASAP,*any,*any,*any,*any,*asap`
GERMANY_MOBILE,+4915
GERMANY_MOBILE,+4916
GERMANY_MOBILE,+4917`
rates := `RT_1CENT,0,1,1s,1s,0s
RT_DATA_2c,0,0.002,10,10,0
RT_SMS_5c,0,0.005,1,1,0`
rates := `RT_1CENT,0,0,,1,1s,1s,0s
RT_DATA_2c,0,0,,0.002,10,10,0
RT_SMS_5c,0,0,,0.005,1,1,0`
destinationRates := `DR_RETAIL,GERMANY,RT_1CENT,*up,4
DR_RETAIL,GERMANY_MOBILE,RT_1CENT,*up,4
DR_DATA_1,*any,RT_DATA_2c,*up,4

View File

@@ -37,8 +37,8 @@ func TestSetStorageDtChrg1(t *testing.T) {
func TestLoadCsvTpDtChrg1(t *testing.T) {
timings := `TM1,*any,*any,*any,*any,00:00:00
TM2,*any,*any,*any,*any,01:00:00`
rates := `RT_DATA_2c,0,0.002,10,10,0
RT_DATA_1c,0,0.001,10,10,0`
rates := `RT_DATA_2c,0,0,,0.002,10,10,0
RT_DATA_1c,0,0,,0.001,10,10,0`
destinationRates := `DR_DATA_1,*any,RT_DATA_2c,*up,4
DR_DATA_2,*any,RT_DATA_1c,*up,4`
ratingPlans := `RP_DATA1,DR_DATA_1,TM1,10

View File

@@ -42,8 +42,8 @@ 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
RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s`
rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,,0,20s,20s,0s
RT_UK_Mobile_BIG5,0.01,0,,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

View File

@@ -42,8 +42,8 @@ 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
RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s`
rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,,0,20s,20s,0s
RT_UK_Mobile_BIG5,0.01,0,,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

View File

@@ -42,8 +42,8 @@ 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
RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s`
rates := `RT_UK_Mobile_BIG5_PKG,0.01,0,,0,20s,20s,0s
RT_UK_Mobile_BIG5,0.01,0,,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

View File

@@ -36,7 +36,7 @@ func TestSMSSetStorageSmsChrg1(t *testing.T) {
func TestSMSLoadCsvTpSmsChrg1(t *testing.T) {
timings := `ALWAYS,*any,*any,*any,*any,00:00:00`
rates := `RT_SMS_5c,0,0.005,1,1,0`
rates := `RT_SMS_5c,0,0,,0.005,1,1,0`
destinationRates := `DR_SMS_1,*any,RT_SMS_5c,*up,4`
ratingPlans := `RP_SMS1,DR_SMS_1,ALWAYS,10`
ratingProfiles := `*out,cgrates.org,sms,*any,2012-01-01T00:00:00Z,RP_SMS1,`

View File

@@ -86,8 +86,8 @@ func (self *TPRate) AsExportSlice() [][]string {
}
// Needed so we make sure we always use SetDurations() on a newly created value
func NewRateSlot(connectFee, rate float64, rateUnit, rateIncrement, grpInterval string) (*RateSlot, error) {
rs := &RateSlot{ConnectFee: connectFee, Rate: rate, RateUnit: rateUnit, RateIncrement: rateIncrement,
func NewRateSlot(connectFee, maxCost float64, maxCostStrategy string, rate float64, rateUnit, rateIncrement, grpInterval string) (*RateSlot, error) {
rs := &RateSlot{ConnectFee: connectFee, Rate: rate, RateUnit: rateUnit, RateIncrement: rateIncrement, MaxCost: maxCost, MaxCostStrategy: maxCostStrategy,
GroupIntervalStart: grpInterval}
if err := rs.SetDurations(); err != nil {
return nil, err
@@ -103,6 +103,8 @@ type RateSlot struct {
GroupIntervalStart string // Group position
rateUnitDur time.Duration
rateIncrementDur time.Duration
MaxCost float64
MaxCostStrategy string
groupIntervalStartDur time.Duration
}

View File

@@ -60,7 +60,7 @@ const (
CDR_STATS_CSV = "CdrStats.csv"
TIMINGS_NRCOLS = 6
DESTINATIONS_NRCOLS = 2
RATES_NRCOLS = 6
RATES_NRCOLS = 8
DESTINATION_RATES_NRCOLS = 5
DESTRATE_TIMINGS_NRCOLS = 4
RATE_PROFILES_NRCOLS = 7
@@ -130,6 +130,8 @@ const (
SMS = "*sms"
DATA = "*data"
VOICE = "*voice"
MAX_COST_FREE = "*free"
MAX_COST_DISCONNECT = "*disconnect"
TOR = "tor"
HOURS = "hours"
MINUTES = "minutes"