CallDescriptor.AllowNegative -> DenyNegativeAccount, ApierV1.DebitUsageWithOptions modifications

This commit is contained in:
DanB
2016-11-24 14:42:20 +01:00
parent 99750d6098
commit 22038034cc
9 changed files with 86 additions and 107 deletions

View File

@@ -26,34 +26,26 @@ import (
// account to go negative if the cost calculated is greater than the balance
func (apier *ApierV1) DebitUsage(usageRecord engine.UsageRecord, reply *string) error {
return apier.DebitUsageWithOptions(AttrDebitUsageWithOptions{
Options: AttrDebitUsageOptions{AllowNegative: true},
UsageRecord: usageRecord,
UsageRecord: &usageRecord,
AllowNegativeAccount: true,
}, reply)
}
// AttrDebitUsageOptions represents options that
// are applied to the DebitUsage request
type AttrDebitUsageOptions struct {
AllowNegative bool
}
// AttrDebitUsageWithOptions represents the DebitUsage request
type AttrDebitUsageWithOptions struct {
Options AttrDebitUsageOptions
UsageRecord engine.UsageRecord
UsageRecord *engine.UsageRecord
AllowNegativeAccount bool // allow account to go negative during debit
}
// DebitUsageWithOptions will debit the account based on the usage cost with
// additional options to control if the balance can go negative
func (apier *ApierV1) DebitUsageWithOptions(usageRecordWithOptions AttrDebitUsageWithOptions, reply *string) error {
var usageRecord = &usageRecordWithOptions.UsageRecord
var options = &usageRecordWithOptions.Options
func (apier *ApierV1) DebitUsageWithOptions(args AttrDebitUsageWithOptions, reply *string) error {
usageRecord := args.UsageRecord
if missing := utils.MissingStructFields(usageRecord, []string{"Account", "Destination", "Usage"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
err := engine.LoadUserProfile(usageRecord, "")
err := engine.LoadUserProfile(args.UsageRecord, "")
if err != nil {
*reply = err.Error()
return err
@@ -83,14 +75,11 @@ func (apier *ApierV1) DebitUsageWithOptions(usageRecordWithOptions AttrDebitUsag
}
// Get the call descriptor from the usage record
cd, err := usageRecord.AsCallDescriptor(apier.Config.DefaultTimezone)
cd, err := usageRecord.AsCallDescriptor(apier.Config.DefaultTimezone, !args.AllowNegativeAccount)
if err != nil {
return utils.NewErrServerError(err)
}
// Apply options
cd.AllowNegative = options.AllowNegative
// Calculate the cost for usage and debit the account
var cc engine.CallCost
if err := apier.Responder.Debit(cd, &cc); err != nil {

View File

@@ -18,8 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package v1
import (
"flag"
"os"
"testing"
"time"
@@ -34,7 +32,7 @@ var (
responder *engine.Responder
)
func TestMain(m *testing.M) {
func init() {
apierDebitStorage, _ = engine.NewMapStorage()
cfg, _ := config.NewDefaultCGRConfig()
responder := new(engine.Responder)
@@ -47,9 +45,6 @@ func TestMain(m *testing.M) {
Config: cfg,
Responder: responder,
}
flag.Parse()
os.Exit(m.Run())
}
func TestDebitUsageWithOptions(t *testing.T) {
@@ -123,11 +118,7 @@ func TestDebitUsageWithOptions(t *testing.T) {
t.Error(err)
}
allowNegativeOpt := AttrDebitUsageOptions{
AllowNegative: false,
}
usageRecord := engine.UsageRecord{
usageRecord := &engine.UsageRecord{
Tenant: cgrTenant,
Account: "account1",
Destination: "*any",
@@ -140,7 +131,8 @@ func TestDebitUsageWithOptions(t *testing.T) {
}
var reply string
if err := apierDebit.DebitUsageWithOptions(AttrDebitUsageWithOptions{Options: allowNegativeOpt, UsageRecord: usageRecord}, &reply); err != nil {
if err := apierDebit.DebitUsageWithOptions(AttrDebitUsageWithOptions{UsageRecord: usageRecord,
AllowNegativeAccount: false}, &reply); err != nil {
t.Error(err)
}
@@ -151,6 +143,7 @@ func TestDebitUsageWithOptions(t *testing.T) {
}
eAcntVal := 9.0
if resolvedAccount.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, resolvedAccount.BalanceMap[utils.MONETARY].GetTotalValue())
t.Errorf("Expected: %f, received: %f", eAcntVal,
resolvedAccount.BalanceMap[utils.MONETARY].GetTotalValue())
}
}

View File

@@ -150,17 +150,17 @@ 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
PerformRounding bool // flag for rating info rounding
DryRun bool
AllowNegative bool
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
DenyNegativeAccount bool // prevent account going on negative during debit
account *Account
testCallcost *CallCost // testing purpose only!
}
func (cd *CallDescriptor) ValidateCallData() error {
@@ -700,7 +700,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, cd.DryRun, cd.AllowNegative)
cc, err = cd.debit(account, cd.DryRun, !cd.DenyNegativeAccount)
return 0, err
}, 0, memberIds.Slice()...)
} else {
@@ -755,7 +755,7 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
cd.DurationIndex -= initialDuration - remainingDuration
}
//log.Print("Remaining duration: ", remainingDuration)
cc, err = cd.debit(account, cd.DryRun, true)
cc, err = cd.debit(account, cd.DryRun, !cd.DenyNegativeAccount)
//log.Print(balanceMap[0].Value, balanceMap[1].Value)
return 0, err
}, 0, memberIDs.Slice()...)

View File

@@ -284,7 +284,7 @@ func TestGetCostRounding(t *testing.T) {
func TestDebitRounding(t *testing.T) {
t1 := time.Date(2017, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2017, time.February, 2, 17, 33, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Subject: "round", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, AllowNegative: true}
cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Subject: "round", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0}
result, _ := cd.Debit()
if result.Cost != 0.30006 || result.GetConnectFee() != 0 { // should be 0.3 :(
t.Error("bad cost", utils.ToIJSON(result))
@@ -294,7 +294,7 @@ func TestDebitRounding(t *testing.T) {
func TestDebitPerformRounding(t *testing.T) {
t1 := time.Date(2017, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2017, time.February, 2, 17, 33, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Subject: "round", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, PerformRounding: true, AllowNegative: true}
cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Subject: "round", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, PerformRounding: true}
result, _ := cd.Debit()
if result.Cost != 0.3001 || result.GetConnectFee() != 0 { // should be 0.3 :(
t.Error("bad cost", utils.ToIJSON(result))
@@ -793,16 +793,15 @@ func TestDebitRatingInfoOnZeroTime(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, 27, 0, time.UTC),
MaxCostSoFar: 0,
AllowNegative: true,
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, 27, 0, time.UTC),
MaxCostSoFar: 0,
}
cc, err := cd.Debit()
if err != nil ||
@@ -919,7 +918,6 @@ func TestDebitRoundingRefund(t *testing.T) {
TimeEnd: time.Date(2016, 3, 4, 13, 53, 00, 0, time.UTC),
MaxCostSoFar: 0,
PerformRounding: true,
AllowNegative: true,
}
acc, err := accountingStorage.GetAccount("cgrates.org:dy")
if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1 {
@@ -1204,15 +1202,14 @@ func TestMaxDebitDurationNoGreatherThanInitialDuration(t *testing.T) {
func TestDebitAndMaxDebit(t *testing.T) {
cd1 := &CallDescriptor{
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 34, 10, 0, time.UTC),
Direction: "*out",
Category: "0",
Tenant: "vdf",
Subject: "minu_from_tm",
Account: "minu",
Destination: "0723",
AllowNegative: true,
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 10, 21, 18, 34, 10, 0, time.UTC),
Direction: "*out",
Category: "0",
Tenant: "vdf",
Subject: "minu_from_tm",
Account: "minu",
Destination: "0723",
}
cd2 := cd1.Clone()
cc1, err1 := cd1.Debit()

View File

@@ -866,7 +866,8 @@ type UsageRecord struct {
func (self *UsageRecord) AsStoredCdr(timezone string) (*CDR, error) {
var err error
cdr := &CDR{CGRID: self.GetId(), ToR: self.ToR, RequestType: self.RequestType, Direction: self.Direction, Tenant: self.Tenant, Category: self.Category, Account: self.Account, Subject: self.Subject, Destination: self.Destination}
cdr := &CDR{CGRID: self.GetId(), ToR: self.ToR, RequestType: self.RequestType, Direction: self.Direction,
Tenant: self.Tenant, Category: self.Category, Account: self.Account, Subject: self.Subject, Destination: self.Destination}
if cdr.SetupTime, err = utils.ParseTimeDetectLayout(self.SetupTime, timezone); err != nil {
return nil, err
}
@@ -885,18 +886,18 @@ func (self *UsageRecord) AsStoredCdr(timezone string) (*CDR, error) {
return cdr, nil
}
func (self *UsageRecord) AsCallDescriptor(timezone string) (*CallDescriptor, error) {
func (self *UsageRecord) AsCallDescriptor(timezone string, denyNegative bool) (*CallDescriptor, error) {
var err error
cd := &CallDescriptor{
CgrID: self.GetId(),
TOR: self.ToR,
Direction: self.Direction,
Tenant: self.Tenant,
Category: self.Category,
Subject: self.Subject,
Account: self.Account,
Destination: self.Destination,
AllowNegative: true,
CgrID: self.GetId(),
TOR: self.ToR,
Direction: self.Direction,
Tenant: self.Tenant,
Category: self.Category,
Subject: self.Subject,
Account: self.Account,
Destination: self.Destination,
DenyNegativeAccount: denyNegative,
}
timeStr := self.AnswerTime
if len(timeStr) == 0 { // In case of auth, answer time will not be defined, so take it out of setup one

View File

@@ -473,8 +473,10 @@ func TestUsageReqAsCD(t *testing.T) {
Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", Usage: "0.00000001",
}
eCD := &CallDescriptor{CgrID: "9473e7b2e075d168b9da10ae957ee68fe5a217e4", TOR: req.ToR, Direction: req.Direction, Tenant: req.Tenant, Category: req.Category, Account: req.Account, Subject: req.Subject, Destination: req.Destination, TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Add(time.Duration(10)), AllowNegative: true}
if cd, err := req.AsCallDescriptor(""); err != nil {
eCD := &CallDescriptor{CgrID: "9473e7b2e075d168b9da10ae957ee68fe5a217e4", TOR: req.ToR, Direction: req.Direction, Tenant: req.Tenant,
Category: req.Category, Account: req.Account, Subject: req.Subject, Destination: req.Destination,
TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Add(time.Duration(10)), DenyNegativeAccount: true}
if cd, err := req.AsCallDescriptor("", true); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCD, cd) {
t.Errorf("Expected: %+v, received: %+v", eCD, cd)

View File

@@ -144,15 +144,14 @@ func TestExecuteActions(t *testing.T) {
func TestDebit(t *testing.T) {
cd := &engine.CallDescriptor{
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12344",
Account: "12344",
Destination: "447956933443",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 10, 0, time.UTC),
AllowNegative: true,
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12344",
Account: "12344",
Destination: "447956933443",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 10, 0, time.UTC),
}
if cc, err := cd.Debit(); err != nil {
t.Error(err)

View File

@@ -144,15 +144,14 @@ func TestExecuteActions2(t *testing.T) {
func TestDebit2(t *testing.T) {
cd := &engine.CallDescriptor{
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956933443",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 10, 0, time.UTC),
AllowNegative: true,
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12345",
Account: "12345",
Destination: "447956933443",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 10, 0, time.UTC),
}
if cc, err := cd.Debit(); err != nil {
t.Error(err)

View File

@@ -140,15 +140,14 @@ func TestExecuteActions3(t *testing.T) {
func TestDebit3(t *testing.T) {
cd := &engine.CallDescriptor{
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12346",
Account: "12346",
Destination: "447956933443",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 10, 0, time.UTC),
AllowNegative: true,
Direction: "*out",
Category: "call",
Tenant: "cgrates.org",
Subject: "12346",
Account: "12346",
Destination: "447956933443",
TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 4, 6, 0, 10, 0, time.UTC),
}
if cc, err := cd.Debit(); err != nil {
t.Error(err)