first rounding draft

This commit is contained in:
Radu Ioan Fericean
2016-03-02 23:05:21 +02:00
parent 45e5157690
commit d76006cf3f
11 changed files with 251 additions and 292 deletions

View File

@@ -24,7 +24,7 @@ func init() {
c := &CmdDebit{
name: "debit",
rpcMethod: "Responder.Debit",
clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject"},
clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject", "DryRun"},
}
commands[c.Name()] = c
c.CommandExecuter = &CommandExecuter{c}

View File

@@ -1,67 +0,0 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012-2015 ITsysCOM
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package console
import "github.com/cgrates/cgrates/engine"
func init() {
c := &CmdFakeDebit{
name: "debit_fake",
rpcMethod: "Responder.FakeDebit",
clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject"},
}
commands[c.Name()] = c
c.CommandExecuter = &CommandExecuter{c}
}
// Commander implementation
type CmdFakeDebit struct {
name string
rpcMethod string
rpcParams *engine.CallDescriptor
clientArgs []string
*CommandExecuter
}
func (self *CmdFakeDebit) Name() string {
return self.name
}
func (self *CmdFakeDebit) RpcMethod() string {
return self.rpcMethod
}
func (self *CmdFakeDebit) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &engine.CallDescriptor{Direction: "*out"}
}
return self.rpcParams
}
func (self *CmdFakeDebit) PostprocessRpcParams() error {
return nil
}
func (self *CmdFakeDebit) RpcResult() interface{} {
return &engine.CallCost{}
}
func (self *CmdFakeDebit) ClientArgs() []string {
return self.clientArgs
}

View File

@@ -532,31 +532,6 @@ func (ub *Account) GetDefaultMoneyBalance() *Balance {
return defaultBalance
}
func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, count bool) {
var balance *Balance
unitType := cd.TOR
cc := cd.CreateCallCost()
if increment.BalanceInfo.UnitBalanceUuid != "" {
if balance = ub.BalanceMap[unitType].GetBalance(increment.BalanceInfo.UnitBalanceUuid); balance == nil {
return
}
balance.AddValue(increment.Duration.Seconds())
if count {
ub.countUnits(-increment.Duration.Seconds(), unitType, cc, balance)
}
}
// check money too
if increment.BalanceInfo.MoneyBalanceUuid != "" {
if balance = ub.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil {
return
}
balance.AddValue(increment.Cost)
if count {
ub.countUnits(-increment.Cost, utils.MONETARY, cc, balance)
}
}
}
// Scans the action trigers and execute the actions for which trigger is met
func (acc *Account) ExecuteActionTriggers(a *Action) {
if acc.executingTriggers {

View File

@@ -1127,33 +1127,6 @@ func TestAccountUnitCountingOutboundInbound(t *testing.T) {
}
}
func TestAccountRefund(t *testing.T) {
ub := &Account{
BalanceMap: map[string]Balances{
utils.MONETARY: Balances{
&Balance{Uuid: "moneya", Value: 100},
},
utils.VOICE: Balances{
&Balance{Uuid: "minutea", Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}},
&Balance{Uuid: "minuteb", Value: 10, DestinationIDs: utils.StringMap{"RET": true}},
},
},
}
increments := Increments{
&Increment{Cost: 2, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "", MoneyBalanceUuid: "moneya"}},
&Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minutea", MoneyBalanceUuid: "moneya"}},
&Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: ""}},
}
for _, increment := range increments {
ub.refundIncrement(increment, &CallDescriptor{TOR: utils.VOICE}, false)
}
if ub.BalanceMap[utils.MONETARY][0].GetValue() != 104 ||
ub.BalanceMap[utils.VOICE][0].GetValue() != 13 ||
ub.BalanceMap[utils.VOICE][1].GetValue() != 14 {
t.Error("Error refunding money: ", ub.BalanceMap[utils.VOICE][1].GetValue())
}
}
func TestDebitShared(t *testing.T) {
cc := &CallCost{
Tenant: "vdf",
@@ -1422,7 +1395,7 @@ func TestDebitGenericBalance(t *testing.T) {
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.4999 ||
if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.49999 ||
rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 {
t.Logf("%+v", cc.Timespans[0].Increments[0])
t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.GENERIC][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue())
@@ -1465,7 +1438,7 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) {
if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" {
t.Error("Error setting balance id to increment: ", cc.Timespans[0])
}
if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.4999 ||
if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.49999 ||
rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 {
t.Logf("%+v", cc.Timespans[0].Increments[0])
t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.GENERIC][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue())

View File

@@ -357,6 +357,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
b.SubstractValue(amount)
inc.BalanceInfo.UnitBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.ID
inc.BalanceInfo.RateInterval = nil
inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR}
inc.Cost = 0
inc.paid = true
@@ -428,6 +429,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
cost, inc.Cost = 0.0, 0.0
inc.BalanceInfo.MoneyBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.ID
inc.BalanceInfo.RateInterval = ts.RateInterval
inc.paid = true
if count {
ub.countUnits(cost, utils.MONETARY, cc, b)
@@ -446,6 +448,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala
b.SubstractValue(amount)
inc.BalanceInfo.UnitBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.ID
inc.BalanceInfo.RateInterval = nil
inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR}
if cost != 0 {
inc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid
@@ -535,6 +538,9 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala
amount, inc.Cost = 0.0, 0.0
inc.BalanceInfo.MoneyBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.ID
if b.RatingSubject != "" {
inc.BalanceInfo.RateInterval = ts.RateInterval
}
inc.paid = true
if count {
ub.countUnits(amount, utils.MONETARY, cc, b)
@@ -550,6 +556,9 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala
cd.MaxCostSoFar += amount
inc.BalanceInfo.MoneyBalanceUuid = b.Uuid
inc.BalanceInfo.AccountId = ub.ID
if b.RatingSubject != "" {
inc.BalanceInfo.RateInterval = ts.RateInterval
}
inc.paid = true
if count {
ub.countUnits(amount, utils.MONETARY, cc, b)

View File

@@ -136,13 +136,12 @@ func (cc *CallCost) ToDataCost() (*DataCost, error) {
dc.DataSpans[i].Increments = make([]*DataIncrement, len(ts.Increments))
for j, incr := range ts.Increments {
dc.DataSpans[i].Increments[j] = &DataIncrement{
Amount: incr.Duration.Seconds(),
Cost: incr.Cost,
BalanceInfo: incr.BalanceInfo,
BalanceRateInterval: incr.BalanceRateInterval,
UnitInfo: incr.UnitInfo,
CompressFactor: incr.CompressFactor,
paid: incr.paid,
Amount: incr.Duration.Seconds(),
Cost: incr.Cost,
BalanceInfo: incr.BalanceInfo,
UnitInfo: incr.UnitInfo,
CompressFactor: incr.CompressFactor,
paid: incr.paid,
}
}
}
@@ -182,6 +181,43 @@ func (cc *CallCost) updateCost() {
cc.Cost = cost
}
func (cc *CallCost) Round() {
if len(cc.Timespans) == 0 || cc.Timespans[0] == nil {
return
}
var totalCorrectionCost float64
for _, ts := range cc.Timespans {
if len(ts.Increments) == 0 {
continue // safe check
}
inc := ts.Increments[0]
if inc.BalanceInfo.MoneyBalanceUuid == "" || inc.Cost == 0 {
// this is a unit payied timespan, nothing to round
continue
}
cost := ts.CalculateCost()
roundedCost := utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals,
ts.RateInterval.Rating.RoundingMethod)
correctionCost := roundedCost - cost
//log.Print(cost, roundedCost, correctionCost)
roundInc := &Increment{
Cost: correctionCost,
BalanceInfo: inc.BalanceInfo,
}
totalCorrectionCost += correctionCost
ts.Cost += correctionCost
ts.RoundIncrements = append(ts.RoundIncrements, roundInc)
}
cc.Cost += totalCorrectionCost
}
func (cc *CallCost) GetRoundIncrements() (roundIncrements Increments) {
for _, ts := range cc.Timespans {
roundIncrements = append(roundIncrements, ts.RoundIncrements...)
}
return
}
func (cc *CallCost) MatchCCFilter(bf *BalanceFilter) bool {
if bf == nil {
return true

View File

@@ -150,14 +150,16 @@ type CallDescriptor struct {
TOR string // used unit balances selector
ExtraFields map[string]string // Extra fields, mostly used for user profile matching
// session limits
MaxRate float64
MaxRateUnit time.Duration
MaxCostSoFar float64
CgrID string
RunID string
ForceDuration bool // for Max debit if less than duration return err
account *Account
testCallcost *CallCost // testing purpose only!
MaxRate float64
MaxRateUnit time.Duration
MaxCostSoFar float64
CgrID string
RunID string
ForceDuration bool // for Max debit if less than duration return err
PerformRounding bool // flag for rating info rounding
DryRun bool
account *Account
testCallcost *CallCost // testing purpose only!
}
func (cd *CallDescriptor) ValidateCallData() error {
@@ -612,6 +614,7 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura
}
}
totalDuration += incr.Duration
//log.Print("INC: ", utils.ToJSON(incr))
if totalDuration >= initialDuration {
// we have enough, return
//utils.Logger.Debug(fmt.Sprintf("2_INIT DUR %v, TOTAL DUR: %v", initialDuration, totalDuration))
@@ -619,7 +622,8 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura
}
}
}
//utils.Logger.Debug(fmt.Sprintf("3_INIT DUR %v, TOTAL DUR: %v", initialDuration, totalDuration))
utils.Logger.Debug(fmt.Sprintf("3_INIT DUR %v, TOTAL DUR: %v", initialDuration, totalDuration))
return utils.MinDuration(initialDuration, totalDuration), nil
}
@@ -660,9 +664,6 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool)
}
return cc, nil
}
if !dryRun {
defer accountingStorage.SetAccount(account)
}
if cd.TOR == "" {
cd.TOR = utils.VOICE
}
@@ -676,6 +677,15 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool)
cc.updateCost()
cc.UpdateRatedUsage()
cc.Timespans.Compress()
if !dryRun {
accountingStorage.SetAccount(account)
}
if cd.PerformRounding {
cc.Round()
rcd := cc.CreateCallDescriptor()
rcd.Increments = cc.GetRoundIncrements()
rcd.Round()
}
//log.Printf("OUT CC: ", cc)
return
}
@@ -689,26 +699,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
} else {
if memberIds, sgerr := account.GetUniqueSharedGroupMembers(cd); sgerr == nil {
_, err = Guardian.Guard(func() (interface{}, error) {
cc, err = cd.debit(account, false, true)
return 0, err
}, 0, memberIds.Slice()...)
} else {
return nil, sgerr
}
return cc, err
}
}
func (cd *CallDescriptor) FakeDebit() (cc *CallCost, err error) {
cd.account = nil // make sure it's not cached
// lock all group members
if account, err := cd.getAccount(); err != nil || account == nil {
utils.Logger.Err(fmt.Sprintf("Account: %s, not found", cd.GetAccountKey()))
return nil, utils.ErrAccountNotFound
} else {
if memberIds, sgerr := account.GetUniqueSharedGroupMembers(cd); sgerr == nil {
_, err = Guardian.Guard(func() (interface{}, error) {
cc, err = cd.debit(account, true, true)
cc, err = cd.debit(account, cd.DryRun, true)
return 0, err
}, 0, memberIds.Slice()...)
} else {
@@ -763,7 +754,8 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
cd.TimeEnd = cd.TimeStart.Add(remainingDuration)
cd.DurationIndex -= initialDuration - remainingDuration
}
cc, err = cd.debit(account, false, true)
//log.Print("Remaining duration: ", remainingDuration)
cc, err = cd.debit(account, cd.DryRun, true)
//log.Print(balanceMap[0].Value, balanceMap[1].Value)
return 0, err
}, 0, memberIDs.Slice()...)
@@ -777,21 +769,17 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
return cc, err
}
func (cd *CallDescriptor) RefundIncrements() (left float64, err error) {
cd.account = nil // make sure it's not cached
func (cd *CallDescriptor) RefundIncrements() error {
// get account list for locking
// all must be locked in order to use cache
accMap := make(map[string]struct{})
var accountIDs []string
accMap := make(utils.StringMap)
cd.Increments.Decompress()
for _, increment := range cd.Increments {
accMap[increment.BalanceInfo.AccountId] = struct{}{}
}
for key := range accMap {
accountIDs = append(accountIDs, key)
accMap[increment.BalanceInfo.AccountId] = true
}
accountIDs := accMap.Slice()
// start increment refunding loop
Guardian.Guard(func() (interface{}, error) {
_, err := Guardian.Guard(func() (interface{}, error) {
accountsCache := make(map[string]*Account)
for _, increment := range cd.Increments {
account, found := accountsCache[increment.BalanceInfo.AccountId]
@@ -808,11 +796,70 @@ func (cd *CallDescriptor) RefundIncrements() (left float64, err error) {
continue
}
//utils.Logger.Info(fmt.Sprintf("Refunding increment %+v", increment))
account.refundIncrement(increment, cd, true)
var balance *Balance
unitType := cd.TOR
cc := cd.CreateCallCost()
if increment.BalanceInfo.UnitBalanceUuid != "" {
if balance = account.BalanceMap[unitType].GetBalance(increment.BalanceInfo.UnitBalanceUuid); balance == nil {
return 0, nil
}
balance.AddValue(increment.Duration.Seconds())
account.countUnits(-increment.Duration.Seconds(), unitType, cc, balance)
}
// check money too
if increment.BalanceInfo.MoneyBalanceUuid != "" {
if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil {
return 0, nil
}
balance.AddValue(increment.Cost)
account.countUnits(-increment.Cost, utils.MONETARY, cc, balance)
}
}
return 0, err
return 0, nil
}, 0, accountIDs...)
return 0, err
return err
}
func (cd *CallDescriptor) Round() error {
// get account list for locking
// all must be locked in order to use cache
accMap := make(utils.StringMap)
for _, inc := range cd.Increments {
accMap[inc.BalanceInfo.AccountId] = true
}
accountIDs := accMap.Slice()
// start increment refunding loop
_, err := Guardian.Guard(func() (interface{}, error) {
accountsCache := make(map[string]*Account)
for _, increment := range cd.Increments {
account, found := accountsCache[increment.BalanceInfo.AccountId]
if !found {
if acc, err := accountingStorage.GetAccount(increment.BalanceInfo.AccountId); err == nil && acc != nil {
account = acc
accountsCache[increment.BalanceInfo.AccountId] = account
// will save the account only once at the end of the function
defer accountingStorage.SetAccount(account)
}
}
if account == nil {
utils.Logger.Warning(fmt.Sprintf("Could not get the account to be refunded: %s", increment.BalanceInfo.AccountId))
continue
}
cc := cd.CreateCallCost()
if increment.BalanceInfo.MoneyBalanceUuid != "" {
var balance *Balance
if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil {
return 0, nil
}
//log.Print("BEFORE: ", balance.GetValue(), increment.Cost)
balance.AddValue(-increment.Cost)
//log.Print("AFTER: ", balance.GetValue(), increment.Cost)
account.countUnits(increment.Cost, utils.MONETARY, cc, balance)
}
}
return 0, nil
}, 0, accountIDs...)
return err
}
func (cd *CallDescriptor) FlushCache() (err error) {
@@ -855,7 +902,10 @@ func (cd *CallDescriptor) Clone() *CallDescriptor {
FallbackSubject: cd.FallbackSubject,
//RatingInfos: cd.RatingInfos,
//Increments: cd.Increments,
TOR: cd.TOR,
TOR: cd.TOR,
ForceDuration: cd.ForceDuration,
PerformRounding: cd.PerformRounding,
DryRun: cd.DryRun,
}
}

View File

@@ -566,7 +566,7 @@ func TestGetMaxSessiontWithBlocker(t *testing.T) {
MaxCostSoFar: 0,
}
result, err := cd.GetMaxSessionDuration()
expected := 985 * time.Second
expected := 30 * time.Minute
if result != expected || err != nil {
t.Errorf("Expected %v was %v (%v)", expected, result, err)
}
@@ -630,7 +630,7 @@ func TestGetCostRoundingIssue(t *testing.T) {
MaxCostSoFar: 0,
}
cc, err := cd.GetCost()
expected := 0.39
expected := 0.17
if cc.Cost != expected || err != nil {
t.Log(utils.ToIJSON(cc))
t.Errorf("Expected %v was %+v", expected, cc)
@@ -752,25 +752,27 @@ func TestGetCostMaxDebitRoundingIssue(t *testing.T) {
at.Execute()
}
cd := &CallDescriptor{
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "dy",
Account: "dy",
Destination: "0723123113",
TimeStart: time.Date(2015, 10, 26, 13, 29, 27, 0, time.UTC),
TimeEnd: time.Date(2015, 10, 26, 13, 29, 51, 0, time.UTC),
MaxCostSoFar: 0,
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "dy",
Account: "dy",
Destination: "0723123113",
TimeStart: time.Date(2015, 10, 26, 13, 29, 27, 0, time.UTC),
TimeEnd: time.Date(2015, 10, 26, 13, 29, 51, 0, time.UTC),
MaxCostSoFar: 0,
PerformRounding: true,
}
acc, err := accountingStorage.GetAccount("cgrates.org:dy")
if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1 {
t.Errorf("Error getting account: %+v (%v)", utils.ToIJSON(acc), err)
}
cc, err := cd.MaxDebit()
expected := 0.39
expected := 0.17
if cc.Cost != expected || err != nil {
t.Log(utils.ToIJSON(cc))
t.Errorf("Expected %v was %+v", expected, cc)
t.Errorf("Expected %v was %+v (%v)", expected, cc, err)
}
acc, err = accountingStorage.GetAccount("cgrates.org:dy")
if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1-expected {
@@ -1402,6 +1404,35 @@ func TestCDDataGetCost(t *testing.T) {
}
}
func TestCDRefundIncrements(t *testing.T) {
ub := &Account{
ID: "test:ref",
BalanceMap: map[string]Balances{
utils.MONETARY: Balances{
&Balance{Uuid: "moneya", Value: 100},
},
utils.VOICE: Balances{
&Balance{Uuid: "minutea", Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}},
&Balance{Uuid: "minuteb", Value: 10, DestinationIDs: utils.StringMap{"RET": true}},
},
},
}
accountingStorage.SetAccount(ub)
increments := Increments{
&Increment{Cost: 2, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "", MoneyBalanceUuid: "moneya", AccountId: ub.ID}},
&Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minutea", MoneyBalanceUuid: "moneya", AccountId: ub.ID}},
&Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: "", AccountId: ub.ID}},
}
cd := &CallDescriptor{TOR: utils.VOICE, Increments: increments}
cd.RefundIncrements()
ub, _ = accountingStorage.GetAccount(ub.ID)
if ub.BalanceMap[utils.MONETARY][0].GetValue() != 104 ||
ub.BalanceMap[utils.VOICE][0].GetValue() != 13 ||
ub.BalanceMap[utils.VOICE][1].GetValue() != 14 {
t.Error("Error refunding money: ", ub.BalanceMap[utils.VOICE][1].GetValue())
}
}
/*************** BENCHMARKS ********************/
func BenchmarkStorageGetting(b *testing.B) {
b.StopTimer()

View File

@@ -150,41 +150,6 @@ func (rs *Responder) Debit(arg *CallDescriptor, reply *CallCost) (err error) {
return
}
func (rs *Responder) FakeDebit(arg *CallDescriptor, reply *CallCost) (err error) {
if arg.Subject == "" {
arg.Subject = arg.Account
}
// replace aliases
if err := LoadAlias(
&AttrMatchingAlias{
Destination: arg.Destination,
Direction: arg.Direction,
Tenant: arg.Tenant,
Category: arg.Category,
Account: arg.Account,
Subject: arg.Subject,
Context: utils.ALIAS_CONTEXT_RATING,
}, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound {
return err
}
// replace user profile fields
if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil {
return err
}
if rs.Bal != nil {
r, e := rs.getCallCost(arg, "Responder.FakeDebit")
*reply, err = *r, e
} else {
r, e := arg.FakeDebit()
if e != nil {
return e
} else if r != nil {
*reply = *r
}
}
return
}
func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) {
if item, err := rs.getCache().Get(utils.MAX_DEBIT_CACHE_PREFIX + arg.CgrID + arg.RunID); err == nil && item != nil {
*reply = *(item.Value.(*CallCost))
@@ -259,7 +224,7 @@ func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err
if rs.Bal != nil {
*reply, err = rs.callMethod(arg, "Responder.RefundIncrements")
} else {
*reply, err = arg.RefundIncrements()
err = arg.RefundIncrements()
}
rs.getCache().Cache(utils.REFUND_INCR_CACHE_PREFIX+arg.CgrID+arg.RunID, &cache2go.CacheItem{
Value: reply,

View File

@@ -37,18 +37,18 @@ type TimeSpan struct {
RateInterval *RateInterval
DurationIndex time.Duration // the call duration so far till TimeEnd
Increments Increments
RoundIncrements Increments
MatchedSubject, MatchedPrefix, MatchedDestId, RatingPlanId string
CompressFactor int
}
type Increment struct {
Duration time.Duration
Cost float64
BalanceInfo *BalanceInfo // need more than one for units with cost
BalanceRateInterval *RateInterval
UnitInfo *UnitInfo
CompressFactor int
paid bool
Duration time.Duration
Cost float64
BalanceInfo *BalanceInfo // need more than one for units with cost
UnitInfo *UnitInfo
CompressFactor int
paid bool
}
// Holds the minute information related to a specified timespan
@@ -69,12 +69,14 @@ func (mi *UnitInfo) Equal(other *UnitInfo) bool {
type BalanceInfo struct {
UnitBalanceUuid string
MoneyBalanceUuid string
RateInterval *RateInterval
AccountId string // used when debited from shared balance
}
func (bi *BalanceInfo) Equal(other *BalanceInfo) bool {
return bi.UnitBalanceUuid == other.UnitBalanceUuid &&
bi.MoneyBalanceUuid == other.MoneyBalanceUuid &&
reflect.DeepEqual(bi.RateInterval, other.RateInterval) &&
bi.AccountId == other.AccountId
}
@@ -213,11 +215,10 @@ func (tss *TimeSpans) Decompress() { // must be pointer receiver
func (incr *Increment) Clone() *Increment {
nIncr := &Increment{
Duration: incr.Duration,
Cost: incr.Cost,
BalanceRateInterval: incr.BalanceRateInterval,
UnitInfo: incr.UnitInfo,
BalanceInfo: incr.BalanceInfo,
Duration: incr.Duration,
Cost: incr.Cost,
UnitInfo: incr.UnitInfo,
BalanceInfo: incr.BalanceInfo,
}
return nIncr
}
@@ -226,7 +227,6 @@ func (incr *Increment) Equal(other *Increment) bool {
return incr.Duration == other.Duration &&
incr.Cost == other.Cost &&
((incr.BalanceInfo == nil && other.BalanceInfo == nil) || incr.BalanceInfo.Equal(other.BalanceInfo)) &&
((incr.BalanceRateInterval == nil && other.BalanceRateInterval == nil) || reflect.DeepEqual(incr.BalanceRateInterval, other.BalanceRateInterval)) &&
((incr.UnitInfo == nil && other.UnitInfo == nil) || incr.UnitInfo.Equal(other.UnitInfo))
}
@@ -352,10 +352,7 @@ func (ts *TimeSpan) createIncrementsSlice() {
//incrementCost := rate / rateUnit.Seconds() * rateIncrement.Seconds()
nbIncrements := int(ts.GetDuration() / rateIncrement)
incrementCost := ts.CalculateCost() / float64(nbIncrements)
// no more rounding at increment leve as it deviates from intended cost
// move rounding at highest point possible
//incrementCost = utils.Round(incrementCost, ts.RateInterval.Rating.RoundingDecimals,
// ts.RateInterval.Rating.RoundingMethod)
incrementCost = utils.Round(incrementCost, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
for s := 0; s < nbIncrements; s++ {
inc := &Increment{
Duration: rateIncrement,

View File

@@ -742,7 +742,7 @@ func TestTSTimespanCreateIncrements(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.07 {
if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.066667 {
t.Error("Wrong second slice: ", ts.Increments[2].Cost)
}
}
@@ -1510,39 +1510,34 @@ func TestTSIncrementsCompressDecompress(t *testing.T) {
&TimeSpan{
Increments: Increments{
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
},
},
@@ -1562,39 +1557,34 @@ func TestTSMultipleIncrementsCompressDecompress(t *testing.T) {
&TimeSpan{
Increments: Increments{
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
&Increment{
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", "3"},
BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
Duration: time.Minute,
Cost: 10.4,
BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"},
UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE},
},
},
},