fix for MaxDebit bug

max duration was calculated badly
This commit is contained in:
Radu Ioan Fericean
2014-03-04 22:02:20 +02:00
parent 8967c2fa3a
commit 0b0474fa23
6 changed files with 84 additions and 26 deletions

View File

@@ -83,7 +83,8 @@ func (b *Balance) Clone() *Balance {
}
// Returns the available number of seconds for a specified credit
func (b *Balance) GetMinutesForCredit(cd *CallDescriptor, initialCredit float64) (duration time.Duration, credit float64) {
func (b *Balance) GetMinutesForCredit(origCD *CallDescriptor, initialCredit float64) (duration time.Duration, credit float64) {
cd := origCD.Clone()
duration = time.Duration(b.Value) * time.Second
credit = initialCredit
cc, err := b.GetCost(cd)
@@ -112,6 +113,7 @@ func (b *Balance) GetCost(cd *CallDescriptor) (*CallCost, error) {
if b.RateSubject != "" {
cd.Subject = b.RateSubject
cd.Account = cd.Subject
cd.RatingInfos = nil
return cd.GetCost()
}
cc := cd.CreateCallCost()
@@ -221,15 +223,21 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *Account, moneyBalan
if moneyBal != nil && b.Value >= seconds {
b.Value -= seconds
b.Value = utils.Round(b.Value, roundingDecimals, utils.ROUNDING_MIDDLE)
moneyBal.Value -= cost
nInc.BalanceInfo.MinuteBalanceUuid = b.Uuid
nInc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid
nInc.BalanceInfo.AccountId = ub.Id
nInc.MinuteInfo = &MinuteInfo{newCC.Destination, seconds}
if cost != 0 {
nInc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid
moneyBal.Value -= cost
moneyBal.Value = utils.Round(moneyBal.Value, roundingDecimals, utils.ROUNDING_MIDDLE)
}
nInc.paid = true
if count {
ub.countUnits(&Action{BalanceType: MINUTES, Direction: newCC.Direction, Balance: &Balance{Value: seconds, DestinationId: newCC.Destination}})
ub.countUnits(&Action{BalanceType: CREDIT, Direction: newCC.Direction, Balance: &Balance{Value: cost, DestinationId: newCC.Destination}})
if cost != 0 {
ub.countUnits(&Action{BalanceType: CREDIT, Direction: newCC.Direction, Balance: &Balance{Value: cost, DestinationId: newCC.Destination}})
}
}
} else {
increment.paid = false

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"fmt"
"reflect"
"time"
)
@@ -32,7 +31,7 @@ type CallCost struct {
}
// Pretty printing for call cost
func (cc *CallCost) String() (r string) {
/*func (cc *CallCost) String() (r string) {
connectFee := 0.0
if cc.deductConnectFee {
connectFee = cc.GetConnectFee()
@@ -43,7 +42,7 @@ func (cc *CallCost) String() (r string) {
}
r += " )"
return
}
}*/
// Merges the received timespan if they are similar (same activation period, same interval, same minute info.
func (cc *CallCost) Merge(other *CallCost) {

View File

@@ -397,7 +397,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
}
err := cd.LoadRatingPlans()
if err != nil {
Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetAccountKey(), err))
Logger.Err(fmt.Sprintf("error getting cost for key %s: %v", cd.GetKey(cd.Subject), err))
return &CallCost{Cost: -1}, err
}
timespans := cd.splitInTimeSpans(nil)
@@ -659,7 +659,7 @@ func (cd *CallDescriptor) Clone() *CallDescriptor {
CallDuration: cd.CallDuration,
Amount: cd.Amount,
FallbackSubject: cd.FallbackSubject,
RatingInfos: cd.RatingInfos,
Increments: cd.Increments,
//RatingInfos: cd.RatingInfos,
//Increments: cd.Increments,
}
}

View File

@@ -422,7 +422,7 @@ func TestDebitAndMaxDebit(t *testing.T) {
Subject: "minu_from_tm",
Account: "minu",
Destination: "0723",
Amount: 5400}
}
cd2 := cd1.Clone()
cc1, err1 := cd1.Debit()
cc2, err2 := cd2.MaxDebit()
@@ -434,6 +434,38 @@ func TestDebitAndMaxDebit(t *testing.T) {
}
}
func TestMaxDebitZeroDefinedRate(t *testing.T) {
ap, _ := accountingStorage.GetActionTimings("TOPUP1000UKMOB_AT")
for _, at := range ap {
at.Execute()
}
ap, _ = accountingStorage.GetActionTimings("TOPUP10_AT")
for _, at := range ap {
at.Execute()
}
cd1 := &CallDescriptor{
Direction: "*out",
TOR: "call",
Tenant: "cgrates.directvoip.co.uk",
Subject: "12345",
Account: "12345",
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),
LoopIndex: 0,
CallDuration: 0}
cc, err := cd1.MaxDebit()
if err != nil {
t.Error("Error maxdebiting: ", err)
}
if cc.GetDuration() != 49*time.Second {
t.Error("Error obtaining max debit duration: ", cc.GetDuration())
}
if cc.Cost != 0.9 {
t.Error("Error in max debit cost: ", cc.Cost)
}
}
/*********************************** BENCHMARKS ***************************************/
func BenchmarkStorageGetting(b *testing.B) {
b.StopTimer()

View File

@@ -37,6 +37,7 @@ func TestHistoryDestinations(t *testing.T) {
scribe := historyScribe.(*history.MockScribe)
buf := scribe.BufMap[history.DESTINATIONS_FN]
expected := `[{"Id":"ALL","Prefixes":["49","41","43"]},
{"Id":"DST_UK_Mobile_BIG5","Prefixes":["447956"]},
{"Id":"GERMANY","Prefixes":["49"]},
{"Id":"GERMANY_O2","Prefixes":["41"]},
{"Id":"GERMANY_PREMIUM","Prefixes":["43"]},

View File

@@ -43,12 +43,15 @@ RET,0724
PSTN_71,+4971
PSTN_72,+4972
PSTN_70,+4970
DST_UK_Mobile_BIG5,447956
`
timings = `
WORKDAYS_00,*any,*any,*any,1;2;3;4;5,00:00:00
WORKDAYS_18,*any,*any,*any,1;2;3;4;5,18:00:00
WEEKENDS,*any,*any,*any,6;7,00:00:00
ONE_TIME_RUN,2012,,,,*asap
ALWAYS,*any,*any,*any,*any,00:00:00
ASAP,*any,*any,*any,*any,*asap
`
rates = `
R1,0,0.2,60s,1s,0,*middle,2
@@ -61,6 +64,8 @@ LANDLINE_OFFPEAK,0,1,1s,1s,60s,*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,1s,1s,0s,*up,4
RT_UK_Mobile_BIG5_PKG,0,0,20s,20s,0s,*up,8
RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s,*up,8
`
destinationRates = `
RT_STANDARD,GERMANY,R1
@@ -75,8 +80,10 @@ 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
`
destinationRateTimings = `
ratingPlans = `
STANDARD,RT_STANDARD,WORKDAYS_00,10
STANDARD,RT_STD_WEEKEND,WORKDAYS_18,10
STANDARD,RT_STD_WEEKEND,WEEKENDS,10
@@ -91,6 +98,8 @@ TDRT,T1,WORKDAYS_00,10
TDRT,T2,WORKDAYS_00,10
G,RT_STANDARD,WORKDAYS_00,10
R,P1,WORKDAYS_00,10
RP_UK_Mobile_BIG5_PKG,DR_UK_Mobile_BIG5_PKG,ALWAYS,10
RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10
`
ratingProfiles = `
CUSTOMER_1,0,*out,rif:from:tm,2012-01-01T00:00:00Z,PREMIUM,danb
@@ -109,6 +118,8 @@ vdf,0,*out,fallback1,2013-11-18T13:45:00Z,G,fallback2
vdf,0,*out,fallback1,2013-11-18T13:46:00Z,G,fallback2
vdf,0,*out,fallback1,2013-11-18T13:47:00Z,G,fallback2
vdf,0,*out,fallback2,2013-11-18T13:45:00Z,R,rif
cgrates.directvoip.co.uk,call,*out,*any,2013-01-06T00:00:00Z,RP_UK,
cgrates.directvoip.co.uk,call,*out,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,
`
sharedGroups = `
SG1,*any,*lowest_first,,
@@ -119,24 +130,33 @@ SG2,*any,*lowest_first,EVENING,
MINI,*topup_reset,*monetary,*out,10,*unlimited,,,10,,,10
MINI,*topup,*minutes,*out,100,*unlimited,NAT,test,10,,,10
SHARED,*topup,*monetary,*out,100,*unlimited,,,10,SG1,,10
TOPUP10_AC,*topup_reset,*monetary,*out,1,*unlimited,*any,,10,,,10
TOPUP1000UKMOB_AC,*topup_reset,*minutes,*out,40,*unlimited,DST_UK_Mobile_BIG5,discounted_minutes,10,,,10
`
actionTimings = `
MORE_MINUTES,MINI,ONE_TIME_RUN,10
MORE_MINUTES,SHARED,ONE_TIME_RUN,10
TOPUP10_AT,TOPUP10_AC,ASAP,10
TOPUP1000UKMOB_AT,TOPUP1000UKMOB_AC,ASAP,10
`
actionTriggers = `
STANDARD_TRIGGER,*minutes,*out,*min_counter,10,GERMANY_O2,SOME_1,10
STANDARD_TRIGGER,*minutes,*out,*max_balance,200,GERMANY,SOME_2,10
STANDARD_TRIGGERS,*monetary,*out,*min_balance,2,,LOG_WARNING,10
STANDARD_TRIGGERS,*monetary,*out,*max_balance,20,,LOG_WARNING,10
STANDARD_TRIGGERS,*monetary,*out,*max_counter,5,FS_USERS,LOG_WARNING,10
`
accountActions = `
vdf,minitsboy;a1;a2,*out,MORE_MINUTES,STANDARD_TRIGGER
cgrates.directvoip.co.uk,12345,*out,TOPUP10_AT,STANDARD_TRIGGERS
cgrates.directvoip.co.uk,12345,*out,TOPUP1000UKMOB_AT,STANDARD_TRIGGERS
`
)
var csvr *CSVReader
func init() {
csvr = NewStringCSVReader(dataStorage, accountingStorage, ',', destinations, timings, rates, destinationRates, destinationRateTimings, ratingProfiles, sharedGroups, actions, actionTimings, actionTriggers, accountActions)
csvr = NewStringCSVReader(dataStorage, accountingStorage, ',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionTimings, actionTriggers, accountActions)
csvr.LoadDestinations()
csvr.LoadTimings()
csvr.LoadRates()
@@ -154,7 +174,7 @@ func init() {
}
func TestLoadDestinations(t *testing.T) {
if len(csvr.destinations) != 9 {
if len(csvr.destinations) != 10 {
t.Error("Failed to load destinations: ", len(csvr.destinations))
}
for _, d := range csvr.destinations {
@@ -195,14 +215,12 @@ func TestLoadDestinations(t *testing.T) {
if !reflect.DeepEqual(d.Prefixes, []string{`+4970`}) {
t.Error("Faild to load destinations", d)
}
default:
t.Error("Unknown destination tag!")
}
}
}
func TestLoadTimimgs(t *testing.T) {
if len(csvr.timings) != 4 {
if len(csvr.timings) != 6 {
t.Error("Failed to load timings: ", csvr.timings)
}
timing := csvr.timings["WORKDAYS_00"]
@@ -252,7 +270,7 @@ func TestLoadTimimgs(t *testing.T) {
}
func TestLoadRates(t *testing.T) {
if len(csvr.rates) != 9 {
if len(csvr.rates) != 11 {
t.Error("Failed to load rates: ", csvr.rates)
}
rate := csvr.rates["R1"].RateSlots[0]
@@ -322,7 +340,7 @@ func TestLoadRates(t *testing.T) {
}
func TestLoadDestinationRates(t *testing.T) {
if len(csvr.destinationRates) != 7 {
if len(csvr.destinationRates) != 9 {
t.Error("Failed to load destinationrates: ", csvr.destinationRates)
}
drs := csvr.destinationRates["RT_STANDARD"]
@@ -434,7 +452,7 @@ func TestLoadDestinationRates(t *testing.T) {
}
func TestLoadDestinationRateTimings(t *testing.T) {
if len(csvr.ratingPlans) != 7 {
if len(csvr.ratingPlans) != 9 {
t.Error("Failed to load rate timings: ", csvr.ratingPlans)
}
rplan := csvr.ratingPlans["STANDARD"]
@@ -554,7 +572,7 @@ func TestLoadDestinationRateTimings(t *testing.T) {
}
func TestLoadRatingProfiles(t *testing.T) {
if len(csvr.ratingProfiles) != 12 {
if len(csvr.ratingProfiles) != 14 {
t.Error("Failed to load rating profiles: ", len(csvr.ratingProfiles), csvr.ratingProfiles)
}
rp := csvr.ratingProfiles["*out:test:0:trp"]
@@ -572,7 +590,7 @@ func TestLoadRatingProfiles(t *testing.T) {
}
func TestLoadActions(t *testing.T) {
if len(csvr.actions) != 2 {
if len(csvr.actions) != 4 {
t.Error("Failed to load actions: ", csvr.actions)
}
as1 := csvr.actions["MINI"]
@@ -681,7 +699,7 @@ func TestLoadSharedGroups(t *testing.T) {
}
func TestLoadActionTimings(t *testing.T) {
if len(csvr.actionsTimings) != 1 {
if len(csvr.actionsTimings) != 3 {
t.Error("Failed to load action timings: ", csvr.actionsTimings)
}
atm := csvr.actionsTimings["MORE_MINUTES"][0]
@@ -707,7 +725,7 @@ func TestLoadActionTimings(t *testing.T) {
}
func TestLoadActionTriggers(t *testing.T) {
if len(csvr.actionsTriggers) != 1 {
if len(csvr.actionsTriggers) != 2 {
t.Error("Failed to load action triggers: ", csvr.actionsTriggers)
}
atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0]
@@ -743,8 +761,8 @@ func TestLoadActionTriggers(t *testing.T) {
}
func TestLoadAccountActions(t *testing.T) {
if len(csvr.accountActions) != 1 {
t.Error("Failed to load account actions: ", csvr.accountActions)
if len(csvr.accountActions) != 3 {
t.Error("Failed to load account actions: ", csvr.accountActions[2])
}
aa := csvr.accountActions[0]
expected := &Account{