mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
CallDescriptor.AllowNegative -> DenyNegativeAccount, ApierV1.DebitUsageWithOptions modifications
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()...)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user