Updated balance debit

This commit is contained in:
Trial97
2021-09-24 14:59:59 +03:00
committed by Dan Christian Bogos
parent fb48016c51
commit c182ca65f5
10 changed files with 348 additions and 155 deletions

View File

@@ -392,8 +392,8 @@ func (acc *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun bo
// try every balance multiple times in case one becomes active or ratig changes
unitBalanceChecker = false
for _, balance := range usefulUnitBalances {
partCC, debitErr := balance.debitUnits(cd, balance.account,
usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0)
partCC, debitErr := balance.debit(cd, balance.account,
usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0, true)
if debitErr != nil {
return nil, debitErr
}
@@ -429,8 +429,8 @@ func (acc *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun bo
// try every balance multiple times in case one becomes active or ratig changes
moneyBalanceChecker = false
for _, balance := range usefulMoneyBalances {
partCC, debitErr := balance.debitMoney(cd, balance.account,
usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0)
partCC, debitErr := balance.debit(cd, balance.account,
usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0,false)
if debitErr != nil {
return nil, debitErr
}
@@ -533,7 +533,6 @@ func (acc *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun bo
Value: defaultBalance.Value,
}
increment.BalanceInfo.AccountID = acc.ID
increment.paid = true
if count {
acc.countUnits(
cost,
@@ -859,39 +858,39 @@ func (acc *Account) Clone() *Account {
}
// DebitConnectionFee debits the connection fee
func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balances, count bool, block bool) (bool, Balance) {
func (acc *Account) DebitConnectionFee(cc *CallCost, ufMoneyBalances Balances, count bool, block bool) (bool, Balance) {
var debitedBalance Balance
if cc.deductConnectFee {
connectFee := cc.GetConnectFee()
//log.Print("CONNECT FEE: %f", connectFee)
connectFeePaid := false
for _, b := range usefulMoneyBalances {
if b.GetValue() >= connectFee {
b.SubstractValue(connectFee)
// the conect fee is not refundable!
if count {
acc.countUnits(connectFee, utils.MetaMonetary, cc, b)
}
connectFeePaid = true
debitedBalance = *b
break
}
if b.Blocker && block { // stop here
return false, debitedBalance
}
}
// debit connect fee
if connectFee > 0 && !connectFeePaid {
cc.negativeConnectFee = true
// there are no money for the connect fee; go negative
b := acc.GetDefaultMoneyBalance()
if !cc.deductConnectFee {
return true, debitedBalance
}
connectFee := cc.GetConnectFee()
//log.Print("CONNECT FEE: %f", connectFee)
var connectFeePaid bool
for _, b := range ufMoneyBalances {
if b.GetValue() >= connectFee {
b.SubstractValue(connectFee)
debitedBalance = *b
// the conect fee is not refundable!
if count {
acc.countUnits(connectFee, utils.MetaMonetary, cc, b)
}
connectFeePaid = true
debitedBalance = *b
break
}
if b.Blocker && block { // stop here
return false, debitedBalance
}
}
// debit connect fee
if connectFee > 0 && !connectFeePaid {
cc.negativeConnectFee = true
// there are no money for the connect fee; go negative
b := acc.GetDefaultMoneyBalance()
b.SubstractValue(connectFee)
debitedBalance = *b
// the conect fee is not refundable!
if count {
acc.countUnits(connectFee, utils.MetaMonetary, cc, b)
}
}
return true, debitedBalance

View File

@@ -533,7 +533,7 @@ func TestDebitCreditHasCredit(t *testing.T) {
}
rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{
utils.MetaVoice: {b1},
utils.MetaMonetary: {&Balance{Uuid: "moneya", Value: 110}},
utils.MetaMonetary: {{Uuid: "moneya", Value: 110}},
}}
var err error
cc, err = rifsBalance.debitCreditBalance(cd, false, false, true)

View File

@@ -267,11 +267,10 @@ func (b *Balance) GetCost(cd *CallDescriptor, getStandardIfEmpty bool) (*CallCos
if getStandardIfEmpty {
cd.RatingInfos = nil
return cd.getCost()
} else {
cc := cd.CreateCallCost()
cc.Cost = 0
return cc, nil
}
cc := cd.CreateCallCost()
cc.Cost = 0
return cc, nil
}
func (b *Balance) GetValue() float64 {
@@ -302,7 +301,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
if !b.IsActiveAt(cd.TimeStart) || b.GetValue() <= 0 {
return
}
if duration, err := utils.ParseZeroRatingSubject(cd.ToR, b.RatingSubject, config.CgrConfig().RalsCfg().BalanceRatingSubject); err == nil {
if duration, err := utils.ParseZeroRatingSubject(cd.ToR, b.RatingSubject, config.CgrConfig().RalsCfg().BalanceRatingSubject, true); err == nil {
// we have *zero based units
cc = cd.CreateCallCost()
cc.Timespans = append(cc.Timespans, &TimeSpan{
@@ -359,12 +358,10 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
}
inc.BalanceInfo.AccountID = ub.ID
inc.Cost = 0
inc.paid = true
if count {
ub.countUnits(amount, cc.ToR, cc, b)
}
} else {
inc.paid = false
// delete the rest of the unpiad increments/timespans
if incIndex == 0 {
// cut the entire current timespan
@@ -438,7 +435,6 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
amount = utils.Round(amount/b.Factor.GetValue(cd.ToR), globalRoundingDecimals, utils.MetaRoundingUp)
}
cost := inc.Cost
inc.paid = false
if strategy == utils.MetaMaxCostDisconnect && cd.MaxCostSoFar >= maxCost {
// cut the entire current timespan
cc.maxCostDisconect = true
@@ -462,7 +458,6 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
RateInterval: ts.RateInterval,
}
inc.BalanceInfo.AccountID = ub.ID
inc.paid = true
if count {
ub.countUnits(cost, utils.MetaMonetary, cc, b)
}
@@ -501,7 +496,6 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
}
cd.MaxCostSoFar += cost
}
inc.paid = true
if count {
ub.countUnits(amount, cc.ToR, cc, b)
if cost != 0 {
@@ -509,7 +503,6 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
}
}
} else {
inc.paid = false
// delete the rest of the unpaid increments/timespans
if incIndex == 0 {
// cut the entire current timespan
@@ -544,7 +537,6 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala
var ok bool
//log.Print("cc: " + utils.ToJSON(cc))
if debitConnectFee {
// this is the first add, debit the connect fee
if ok, debitedConnectFeeBalance = ub.DebitConnectionFee(cc, moneyBalances, count, true); !ok {
// balance is blocker
@@ -567,7 +559,11 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala
return nil, errors.New("timespan with no rate interval assigned")
}
if tsIndex == 0 && ts.RateInterval.Rating.ConnectFee > 0 && debitConnectFee && cc.deductConnectFee && ok {
if tsIndex == 0 &&
ts.RateInterval.Rating.ConnectFee > 0 &&
debitConnectFee &&
cc.deductConnectFee &&
ok {
inc := &Increment{
Duration: 0,
@@ -593,13 +589,16 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala
// check standard subject tags
//log.Printf("INC: %+v", inc)
if tsIndex == 0 && incIndex == 0 && ts.RateInterval.Rating.ConnectFee > 0 && cc.deductConnectFee && ok {
if tsIndex == 0 &&
incIndex == 0 &&
ts.RateInterval.Rating.ConnectFee > 0 &&
cc.deductConnectFee &&
ok {
// go to nextincrement
continue
}
amount := inc.Cost
inc.paid = false
if strategy == utils.MetaMaxCostDisconnect && cd.MaxCostSoFar >= maxCost {
// cut the entire current timespan
cc.maxCostDisconect = true
@@ -625,7 +624,6 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala
if b.RatingSubject != "" {
inc.BalanceInfo.Monetary.RateInterval = ts.RateInterval
}
inc.paid = true
if count {
ub.countUnits(amount, utils.MetaMonetary, cc, b)
}
@@ -647,12 +645,10 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala
if b.RatingSubject != "" {
inc.BalanceInfo.Monetary.RateInterval = ts.RateInterval
}
inc.paid = true
if count {
ub.countUnits(amount, utils.MetaMonetary, cc, b)
}
} else {
inc.paid = false
// delete the rest of the unpiad increments/timespans
if incIndex == 0 {
// cut the entire current timespan
@@ -826,3 +822,278 @@ func (bl *BalanceSummary) FieldAsInterface(fldPath []string) (val interface{}, e
return bl.Initial, nil
}
}
// debitUnits will debit units for call descriptor.
// returns the amount debited within cc
func (b *Balance) debit(cd *CallDescriptor, ub *Account, moneyBalances Balances,
count, dryRun, debitConnectFee, isUnitBal bool) (cc *CallCost, err error) {
if !b.IsActiveAt(cd.TimeStart) || b.GetValue() <= 0 {
return
}
tor := cd.ToR
if !isUnitBal {
tor = utils.MetaMonetary
}
if duration, err_ := utils.ParseZeroRatingSubject(tor, b.RatingSubject,
config.CgrConfig().RalsCfg().BalanceRatingSubject, isUnitBal); err_ == nil {
// we have *zero based units
cc = cd.CreateCallCost()
ts := &TimeSpan{
TimeStart: cd.TimeStart,
TimeEnd: cd.TimeEnd,
}
cc.Timespans = TimeSpans{ts}
ts.RoundToDuration(duration)
ts.RateInterval = &RateInterval{
Rating: &RIRate{
Rates: RateGroups{
&RGRate{
GroupIntervalStart: 0,
Value: 0,
RateIncrement: duration,
RateUnit: duration,
},
},
},
}
prefix, destid := b.getMatchingPrefixAndDestID(cd.Destination)
if prefix == utils.EmptyString {
prefix = cd.Destination
}
if destid == utils.EmptyString {
destid = utils.MetaAny
}
ts.setRatingInfo(&RatingInfo{
MatchedSubject: b.Uuid,
MatchedPrefix: prefix,
MatchedDestId: destid,
RatingPlanId: utils.MetaNone,
})
ts.createIncrementsSlice()
//log.Printf("CC: %+v", ts)
for incIndex, inc := range ts.Increments {
//log.Printf("INCREMENET: %+v", inc)
amount := float64(inc.Duration)
if b.Factor != nil {
amount = utils.Round(amount/b.Factor.GetValue(tor),
globalRoundingDecimals, utils.MetaRoundingUp)
}
if b.GetValue() >= amount {
b.SubstractValue(amount)
inc.BalanceInfo.Unit = &UnitInfo{
UUID: b.Uuid,
ID: b.ID,
Value: b.Value,
DestinationID: cc.Destination,
Consumed: amount,
ToR: tor, //aici
RateInterval: nil,
}
inc.BalanceInfo.AccountID = ub.ID
inc.Cost = 0
if count {
ub.countUnits(amount, tor, cc, b)
}
continue
}
// delete the rest of the unpiad increments/timespans
if incIndex == 0 {
// cut the entire current timespan
return nil, nil
}
ts.SplitByIncrement(incIndex)
if len(cc.Timespans) == 0 {
cc = nil
}
return
}
return
}
// no rating subject
//log.Print("B: ", utils.ToJSON(b))
//log.Printf("}}}}}}} %+v", cd.testCallcost)
cc, err = b.GetCost(cd, true)
if err != nil {
return nil, err
}
var debitedConnectFeeBalance Balance
var connectFeeDebited bool
//log.Print("cc: " + utils.ToJSON(cc))
if debitConnectFee {
// this is the first add, debit the connect fee
if connectFeeDebited, debitedConnectFeeBalance = ub.DebitConnectionFee(cc, moneyBalances, count, true); !connectFeeDebited {
// balance is blocker
return nil, nil
}
}
cc.Timespans.Decompress()
//log.Printf("CallCost In Debit: %+v", cc)
//for _, ts := range cc.Timespans {
// log.Printf("CC_TS: %+v", ts.RateInterval.Rating.Rates[0])
//}
for tsIndex, ts := range cc.Timespans {
if ts.RateInterval == nil {
utils.Logger.Err(fmt.Sprintf("Nil RateInterval ERROR on TS: %+v, CC: %+v, from CD: %+v", ts, cc, cd))
return nil, errors.New("timespan with no rate interval assigned")
}
if ts.Increments == nil {
ts.createIncrementsSlice()
}
//log.Printf("TS: %+v", ts)
if tsIndex == 0 &&
ts.RateInterval.Rating.ConnectFee > 0 &&
debitConnectFee &&
cc.deductConnectFee &&
connectFeeDebited {
ts.Increments = append([]*Increment{{
Duration: 0,
Cost: ts.RateInterval.Rating.ConnectFee,
BalanceInfo: &DebitInfo{
Monetary: &MonetaryInfo{
UUID: debitedConnectFeeBalance.Uuid,
ID: debitedConnectFeeBalance.ID,
Value: debitedConnectFeeBalance.Value,
},
AccountID: ub.ID,
},
}}, ts.Increments...)
}
maxCost, strategy := ts.RateInterval.GetMaxCost()
//log.Printf("Timing: %+v", ts.RateInterval.Timing)
//log.Printf("RGRate: %+v", ts.RateInterval.Rating)
for incIndex, inc := range ts.Increments {
// check standard subject tags
//log.Printf("INC: %+v", inc)
if tsIndex == 0 &&
incIndex == 0 &&
ts.RateInterval.Rating.ConnectFee > 0 &&
cc.deductConnectFee &&
connectFeeDebited {
// go to nextincrement
continue
}
if cd.MaxCostSoFar >= maxCost {
if strategy == utils.MetaMaxCostFree {
inc.Cost = 0.0
inc.BalanceInfo.Monetary = &MonetaryInfo{
UUID: b.Uuid,
ID: b.ID,
Value: b.Value,
}
inc.BalanceInfo.AccountID = ub.ID
if b.RatingSubject != utils.EmptyString || isUnitBal {
inc.BalanceInfo.Monetary.RateInterval = ts.RateInterval
}
if count {
ub.countUnits(inc.Cost, utils.MetaMonetary, cc, b)
}
//log.Printf("TS: %+v", cc.Cost)
// go to nextincrement
continue
} else if cc.maxCostDisconect = strategy == utils.MetaMaxCostDisconnect; cc.maxCostDisconect && dryRun {
// cut the entire current timespan
if incIndex == 0 {
// cut the entire current timespan
cc.Timespans = cc.Timespans[:tsIndex]
} else {
ts.SplitByIncrement(incIndex)
cc.Timespans = cc.Timespans[:tsIndex+1]
}
return
}
}
// debit minutes and money
amount := float64(inc.Duration)
cost := inc.Cost
canDebitCost := b.GetValue() >= cost
var moneyBal *Balance
if isUnitBal {
if b.Factor != nil {
amount = utils.Round(amount/b.Factor.GetValue(cd.ToR), globalRoundingDecimals, utils.MetaRoundingUp)
}
for _, mb := range moneyBalances {
if mb.GetValue() >= cost {
moneyBal = mb
break
}
}
if cost != 0 && moneyBal == nil && (!dryRun || ub.AllowNegative) { // Fix for issue #685
utils.Logger.Warning(fmt.Sprintf("<RALs> Going negative on account %s with AllowNegative: false", cd.GetAccountKey()))
moneyBal = ub.GetDefaultMoneyBalance()
}
canDebitCost = b.GetValue() >= amount && (moneyBal != nil || cost == 0)
}
if !canDebitCost {
// delete the rest of the unpaid increments/timespans
if incIndex == 0 {
// cut the entire current timespan
cc.Timespans = cc.Timespans[:tsIndex]
} else {
ts.SplitByIncrement(incIndex)
cc.Timespans = cc.Timespans[:tsIndex+1]
}
if len(cc.Timespans) == 0 {
cc = nil
}
return
}
if isUnitBal { // unit balance
b.SubstractValue(amount)
inc.BalanceInfo.Unit = &UnitInfo{
UUID: b.Uuid,
ID: b.ID,
Value: b.Value,
DestinationID: cc.Destination,
Consumed: amount,
ToR: cc.ToR,
RateInterval: ts.RateInterval,
}
inc.BalanceInfo.AccountID = ub.ID
if cost != 0 {
moneyBal.SubstractValue(cost)
inc.BalanceInfo.Monetary = &MonetaryInfo{
UUID: moneyBal.Uuid,
ID: moneyBal.ID,
Value: moneyBal.Value,
}
cd.MaxCostSoFar += cost
}
if count {
ub.countUnits(amount, cc.ToR, cc, b)
if cost != 0 {
ub.countUnits(cost, utils.MetaMonetary, cc, moneyBal)
}
}
} else { // monetary balance
b.SubstractValue(cost)
cd.MaxCostSoFar += cost
inc.BalanceInfo.Monetary = &MonetaryInfo{
UUID: b.Uuid,
ID: b.ID,
Value: b.Value,
}
inc.BalanceInfo.AccountID = ub.ID
if b.RatingSubject != "" {
inc.BalanceInfo.Monetary.RateInterval = ts.RateInterval
}
if count {
ub.countUnits(cost, utils.MetaMonetary, cc, b)
}
}
}
}
if !isUnitBal && len(cc.Timespans) == 0 {
cc = nil
}
return
}

View File

@@ -99,15 +99,6 @@ func (cc *CallCost) CreateCallDescriptor() *CallDescriptor {
}
}
func (cc *CallCost) IsPaid() bool {
for _, ts := range cc.Timespans {
if paid, _ := ts.IsPaid(); !paid {
return false
}
}
return true
}
func (cc *CallCost) ToDataCost() (*DataCost, error) {
if cc.ToR == utils.MetaVoice {
return nil, errors.New("Not a data call!")
@@ -145,7 +136,6 @@ func (cc *CallCost) ToDataCost() (*DataCost, error) {
Cost: incr.Cost,
BalanceInfo: incr.BalanceInfo,
CompressFactor: incr.CompressFactor,
paid: incr.paid,
}
}
}

View File

@@ -302,7 +302,6 @@ func TestCallcostCallCostToDataCost(t *testing.T) {
},
},
},
paid: false,
Duration: 12 * time.Second,
CompressFactor: 2,
},
@@ -324,7 +323,6 @@ func TestCallcostCallCostToDataCost(t *testing.T) {
},
},
},
paid: true,
Duration: 24 * time.Second,
CompressFactor: 2,
},
@@ -429,55 +427,6 @@ func TestCallcostUpdateRatedUsage(t *testing.T) {
}
}
func TestCallcostIsPaidFalse(t *testing.T) {
cc := &CallCost{
Timespans: TimeSpans{
{
Increments: Increments{
&Increment{
paid: false,
},
},
},
},
}
rcv := cc.IsPaid()
if rcv != false {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", false, rcv)
}
}
func TestCallcostIsPaidTrue(t *testing.T) {
cc := &CallCost{
Timespans: TimeSpans{
{
MatchedSubject: "1001",
Increments: Increments{
&Increment{
paid: true,
},
},
},
{
MatchedSubject: "1002",
Increments: Increments{
&Increment{
paid: true,
},
},
},
},
}
rcv := cc.IsPaid()
if rcv != true {
t.Errorf("\nexpected: <%+v>, \nreceived: <%+v>", true, rcv)
}
}
func TestCallcostUpdateCost(t *testing.T) {
cc := &CallCost{
deductConnectFee: false,
@@ -573,7 +522,6 @@ func TestCallcostUpdateCost(t *testing.T) {
},
},
},
paid: false,
Duration: 12 * time.Second,
CompressFactor: 2,
},
@@ -595,7 +543,6 @@ func TestCallcostUpdateCost(t *testing.T) {
},
},
},
paid: true,
Duration: 24 * time.Second,
CompressFactor: 2,
},
@@ -720,7 +667,6 @@ func TestCallcostGetStartTime(t *testing.T) {
},
},
},
paid: false,
Duration: 12 * time.Second,
CompressFactor: 2,
},
@@ -742,7 +688,6 @@ func TestCallcostGetStartTime(t *testing.T) {
},
},
},
paid: true,
Duration: 24 * time.Second,
CompressFactor: 2,
},

View File

@@ -1957,7 +1957,6 @@ func TestCalldescRefundIncrementsNoBalanceInfo(t *testing.T) {
Increments: Increments{
{
Duration: time.Second,
paid: true,
Cost: 5,
},
},

View File

@@ -42,5 +42,4 @@ type DataIncrement struct {
Cost float64
BalanceInfo *DebitInfo // need more than one for units with cost
CompressFactor int
paid bool
}

View File

@@ -51,7 +51,6 @@ type Increment struct {
Cost float64
BalanceInfo *DebitInfo // need more than one for units with cost
CompressFactor int
paid bool
}
// Holds information about the balance that made a specific payment
@@ -466,7 +465,6 @@ func (ts *TimeSpan) createIncrementsSlice() {
if ts.RateInterval == nil {
return
}
ts.Increments = make([]*Increment, 0)
// create rated units series
_, rateIncrement, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
// we will use the calculated cost and devide by nb of increments
@@ -475,36 +473,23 @@ func (ts *TimeSpan) createIncrementsSlice() {
nbIncrements := int(ts.GetDuration() / rateIncrement)
if nbIncrements > config.CgrConfig().RalsCfg().MaxIncrements {
utils.Logger.Warning(fmt.Sprintf("error: <%s with %+v>, when creating increments slice, TimeSpan: %s", utils.ErrMaxIncrementsExceeded, nbIncrements, utils.ToJSON(ts)))
ts.Increments = make([]*Increment, 0)
return
}
incrementCost := ts.CalculateCost() / float64(nbIncrements)
incrementCost = utils.Round(incrementCost, globalRoundingDecimals, utils.MetaRoundingMiddle)
for s := 0; s < nbIncrements; s++ {
inc := &Increment{
ts.Increments = make([]*Increment, nbIncrements)
for i := range ts.Increments {
ts.Increments[i] = &Increment{
Duration: rateIncrement,
Cost: incrementCost,
BalanceInfo: &DebitInfo{},
}
ts.Increments = append(ts.Increments, inc)
}
// put the rounded cost back in timespan
ts.Cost = incrementCost * float64(nbIncrements)
}
// returns whether the timespan has all increments marked as paid and if not
// it also returns the first unpaied increment
func (ts *TimeSpan) IsPaid() (bool, int) {
if ts.Increments.Length() == 0 {
return false, 0
}
for incrementIndex, increment := range ts.Increments {
if !increment.paid {
return false, incrementIndex
}
}
return true, len(ts.Increments)
}
/*
Splits the given timespan according to how it relates to the interval.
It will modify the endtime of the received timespan and it will return
@@ -592,12 +577,12 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval, data bool) (nts *TimeSp
}
// Split the timespan at the given increment start
func (ts *TimeSpan) SplitByIncrement(index int) *TimeSpan {
func (ts *TimeSpan) SplitByIncrement(index int) (newTs *TimeSpan) {
if index <= 0 || index >= len(ts.Increments) {
return nil
return
}
timeStart := ts.GetTimeStartForIncrement(index)
newTs := &TimeSpan{
newTs = &TimeSpan{
RateInterval: ts.RateInterval,
TimeStart: timeStart,
TimeEnd: ts.TimeEnd,
@@ -608,7 +593,7 @@ func (ts *TimeSpan) SplitByIncrement(index int) *TimeSpan {
newTs.Increments = ts.Increments[index:]
ts.Increments = ts.Increments[:index]
ts.SetNewDurationIndex(newTs)
return newTs
return
}
// Split the timespan at the given second
@@ -725,11 +710,12 @@ func (ts *TimeSpan) GetTimeStartForIncrement(index int) time.Time {
}
func (ts *TimeSpan) RoundToDuration(duration time.Duration) {
if duration < ts.GetDuration() {
duration = utils.RoundDuration(duration, ts.GetDuration())
tsDur := ts.GetDuration()
if duration < tsDur {
duration = utils.RoundDuration(duration, tsDur)
}
if duration > ts.GetDuration() {
initialDuration := ts.GetDuration()
if duration > tsDur {
initialDuration := tsDur
ts.TimeEnd = ts.TimeStart.Add(duration)
ts.DurationIndex = ts.DurationIndex + (duration - initialDuration)
}

View File

@@ -391,9 +391,13 @@ func MinDuration(d1, d2 time.Duration) time.Duration {
// ParseZeroRatingSubject will parse the subject in the balance
// returns duration if able to extract it from subject
// returns error if not able to parse duration (ie: if ratingSubject is standard one)
func ParseZeroRatingSubject(tor, rateSubj string, defaultRateSubj map[string]string) (time.Duration, error) {
func ParseZeroRatingSubject(tor, rateSubj string, defaultRateSubj map[string]string, isUnitBal bool) (time.Duration, error) {
rateSubj = strings.TrimSpace(rateSubj)
if rateSubj == "" || rateSubj == MetaAny {
if !isUnitBal && rateSubj == EmptyString {
return 0, errors.New("no rating subject for monetary")
}
if rateSubj == EmptyString ||
rateSubj == MetaAny {
var hasToR bool
if rateSubj, hasToR = defaultRateSubj[tor]; !hasToR {
rateSubj = defaultRateSubj[MetaAny]
@@ -403,8 +407,8 @@ func ParseZeroRatingSubject(tor, rateSubj string, defaultRateSubj map[string]str
return 0, errors.New("malformed rating subject: " + rateSubj)
}
durStr := rateSubj[len(MetaRatingSubjectPrefix):]
if _, err := strconv.ParseFloat(durStr, 64); err == nil { // No time unit, postpend
durStr += "ns"
if val, err := strconv.ParseFloat(durStr, 64); err == nil { // No time unit, postpend
return time.Duration(val), nil // just return the float value converted(this should be faster than reparsing the string)
}
return time.ParseDuration(durStr)
}

View File

@@ -748,24 +748,24 @@ func TestParseZeroRatingSubject(t *testing.T) {
MetaVoice: "*zero1s",
}
for i, s := range subj {
if d, err := ParseZeroRatingSubject(MetaVoice, s, dfltRatingSubject); err != nil || d != dur[i] {
if d, err := ParseZeroRatingSubject(MetaVoice, s, dfltRatingSubject, true); err != nil || d != dur[i] {
t.Error("Error parsing rating subject: ", s, d, err)
}
}
if d, err := ParseZeroRatingSubject(MetaData, EmptyString, dfltRatingSubject); err != nil || d != time.Nanosecond {
if d, err := ParseZeroRatingSubject(MetaData, EmptyString, dfltRatingSubject, true); err != nil || d != time.Nanosecond {
t.Error("Error parsing rating subject: ", EmptyString, d, err)
}
if d, err := ParseZeroRatingSubject(MetaSMS, EmptyString, dfltRatingSubject); err != nil || d != time.Nanosecond {
if d, err := ParseZeroRatingSubject(MetaSMS, EmptyString, dfltRatingSubject, true); err != nil || d != time.Nanosecond {
t.Error("Error parsing rating subject: ", EmptyString, d, err)
}
if d, err := ParseZeroRatingSubject(MetaMMS, EmptyString, dfltRatingSubject); err != nil || d != time.Nanosecond {
if d, err := ParseZeroRatingSubject(MetaMMS, EmptyString, dfltRatingSubject, true); err != nil || d != time.Nanosecond {
t.Error("Error parsing rating subject: ", EmptyString, d, err)
}
if d, err := ParseZeroRatingSubject(MetaMonetary, EmptyString, dfltRatingSubject); err != nil || d != time.Nanosecond {
if d, err := ParseZeroRatingSubject(MetaMonetary, EmptyString, dfltRatingSubject, true); err != nil || d != time.Nanosecond {
t.Error("Error parsing rating subject: ", EmptyString, d, err)
}
expecting := "malformed rating subject: test"
if _, err := ParseZeroRatingSubject(MetaMonetary, "test", dfltRatingSubject); err == nil || err.Error() != expecting {
if _, err := ParseZeroRatingSubject(MetaMonetary, "test", dfltRatingSubject, true); err == nil || err.Error() != expecting {
t.Errorf("Expecting: %+v, received: %+v ", expecting, err)
}
}