mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Add possibility to set/overwrite balance Factors through API
Constructor looks inside the params' balance map for the Factors key. It expects either a string representing a JSON serialized map or the map itself. BalanceFilter Clone function has been updated to set a deep copy of the original Factors map instead of a shallow one. BalanceFilter getter function for Factors now returns nil instead of an empty map. It's slightly more memory efficient and assignment to this map will not be attempted, so it's panic proof. BalanceFilter.ModifyBalance now updates Factors only if the key is found in the request params' Balance map. Setting Factors to null is also possible as long as the Factors key exists and is set to null. Note: only *set_balance can overwrite the Factors map, all the others can only set it if the balance does exist prior to sending the request. Update balance integration tests.
This commit is contained in:
committed by
Dan Christian Bogos
parent
3062113a61
commit
31842bf3f5
@@ -1145,6 +1145,8 @@ func (cdrP *cdrLogProvider) FieldAsInterface(fldPath []string) (data any, err er
|
||||
data = cdrP.action.Balance.Categories.String()
|
||||
case utils.SharedGroups:
|
||||
data = cdrP.action.Balance.SharedGroups.String()
|
||||
case utils.Factors:
|
||||
data = cdrP.action.Balance.Factors.String()
|
||||
}
|
||||
case utils.MetaAct:
|
||||
switch fldPath[1] {
|
||||
|
||||
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package engine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
@@ -47,8 +48,8 @@ type BalanceFilter struct {
|
||||
}
|
||||
|
||||
// NewBalanceFilter creates a new BalanceFilter based on given filter
|
||||
func NewBalanceFilter(filter map[string]any, defaultTimezone string) (bf *BalanceFilter, err error) {
|
||||
bf = new(BalanceFilter)
|
||||
func NewBalanceFilter(filter map[string]any, defaultTimezone string) (*BalanceFilter, error) {
|
||||
bf := new(BalanceFilter)
|
||||
if id, has := filter[utils.ID]; has {
|
||||
bf.ID = utils.StringPointer(utils.IfaceAsString(id))
|
||||
}
|
||||
@@ -59,23 +60,23 @@ func NewBalanceFilter(filter map[string]any, defaultTimezone string) (bf *Balanc
|
||||
// bf.Type = utils.StringPointer(utils.IfaceAsString(ty))
|
||||
// }
|
||||
if val, has := filter[utils.Value]; has {
|
||||
var value float64
|
||||
if value, err = utils.IfaceAsTFloat64(val); err != nil {
|
||||
return
|
||||
value, err := utils.IfaceAsTFloat64(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bf.Value = &utils.ValueFormula{Static: math.Abs(value)}
|
||||
}
|
||||
if exp, has := filter[utils.ExpiryTime]; has {
|
||||
var expTime time.Time
|
||||
if expTime, err = utils.IfaceAsTime(exp, defaultTimezone); err != nil {
|
||||
return
|
||||
expTime, err := utils.IfaceAsTime(exp, defaultTimezone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bf.ExpirationDate = utils.TimePointer(expTime)
|
||||
}
|
||||
if weight, has := filter[utils.Weight]; has {
|
||||
var value float64
|
||||
if value, err = utils.IfaceAsFloat64(weight); err != nil {
|
||||
return
|
||||
value, err := utils.IfaceAsFloat64(weight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bf.Weight = utils.Float64Pointer(value)
|
||||
}
|
||||
@@ -95,20 +96,39 @@ func NewBalanceFilter(filter map[string]any, defaultTimezone string) (bf *Balanc
|
||||
bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(utils.IfaceAsString(tim)))
|
||||
}
|
||||
if dis, has := filter[utils.Disabled]; has {
|
||||
var value bool
|
||||
if value, err = utils.IfaceAsBool(dis); err != nil {
|
||||
return
|
||||
value, err := utils.IfaceAsBool(dis)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bf.Disabled = utils.BoolPointer(value)
|
||||
}
|
||||
if blk, has := filter[utils.Blocker]; has {
|
||||
var value bool
|
||||
if value, err = utils.IfaceAsBool(blk); err != nil {
|
||||
return
|
||||
value, err := utils.IfaceAsBool(blk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bf.Blocker = utils.BoolPointer(value)
|
||||
}
|
||||
return
|
||||
if facVal, has := filter[utils.Factors]; has {
|
||||
var err error
|
||||
var facBytes []byte
|
||||
|
||||
switch v := facVal.(type) {
|
||||
case string:
|
||||
facBytes = []byte(v)
|
||||
default:
|
||||
facBytes, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var vf ValueFactors
|
||||
if err := json.Unmarshal(facBytes, &vf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bf.Factors = &vf
|
||||
}
|
||||
return bf, nil
|
||||
}
|
||||
|
||||
func (bp *BalanceFilter) CreateBalance() *Balance {
|
||||
@@ -125,7 +145,7 @@ func (bp *BalanceFilter) CreateBalance() *Balance {
|
||||
Timings: bp.Timings,
|
||||
TimingIDs: bp.GetTimingIDs(),
|
||||
Disabled: bp.GetDisabled(),
|
||||
Factors: bp.GetFactor(),
|
||||
Factors: bp.GetFactors(),
|
||||
Blocker: bp.GetBlocker(),
|
||||
}
|
||||
return b.Clone()
|
||||
@@ -187,8 +207,11 @@ func (bf *BalanceFilter) Clone() *BalanceFilter {
|
||||
*result.Disabled = *bf.Disabled
|
||||
}
|
||||
if bf.Factors != nil {
|
||||
result.Factors = new(ValueFactors)
|
||||
*result.Factors = *bf.Factors
|
||||
cln := make(ValueFactors, len(*bf.Factors))
|
||||
for key, value := range *bf.Factors {
|
||||
cln[key] = value
|
||||
}
|
||||
result.Factors = &cln
|
||||
}
|
||||
if bf.Blocker != nil {
|
||||
result.Blocker = new(bool)
|
||||
@@ -359,9 +382,9 @@ func (bp *BalanceFilter) GetExpirationDate() time.Time {
|
||||
return *bp.ExpirationDate
|
||||
}
|
||||
|
||||
func (bp *BalanceFilter) GetFactor() ValueFactors {
|
||||
func (bp *BalanceFilter) GetFactors() ValueFactors {
|
||||
if bp == nil || bp.Factors == nil {
|
||||
return ValueFactors{}
|
||||
return nil
|
||||
}
|
||||
return *bp.Factors
|
||||
}
|
||||
@@ -408,6 +431,9 @@ func (bf *BalanceFilter) ModifyBalance(b *Balance) {
|
||||
if bf.Weight != nil {
|
||||
b.Weight = *bf.Weight
|
||||
}
|
||||
if bf.Factors != nil {
|
||||
b.Factors = *bf.Factors
|
||||
}
|
||||
if bf.Blocker != nil {
|
||||
b.Blocker = *bf.Blocker
|
||||
}
|
||||
|
||||
@@ -851,7 +851,7 @@ func (bl *BalanceSummary) FieldAsInterface(fldPath []string) (val any, err error
|
||||
}
|
||||
}
|
||||
|
||||
// debitUnits will debit units for call descriptor.
|
||||
// debit 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, fltrS *FilterS) (cc *CallCost, err error) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/birpc/context"
|
||||
v1 "github.com/cgrates/cgrates/apier/v1"
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/sessions"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
@@ -267,8 +268,10 @@ cgrates.org,sms,1001,2014-01-14T00:00:00Z,RP_ANY,`,
|
||||
// unit were subtracted from the *monetary balance.
|
||||
// 4. Try to refund the debit made in the previous step to check whether factor is taken
|
||||
// into consideration for refunds as well.
|
||||
// 5. Do the above steps also for SessionSv1.ProcessCDR with a different usage.
|
||||
// 6. Initiate a prepaid session (usage 10s), update it twice (usages 5s and 2s), terminate,
|
||||
// 5. Do the above steps also for SessionSv1.ProcessCDR with a different usage and a different
|
||||
// factor (that we overwrite through the APIerSv1.SetBalance API).
|
||||
// 6. Set a *voice balance with 100s. Initiate a prepaid session (usage 10s), update it twice
|
||||
// (usages 5s and 2s), terminate,
|
||||
// and process CDR.
|
||||
// 7. Check to see if balance_voice was debitted 34s ((10s+5s+2s) * voiceFactor, where
|
||||
// voiceFactor is 2) and then also check if it applies correctly for refund (similar
|
||||
@@ -306,7 +309,8 @@ func TestBalanceFactor(t *testing.T) {
|
||||
},
|
||||
|
||||
"schedulers": {
|
||||
"enabled": true
|
||||
"enabled": true,
|
||||
"cdrs_conns": ["*internal"]
|
||||
},
|
||||
|
||||
"apiers": {
|
||||
@@ -334,7 +338,6 @@ cgrates.org,1001,PACKAGE_1001,,,`,
|
||||
PACKAGE_1001,ACT_TOPUP,*asap,10`,
|
||||
utils.ActionsCsv: `#ActionsId[0],Action[1],ExtraParameters[2],Filter[3],BalanceId[4],BalanceType[5],Categories[6],DestinationIds[7],RatingSubject[8],SharedGroup[9],ExpiryTime[10],TimingIds[11],Units[12],BalanceWeight[13],BalanceBlocker[14],BalanceDisabled[15],Weight[16]
|
||||
ACT_TOPUP,*topup_reset,"{""smsFactor"":4}",,balance_sms,*sms,,,,,*unlimited,,10,20,false,false,20
|
||||
ACT_TOPUP,*topup_reset,"{""voiceFactor"":2}",,balance_voice,*voice,call,,,,*unlimited,,100s,20,false,false,20
|
||||
ACT_TOPUP,*topup_reset,,,balance_monetary,*monetary,,*any,,,*unlimited,,5,10,false,false,20`,
|
||||
utils.ChargersCsv: `#Id,ActionsId,TimingId,Weight
|
||||
#Tenant,ID,FilterIDs,ActivationInterval,RunID,AttributeIDs,Weight
|
||||
@@ -373,23 +376,18 @@ cgrates.org,call,1001,2014-01-14T00:00:00Z,RP_VOICE,`,
|
||||
if err := client.Call(context.Background(), utils.APIerSv2GetAccount, attrs, &acnt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(acnt.BalanceMap) != 3 ||
|
||||
if len(acnt.BalanceMap) != 2 ||
|
||||
len(acnt.BalanceMap[utils.MetaMonetary]) != 1 ||
|
||||
len(acnt.BalanceMap[utils.MetaSMS]) != 1 ||
|
||||
len(acnt.BalanceMap[utils.MetaVoice]) != 1 {
|
||||
t.Fatalf("expected account to have one balance of type *monetary, one of type *sms and one of type *voice, received %v", acnt)
|
||||
len(acnt.BalanceMap[utils.MetaSMS]) != 1 {
|
||||
t.Fatalf("expected account to have one balance of type *monetary and one of type *sms received %v", acnt)
|
||||
}
|
||||
smsBalance := acnt.BalanceMap[utils.MetaSMS][0]
|
||||
if smsBalance.ID != "balance_sms" || smsBalance.Value != 10 {
|
||||
t.Fatalf("received account with unexpected *sms balance: %v", smsBalance)
|
||||
t.Fatalf("*sms balance value: want %v, got %v", 10, smsBalance)
|
||||
}
|
||||
monetaryBalance := acnt.BalanceMap[utils.MetaMonetary][0]
|
||||
if monetaryBalance.ID != "balance_monetary" || monetaryBalance.Value != 5 {
|
||||
t.Fatalf("received account with unexpected *monetary balance: %v", monetaryBalance)
|
||||
}
|
||||
voiceBalance := acnt.BalanceMap[utils.MetaVoice][0]
|
||||
if voiceBalance.ID != "balance_voice" || voiceBalance.Value != float64(100*time.Second) {
|
||||
t.Fatalf("received account with unexpected *voice balance: %v", voiceBalance)
|
||||
t.Fatalf("*monetary balance value: want %v, got %v", 5, monetaryBalance)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -431,19 +429,23 @@ cgrates.org,call,1001,2014-01-14T00:00:00Z,RP_VOICE,`,
|
||||
if len(cdrs) != 1 {
|
||||
t.Fatalf("expected to receive only one CDR: %v", utils.ToJSON(cdrs))
|
||||
}
|
||||
smsBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[0]", "Value"})
|
||||
smsBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{
|
||||
"AccountSummary", "BalanceSummaries", "balance_sms", "Value",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("could not retrieve *sms balance current value: %v", err)
|
||||
}
|
||||
if smsBalanceValue != 2. {
|
||||
t.Errorf("unexpected balance value: expected %v, received %v", 2., smsBalanceValue)
|
||||
t.Errorf("*sms balance value: want %v, got %v", 2, smsBalanceValue)
|
||||
}
|
||||
monetaryBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[2]", "Value"})
|
||||
monetaryBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{
|
||||
"AccountSummary", "BalanceSummaries", "balance_monetary", "Value",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("could not retrieve *sms balance current value: %v", err)
|
||||
}
|
||||
if monetaryBalanceValue != 3. {
|
||||
t.Errorf("unexpected balance value: expected %v, received %v", 3., monetaryBalanceValue)
|
||||
t.Errorf("monetary balance value: want %v, got %v", 3, monetaryBalanceValue)
|
||||
}
|
||||
|
||||
// Attempt refund to check if factor also applies when refunding increments.
|
||||
@@ -472,18 +474,78 @@ cgrates.org,call,1001,2014-01-14T00:00:00Z,RP_VOICE,`,
|
||||
}
|
||||
smsBalance := acnt.BalanceMap[utils.MetaSMS][0]
|
||||
if smsBalance.ID != "balance_sms" || smsBalance.Value != 10 {
|
||||
t.Fatalf("received account with unexpected *sms balance: %v", smsBalance)
|
||||
t.Errorf("*sms balance: want %v, got %v", 10, smsBalance)
|
||||
}
|
||||
monetaryBalance := acnt.BalanceMap[utils.MetaMonetary][0]
|
||||
if monetaryBalance.ID != "balance_monetary" || monetaryBalance.Value != 5 {
|
||||
t.Fatalf("received account with unexpected *monetary balance: %v", monetaryBalance)
|
||||
t.Errorf("*monetary balance: want %v, got %v", 5, monetaryBalance)
|
||||
}
|
||||
smsBalanceFactor, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[0]", "Factors", "smsFactor"})
|
||||
smsBalanceFactor, err := cdrs[0].CostDetails.FieldAsInterface([]string{
|
||||
"AccountSummary", "BalanceSummaries", "balance_sms", "Factors", "smsFactor",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("could not retrieve *sms balance factor: %v", err)
|
||||
}
|
||||
if smsBalanceFactor != 4. {
|
||||
t.Errorf("unexpected balance factor: expected %v, received %v", 4., smsBalanceValue)
|
||||
t.Errorf("balance factor: want %v, got %v", 4, smsBalanceValue)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("AlterBalanceFactorThroughAPI", func(t *testing.T) {
|
||||
// Decrease the smsFactor from balance_sms from 4 to 3.
|
||||
var replySetBalance string
|
||||
if err := client.Call(context.Background(), utils.APIerSv1SetBalance,
|
||||
&utils.AttrSetBalance{
|
||||
Tenant: "cgrates.org",
|
||||
Account: "1001",
|
||||
BalanceType: utils.MetaSMS,
|
||||
// Value: 100_000_000_000, // 100s
|
||||
// Value: 20,
|
||||
Balance: map[string]any{
|
||||
utils.ID: "balance_sms",
|
||||
utils.Factors: map[string]float64{
|
||||
"smsFactor": 3,
|
||||
},
|
||||
},
|
||||
ActionExtraData: &map[string]any{
|
||||
"BalanceID": "~*acnt.BalanceID",
|
||||
"NewFactors": "~*acnt.Factors",
|
||||
},
|
||||
Cdrlog: true,
|
||||
}, &replySetBalance); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var cdrs []*engine.CDR
|
||||
if err := client.Call(context.Background(), utils.CDRsV1GetCDRs, &utils.RPCCDRsFilterWithAPIOpts{
|
||||
RPCCDRsFilter: &utils.RPCCDRsFilter{
|
||||
RunIDs: []string{"*set_balance"},
|
||||
}}, &cdrs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(cdrs) != 1 ||
|
||||
cdrs[0].Cost != 0 ||
|
||||
cdrs[0].ExtraFields[utils.BalanceID] != "balance_sms" ||
|
||||
cdrs[0].ExtraFields["NewFactors"] != "{\"smsFactor\":3}" ||
|
||||
cdrs[0].RunID != utils.MetaSetBalance ||
|
||||
cdrs[0].Source != utils.CDRLog ||
|
||||
cdrs[0].ToR != utils.MetaSMS {
|
||||
t.Errorf("unexpected cdr received: %v", utils.ToJSON(cdrs))
|
||||
}
|
||||
|
||||
var acnt engine.Account
|
||||
if err := client.Call(context.Background(), utils.APIerSv2GetAccount,
|
||||
&utils.AttrGetAccount{
|
||||
Tenant: "cgrates.org",
|
||||
Account: "1001",
|
||||
}, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
updatedSMSBalance := acnt.BalanceMap[utils.MetaSMS][0]
|
||||
if updatedSMSBalance.ID != "balance_sms" ||
|
||||
updatedSMSBalance.Value != 10 ||
|
||||
updatedSMSBalance.Factors["smsFactor"] != 3 {
|
||||
t.Fatalf("updated balance_sms: want Value 10 and smsFactor 3, got %v", updatedSMSBalance)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -522,23 +584,47 @@ cgrates.org,call,1001,2014-01-14T00:00:00Z,RP_VOICE,`,
|
||||
if len(cdrs) != 1 {
|
||||
t.Fatalf("expected to receive only one CDR: %v", utils.ToJSON(cdrs))
|
||||
}
|
||||
smsBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[0]", "Value"})
|
||||
smsBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries", "balance_sms", "Value"})
|
||||
if err != nil {
|
||||
t.Fatalf("could not retrieve *sms balance current value: %v", err)
|
||||
}
|
||||
if smsBalanceValue != 2. {
|
||||
t.Errorf("unexpected balance value: expected %v, received %v", 2., smsBalanceValue)
|
||||
if smsBalanceValue != 1. {
|
||||
t.Errorf("balance value: want %v, got %v", 1., smsBalanceValue)
|
||||
}
|
||||
monetaryBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[2]", "Value"})
|
||||
monetaryBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries", "balance_monetary", "Value"})
|
||||
if err != nil {
|
||||
t.Fatalf("could not retrieve *sms balance current value: %v", err)
|
||||
}
|
||||
if monetaryBalanceValue != 1. {
|
||||
t.Errorf("unexpected balance value: expected %v, received %v", 1., monetaryBalanceValue)
|
||||
if monetaryBalanceValue != 2. {
|
||||
t.Errorf("balance value: want %v, got %v", 2., monetaryBalanceValue)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("PrepaidSession", func(t *testing.T) {
|
||||
/*
|
||||
Add balance via API. Should be equivalent to the following Actions.csv entry:
|
||||
ACT_TOPUP,*topup_reset,"{""voiceFactor"":2}",,balance_voice,*voice,call,,,,*unlimited,,100s,20,false,false,20
|
||||
*/
|
||||
var replyAddBalance string
|
||||
if err := client.Call(context.Background(), utils.APIerSv1AddBalance,
|
||||
&v1.AttrAddBalance{
|
||||
Tenant: "cgrates.org",
|
||||
Account: "1001",
|
||||
BalanceType: utils.MetaVoice,
|
||||
Value: 100_000_000_000, // 100s
|
||||
Balance: map[string]any{
|
||||
utils.ID: "balance_voice",
|
||||
utils.Categories: "call",
|
||||
utils.ExpiryTime: utils.MetaUnlimited,
|
||||
utils.Weight: 20,
|
||||
utils.Factors: map[string]float64{
|
||||
"voiceFactor": 2,
|
||||
},
|
||||
},
|
||||
Overwrite: true,
|
||||
}, &replyAddBalance); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var replyInit sessions.V1InitSessionReply
|
||||
if err := client.Call(context.Background(), utils.SessionSv1InitiateSession,
|
||||
&sessions.V1InitSessionArgs{
|
||||
@@ -688,12 +774,12 @@ cgrates.org,call,1001,2014-01-14T00:00:00Z,RP_VOICE,`,
|
||||
if len(cdrs) != 1 {
|
||||
t.Fatalf("expected to receive only one CDR: %v", utils.ToJSON(cdrs))
|
||||
}
|
||||
voiceBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries[1]", "Value"})
|
||||
voiceBalanceValue, err := cdrs[0].CostDetails.FieldAsInterface([]string{"AccountSummary", "BalanceSummaries", "balance_voice", "Value"})
|
||||
if err != nil {
|
||||
t.Fatalf("could not retrieve *voice balance current value: %v", err)
|
||||
}
|
||||
if voiceBalanceValue != float64(66*time.Second) {
|
||||
t.Errorf("unexpected balance value: expected %v, received %v", float64(66*time.Second), voiceBalanceValue)
|
||||
t.Errorf("*voice balance value: want %v, got %v", float64(66*time.Second), voiceBalanceValue)
|
||||
}
|
||||
|
||||
// Attempt refund to check if factor also applies when refunding increments.
|
||||
@@ -720,7 +806,7 @@ cgrates.org,call,1001,2014-01-14T00:00:00Z,RP_VOICE,`,
|
||||
}
|
||||
voiceBalance := acnt.BalanceMap[utils.MetaVoice][0]
|
||||
if voiceBalance.ID != "balance_voice" || voiceBalance.Value != float64(100*time.Second) {
|
||||
t.Fatalf("received account with unexpected *voice balance: %v", voiceBalance)
|
||||
t.Fatalf("*voice balance value: want %v, got %v", 100_000_000_000, voiceBalance)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -761,7 +847,7 @@ func TestBalanceCDRLog(t *testing.T) {
|
||||
|
||||
"schedulers": {
|
||||
"enabled": true,
|
||||
"cdrs_conns": ["*localhost"]
|
||||
"cdrs_conns": ["*internal"]
|
||||
},
|
||||
|
||||
"apiers": {
|
||||
|
||||
Reference in New Issue
Block a user