mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-16 21:59:53 +05:00
RateS - FixedFee should be possible without RecurrentFee, AccountS updates
This commit is contained in:
@@ -51,7 +51,7 @@ func (aB *abstractBalance) id() string {
|
||||
|
||||
// debitAbstracts implements the balanceOperator interface
|
||||
func (aB *abstractBalance) debitAbstracts(usage *decimal.Big,
|
||||
cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) {
|
||||
cgrEv *utils.CGREvent, dbted *decimal.Big) (ec *utils.EventCharges, err error) {
|
||||
|
||||
evNm := utils.MapStorage{
|
||||
utils.MetaOpts: cgrEv.APIOpts,
|
||||
@@ -116,7 +116,7 @@ func (aB *abstractBalance) debitAbstracts(usage *decimal.Big,
|
||||
aB.connMgr, cgrEv,
|
||||
aB.attrSConns, aB.blnCfg.AttributeIDs,
|
||||
aB.rateSConns, aB.blnCfg.RateProfileIDs,
|
||||
costIcrm); err != nil {
|
||||
costIcrm, dbted); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -210,6 +210,6 @@ func (aB *abstractBalance) debitAbstracts(usage *decimal.Big,
|
||||
|
||||
// debitConcretes implements the balanceOperator interface
|
||||
func (aB *abstractBalance) debitConcretes(_ *decimal.Big,
|
||||
_ *utils.CGREvent) (ec *utils.EventCharges, err error) {
|
||||
_ *utils.CGREvent, _ *decimal.Big) (ec *utils.EventCharges, err error) {
|
||||
return nil, utils.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/config"
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"github.com/ericlagergren/decimal"
|
||||
)
|
||||
@@ -220,6 +218,7 @@ func TestABDebitUsageFromConcretes1(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
func TestABDebitAbstracts(t *testing.T) {
|
||||
aB := &abstractBalance{
|
||||
blnCfg: &utils.Balance{
|
||||
@@ -1271,3 +1270,4 @@ func TestAMCostWithUnitFactor(t *testing.T) {
|
||||
t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -156,6 +156,7 @@ func (aS *AccountS) accountsDebit(acnts []*utils.AccountWithWeight,
|
||||
} else {
|
||||
usage = decimal.New(int64(usgEv), 0)
|
||||
}
|
||||
dbted := decimal.New(0, 0)
|
||||
acntBkps := make([]utils.AccountBalancesBackup, len(acnts))
|
||||
for i, acnt := range acnts {
|
||||
if usage.Cmp(decimal.New(0, 0)) == 0 {
|
||||
@@ -164,7 +165,7 @@ func (aS *AccountS) accountsDebit(acnts []*utils.AccountWithWeight,
|
||||
acntBkps[i] = acnt.Account.AccountBalancesBackup()
|
||||
var ecDbt *utils.EventCharges
|
||||
if ecDbt, err = aS.accountDebit(acnt.Account,
|
||||
new(decimal.Big).Copy(usage), cgrEv, concretes); err != nil {
|
||||
new(decimal.Big).Copy(usage), cgrEv, concretes, dbted); err != nil {
|
||||
if store {
|
||||
restoreAccounts(aS.dm, acnts, acntBkps)
|
||||
}
|
||||
@@ -189,6 +190,7 @@ func (aS *AccountS) accountsDebit(acnts []*utils.AccountWithWeight,
|
||||
used = ecDbt.Abstracts.Big
|
||||
}
|
||||
usage = utils.SubstractBig(usage, used)
|
||||
dbted = utils.SumBig(dbted, used)
|
||||
ec.Merge(ecDbt)
|
||||
}
|
||||
return
|
||||
@@ -196,7 +198,7 @@ func (aS *AccountS) accountsDebit(acnts []*utils.AccountWithWeight,
|
||||
|
||||
// accountDebit will debit the usage out of an Account
|
||||
func (aS *AccountS) accountDebit(acnt *utils.Account, usage *decimal.Big,
|
||||
cgrEv *utils.CGREvent, concretes bool) (ec *utils.EventCharges, err error) {
|
||||
cgrEv *utils.CGREvent, concretes bool, dbted *decimal.Big) (ec *utils.EventCharges, err error) {
|
||||
// Find balances matching event
|
||||
blcsWithWeight := make(utils.BalancesWithWeight, 0, len(acnt.Balances))
|
||||
for _, blnCfg := range acnt.Balances {
|
||||
@@ -223,7 +225,7 @@ func (aS *AccountS) accountDebit(acnt *utils.Account, usage *decimal.Big,
|
||||
return // no more debit
|
||||
}
|
||||
var ecDbt *utils.EventCharges
|
||||
if ecDbt, err = debFunc(new(decimal.Big).Copy(usage), cgrEv); err != nil {
|
||||
if ecDbt, err = debFunc(new(decimal.Big).Copy(usage), cgrEv, dbted); err != nil {
|
||||
if err == utils.ErrFilterNotPassingNoCaps ||
|
||||
err == utils.ErrNotImplemented {
|
||||
err = nil
|
||||
@@ -244,6 +246,7 @@ func (aS *AccountS) accountDebit(acnt *utils.Account, usage *decimal.Big,
|
||||
used = ecDbt.Abstracts.Big
|
||||
}
|
||||
usage = utils.SubstractBig(usage, used)
|
||||
dbted = utils.SumBig(dbted, used)
|
||||
ec.Merge(ecDbt)
|
||||
ec.Accounts[acnt.ID] = acnt
|
||||
}
|
||||
|
||||
@@ -294,20 +294,20 @@ func TestAccountDebit(t *testing.T) {
|
||||
|
||||
usage := &utils.Decimal{decimal.New(190, 0)}
|
||||
expected := "NOT_FOUND:invalid_filter_format"
|
||||
if _, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true); err == nil || err.Error() != expected {
|
||||
if _, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true, decimal.New(0, 0)); err == nil || err.Error() != expected {
|
||||
t.Errorf("Expected %+v, received %+v", expected, err)
|
||||
}
|
||||
accPrf.Balances["ConcreteBalance1"].Weights[0].FilterIDs = []string{}
|
||||
|
||||
accPrf.Balances["ConcreteBalance1"].Type = "not_a_type"
|
||||
expected = "unsupported balance type: <not_a_type>"
|
||||
if _, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true); err == nil || err.Error() != expected {
|
||||
if _, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true, decimal.New(0, 0)); err == nil || err.Error() != expected {
|
||||
t.Errorf("Expected %+v, received %+v", expected, err)
|
||||
}
|
||||
accPrf.Balances["ConcreteBalance1"].Type = utils.MetaConcrete
|
||||
|
||||
usage = &utils.Decimal{decimal.New(0, 0)}
|
||||
if _, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true); err != nil {
|
||||
if _, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true, decimal.New(0, 0)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
usage = &utils.Decimal{decimal.New(190, 0)}
|
||||
@@ -319,13 +319,13 @@ func TestAccountDebit(t *testing.T) {
|
||||
},
|
||||
}
|
||||
expected = "NOT_FOUND:invalid_format_type"
|
||||
if _, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true); err == nil || err.Error() != expected {
|
||||
if _, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true, decimal.New(0, 0)); err == nil || err.Error() != expected {
|
||||
t.Errorf("Expected %+v, received %+v", expected, err)
|
||||
}
|
||||
accPrf.Balances["ConcreteBalance1"].UnitFactors[0].FilterIDs = []string{}
|
||||
|
||||
expectedUsage := &utils.Decimal{decimal.New(150, 0)}
|
||||
if evCh, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true); err != nil {
|
||||
if evCh, err := accnts.accountDebit(accPrf, usage.Big, cgrEvent, true, decimal.New(0, 0)); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(evCh.Concretes.Big, expectedUsage.Big) {
|
||||
t.Errorf("Expected %+v, received %+v", utils.ToJSON(expectedUsage.Big), utils.ToJSON(evCh.Concretes.Big))
|
||||
@@ -1329,37 +1329,96 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) {
|
||||
data := engine.NewInternalDB(nil, nil, true)
|
||||
dm := engine.NewDataManager(data, cfg.CacheCfg(), nil)
|
||||
fltrS := engine.NewFilterS(cfg, nil, dm)
|
||||
|
||||
// set the internal AttributeS within connMngr
|
||||
attrSConn := make(chan birpc.ClientConnector, 1)
|
||||
attrSrv, _ := birpc.NewService(engine.NewAttributeService(dm, fltrS, cfg), utils.AttributeSv1, true)
|
||||
attrSrv.UpdateMethodName(func(key string) (newKey string) { return strings.TrimPrefix(key, utils.V1Prfx) }) // update the name of the functions
|
||||
attrSConn <- attrSrv
|
||||
cfg.AccountSCfg().AttributeSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes)}
|
||||
// Set the internal rateS within connMngr
|
||||
rateSConn := make(chan birpc.ClientConnector, 1)
|
||||
srv, _ := birpc.NewService(rates.NewRateS(cfg, fltrS, dm), utils.RateSv1, true)
|
||||
srv.UpdateMethodName(func(key string) (newKey string) { return strings.TrimPrefix(key, "V1") }) // update the name of the functions
|
||||
rateSConn <- srv
|
||||
connMngr := engine.NewConnManager(cfg, map[string]chan birpc.ClientConnector{
|
||||
utils.ConcatenatedKey(utils.MetaInternal, utils.MetaRateS): rateSConn,
|
||||
})
|
||||
rateSrv, _ := birpc.NewService(rates.NewRateS(cfg, fltrS, dm), utils.RateSv1, true)
|
||||
rateSrv.UpdateMethodName(func(key string) (newKey string) { return strings.TrimPrefix(key, utils.V1Prfx) }) // update the name of the functions
|
||||
rateSConn <- rateSrv
|
||||
|
||||
cfg.AccountSCfg().RateSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaRateS)}
|
||||
|
||||
rtPfl := &utils.RateProfile{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "RP_1",
|
||||
Rates: map[string]*utils.Rate{
|
||||
"RT_1": {
|
||||
ID: "RT_1",
|
||||
IntervalRates: []*utils.IntervalRate{
|
||||
{
|
||||
IntervalStart: utils.NewDecimal(0, 0),
|
||||
RecurrentFee: utils.NewDecimal(1, 2), // 0.01 per second
|
||||
Unit: utils.NewDecimal(int64(time.Second), 0),
|
||||
Increment: utils.NewDecimal(int64(time.Second), 0),
|
||||
},
|
||||
},
|
||||
connMngr := engine.NewConnManager(cfg, map[string]chan birpc.ClientConnector{
|
||||
utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes): attrSConn,
|
||||
utils.ConcatenatedKey(utils.MetaInternal, utils.MetaRateS): rateSConn,
|
||||
})
|
||||
|
||||
// provision the data
|
||||
atrPrfl := &engine.AttributeProfile{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "ATTR_ATTACH_RATES_PROFILE_RP_2",
|
||||
Contexts: []string{utils.MetaAny},
|
||||
Attributes: []*engine.Attribute{
|
||||
{
|
||||
Path: "*opts.RateSProfile",
|
||||
Type: utils.MetaConstant,
|
||||
Value: config.NewRSRParsersMustCompile("RP_2", utils.InfieldSep),
|
||||
},
|
||||
},
|
||||
Blocker: false,
|
||||
}
|
||||
if err := dm.SetRateProfile(context.Background(), rtPfl, true); err != nil {
|
||||
if err := dm.SetAttributeProfile(context.Background(), atrPrfl, true); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
rtPflls := []*utils.RateProfile{
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "RP_1",
|
||||
Rates: map[string]*utils.Rate{
|
||||
"RT_1": {
|
||||
ID: "RT_1", // lower preference, matching always
|
||||
IntervalRates: []*utils.IntervalRate{
|
||||
{
|
||||
IntervalStart: utils.NewDecimal(0, 0),
|
||||
RecurrentFee: utils.NewDecimal(1, 2), // 0.01 per second
|
||||
Unit: utils.NewDecimal(int64(time.Second), 0),
|
||||
Increment: utils.NewDecimal(int64(time.Second), 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "RP_2", // higher preference, only matching with filter
|
||||
FilterIDs: []string{"*string:~*opts.RateSProfile:RP_2"},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
Rates: map[string]*utils.Rate{
|
||||
"RT_2": {
|
||||
ID: "RT_2",
|
||||
IntervalRates: []*utils.IntervalRate{
|
||||
{
|
||||
IntervalStart: utils.NewDecimal(0, 0),
|
||||
RecurrentFee: utils.NewDecimal(2, 2), // 0.02 per second
|
||||
Unit: utils.NewDecimal(int64(time.Second), 0),
|
||||
Increment: utils.NewDecimal(int64(time.Second), 0),
|
||||
},
|
||||
{
|
||||
IntervalStart: utils.NewDecimal(int64(time.Minute), 0),
|
||||
FixedFee: utils.NewDecimal(1, 1), // 0.1 for the rest of call
|
||||
Unit: utils.NewDecimal(int64(time.Second), 0),
|
||||
Increment: utils.NewDecimal(int64(time.Second), 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
for _, rtPfl := range rtPflls {
|
||||
if err := dm.SetRateProfile(context.Background(), rtPfl, true); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
accnts := NewAccountS(cfg, fltrS, connMngr, dm)
|
||||
|
||||
ab1ID := "AB1"
|
||||
@@ -1369,8 +1428,9 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) {
|
||||
cb2ID := "CB2"
|
||||
// populate the Account
|
||||
acnt1 := &utils.Account{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "TestV1DebitAbstractsEventCharges1",
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "TestV1DebitAbstractsEventCharges1",
|
||||
FilterIDs: []string{"*string:*~req.Account:AnotherAccount"},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 10,
|
||||
@@ -1458,7 +1518,8 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) {
|
||||
Increment: utils.NewDecimal(int64(time.Second), 0),
|
||||
},
|
||||
},
|
||||
Units: utils.NewDecimal(125, 2), // 1.25
|
||||
AttributeIDs: []string{utils.MetaNone},
|
||||
Units: utils.NewDecimal(125, 2), // 1.25
|
||||
},
|
||||
//5m25s ABSTR, 4.05 CONCR
|
||||
},
|
||||
@@ -1501,7 +1562,8 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) {
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
Units: utils.NewDecimal(5, 1), //0.5 covering partially the AB1
|
||||
AttributeIDs: []string{utils.MetaNone},
|
||||
Units: utils.NewDecimal(5, 1), //0.5 covering partially the AB1
|
||||
},
|
||||
// 7m25s ABSTR, 4.55 CONCR
|
||||
cb2ID: &utils.Balance{ // absorb all costs, standard rating used when primary debiting
|
||||
@@ -1515,14 +1577,13 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) {
|
||||
Increment: utils.NewDecimal(int64(time.Second), 0),
|
||||
},
|
||||
},
|
||||
Units: utils.NewDecimal(35, 2), //0.3 + 0.05 covering 5s it's own with RateS
|
||||
AttributeIDs: []string{"ATTR_ATTACH_RATES_PROFILE_RP_2"},
|
||||
Units: utils.NewDecimal(35, 2), //0.3 + 0.05 covering 5s it's own with RateS
|
||||
// ToDo: change rating with a lower preference here, should have multiple groups also // RateProfileIDs: ["TWOCENTS"]
|
||||
},
|
||||
// 7m25s ABSTR, 4.85 CONCR to cover the AB1
|
||||
|
||||
// 7m30 ABSTR, 4.9 CONCR with 5s covered by CB2 with RateS
|
||||
|
||||
// 7m35s ABSTR, 4,95 CONCR with negative 0.5 on CB2
|
||||
// 7m26 ABSTR, 4.95 CONCR with remaining flat covered by CB2 with RateS, RP_2, -0.05 on CB2
|
||||
|
||||
},
|
||||
}
|
||||
@@ -1530,7 +1591,7 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
eEvChgs := utils.ExtEventCharges{
|
||||
eEvChgs := &utils.ExtEventCharges{
|
||||
Abstracts: utils.Float64Pointer(475000000000),
|
||||
Concretes: utils.Float64Pointer(5.15),
|
||||
Charges: []*utils.ChargeEntry{
|
||||
@@ -1681,56 +1742,63 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) {
|
||||
ID: "TestV1DebitAbstractsEventCharges",
|
||||
Tenant: utils.CGRateSorg,
|
||||
APIOpts: map[string]interface{}{
|
||||
utils.MetaUsage: "7m35s",
|
||||
utils.MetaUsage: "7m26s",
|
||||
//utils.MetaUsage: "2m1s",
|
||||
},
|
||||
},
|
||||
}
|
||||
var rply utils.ExtEventCharges
|
||||
if err := accnts.V1DebitAbstracts(args, &rply); err != nil {
|
||||
var rcvEC utils.ExtEventCharges
|
||||
if err := accnts.V1DebitAbstracts(args, &rcvEC); err != nil {
|
||||
t.Error(err)
|
||||
//} else if eEvChgs.Equals(&rcvEC) {
|
||||
} else if !reflect.DeepEqual(eEvChgs, rcvEC) {
|
||||
t.Errorf("expecting: %s, \nreceived: %s\n", utils.ToIJSON(eEvChgs), utils.ToIJSON(rcvEC))
|
||||
}
|
||||
|
||||
acnt1.Balances[ab1ID].Units = utils.NewDecimal(int64(10*time.Second), 0)
|
||||
acnt1.Balances[cb1ID].Units = utils.NewDecimal(-200, 0)
|
||||
acnt1.Balances[ab2ID].Units = &utils.Decimal{new(decimal.Big).CopySign(decimal.New(0, 0), decimal.New(-1, 0))} // negative 0
|
||||
acnt1.Balances[cb2ID].Units = utils.NewDecimal(0, 0)
|
||||
if rcv, err := dm.GetAccount(acnt1.Tenant, acnt1.ID); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rcv, acnt1) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(acnt1), utils.ToJSON(rcv))
|
||||
}
|
||||
/*
|
||||
acnt1.Balances[ab1ID].Units = utils.NewDecimal(int64(10*time.Second), 0)
|
||||
acnt1.Balances[cb1ID].Units = utils.NewDecimal(-200, 0)
|
||||
acnt1.Balances[ab2ID].Units = &utils.Decimal{new(decimal.Big).CopySign(decimal.New(0, 0), decimal.New(-1, 0))} // negative 0
|
||||
acnt1.Balances[cb2ID].Units = utils.NewDecimal(0, 0)
|
||||
if rcv, err := dm.GetAccount(acnt1.Tenant, acnt1.ID); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rcv, acnt1) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(acnt1), utils.ToJSON(rcv))
|
||||
}
|
||||
|
||||
acnt2.Balances[ab1ID].Units = utils.NewDecimal(int64(10*time.Second), 0)
|
||||
acnt2.Balances[cb1ID].Units = utils.NewDecimal(-1, 1)
|
||||
if rcv, err := dm.GetAccount(acnt2.Tenant, acnt2.ID); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rcv, acnt2) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(acnt2), utils.ToJSON(rcv))
|
||||
}
|
||||
|
||||
extAcnt1, err := acnt1.AsExtAccount()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
extAcnt2, err := acnt2.AsExtAccount()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
acnt2.Balances[ab1ID].Units = utils.NewDecimal(int64(10*time.Second), 0)
|
||||
acnt2.Balances[cb1ID].Units = utils.NewDecimal(-1, 1)
|
||||
if rcv, err := dm.GetAccount(acnt2.Tenant, acnt2.ID); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(rcv, acnt2) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(acnt2), utils.ToJSON(rcv))
|
||||
}
|
||||
|
||||
//as the names of accounting, charges, UF are GENUUIDs generator, we will change their names for comparing
|
||||
eEvChgs.Accounts = map[string]*utils.ExtAccount{
|
||||
"TestV1DebitAbstractsEventCharges1": extAcnt1,
|
||||
"TestV1DebitAbstractsEventCharges2": extAcnt2,
|
||||
}
|
||||
eEvChgs.Charges = rply.Charges
|
||||
eEvChgs.Accounting = rply.Accounting
|
||||
eEvChgs.UnitFactors = rply.UnitFactors
|
||||
eEvChgs.Accounts = rply.Accounts
|
||||
eEvChgs.Rating = rply.Rating
|
||||
if !reflect.DeepEqual(eEvChgs, rply) {
|
||||
t.Errorf("Expected %+v, received %+v", utils.ToJSON(eEvChgs), utils.ToJSON(rply))
|
||||
}
|
||||
|
||||
extAcnt1, err := acnt1.AsExtAccount()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
extAcnt2, err := acnt2.AsExtAccount()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//as the names of accounting, charges, UF are GENUUIDs generator, we will change their names for comparing
|
||||
eEvChgs.Accounts = map[string]*utils.ExtAccount{
|
||||
"TestV1DebitAbstractsEventCharges1": extAcnt1,
|
||||
"TestV1DebitAbstractsEventCharges2": extAcnt2,
|
||||
}
|
||||
eEvChgs.Charges = rply.Charges
|
||||
eEvChgs.Accounting = rply.Accounting
|
||||
eEvChgs.UnitFactors = rply.UnitFactors
|
||||
eEvChgs.Accounts = rply.Accounts
|
||||
eEvChgs.Rating = rply.Rating
|
||||
if !reflect.DeepEqual(eEvChgs, rply) {
|
||||
t.Errorf("Expected %+v, received %+v", utils.ToJSON(eEvChgs), utils.ToJSON(rply))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -68,7 +68,7 @@ func (cB *concreteBalance) id() string {
|
||||
|
||||
// debitAbstracts implements the balanceOperator interface
|
||||
func (cB *concreteBalance) debitAbstracts(aUnits *decimal.Big,
|
||||
cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) {
|
||||
cgrEv *utils.CGREvent, dbted *decimal.Big) (ec *utils.EventCharges, err error) {
|
||||
evNm := cgrEv.AsDataProvider()
|
||||
// pass the general balance filters
|
||||
var pass bool
|
||||
@@ -90,7 +90,7 @@ func (cB *concreteBalance) debitAbstracts(aUnits *decimal.Big,
|
||||
cB.connMgr, cgrEv,
|
||||
cB.attrSConns, cB.blnCfg.AttributeIDs,
|
||||
cB.rateSConns, cB.blnCfg.RateProfileIDs,
|
||||
costIcrm); err != nil {
|
||||
costIcrm, dbted); err != nil {
|
||||
return
|
||||
}
|
||||
ec = utils.NewEventCharges()
|
||||
@@ -148,7 +148,7 @@ func (cB *concreteBalance) debitAbstracts(aUnits *decimal.Big,
|
||||
|
||||
// debitConcretes implements the balanceOperator interface
|
||||
func (cB *concreteBalance) debitConcretes(cUnits *decimal.Big,
|
||||
cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) {
|
||||
cgrEv *utils.CGREvent, debited *decimal.Big) (ec *utils.EventCharges, err error) {
|
||||
evNm := cgrEv.AsDataProvider()
|
||||
// pass the general balance filters
|
||||
var pass bool
|
||||
|
||||
@@ -18,19 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/cgrates/birpc"
|
||||
"github.com/cgrates/birpc/context"
|
||||
"github.com/cgrates/cgrates/config"
|
||||
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"github.com/ericlagergren/decimal"
|
||||
)
|
||||
|
||||
/*
|
||||
func TestCBDebitUnits(t *testing.T) {
|
||||
// with limit and unit factor
|
||||
cb := &concreteBalance{
|
||||
@@ -831,3 +819,4 @@ func TestCBSDebitAbstractsCoverProcessAttributes2(t *testing.T) { // coverage pu
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -78,8 +78,8 @@ func newBalanceOperator(acntID string, blncCfg *utils.Balance, cncrtBlncs []*con
|
||||
// balanceOperator is the implementation of a balance type
|
||||
type balanceOperator interface {
|
||||
id() string // balance id
|
||||
debitAbstracts(usage *decimal.Big, cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error)
|
||||
debitConcretes(usage *decimal.Big, cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error)
|
||||
debitAbstracts(usage *decimal.Big, cgrEv *utils.CGREvent, dbted *decimal.Big) (ec *utils.EventCharges, err error)
|
||||
debitConcretes(usage *decimal.Big, cgrEv *utils.CGREvent, dbted *decimal.Big) (ec *utils.EventCharges, err error)
|
||||
}
|
||||
|
||||
// roundUnitsWithIncrements rounds the usage based on increments
|
||||
@@ -197,7 +197,7 @@ func debitConcreteUnits(cUnits *decimal.Big,
|
||||
clnedUnts := cloneUnitsFromConcretes(cncrtBlncs)
|
||||
for _, cB := range cncrtBlncs {
|
||||
var ecCncrt *utils.EventCharges
|
||||
if ecCncrt, err = cB.debitConcretes(new(decimal.Big).Copy(cUnits), cgrEv); err != nil {
|
||||
if ecCncrt, err = cB.debitConcretes(new(decimal.Big).Copy(cUnits), cgrEv, nil); err != nil {
|
||||
restoreUnitsFromClones(cncrtBlncs, clnedUnts)
|
||||
return nil, err
|
||||
}
|
||||
@@ -223,12 +223,13 @@ func maxDebitAbstractsFromConcretes(aUnits *decimal.Big,
|
||||
acntID string, cncrtBlncs []*concreteBalance,
|
||||
connMgr *engine.ConnManager, cgrEv *utils.CGREvent,
|
||||
attrSConns, attributeIDs, rateSConns, rpIDs []string,
|
||||
costIcrm *utils.CostIncrement) (ec *utils.EventCharges, err error) {
|
||||
costIcrm *utils.CostIncrement, dbtedAUnts *decimal.Big) (ec *utils.EventCharges, err error) {
|
||||
// Init EventCharges
|
||||
calculateCost := costIcrm.RecurrentFee == nil && costIcrm.FixedFee == nil
|
||||
//var attrIDs []string // will be populated if attributes are processed successfully
|
||||
// process AttributeS if needed
|
||||
if calculateCost && len(attributeIDs) != 0 { // cost unknown, apply AttributeS to query from RateS
|
||||
if calculateCost &&
|
||||
len(attributeIDs) != 0 && attributeIDs[0] != utils.MetaNone { // cost unknown, apply AttributeS to query from RateS
|
||||
var rplyAttrS *engine.AttrSProcessEventReply
|
||||
if rplyAttrS, err = processAttributeS(connMgr, cgrEv, attrSConns,
|
||||
attributeIDs); err != nil {
|
||||
@@ -256,11 +257,15 @@ func maxDebitAbstractsFromConcretes(aUnits *decimal.Big,
|
||||
}
|
||||
if calculateCost {
|
||||
var rplyCost *utils.RateProfileCost
|
||||
cgrEv.APIOpts[utils.OptsRatesIntervalStart] = dbtedAUnts
|
||||
cgrEv.APIOpts[utils.OptsRatesUsage] = aUnits
|
||||
if rplyCost, err = rateSCostForEvent(connMgr, cgrEv, rateSConns, rpIDs); err != nil {
|
||||
err = utils.NewErrRateS(err)
|
||||
return
|
||||
}
|
||||
// cleanup the opts
|
||||
delete(cgrEv.APIOpts, utils.OptsRatesIntervalStart)
|
||||
delete(cgrEv.APIOpts, utils.OptsRatesUsage)
|
||||
costIcrm = costIcrm.Clone() // so we don't modify the original
|
||||
costIcrm.FixedFee = utils.NewDecimalFromFloat64(rplyCost.Cost)
|
||||
}
|
||||
|
||||
@@ -475,7 +475,7 @@ func TestMaxDebitUsageFromConcretes(t *testing.T) {
|
||||
nil, nil, nil, nil, &utils.CostIncrement{
|
||||
Increment: utils.NewDecimal(1, 0),
|
||||
RecurrentFee: utils.NewDecimal(1, 0),
|
||||
}); err != nil {
|
||||
}, decimal.New(0, 0)); err != nil {
|
||||
t.Error(err)
|
||||
} else if cb1.blnCfg.Units.Cmp(decimal.New(0, 0)) != 0 {
|
||||
t.Errorf("balance remaining: %s", cb1.blnCfg.Units)
|
||||
@@ -493,7 +493,7 @@ func TestMaxDebitUsageFromConcretes(t *testing.T) {
|
||||
nil, nil, nil, nil, &utils.CostIncrement{
|
||||
Increment: utils.NewDecimal(1, 0),
|
||||
RecurrentFee: utils.NewDecimal(1, 0),
|
||||
}); err == nil || err != utils.ErrMaxIncrementsExceeded {
|
||||
}, decimal.New(0, 0)); err == nil || err != utils.ErrMaxIncrementsExceeded {
|
||||
t.Error(err)
|
||||
} else if cb1.blnCfg.Units.Cmp(decimal.New(500, 0)) != 0 {
|
||||
t.Errorf("balance remaining: %s", cb1.blnCfg.Units)
|
||||
@@ -793,7 +793,7 @@ func TestMaxDebitAbstractFromConcretesInsufficientCredit(t *testing.T) {
|
||||
nil, nil, nil, nil, &utils.CostIncrement{
|
||||
Increment: utils.NewDecimal(2, 0),
|
||||
RecurrentFee: utils.NewDecimal(1, 0),
|
||||
}); err == nil || err.Error() != expectedErr {
|
||||
}, decimal.New(0, 0)); err == nil || err.Error() != expectedErr {
|
||||
t.Errorf("Expected %+v, received %+v", expectedErr, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -448,9 +448,6 @@ func (alS *AttributeService) V1ProcessEvent(ctx *context.Context, args *AttrArgs
|
||||
if args.CGREvent == nil {
|
||||
return utils.NewErrMandatoryIeMissing(utils.CGREventString)
|
||||
}
|
||||
if args.Event == nil {
|
||||
return utils.NewErrMandatoryIeMissing(utils.Event)
|
||||
}
|
||||
tnt := args.Tenant
|
||||
if tnt == utils.EmptyString {
|
||||
tnt = alS.cgrcfg.GeneralCfg().DefaultTenant
|
||||
@@ -462,13 +459,18 @@ func (alS *AttributeService) V1ProcessEvent(ctx *context.Context, args *AttrArgs
|
||||
}
|
||||
args.CGREvent = args.CGREvent.Clone()
|
||||
eNV := utils.MapStorage{
|
||||
utils.MetaReq: args.CGREvent.Event,
|
||||
utils.MetaOpts: args.APIOpts,
|
||||
utils.MetaVars: utils.MapStorage{
|
||||
utils.ProcessRuns: 0,
|
||||
},
|
||||
utils.MetaTenant: tnt,
|
||||
}
|
||||
if args.APIOpts != nil {
|
||||
eNV[utils.MetaOpts] = args.APIOpts
|
||||
}
|
||||
if args.CGREvent.Event != nil {
|
||||
eNV[utils.MetaReq] = args.CGREvent.Event
|
||||
}
|
||||
|
||||
var lastID string
|
||||
matchedIDs := make([]string, 0, processRuns)
|
||||
alteredFields := make(utils.StringSet)
|
||||
|
||||
@@ -222,13 +222,6 @@ func computeRateSIntervals(rts []*orderedRate, intervalStart, usage *decimal.Big
|
||||
if !isLastIRt && rt.IntervalRates[j+1].IntervalStart.Cmp(iRtUsageSIdx) <= 0 {
|
||||
continue // the next interval changes the rating
|
||||
}
|
||||
if isLastIRt {
|
||||
iRtUsageEIdx = rtUsageEIdx
|
||||
} else if rt.IntervalRates[j+1].IntervalStart.Cmp(rtUsageEIdx) > 0 {
|
||||
iRtUsageEIdx = rtUsageEIdx
|
||||
} else {
|
||||
iRtUsageEIdx = rt.IntervalRates[j+1].IntervalStart.Big
|
||||
}
|
||||
if iRt.Increment.Cmp(decimal.New(0, 0)) == 0 {
|
||||
return nil, fmt.Errorf("zero increment to be charged within rate: <%s>", rt.UID())
|
||||
}
|
||||
@@ -241,6 +234,17 @@ func computeRateSIntervals(rts []*orderedRate, intervalStart, usage *decimal.Big
|
||||
Usage: utils.NewDecimal(utils.InvalidUsage, 0),
|
||||
})
|
||||
}
|
||||
if rt.IntervalRates[j].RecurrentFee == nil { // RecurrentFee not defined, go to the next increment
|
||||
continue
|
||||
}
|
||||
if isLastIRt {
|
||||
iRtUsageEIdx = rtUsageEIdx
|
||||
} else if rt.IntervalRates[j+1].IntervalStart.Cmp(rtUsageEIdx) > 0 {
|
||||
iRtUsageEIdx = rtUsageEIdx
|
||||
} else {
|
||||
iRtUsageEIdx = rt.IntervalRates[j+1].IntervalStart.Big
|
||||
}
|
||||
|
||||
iRtUsage := utils.SubstractBig(iRtUsageEIdx, iRtUsageSIdx)
|
||||
intUsage := iRtUsage
|
||||
intIncrm := iRt.Increment.Big
|
||||
|
||||
@@ -191,8 +191,11 @@ func (rS *RateS) rateProfileCostForEvent(ctx *context.Context, rtPfl *utils.Rate
|
||||
return nil, fmt.Errorf("<%s> cannot convert <%+v> max cost to Float64", utils.RateS, rtPfl.MaxCost)
|
||||
}
|
||||
}
|
||||
|
||||
if rpCost.RateSIntervals, err = computeRateSIntervals(ordRts, decimal.New(0, 0), usage); err != nil {
|
||||
var ivalStart *decimal.Big
|
||||
if ivalStart, err = args.IntervalStart(); err != nil {
|
||||
return
|
||||
}
|
||||
if rpCost.RateSIntervals, err = computeRateSIntervals(ordRts, ivalStart, usage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// in case we have error it is returned in the function from above
|
||||
|
||||
@@ -974,6 +974,14 @@ func (args *ArgsCostForEvent) Usage() (usage *decimal.Big, err error) {
|
||||
return decimal.New(int64(time.Minute), 0), nil
|
||||
}
|
||||
|
||||
// IntervalStart returns the inerval start out of APIOpts received for the event
|
||||
func (args *ArgsCostForEvent) IntervalStart() (ivlStart *decimal.Big, err error) {
|
||||
if iface, has := args.APIOpts[OptsRatesIntervalStart]; has {
|
||||
return IfaceAsBig(iface)
|
||||
}
|
||||
return decimal.New(0, 0), nil
|
||||
}
|
||||
|
||||
type TPActionProfile struct {
|
||||
TPid string
|
||||
Tenant string
|
||||
|
||||
@@ -879,6 +879,7 @@ const (
|
||||
MetaCostIncrement = "*costIncrement"
|
||||
Length = "Length"
|
||||
MIN_PREFIX_MATCH = 1
|
||||
V1Prfx = "V1"
|
||||
)
|
||||
|
||||
// Migrator Action
|
||||
@@ -2215,6 +2216,7 @@ const (
|
||||
OptsRoutesOffset = "*routes_offset"
|
||||
OptsRatesStartTime = "*ratesStartTime"
|
||||
OptsRatesUsage = "*ratesUsage"
|
||||
OptsRatesIntervalStart = "*ratesIntervalStart"
|
||||
OptsSessionsTTL = "*sessionsTTL"
|
||||
OptsSessionsTTLMaxDelay = "*sessionsTTLMaxDelay"
|
||||
OptsSessionsTTLLastUsed = "*sessionsTTLLastUsed"
|
||||
|
||||
@@ -1966,3 +1966,41 @@ func TestAsExtRateSIntervalErrorsCheck(t *testing.T) {
|
||||
}
|
||||
rI.Increments[0].Rate.IntervalRates[0].Increment = NewDecimal(0, 0)
|
||||
}
|
||||
|
||||
func TestCostForIntervalsWithPartialIntervals(t *testing.T) {
|
||||
rtIvls := []*RateSInterval{
|
||||
{
|
||||
IntervalStart: NewDecimal(int64(2*time.Minute), 0),
|
||||
CompressFactor: 1,
|
||||
Increments: []*RateSIncrement{
|
||||
{
|
||||
IncrementStart: NewDecimal(int64(2*time.Minute), 0),
|
||||
Rate: &Rate{
|
||||
ID: "RT_2",
|
||||
IntervalRates: []*IntervalRate{
|
||||
{
|
||||
IntervalStart: NewDecimal(0, 0),
|
||||
RecurrentFee: NewDecimal(2, 2),
|
||||
Unit: NewDecimal(int64(time.Second), 0),
|
||||
Increment: NewDecimal(int64(time.Second), 0),
|
||||
},
|
||||
{
|
||||
IntervalStart: NewDecimal(int64(time.Minute), 0),
|
||||
FixedFee: NewDecimal(2, 2),
|
||||
Unit: NewDecimal(int64(time.Second), 0),
|
||||
Increment: NewDecimal(int64(time.Second), 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
IntervalRateIndex: 1,
|
||||
CompressFactor: 1,
|
||||
Usage: NewDecimal(-1, 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if cost := CostForIntervals(rtIvls); cost.Cmp(decimal.New(2, 2)) != 0 {
|
||||
t.Errorf("received cost: %s", cost)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user